Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e8b6b96
feat: add agenda page and integrate into navigation and footer
yokwejuste Jun 13, 2026
29d3078
feat: add speaker detail page and integrate speaker data into the agenda
yokwejuste Jun 13, 2026
db7db73
feat: enhance speaker detail layout and improve styling for better re…
yokwejuste Jun 13, 2026
5b86d1d
feat: refine speaker abstracts and update agenda subtitles for clarity
yokwejuste Jun 13, 2026
2bf1c6c
feat: implement agenda day toggle and enhance speaker detail layout w…
yokwejuste Jun 13, 2026
dcaa328
Merge remote-tracking branch 'origin/main' into feat/agenda
yokwejuste Jun 15, 2026
baf6c5c
feat: enhance agenda layout with new CSS styles and session grouping …
yokwejuste Jun 15, 2026
524a5c6
feat: update speakers section titles and subtitles for improved clarity
yokwejuste Jun 15, 2026
e4b9fc8
feat: populate agenda with full PyCon Cameroon 2026 schedule
yokwejuste Jun 15, 2026
e927f79
feat: add 33 confirmed speakers with photos to speakers page
yokwejuste Jun 15, 2026
6070fb1
feat: reorder speakers list with featured speakers first
yokwejuste Jun 15, 2026
87e8d4e
feat: add .gstack directory to .gitignore
yokwejuste Jun 15, 2026
4abee72
feat: improve agenda readability and link speaker names
yokwejuste Jun 15, 2026
ba8178a
feat: summarize speaker bios and remove Petr Andreev from agenda
yokwejuste Jun 15, 2026
5608abe
feat: remove Petr Andreev's photo from speakers directory
yokwejuste Jun 15, 2026
790cf5c
feat: sort speakers alphabetically and mark Sema's session as workshop
yokwejuste Jun 15, 2026
80c828c
feat: add Dr. Dorothée MAA mental-health plenary workshop
yokwejuste Jun 15, 2026
02bf0c2
fix: stack agenda session metadata for consistent alignment
yokwejuste Jun 15, 2026
4c547ee
fix: improve break-row text contrast in agenda for both themes
yokwejuste Jun 15, 2026
4d5d2c6
feat: remove Petr Andreev from speakers list
yokwejuste Jun 15, 2026
d576802
feat: make Sema's session a plenary mixed-audience workshop
yokwejuste Jun 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,4 @@ dist

# Mac
.DS_Store
.gstack/
Binary file added public/speakers/Adonis_Simo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Adrien_Sani.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Ariane_Djeupang_J.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Ayuk_Princelen_Tanyi.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Caleb_Jephuneh.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Claude_Ndanda.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Dorothee_Maa.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Emambou_Ulrich.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Fabiol_Dikongue.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Harmony_Elendu.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Hypolit_Zeuchieu.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Jean_Marc_Wogue.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Jerry_Davis_Ndjana_Mengue.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Johnpaul_Hampo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Kafui_Alordo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Kaizy_Anne_Kum.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Leslye_Nkwa.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Linuce_Demanou.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Lobga_Julius.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Marcela_Djoukouo_Talotsing.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Marielle_Daha.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Muluh_Azinwi_Success_Ndahili.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Mveng_Mboda_Pascal_Franck.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Mvenyi_Donald.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/speakers/Ndongmo_Christian.webp
Binary file added public/speakers/Ngongang_Djanze_Nel_Aldric.webp
Binary file added public/speakers/Ntui_Raoul.webp
Binary file added public/speakers/Parkson_Tano_Daniel.webp
Binary file added public/speakers/Patrick_Nounga.webp
Binary file added public/speakers/Sema_Kumbela_Fombutu.webp
Binary file added public/speakers/Tayo_Tate_Desmond_Corentin.webp
Binary file added public/speakers/Vanessa_Manessong.webp
Binary file added public/speakers/Yannik_Kadjie.webp
Binary file added public/speakers/Yunwen_Eric.webp
4 changes: 4 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const CodeOfConduct = lazy(() => import('./pages/CodeOfConduct'));
const UbuCon = lazy(() => import('./pages/UbuCon'));
const FinancialAid = lazy(() => import('./pages/FinancialAid'));
const TouristSites = lazy(() => import('./pages/TouristSites'));
const Agenda = lazy(() => import('./pages/Agenda'));
const SpeakerDetail = lazy(() => import('./pages/SpeakerDetail'));

class ErrorBoundary extends Component {
constructor(props) {
Expand Down Expand Up @@ -86,6 +88,8 @@ function App() {
<Route path="ubucon" element={<LazyPage><UbuCon /></LazyPage>} />
<Route path="financial-aid" element={<LazyPage><FinancialAid /></LazyPage>} />
<Route path="tourist-sites" element={<LazyPage><TouristSites /></LazyPage>} />
<Route path="agenda" element={<LazyPage><Agenda /></LazyPage>} />
<Route path="speakers/:speakerId" element={<LazyPage><SpeakerDetail /></LazyPage>} />
<Route path="*" element={<LegacyRedirect />} />
</Route>

Expand Down
1 change: 1 addition & 0 deletions src/components/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const Footer = () => {
<div>
<h4 className="footer-title">{t('footer.program')}</h4>
<div className="footer-links">
<Link to={l('/agenda')}>{t('footer.agenda')}</Link>
<Link to={l('/speakers')}>{t('footer.speakers')}</Link>
<Link to={l('/speakers#guidelines')}>{t('footer.proposalGuidelines')}</Link>
<Link to={l('/venue')}>{t('footer.venue')}</Link>
Expand Down
2 changes: 2 additions & 0 deletions src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const Navbar = () => {

<div className={`nav-links ${isOpen ? 'active' : ''}`} id="navLinks">
<NavLink to={l('/about')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.about')}</NavLink>
<NavLink to={l('/agenda')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.agenda')}</NavLink>
<NavLink to={l('/speakers')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.speakers')}</NavLink>
<NavLink to={l('/sponsor')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.sponsor')}</NavLink>
<NavLink to={l('/attend')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.attend')}</NavLink>
Expand Down Expand Up @@ -90,6 +91,7 @@ const Navbar = () => {
</div>
<div className="nav-drawer-links">
<NavLink to={l('/about')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.about')}</NavLink>
<NavLink to={l('/agenda')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.agenda')}</NavLink>
<NavLink to={l('/speakers')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.speakers')}</NavLink>
<NavLink to={l('/sponsor')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.sponsor')}</NavLink>
<NavLink to={l('/attend')} className={({ isActive }) => isActive ? "active" : ""}>{t('nav.attend')}</NavLink>
Expand Down
2 changes: 1 addition & 1 deletion src/components/TrackSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const TrackSection = ({ id, bgClass, logo, logoAlt, logoBg, title, titleGradient
{description.map((p, i) => <p key={i}>{p}</p>)}
<div className="mt-md flex gap-sm flex-wrap">
<Link to={l('/speakers')} className="btn btn-primary" style={color !== 'var(--color-orange)' ? { background: color } : {}}>
{t('data.tracks.submitTalk', { label: ctaLabel })}
{t('data.tracks.viewSpeakers', { label: ctaLabel })}
</Link>
<Link to={l('/attend')} className="btn btn-secondary">
{t('data.tracks.attendTrack', { label: ctaLabel })}
Expand Down
90 changes: 90 additions & 0 deletions src/data/agenda.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
export const DAYS = [
{ key: 'day1', date: 'September 17, 2026', label: 'Day 1', sublabel: 'PyCon' },
{ key: 'day2', date: 'September 18, 2026', label: 'Day 2', sublabel: 'UbuCon' },
{ key: 'day3', date: 'September 19, 2026', label: 'Day 3', sublabel: 'PyCon' },
];

export const agenda = {
day1: [
{ time: '08:30', title: 'Registration & Welcome Coffee', type: 'break', room: '' },
{ time: '09:00', title: 'Opening + Keynote 1', type: 'keynote', room: 'Plenary — all attendees', lang: 'enfr' },
{ time: '10:00', title: 'Coffee break', type: 'break', room: '' },
{ time: '10:20', title: 'Prévention et gestion du stress chronique au travail', type: 'workshop', room: 'Plenary — all attendees', speaker: 'Dr. Dorothée MAA', lang: 'fr' },
{ time: '11:05', title: 'From WSGI to ASGI: Modernizing Django', type: 'talk', room: 'Track A', track: 'Web / Security', speaker: 'Parkson Tano Daniel', lang: 'fr' },
{ time: '11:05', title: 'Turning Python Projects into AI-Powered Tools', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Caleb Jephuneh', lang: 'enfr' },
{ time: '11:05', title: 'Architecture à budget zéro : cloud design decisions', type: 'talk', room: 'Track C', track: 'DevOps / Cloud', speaker: 'Jerry Davis Ndjana Mengue', lang: 'fr' },
{ time: '11:50', title: 'Django + LLMs: Real AI Features Without Overengineering', type: 'talk', room: 'Track A', track: 'Web / Security', speaker: 'Ntui Raoul', lang: 'en' },
{ time: '11:50', title: 'Beyond Keywords: Vector Search Engine From Scratch', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Lobga Julius', lang: 'en' },
{ time: '11:50', title: 'Stop Clicking in AWS: Django + Terraform', type: 'talk', room: 'Track C', track: 'DevOps / Cloud', speaker: 'Ndongmo Christian', lang: 'fren' },
{ time: '12:35', title: 'Lunch break', type: 'break', room: '' },
{ time: '13:45', title: 'HTM(X) at the Intersection of the Web', type: 'talk', room: 'Track A', track: 'Web / Security', speaker: 'Kafui Alordo', lang: 'en' },
{ time: '13:45', title: 'Du Notebook à la production : FastAPI + Docker', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Tayo Tate Desmond', lang: 'fren' },
{ time: '13:45', title: 'K3s : The Kubernetes for gentle apps', type: 'talk', room: 'Track C', track: 'DevOps / Cloud', speaker: 'Kamdem Ulrich', lang: 'fr' },
{ time: '14:30', title: "Hacking with Python: Cameroon's Digital Security", type: 'talk', room: 'Track A', track: 'Web / Security', speaker: 'Mvenyi Donald', lang: 'en' },
{ time: '14:30', title: 'Construire son propre serveur MCP avec Python', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Claude Ndanda', lang: 'fr' },
{ time: '14:30', title: 'Security in Web Dev: DevSecOps Best Practices', type: 'talk', room: 'Track C', track: 'DevOps / Cloud', speaker: 'Fabiol Dikongue', lang: 'fr' },
{ time: '15:15', title: 'Coffee break', type: 'break', room: '' },
{ time: '15:30', title: 'From Belief to Impact: Why Skills Alone Are Not Enough', type: 'talk', room: 'Track A', track: 'Web / Security', speaker: 'Linuce Demanou', lang: 'en' },
{ time: '15:30', title: "Patrick's Tips to Get a Job/Internship as a Developer", type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Patrick Nounga', lang: 'fren' },
{ time: '15:30', title: 'Python, Dart and Flutter: Native Performance with FFI', type: 'talk', room: 'Track C', track: 'DevOps / Cloud', speaker: 'Yunwen Eric', lang: 'en' },
{ time: '16:00', title: 'Community games & social hour — quizzes, networking, ice-breakers', type: 'social', room: '' },
{ time: '17:00', title: 'End of Day 1', type: 'break', room: '' },
],
day2: [
{ time: '08:30', title: 'Registration & Welcome Coffee', type: 'break', room: '' },
{ time: '09:00', title: 'Opening — UbuCon Cameroon', type: 'keynote', room: 'Welcome, Ubuntu ecosystem update', lang: 'enfr' },
{ time: '09:30', title: 'Talk — Open Documentation Academy: Your First Open Source Contribution', type: 'talk', room: 'UbuCon Track', speaker: 'Ariane Djeupang J.', lang: 'fren' },
{ time: '10:15', title: 'Coffee break', type: 'break', room: '' },
{ time: '10:30', title: 'Talk — Built by Us: A Cameroonian\'s Roadmap to Open Source', type: 'talk', room: 'UbuCon Track', speaker: "Muluh Azinwi Success Ndahi'li", lang: 'en' },
{ time: '11:15', title: 'Talk — Open Source Impact: How Python Communities Solve Real Problems', type: 'talk', room: 'UbuCon Track', speaker: 'Johnpaul Hampo', lang: 'en' },
{ time: '12:00', title: "Talk — Your First Pull Request Won't Be Perfect", type: 'talk', room: 'UbuCon Track', speaker: 'Harmony Elendu', lang: 'en' },
{ time: '12:45', title: 'Lunch break', type: 'break', room: '' },
{ time: '13:45', title: 'Workshop — Open Documentation Academy (Part 1)', type: 'workshop', room: 'UbuCon Track', speaker: 'Ariane Djeupang J.', lang: 'fren' },
{ time: '15:15', title: 'Coffee break', type: 'break', room: '' },
{ time: '15:30', title: 'Workshop — Open Documentation Academy (Part 2) + Contributor Badge Ceremony', type: 'workshop', room: 'UbuCon Track', speaker: 'Ariane Djeupang J.', lang: 'fren' },
{ time: '16:30', title: 'UbuCon social & community games — Ubuntu networking', type: 'social', room: '' },
{ time: '17:00', title: 'End of Day 2', type: 'break', room: '' },
],
day3: [
{ time: '08:30', title: 'Welcome Coffee', type: 'break', room: '' },
{ time: '09:00', title: 'Keynote 2', type: 'keynote', room: 'Plenary — all attendees', lang: 'enfr' },
{ time: '10:00', title: 'Coffee break', type: 'break', room: '' },
{ time: '10:20', title: 'Python in the Browser: No Install, No Barrier', type: 'talk', room: 'Track A', track: 'Python / Security', speaker: 'Hypolit Zeuchieu', lang: 'fren' },
{ time: '10:20', title: 'Thinking Like a Data Scientist: IBM Methodology', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Tayo Tate Desmond', lang: 'en' },
{ time: '10:20', title: 'IA et marché tech : construire un profil employable', type: 'talk', room: 'Track C', track: 'Web / Community', speaker: 'Vanessa Manessong', lang: 'fr' },
{ time: '11:05', title: 'Speed Up Your Python Program With Concurrency', type: 'talk', room: 'Track A', track: 'Python / Security', speaker: 'Yannik Kadjie', lang: 'fr' },
{ time: '11:05', title: 'Zero-Touch Dashboards: Django + Metabase per-user governance', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Ariane Djeupang J.', lang: 'enfr' },
{ time: '11:05', title: 'Architecture microservices avec Django, FastAPI, RabbitMQ', type: 'talk', room: 'Track C', track: 'Web / Community', speaker: 'Adrien Sani', lang: 'fr' },
{ time: '11:50', title: 'Creative Coding with Matplotlib: Turning Data into Art', type: 'talk', room: 'Track A', track: 'Python / Security', speaker: 'Marielle Daha', lang: 'en' },
{ time: '11:50', title: "AI Is Not a Tool: It's a Workflow (Claude, Codex, Figma)", type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Emambou Ulrich', lang: 'en' },
{ time: '11:50', title: 'Stay in Django, Go Reactive (Django Unicorn)', type: 'talk', room: 'Track C', track: 'Web / Community', speaker: 'Adonis Simo', lang: 'fren' },
{ time: '12:35', title: 'Lunch break', type: 'break', room: '' },
{ time: '13:45', title: "Pentest d'une app Django : attaques réelles et corrections", type: 'talk', room: 'Track A', track: 'Python / Security', speaker: 'Mveng Mboda Pascal Franck', lang: 'fr' },
{ time: '13:45', title: 'L\'importance de la cohérence des données : 03 bonnes pratiques', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Leslye Nkwa', lang: 'fr' },
{ time: '13:45', title: 'Authentification 2FA : TOTP avec Django', type: 'talk', room: 'Track C', track: 'Web / Community', speaker: 'Jean Marc Wogue', lang: 'fr' },
{ time: '14:30', title: 'AutoXAI : Diagnostic automobile intelligent avec Python', type: 'talk', room: 'Track A', track: 'Python / Security', speaker: 'Marcela Djoukouo Talotsing', lang: 'fr' },
{ time: '14:30', title: 'AI-Powered Smart Agriculture: Greenhouse & Poultry Systems', type: 'talk', room: 'Track B', track: 'AI / Data Science', speaker: 'Ayuk Princelen Tanyi', lang: 'en' },
{ time: '15:15', title: 'The Silent Bugs in Tech: Debugging Burnout & Cognitive Overload', type: 'workshop', room: 'Plenary — all attendees', speaker: 'Sema Kumbela Fombutu', lang: 'en' },
{ time: '15:30', title: 'Coffee break', type: 'break', room: '' },
{ time: '15:45', title: 'Lightning talks — open floor (5-min slots, all attendees welcome)', type: 'lightning', room: 'Plenary', lang: 'enfr' },
{ time: '16:15', title: 'Closing ceremony + community games — awards, photos, networking', type: 'social', room: '' },
{ time: '17:00', title: 'End of Day 3 — see you next year!', type: 'break', room: '' },
],
};

export const LANG_LABELS = {
en: 'EN',
fr: 'FR',
enfr: 'EN / FR',
fren: 'FR / EN',
both: 'EN / FR',
};

export const TYPE_STYLES = {
keynote: { color: 'var(--color-orange)', label: 'Keynote' },
talk: { color: 'var(--color-green)', label: 'Talk' },
workshop: { color: 'var(--color-blue)', label: 'Workshop' },
lightning: { color: 'var(--color-gold)', label: 'Lightning' },
break: { color: 'var(--color-text-muted)', label: 'Break' },
social: { color: 'var(--color-red)', label: 'Social' },
};
Loading
Loading