diff --git a/.gitignore b/.gitignore index dec9888..116f3f8 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ dist # Mac .DS_Store +.gstack/ diff --git a/public/speakers/Adonis_Simo.webp b/public/speakers/Adonis_Simo.webp new file mode 100644 index 0000000..da342d9 Binary files /dev/null and b/public/speakers/Adonis_Simo.webp differ diff --git a/public/speakers/Adrien_Sani.webp b/public/speakers/Adrien_Sani.webp new file mode 100644 index 0000000..86e4566 Binary files /dev/null and b/public/speakers/Adrien_Sani.webp differ diff --git a/public/speakers/Ariane_Djeupang_J.webp b/public/speakers/Ariane_Djeupang_J.webp new file mode 100644 index 0000000..40c26b5 Binary files /dev/null and b/public/speakers/Ariane_Djeupang_J.webp differ diff --git a/public/speakers/Ayuk_Princelen_Tanyi.webp b/public/speakers/Ayuk_Princelen_Tanyi.webp new file mode 100644 index 0000000..e6f8ebc Binary files /dev/null and b/public/speakers/Ayuk_Princelen_Tanyi.webp differ diff --git a/public/speakers/Caleb_Jephuneh.webp b/public/speakers/Caleb_Jephuneh.webp new file mode 100644 index 0000000..75d448d Binary files /dev/null and b/public/speakers/Caleb_Jephuneh.webp differ diff --git a/public/speakers/Claude_Ndanda.webp b/public/speakers/Claude_Ndanda.webp new file mode 100644 index 0000000..f790090 Binary files /dev/null and b/public/speakers/Claude_Ndanda.webp differ diff --git a/public/speakers/Dorothee_Maa.webp b/public/speakers/Dorothee_Maa.webp new file mode 100644 index 0000000..7837856 Binary files /dev/null and b/public/speakers/Dorothee_Maa.webp differ diff --git a/public/speakers/Emambou_Ulrich.webp b/public/speakers/Emambou_Ulrich.webp new file mode 100644 index 0000000..c6fc15f Binary files /dev/null and b/public/speakers/Emambou_Ulrich.webp differ diff --git a/public/speakers/Fabiol_Dikongue.webp b/public/speakers/Fabiol_Dikongue.webp new file mode 100644 index 0000000..277a8f9 Binary files /dev/null and b/public/speakers/Fabiol_Dikongue.webp differ diff --git a/public/speakers/Harmony_Elendu.webp b/public/speakers/Harmony_Elendu.webp new file mode 100644 index 0000000..14a452a Binary files /dev/null and b/public/speakers/Harmony_Elendu.webp differ diff --git a/public/speakers/Hypolit_Zeuchieu.webp b/public/speakers/Hypolit_Zeuchieu.webp new file mode 100644 index 0000000..9328189 Binary files /dev/null and b/public/speakers/Hypolit_Zeuchieu.webp differ diff --git a/public/speakers/Jean_Marc_Wogue.webp b/public/speakers/Jean_Marc_Wogue.webp new file mode 100644 index 0000000..bc049af Binary files /dev/null and b/public/speakers/Jean_Marc_Wogue.webp differ diff --git a/public/speakers/Jerry_Davis_Ndjana_Mengue.webp b/public/speakers/Jerry_Davis_Ndjana_Mengue.webp new file mode 100644 index 0000000..c5564e8 Binary files /dev/null and b/public/speakers/Jerry_Davis_Ndjana_Mengue.webp differ diff --git a/public/speakers/Johnpaul_Hampo.webp b/public/speakers/Johnpaul_Hampo.webp new file mode 100644 index 0000000..27d4b3f Binary files /dev/null and b/public/speakers/Johnpaul_Hampo.webp differ diff --git a/public/speakers/Kafui_Alordo.webp b/public/speakers/Kafui_Alordo.webp new file mode 100644 index 0000000..ff89a48 Binary files /dev/null and b/public/speakers/Kafui_Alordo.webp differ diff --git a/public/speakers/Kaizy_Anne_Kum.webp b/public/speakers/Kaizy_Anne_Kum.webp new file mode 100644 index 0000000..4f7b511 Binary files /dev/null and b/public/speakers/Kaizy_Anne_Kum.webp differ diff --git a/public/speakers/Kamdem_Yamen_Ulrich_Laress_Ulrich.webp b/public/speakers/Kamdem_Yamen_Ulrich_Laress_Ulrich.webp new file mode 100644 index 0000000..76dc3d4 Binary files /dev/null and b/public/speakers/Kamdem_Yamen_Ulrich_Laress_Ulrich.webp differ diff --git a/public/speakers/Leslye_Nkwa.webp b/public/speakers/Leslye_Nkwa.webp new file mode 100644 index 0000000..4a0f18f Binary files /dev/null and b/public/speakers/Leslye_Nkwa.webp differ diff --git a/public/speakers/Linuce_Demanou.webp b/public/speakers/Linuce_Demanou.webp new file mode 100644 index 0000000..6f4a74c Binary files /dev/null and b/public/speakers/Linuce_Demanou.webp differ diff --git a/public/speakers/Lobga_Julius.webp b/public/speakers/Lobga_Julius.webp new file mode 100644 index 0000000..92591fb Binary files /dev/null and b/public/speakers/Lobga_Julius.webp differ diff --git a/public/speakers/Marcela_Djoukouo_Talotsing.webp b/public/speakers/Marcela_Djoukouo_Talotsing.webp new file mode 100644 index 0000000..9ed0d06 Binary files /dev/null and b/public/speakers/Marcela_Djoukouo_Talotsing.webp differ diff --git a/public/speakers/Marielle_Daha.webp b/public/speakers/Marielle_Daha.webp new file mode 100644 index 0000000..4b4d896 Binary files /dev/null and b/public/speakers/Marielle_Daha.webp differ diff --git a/public/speakers/Muluh_Azinwi_Success_Ndahili.webp b/public/speakers/Muluh_Azinwi_Success_Ndahili.webp new file mode 100644 index 0000000..a0ed187 Binary files /dev/null and b/public/speakers/Muluh_Azinwi_Success_Ndahili.webp differ diff --git a/public/speakers/Mveng_Mboda_Pascal_Franck.webp b/public/speakers/Mveng_Mboda_Pascal_Franck.webp new file mode 100644 index 0000000..d440454 Binary files /dev/null and b/public/speakers/Mveng_Mboda_Pascal_Franck.webp differ diff --git a/public/speakers/Mvenyi_Donald.webp b/public/speakers/Mvenyi_Donald.webp new file mode 100644 index 0000000..4415ac5 Binary files /dev/null and b/public/speakers/Mvenyi_Donald.webp differ diff --git a/public/speakers/Ndongmo_Christian.webp b/public/speakers/Ndongmo_Christian.webp new file mode 100644 index 0000000..15cad6d Binary files /dev/null and b/public/speakers/Ndongmo_Christian.webp differ diff --git a/public/speakers/Ngongang_Djanze_Nel_Aldric.webp b/public/speakers/Ngongang_Djanze_Nel_Aldric.webp new file mode 100644 index 0000000..75ea1f9 Binary files /dev/null and b/public/speakers/Ngongang_Djanze_Nel_Aldric.webp differ diff --git a/public/speakers/Ntui_Raoul.webp b/public/speakers/Ntui_Raoul.webp new file mode 100644 index 0000000..4a4ef32 Binary files /dev/null and b/public/speakers/Ntui_Raoul.webp differ diff --git a/public/speakers/Parkson_Tano_Daniel.webp b/public/speakers/Parkson_Tano_Daniel.webp new file mode 100644 index 0000000..adb8fbf Binary files /dev/null and b/public/speakers/Parkson_Tano_Daniel.webp differ diff --git a/public/speakers/Patrick_Nounga.webp b/public/speakers/Patrick_Nounga.webp new file mode 100644 index 0000000..7c84aed Binary files /dev/null and b/public/speakers/Patrick_Nounga.webp differ diff --git a/public/speakers/Sema_Kumbela_Fombutu.webp b/public/speakers/Sema_Kumbela_Fombutu.webp new file mode 100644 index 0000000..1f21a96 Binary files /dev/null and b/public/speakers/Sema_Kumbela_Fombutu.webp differ diff --git a/public/speakers/Tayo_Tate_Desmond_Corentin.webp b/public/speakers/Tayo_Tate_Desmond_Corentin.webp new file mode 100644 index 0000000..fd28978 Binary files /dev/null and b/public/speakers/Tayo_Tate_Desmond_Corentin.webp differ diff --git a/public/speakers/Vanessa_Manessong.webp b/public/speakers/Vanessa_Manessong.webp new file mode 100644 index 0000000..aaaed4a Binary files /dev/null and b/public/speakers/Vanessa_Manessong.webp differ diff --git a/public/speakers/Yannik_Kadjie.webp b/public/speakers/Yannik_Kadjie.webp new file mode 100644 index 0000000..af06981 Binary files /dev/null and b/public/speakers/Yannik_Kadjie.webp differ diff --git a/public/speakers/Yunwen_Eric.webp b/public/speakers/Yunwen_Eric.webp new file mode 100644 index 0000000..9ba0b6f Binary files /dev/null and b/public/speakers/Yunwen_Eric.webp differ diff --git a/src/App.jsx b/src/App.jsx index dd425a8..e047424 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -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) { @@ -86,6 +88,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 717e9b9..682d466 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -67,6 +67,7 @@ const Footer = () => {

{t('footer.program')}

+ {t('footer.agenda')} {t('footer.speakers')} {t('footer.proposalGuidelines')} {t('footer.venue')} diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 72b7a00..037aa4c 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -53,6 +53,7 @@ const Navbar = () => {
isActive ? "active" : ""}>{t('nav.about')} + isActive ? "active" : ""}>{t('nav.agenda')} isActive ? "active" : ""}>{t('nav.speakers')} isActive ? "active" : ""}>{t('nav.sponsor')} isActive ? "active" : ""}>{t('nav.attend')} diff --git a/src/components/TrackSection.jsx b/src/components/TrackSection.jsx index 3cc9ac8..e43c94b 100644 --- a/src/components/TrackSection.jsx +++ b/src/components/TrackSection.jsx @@ -31,7 +31,7 @@ const TrackSection = ({ id, bgClass, logo, logoAlt, logoBg, title, titleGradient {description.map((p, i) =>

{p}

)}
- {t('data.tracks.submitTalk', { label: ctaLabel })} + {t('data.tracks.viewSpeakers', { label: ctaLabel })} {t('data.tracks.attendTrack', { label: ctaLabel })} diff --git a/src/data/agenda.js b/src/data/agenda.js new file mode 100644 index 0000000..6bbeb52 --- /dev/null +++ b/src/data/agenda.js @@ -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' }, +}; diff --git a/src/data/speakers.js b/src/data/speakers.js new file mode 100644 index 0000000..de4a29d --- /dev/null +++ b/src/data/speakers.js @@ -0,0 +1,560 @@ +export const speakers = [ { + id: 'adonis-simo', + name: 'Adonis Simo', + photo: '/speakers/Adonis_Simo.webp', + country: 'Cameroon', + title: 'Product Builder, Martha IA , 3X AWS Certified', + bio: 'I\'m a cloud and software engineer with 9+ years of experience. I build products at Martha AI as a Product Builder while working as a Senior Full Stack Web Developer (Node.js & Python). Technically proficient in Python, JavaScript, AWS, and modern web frameworks. I\'m passionate about writing, mentoring, and sharing knowledge with tech communities.', + linkedin: 'https://www.linkedin.com/in/adonis-simo/', + website: 'https://marthaia.com', + talk: { + title: 'Stay in Django, Go Reactive', + category: 'Web Development', + day: 'day3', + track: 'Web / Community', + abstract: 'What if you could build reactive, SPA-like web experiences without ever leaving Django?', + }, + }, + { + id: 'adrien-sani', + name: 'Adrien Sani', + photo: '/speakers/Adrien_Sani.webp', + country: 'Cameroon', + title: 'Développeur Fullstack spécialisé en architectures backend modernes, APIs REST et microservices avec Django, FastAPI et React.', + bio: 'Développeur fullstack passionné par les architectures backend performantes et évolutives. Il conçoit des APIs REST avec Django et FastAPI, des systèmes de simulation métier et des plateformes web avec React et TailwindCSS. Il s\'intéresse aux microservices, à RabbitMQ, à la scalabilité et aux bonnes pratiques de code.', + linkedin: 'https://www.linkedin.com/in/adrien-sani-673b7b394/', + website: 'https://portfolio.adrien-dev.me/', + talk: { + title: 'Concevoir une architecture microservices performante avec Django, FastAPI, RabbitMQ et Traefik', + category: 'Web Development', + day: 'day3', + track: 'Web / Community', + abstract: 'Découvrez comment combiner Django et FastAPI pour construire des APIs performantes et évolutives, prêtes pour une architecture microservices.', + }, + }, + { + id: 'ariane-djeupang-j', + name: 'Ariane Djeupang J', + photo: '/speakers/Ariane_Djeupang_J.webp', + country: 'Cameroon', + title: 'ML Engineer', + bio: 'ML Engineer, Project/Program Manager, and Microsoft MVP awardee passionate about community growth and innovation. She champions diversity and inclusion, actively empowering underrepresented groups in tech through mentorship and community-driven change, building supportive environments where everyone can thrive and feel a genuine sense of belonging.', + talk: { + title: 'Open Documentation Academy: Your First Open Source Contribution', + category: 'Ubuntu & Linux', + day: 'day3', + track: 'AI / Data Science', + abstract: 'Documentation is an accessible entry point for open source newcomers. This workshop uses the Open Documentation Academy to guide participants through finding their first issue, applying basic documentation principles, and submitting a contribution to a real project.', + }, + }, + { + id: 'ayuk-princelen-tanyi', + name: 'Ayuk Princelen Tanyi', + photo: '/speakers/Ayuk_Princelen_Tanyi.webp', + country: 'Cameroon', + title: 'Group leader and ml developer', + bio: 'Young tech enthusiast and ML developer based in Douala with interests in software development, emerging technologies, medicine, and intelligent systems. He builds practical solutions in smart agriculture and automation, aiming to contribute to African technological innovation through creativity, research, and problem-solving.', + linkedin: 'https://www.linkedin.com/feed', + website: 'https://topgamingtips.com', + talk: { + title: 'AI-Powered Smart Agriculture: Building Intelligent Greenhouse and Poultry Systems for Africa', + category: 'IoT & Hardware', + day: 'day3', + track: 'AI / Data Science', + abstract: 'Agriculture in Africa faces major challenges including climate instability, disease outbreaks, inefficient resource management, and limited access to affordable smart farming technologies. This session presents the development of an AI-powered smart greenhouse and poultry monitoring system designed to address these problems using IoT, automation, and machine learning.', + }, + }, { + id: 'caleb-jephuneh', + name: 'Caleb Jephuneh', + photo: '/speakers/Caleb_Jephuneh.webp', + country: 'Cameroon', + title: 'Founder BricklabsAI', + bio: 'Tech entrepreneur with two successful exits and 7+ years in software and AI across startups, enterprises, and agencies. A global AI speaker, he empowers young innovators to build boldly and create real-world impact. In 2025 he won the Young Africa CEO of the Year award at the CIO 100 Awards.', + linkedin: 'https://www.linkedin.com/in/caleb-jephunneh-a96aa81b0/', + website: 'https://www.bricklabsai.org/', + talk: { + title: 'Turning Python Projects into AI-Powered Tools', + category: 'Machine Learning & AI', + day: 'day1', + track: 'AI / Data Science', + abstract: 'What if your existing Python scripts could think, adapt, and make decisions? In this session, we explore practical ways to integrate AI into everyday Python projects without needing a PhD in machine learning. We’ll start with familiar tools like data processing scripts and gradually layer in capabilities such as natural language understanding, predictions, and automation. By the end, you’ll have a clear roadmap for upgrading your current projects into smarter, AI-driven systems.', + }, + }, + { + id: 'claude-ndanda', + name: 'Claude Ndanda', + photo: '/speakers/Claude_Ndanda.webp', + country: 'Cameroon', + title: 'Software Engineer', + bio: 'Software engineer et contributeur open source basé à Douala, alias TrixX. Il combine développement web et mobile (Angular, React, Flask) et sécurité informatique pour bâtir des solutions adaptées au contexte africain. Bug Hunter actif sur HackerOne et développeur chez Zen Africa, il défend un numérique "Made in Africa, Built to Last".', + linkedin: 'https://www.linkedin.com/in/claude-ndanda-trixx-6aa606302/', + website: 'https://zen.africa/', + talk: { + title: 'Construire son propre serveur MCP avec Python : donner des mains à l\'IA', + category: 'Machine Learning & AI', + day: 'day1', + track: 'AI / Data Science', + abstract: 'MCP (Model Context Protocol) est le protocole qui permet à un LLM de communiquer avec des outils externes de façon standardisée. Avec FastMCP en Python et Ollama en local, vous pouvez construire un agent IA custom qui raisonne sur vos propres données sans cloud, sans abonnement, sans outils no-code. Ce talk montre comment construire ce serveur from scratch, avec une démo live et des cas d\'usage concrets adaptés au contexte africain.', + }, + }, + { + id: 'dorothee-maa', + name: 'Dr. Dorothée MAA', + photo: '/speakers/Dorothee_Maa.webp', + country: 'Cameroon', + title: 'Médecin Bucco-Dentiste, Master en Management des Organisations Publiques', + bio: 'Médecin Bucco-Dentiste depuis une dizaine d\'années, elle évolue à l\'intersection de la promotion de la santé, du leadership humain et de la transformation sociale. Engagée pour la santé mentale et le bien-être au travail, elle est Présidente-Fondatrice de l\'Association COHPE et du Hub MEHAH, et auteure d\'ouvrages.', + linkedin: 'https://www.linkedin.com/in/doroth%C3%A9emaa/', + talk: { + title: 'Prévention et gestion du stress chronique au travail', + category: 'Social Impact', + day: 'day1', + abstract: 'Cet atelier porte sur la prévention et la gestion du stress chronique au travail, un enjeu majeur ayant des répercussions sur la santé comme sur la performance. Conçu dans une approche interactive et participative, il favorise les échanges, la réflexion collective et le partage d\'expériences concrètes. L\'objectif est de créer un espace dynamique, accessible et humain, propice à la prise de conscience et au développement de stratégies de mieux-être au travail.', + }, + }, + { + id: 'emambou-ulrich', + name: 'Emambou Ulrich', + photo: '/speakers/Emambou_Ulrich.webp', + country: 'Cameroon', + title: 'Chief Product Officer NOVA HEALTHTECH', + bio: 'Cameroonian product strategist, UX/UI designer, and UX Educator at Friends of Figma Yaoundé. As Chief Product Officer at NOVA-HEALTHTECH, he drives health-tech innovation through product strategy, user research, and experience design, and actively supports Cameroon\'s tech ecosystem through workshops, mentorship, and hackathons.', + linkedin: 'https://www.linkedin.com/in/dev-guy-uix/', + talk: { + title: 'AI Is Not a Tool: It\'s a Workflow: Designing with Claude, Codex & Figma', + category: 'Machine Learning & AI', + day: 'day3', + track: 'AI / Data Science', + abstract: 'A practical, honest breakdown of the AI design workflow that actually works — Claude, Codex, Figma MCP, and when to use each.', + }, + }, + { + id: 'fabiol-dikongue', + name: 'Fabiol Dikongue', + photo: '/speakers/Fabiol_Dikongue.webp', + country: 'Cameroon', + title: 'DevOps Engineer', + bio: 'Senior Software & DevOps Engineer with 7+ years designing, deploying, and securing distributed systems and high-availability microservices. Holding a Master\'s in Operational Cybersecurity, he applies DevSecOps from the ground up and specializes in CI/CD, containerization, and Infrastructure as Code. He is CTO of the Klivar solution.', + linkedin: 'https://www.linkedin.com/in/samuel-dikongue-76b719198/', + website: 'https://dikotech.com', + talk: { + title: 'Security in Web Development: Best Practices for Building Safer Applications', + category: 'DevOps & Infrastructure', + day: 'day1', + track: 'DevOps / Cloud', + abstract: 'A practical overview of web security essentials — covering OWASP Top 10, secure coding, API protection, and DevSecOps practices — to help developers build safer applications from the ground up.', + }, + }, + { + id: 'harmony-elendu', + name: 'Harmony Elendu', + photo: '/speakers/Harmony_Elendu.webp', + country: 'Cameroon', + title: 'Technical Project and Product Manager | Open Source is the Future!', + bio: 'Technical Product and Project Manager with over three years of active experience and six in the technical industry. Skilled in program management, writing, research, and speaking, she has built products, contributed to open source, and led community initiatives, finding fulfillment in improving the world through technology and security.', + linkedin: 'https://www.linkedin.com/in/harmonyelendu/', + website: 'https://linktr.ee/ogaharmony', + talk: { + title: 'Your First Pull Request Won\'t Be Perfect: A Case Study in finding your own Open Source Seat', + category: 'Education', + day: 'day2', + track: 'UbuCon Track', + abstract: 'Open source is a powerful yet often misunderstood career accelerator for software practitioners in Africa, and it is not limited to senior engineers. This talk draws on Harmony Elendu’s journey, from discovering communities to making contributions, overcoming imposter syndrome, and building a public portfolio that led to real opportunities. Whether you code, write docs, design, or support projects, this session shows there is a place for you in open source.', + }, + }, + { + id: 'hypolit-zeuchieu', + name: 'Hypolit Zeuchieu', + photo: '/speakers/Hypolit_Zeuchieu.webp', + country: 'Cameroon', + title: 'Trust Consulting, Backend Developer', + bio: 'Self-taught Python developer based in Yaoundé with a background in mathematics. He came to programming through curiosity about simulation tools during his studies at the University of Dschang. Over two years he has built web projects, automation scripts, and tools, and is passionate about making Python more accessible.', + linkedin: 'https://www.linkedin.com/in/hypolit-zeuchieu-a159392b7', + talk: { + title: 'Python in the Browser: No install, No barrier', + category: 'Python Core', + day: 'day3', + track: 'Python / Security', + abstract: 'What if your Python script could run in the browser, no installation, no server, no barriers? As a Python developer, I asked this question and discovered a whole new world. This talk is about that discovery shared.', + }, + }, + { + id: 'jean-marc-wogue', + name: 'Jean Marc Wogue', + photo: '/speakers/Jean_Marc_Wogue.webp', + country: 'Cameroon', + title: 'web devellopper', + bio: 'developpeur web avec 1 ans d\'experience, tech adventurer, tech researcher, software engineer student', + linkedin: 'https://www.linkedin.com/in/jean-marc-wogue-deffo-562756383/', + talk: { + title: 'l\'authentification avec la 2FA : implementation de TOTP avec django', + category: 'Web Development', + day: 'day3', + track: 'Web / Community', + abstract: 'creer une authentification securisée avec django et la 2FA', + }, + }, + { + id: 'jerry-davis-ndjana-mengue', + name: 'Jerry Davis Ndjana Mengue', + photo: '/speakers/Jerry_Davis_Ndjana_Mengue.webp', + country: 'Cameroon', + title: 'IT MANAGER', + bio: 'just a tech guy', + linkedin: 'https://www.linkedin.com/in/jerry-davis-ndjana-mengue-89229627a?utm_source=share_via&utm_content=profile&utm_medium=member_android', + talk: { + title: 'Architecture à budget zéro : Les décisions de design qui divisent votre facture cloud', + category: 'DevOps & Infrastructure', + day: 'day1', + track: 'DevOps / Cloud', + abstract: 'Le vrai problème Le coût du cloud n\'est pas un problème d\'outil, c\'est un problème de design. Deux applications identiques en fonctionnalités peuvent avoir une facture qui varie de 1 à 50 selon les choix d\'architecture faits au départ. Ce talk présente les principes de conception que j\'ai appris en déployant des applications en production avec un budget quasi nul — pas comme exercice académique, mais par contrainte réelle.', + }, + }, + { + id: 'johnpaul-hampo', + name: 'Johnpaul Hampo', + photo: '/speakers/Johnpaul_Hampo.webp', + country: 'Cameroon', + title: 'Founder, Hamplus Technologies International [Hamplus Hub]', + bio: 'I am a data scientist and a researcher. I program with Python and PHP. For more visit my links [https://GitHub.com/hamplustech | https://LinkedIn.com/in/hamplustech | https://x.com/hamplustech]', + linkedin: 'https://linkedin.com/in/hamplustech', + website: 'https://hamplustech.com', + talk: { + title: 'Open Source Impact: How Python Communities Are Solving Real Problems', + category: 'Open Source', + day: 'day2', + track: 'UbuCon Track', + abstract: 'Open source is not just about code—it’s about communities solving meaningful problems together. In this talk, the impact of open source via python communities on real problems will be shown and discussed, thereby encouraging the listeners to engage in open source projects and also to leverage python communities.', + }, + }, + { + id: 'kafui-alordo', + name: 'Kafui Alordo', + photo: '/speakers/Kafui_Alordo.webp', + country: 'Cameroon', + title: 'Software Developer', + bio: 'Software engineer, community builder, and tech organizer from Ho focused on Python, Django, and practical technology for real-world impact. He is founder and lead organizer of Python User Group Ho, growing developer communities through workshops, meetups, and conferences such as PyHo Conference and Django Girls Ho.', + linkedin: 'https://www.linkedin.com/in/kafui-alordo/', + talk: { + title: 'HTM(X) at the Intersection of the Web', + category: 'Web Development', + day: 'day1', + track: 'Web / Security', + abstract: 'This talk traces the web development journey - from simple server-rendered HTML to the complexity of modern SPAs, how it reshaped the Python/Django developer’s workflow. We revisit what was gained and lost along the way, exploring HTMX as a hypermedia revival that will bring interactivity back to the server, allowing backend-focused developers build modern, dynamic UIs without heavy JavaScript.', + }, + }, + { + id: 'kamdem-yamen-ulrich-laress-ulrich', + name: 'Kamdem Yamen Ulrich Laress Ulrich', + photo: '/speakers/Kamdem_Yamen_Ulrich_Laress_Ulrich.webp', + country: 'Cameroon', + title: 'Freelancer', + bio: 'Ingénieur télécommunications spécialisé en sécurité des systèmes et réseaux. D\'abord développeur backend en freelance, il découvre l\'administration système Linux puis le DevOps, qui fusionne ses deux passions. Certifié sur Terraform et AWS Solutions Architect, il travaille aujourd\'hui en freelance comme DevOps et développeur backend.', + linkedin: 'https://www.linkedin.com/in/ulrich-laress-kamdem-yamen/', + website: 'https://itindepth.odoo.com/blog', + talk: { + title: 'K3s : The Kubernetes for gentle apps', + category: 'DevOps & Infrastructure', + day: 'day1', + track: 'DevOps / Cloud', + abstract: 'Déployer un système tournant sur python mais ne nécessitant pas toute la complexité de Kubernetes, d\'où le choix de k3s from Rancher Labs', + }, + }, + { + id: 'leslye-nkwa', + name: 'Leslye Nkwa', + photo: '/speakers/Leslye_Nkwa.webp', + country: 'Cameroon', + title: 'Alea jacta est.', + bio: 'Étudiante en statistiques et économie, elle applique la data science aux enjeux de développement : jeunesse, emploi décent et innovation en Afrique. Volontaire chez WiMLDS Yaoundé, elle rend la data science accessible aux jeunes femmes africaines et travaille sur l\'analyse de données d\'enquêtes ménages africaines.', + linkedin: 'https://www.linkedin.com/in/leslyenkwatp/', + talk: { + title: 'L\'importance de la cohérence des données: 03 bonnes pratiques', + category: 'Machine Learning & AI', + day: 'day3', + track: 'AI / Data Science', + abstract: 'L\'atelier s\'inspire de mon expérience personnelle en gestion de la cohérence des données d\'enquête sur l\'enquête ménages du Nigeria (GHS-Panel, Banque mondiale) pendant un projet de cours. À travers le cas de la mesure du foncier agricole, nous verrons trois bonnes pratiques essentielles pour tout débutant.', + }, + }, + { + id: 'linuce-demanou', + name: 'Linuce Demanou', + photo: '/speakers/Linuce_Demanou.webp', + country: 'Cameroon', + title: 'Camertronix, ML Engineer', + bio: 'With a background in Mathematics, Linuce Demanou is a 23-year-old making her way in Data Science and AI. Passionate about mentorship, she is writing a book, "Where Hope Meets Dreams," to inspire young women to believe in their potential and pursue impactful paths in technology.', + linkedin: 'https://www.linkedin.com/in/linuce-demanou-kitio-b88822267/', + talk: { + title: 'From Belief to Impact: Why Skills Alone Are Not Enough in Tech', + category: 'Community', + day: 'day1', + track: 'Web / Security', + abstract: 'Success in tech is often reduced to one thing: skills. But in reality, many talented people never reach their potential, not because they lack ability, but because of limiting beliefs shaped by their environment, education, and self-perception. This talk explores the invisible factors that influence growth in tech, from mindset to confidence and exposure. Through personal insights and relatable stories, it highlights why belief is the true starting point of impact.', + }, + }, + { + id: 'lobga-julius', + name: 'Lobga Julius', + photo: '/speakers/Lobga_Julius.webp', + country: 'Cameroon', + title: 'AI and Software Engineer at InchTechs and ICISTEM', + bio: 'AI and Software Engineer with 4+ years bridging software development and machine learning research. He works at InchTechs and as an AI/ML researcher at the ICISTEM Foundation in Yaoundé, holds an M.Sc. in Data Science, and was part of the winning team at the AIMS Sustainable Development Hackathon.', + linkedin: 'https://www.linkedin.com/in/lobga-julius-647b55234', + website: 'https://inchtechs.com/', + talk: { + title: 'Beyond Keywords: Building a Vector Search Engine From Scratch', + category: 'Machine Learning & AI', + day: 'day1', + track: 'AI / Data Science', + abstract: 'Traditional search engines rely heavily on keyword matching, which completely fails the moment a user types a synonym. Modern AI applications solve this using semantic vector search, but the inner workings are often hidden inside complex third-party libraries and black-box neural networks. In this talk, we will demystify semantic search by stepping away from heavy frameworks and building a vector search engine from scratch using pure Python and NumPy.', + }, + }, + { + id: 'marcela-djoukouo-talotsing', + name: 'Marcela Djoukouo Talotsing', + photo: '/speakers/Marcela_Djoukouo_Talotsing.webp', + country: 'Cameroon', + title: 'Prévoir la panne aujourd’hui, sauver une vie demain.', + bio: 'Doctorante en Mécanique Énergétique à l\'Université de Dschang, ses recherches portent sur l\'intégration de l\'IA explicable dans le diagnostic et le pronostic des véhicules. Passionnée par la maintenance prédictive et l\'enseignement, elle œuvre à rendre les outils d\'IA plus compréhensibles et accessibles aux techniciens et ingénieurs.', + linkedin: 'https://linkedin.com/in/marcela-djoukouo-talotsing-8a8815273', + talk: { + title: 'AutoXAI : Rendre le diagnostic automobile intelligent et explicable avec Python', + category: 'IoT & Hardware', + day: 'day3', + track: 'Python / Security', + abstract: 'AutoXAI , un projet combinant matériel, IoT et Python embarqué pour le diagnostic automobile intelligent. À partir de capteurs connectés à des cartes comme ESP32, les données du véhicule sont collectées, traitées et analysées afin de détecter les anomalies et faciliter la maintenance prédictive.', + }, + }, + { + id: 'marielle-daha', + name: 'Marielle Daha', + photo: '/speakers/Marielle_Daha.webp', + country: 'Cameroon', + title: 'CS student & Aspiring ML engineer | University of Yaoundé 1', + bio: 'Computer Science sophomore at the University of Yaoundé 1, specializing in data science, and a self-taught UI designer. She builds with Python across data visualization and AI tools, including KALARA, an offline-first AAC platform for non-verbal autistic students, and believes beautiful output is an underrated Python skill.', + linkedin: 'https://www.linkedin.com/in/marielle-daha-670b39336/', + talk: { + title: 'Creative coding with matplotlib : turning data into art.', + category: 'Python Core', + day: 'day3', + track: 'Python / Security', + abstract: 'Matplotlib can do more than homework plots. This talk walks through concrete techniques : custom styles, generative patterns, artistic compositions, that turn the library into a creative medium.', + }, + }, + { + id: 'muluh-azinwi-success-ndahi-li', + name: 'Muluh Azinwi Success Ndahi\'li', + photo: '/speakers/Muluh_Azinwi_Success_Ndahili.webp', + country: 'Cameroon', + title: 'ABOVE & BEYOND', + bio: 'Cameroonian software developer, community architect, and Google Gemini API Global Winner. He is Technical Lead of AWS Cloud Club UBa, Web Developer Intern at Python Cameroon (where he helped build this conference website), and an Open Dreams Scholar driven by the belief that imagination and technology can reshape Africa\'s future.', + linkedin: 'https://www.linkedin.com/in/success-muluh25', + website: 'https://ndahi.net', + talk: { + title: 'Built by Us: A Young Cameroonian\'s Roadmap to Open Source Contribution', + category: 'Open Source', + day: 'day2', + track: 'UbuCon Track', + abstract: 'Most Cameroonian developers have complained about broken school portals and local platforms that never work, then gone home and done nothing. This talk asks Why? Open source has an image problem: it feels like unmonetized labour. But ignoring local problems has a cost. This talk reframes contribution as the smartest investment a young Cameroonian dev can make.', + }, + }, + { + id: 'mveng-mboda-pascal-franck', + name: 'Mveng Mboda Pascal Franck', + photo: '/speakers/Mveng_Mboda_Pascal_Franck.webp', + country: 'Cameroon', + title: 'Hack The Ambassador and pen pentester', + bio: 'Enthousiaste en cybersécurité, spécialisé en pentest et sécurité défensive, certifié par l\'ISC2. Ambassadeur de la plateforme mondiale Hack The Box à Yaoundé, il pratique aussi le développement Python-Django et forme en administration réseau, systèmes, sécurité et cloud.', + linkedin: 'https://www.linkedin.com/in/mveng?utm_source=share_via&utm_content=profile&utm_medium=member_ios', + talk: { + title: 'Pentest d’une application Django avec Python : attaques réelles et corrections', + category: 'Cybersecurity', + day: 'day3', + track: 'Python / Security', + abstract: 'Les applications web développées avec Django bénéficient par défaut de mécanismes de sécurité robustes. Pourtant, des erreurs de configuration ou de développement peuvent exposer des vulnérabilités critiques.', + }, + }, + { + id: 'mvenyi-donald', + name: 'Mvenyi Donald', + photo: '/speakers/Mvenyi_Donald.webp', + country: 'Cameroon', + title: 'Corynth Tech', + bio: 'I am a software computer engineer and a Cybersecurity professional. I am equally a co-founder of Corynth-Tech, a Cameroon-based tech institution that provide IT services and academic and professional training. I attended PYCONZA 2025 in Johannesburg and looking forward to attend other PyCon conferences, national and international.', + linkedin: 'https://www.linkedin.com/in/mvenyi-donald-293117273', + website: 'https://corynth-tech.com', + talk: { + title: 'Hacking with Python: Increasing Cameroon\'S Digital Security Posture.', + category: 'Cybersecurity', + day: 'day1', + track: 'Web / Security', + abstract: 'Cameroon is one of the fastest-growing digital economies in the world — but with rapid digital growth comes rising cyber threats: SIM swapping, mobile money theft, ransomware targeting hospitals and banks, and data breaches affecting millions. This talk explores how Python developers across the Cameroon can play a vital role in defending our digital future through ethical hacking and cybersecurity.', + }, + }, + { + id: 'ndongmo-christian', + name: 'Ndongmo Christian', + photo: '/speakers/Ndongmo_Christian.webp', + country: 'Cameroon', + title: 'Senior Backend & DevOps Architect @ HooYia | DevOps | Kubernetes | Docker | Youtuber(@GoldenBrainTek)', + bio: 'Christian is a forward-thinking software developer who constantly stays updated with the latest advancements in technology. His dedication to continuous learning fuels his ability to deliver cutting-edge solutions.', + linkedin: 'https://www.linkedin.com/in/ndongmo-christian-4a5537226/', + talk: { + title: 'Stop Clicking in AWS: Reproducible Infrastructure for Django with Terraform', + category: 'DevOps & Infrastructure', + day: 'day1', + track: 'DevOps / Cloud', + abstract: 'Tired of manually configuring AWS every time you deploy your Django app? In this talk, we’ll explore how to use Terraform to define and provision production infrastructure in a reproducible and scalable way. Starting from a simple Django application, we’ll build a minimal yet realistic AWS setup including compute (EC2), database (RDS), and storage (S3), all managed as code. You’ll learn how Infrastructure as Code improves collaboration, reduces errors, and enables consistent deployments.', + }, + }, + { + id: 'ntui-raoul', + name: 'Ntui Raoul', + photo: '/speakers/Ntui_Raoul.webp', + country: 'Cameroon', + title: 'Core team member at Django Cameroon', + bio: 'Ntui Raoul is a software engineer with over 2-3 years experienced in the field who has had the privilege to work with developers across Cameroon. He\'s a community builder at Python Cameroon and Django Cameroon. He has interest in AI, ML and Cloud computing.', + talk: { + title: 'Django + LLMs: Building Real AI Features Without Overengineering', + category: 'Machine Learning & AI', + day: 'day1', + track: 'Web / Security', + abstract: 'Everyone wants “AI features” now, but most Django devs get stuck between messy API calls to OpenAI and overcomplicated ML pipelines. In this talk, I’ll show you how to ship useful AI inside a Django app using 3 patterns that actually work in production.', + }, + }, + { + id: 'parkson-tano-daniel', + name: 'Parkson Tano Daniel', + photo: '/speakers/Parkson_Tano_Daniel.webp', + country: 'Cameroon', + title: 'QUICKERPAY, CTO', + bio: 'Backend-focused full-stack developer with over 7 years of experience specializing in Python and Django. Formerly Senior Developer at Paysika and now CTO at QuickerPay, he builds reliable, secure, high-performance FinTech systems. A former computer science educator, he is passionate about sharing knowledge across the Python ecosystem.', + linkedin: 'https://www.linkedin.com/in/daniel-parkson-tano/', + website: 'https://quickerpay.co/', + talk: { + title: 'From WSGI to ASGI: Modernizing Django with Daphne and Uvicorn', + category: 'Web Development', + day: 'day1', + track: 'Web / Security', + abstract: 'Django’s shift from WSGI to ASGI enables modern real-time capabilities like WebSockets and async processing, which we’ll explore through Django Channels and a comparison of Daphne and Uvicorn in practical deployments.', + }, + }, + { + id: 'patrick-nounga', + name: 'Patrick Nounga', + photo: '/speakers/Patrick_Nounga.webp', + country: 'Cameroon', + title: 'Law & HR professional, Head of board of Directors, Human Resource Volunteers Association', + bio: 'Law and HR professional, ENAM valedictorian, and Labor Administrator contributing to strategic national projects in extractive industries governance and local content. As Chairman of the Board of Human Resource Volunteers, he has led national programs supporting hundreds of young Cameroonians toward professional integration and training.', + linkedin: 'https://www.linkedin.com/in/patrick-n-688058173/', + talk: { + title: 'Patrick\'s Tips to get easly a job/intership as a developer', + category: 'Community', + day: 'day1', + track: 'AI / Data Science', + abstract: 'How to deal with recruiter without experience? during the session, we are going to share some experience to help developpers and graduated to increase their employability', + }, + }, + { + id: 'sema-kumbela-fombutu', + name: 'Sema Kumbela Fombutu', + photo: '/speakers/Sema_Kumbela_Fombutu.webp', + country: 'Cameroon', + title: 'People-Systems-Legacy : building resilient people and durable systems designed to outlast a single generation', + bio: 'Works at the intersection of human development, mental health, and institutional systems. Founder and Lead Psychotherapist at Sanity Global Foundation, advancing mental health research and advocacy in the African context. He also leads several enterprises, focused on building resilient people and durable systems designed to outlast a single generation.', + linkedin: 'https://www.linkedin.com/in/sema-kumbela-fombutu-2765b091?utm_source=share_via&utm_content=profile&utm_medium=member_ios', + talk: { + title: 'The Silent Bugs in Tech: Debugging Burnout, Anxiety,and Cognitive Overload in High-Performance Teams', + category: 'Social Impact', + day: 'day3', + track: 'Web / Community', + abstract: 'Tech teams fix system bugs, but ignore those affecting the people building systems. This talk explores burnout, anxiety, isolation, cognitive overload, and toxic productivity through a systems-thinking lens. We link mental strain to slower delivery, more bugs, poor decisions, and weak cohesion. We examine root causes, hidden costs, and introduce a practical debugging framework with strategies to sustain performance without sacrificing delivery for healthier, resilient, high-performing engineerin', + }, + }, + { + id: 'tayo-tate-desmond-corentin', + name: 'Tayo Tate Desmond Corentin', + photo: '/speakers/Tayo_Tate_Desmond_Corentin.webp', + country: 'Cameroon', + title: 'ISDEV EXPERTS, IA & Data, Data scientist', + bio: 'Ingénieur en informatique et spécialiste IT passionné par l\'IA, Desmond Tayo développe des solutions full-stack et intègre des modèles de langage (LLM). Engagé dans des projets de recherche en Data Science, il optimise les systèmes RAG et automatise les infrastructures cloud pour une IA plus accessible et performante.', + linkedin: 'https://www.linkedin.com/in/tayo-tate/', + website: 'https://medium.com/@desmondtayes6', + talk: { + title: 'Du Notebook à la production : Déployer ses modèles d\'IA avec FastAPI et Docker', + category: 'Machine Learning & AI', + day: 'day3', + track: 'AI / Data Science', + abstract: 'Beaucoup de projets de Data Science ne voient jamais le jour car le passage à la production semble complexe. Ce talk casse cette barrière en proposant un guide pratique : de la création d\'une API avec FastAPI à la conteneurisation avec Docker. On verra comment transformer un simple script de prédiction en un service web robuste, prêt à être intégré dans n\'importe quelle application.', + }, + }, + { + id: 'vanessa-manessong', + name: 'Vanessa Manessong', + photo: '/speakers/Vanessa_Manessong.webp', + country: 'Cameroon', + title: 'Investigative Data Analyst', + bio: 'Investigative data analyst specialising in disinformation campaigns and coordinated inauthentic behaviour. Working at the intersection of data analysis and OSINT, she detects influence operations, identifies the actors, and unpacks their tactics. She also founded an organisation guiding young people toward digital careers and stronger employability.', + linkedin: 'https://www.linkedin.com/in/vanessa-manessong/', + website: 'https://disinfo.africa/', + talk: { + title: 'IA et marché tech : construire un profil adaptable et employable', + category: 'Education', + day: 'day3', + track: 'Web / Community', + abstract: 'À l’ère de l’IA, de plus en plus de jeunes souhaitent construire une carrière dans l’intelligence artificielle et la data. Mais que révèle réellement le marché de l’emploi tech ? À travers une analyse des offres d’emploi au Cameroun, cette conférence mettra en lumière les compétences les plus recherchées aujourd’hui, les tendances autour de l’IA et de la data, ainsi que l’importance de développer un profil polyvalent, adaptable et employable.', + }, + }, + { + id: 'yannik-kadjie', + name: 'Yannik Kadjie', + photo: '/speakers/Yannik_Kadjie.webp', + country: 'Cameroon', + title: 'Software Developer', + bio: 'je suis Yannik KADJIE, un passionné de l\'implementation de solutions accessibles, avec un intérêt particulier pour les technologies Google, en particulier Google Workspace. Je suis enthousiaste à l\'idée de partager mes connaissances et mon expérience sur ce sujet avec vous lors de Google I/O.', + linkedin: 'https://www.linkedin.com/in/yannik-kadjie/', + talk: { + title: 'Speed Up Your Python Program With Concurrency', + category: 'Python Core', + day: 'day3', + track: 'Python / Security', + abstract: 'Dans cette session, nous allons voir concrètement comment améliorer la vitesse d\'exécution de nos tâches automatisées tout en exploitant au mieux les ressources disponibles grâce au concurrency.', + }, + }, + { + id: 'yunwen-eric', + name: 'Yunwen Eric', + photo: '/speakers/Yunwen_Eric.webp', + country: 'Cameroon', + title: 'Building the Future with Flutter | Web Developer | Community Lead | STEM Advocate', + bio: 'Software developer focused on Flutter and web development, passionate about leveraging technology for Africa\'s growth. Beyond coding, he actively engages in community initiatives and advocates for STEM education, dedicated to making a positive impact through software, community engagement, and education.', + linkedin: 'https://www.linkedin.com/in/yunweneric', + website: 'https://neero.io/', + talk: { + title: 'Python, Dart and Flutter: Unlocking Native Performance with FFI', + category: 'Open Source', + day: 'day1', + track: 'DevOps / Cloud', + abstract: 'This session explores how Dart FFI enables Flutter applications to communicate directly with Python-powered systems for building high-performance and intelligent applications beyond traditional APIs. Attendees will learn Dart FFI fundamentals, Python and Flutter interoperability, performance tradeoffs, real-world architecture patterns, and best practices for building scalable cross-platform solutions with open-source tools.', + }, + }, + +]; + +export const TALK_CATEGORIES = [ + 'Keynote', + 'Web Development', + 'Data Science', + 'Machine Learning & AI', + 'DevOps & Infrastructure', + 'Cybersecurity', + 'Python Core', + 'Open Source', + 'Ubuntu & Linux', + 'IoT & Hardware', + 'Social Impact', + 'Education', + 'Community', + 'Workshop', + 'Lightning Talk', +]; diff --git a/src/i18n/en.json b/src/i18n/en.json index 2d5f46d..78b1528 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1,6 +1,7 @@ { "nav": { "about": "About", + "agenda": "Agenda", "speakers": "Speakers", "sponsor": "Sponsor", "attend": "Attend", @@ -15,6 +16,7 @@ "codeOfConduct": "Code of Conduct", "psf": "Python Software Foundation", "program": "Program", + "agenda": "Agenda", "speakers": "Speakers", "proposalGuidelines": "Proposal Guidelines", "venue": "Venue", @@ -32,6 +34,7 @@ "heroSubtitle": "Africa's Python Conference, Reimagined in the Heart of Africa", "heroDate": "September 17th-19th, 2026", "heroLocation": "Yaound\u00e9, Cameroon", + "viewAgenda": "View Agenda", "callForSpeakers": "Call For Speakers", "getYourTicket": "Get Your Ticket", "welcomeTitle": "Welcome to", @@ -157,6 +160,10 @@ "featuredSubtitle": "Meet some of our confirmed speakers for PyCon Cameroon 2026", "comingSoon": "Coming Soon!", "comingSoonText": "Our speaker lineup is currently being finalized. Check back soon to see the amazing speakers joining us at PyCon Cameroon 2026!", + "backToSpeakers": "Back to Speakers", + "aboutSpeaker": "About the Speaker", + "talkDetails": "Talk Details", + "notFound": "Speaker not found.", "whySpeakTitle": "Why", "whySpeakHighlight": "Speak", "whySpeakSuffix": "at PyCon Cameroon?", @@ -504,7 +511,15 @@ "ctaTitle": "Join the UbuCon Track", "ctaText": "Whether you're an Ubuntu power user or just getting started with Linux, the UbuCon track has something for you.", "ctaSubmit": "Submit a Talk", - "ctaTicket": "Get Your Ticket" + "ctaTicket": "Get Your Ticket", + "agendaTitle": "UbuCon", + "agendaHighlight": "Schedule" + }, + "agenda": { + "title": "Conference", + "titleHighlight": "Agenda", + "subtitle": "Three days of Python, Ubuntu, and open source. September 17 to 19, 2026", + "note": "Schedule is subject to change. All times are local (WAT, UTC+1)." }, "financialAid": { "title": "Financial", @@ -537,7 +552,7 @@ "data": { "tracks": { "newTrack": "New Track", - "submitTalk": "Submit a {{label}} Talk", + "viewSpeakers": "{{label}} Speakers", "attendTrack": "Attend {{label}} Track", "trackHighlights": "{{label}} Track Highlights", "ubucon": [ diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 59f8084..8aea367 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -1,6 +1,7 @@ { "nav": { "about": "\u00c0 propos", + "agenda": "Agenda", "speakers": "Intervenants", "sponsor": "Sponsoriser", "attend": "Participer", @@ -15,6 +16,7 @@ "codeOfConduct": "Code de conduite", "psf": "Python Software Foundation", "program": "Programme", + "agenda": "Agenda", "speakers": "Intervenants", "proposalGuidelines": "Guide de soumission", "venue": "Lieu", @@ -32,6 +34,7 @@ "heroSubtitle": "La conf\u00e9rence Python de l'Afrique, r\u00e9imagin\u00e9e au c\u0153ur de l'Afrique", "heroDate": "17-19 septembre 2026", "heroLocation": "Yaound\u00e9, Cameroun", + "viewAgenda": "Voir l'agenda", "callForSpeakers": "Appel aux intervenants", "getYourTicket": "Obtenez votre billet", "welcomeTitle": "Bienvenue \u00e0", @@ -157,6 +160,10 @@ "featuredSubtitle": "D\u00e9couvrez nos intervenants confirm\u00e9s pour PyCon Cameroun 2026", "comingSoon": "Bient\u00f4t disponible !", "comingSoonText": "Notre liste d'intervenants est en cours de finalisation. Revenez bient\u00f4t pour d\u00e9couvrir les intervenants incroyables qui nous rejoindront \u00e0 PyCon Cameroun 2026 !", + "backToSpeakers": "Retour aux intervenants", + "aboutSpeaker": "\u00c0 propos de l'intervenant", + "talkDetails": "D\u00e9tails de la conf\u00e9rence", + "notFound": "Intervenant introuvable.", "whySpeakTitle": "Pourquoi", "whySpeakHighlight": "Intervenir", "whySpeakSuffix": "\u00e0 PyCon Cameroun ?", @@ -505,7 +512,15 @@ "ctaTitle": "Participez au track UbuCon", "ctaText": "Que vous souhaitiez présenter une conférence sur Ubuntu, animer un atelier open source ou simplement apprendre des meilleurs, le track UbuCon à PyCon Cameroun est fait pour vous.", "ctaSubmit": "Soumettre une proposition", - "ctaTicket": "Obtenir votre billet" + "ctaTicket": "Obtenir votre billet", + "agendaTitle": "Programme", + "agendaHighlight": "UbuCon" + }, + "agenda": { + "title": "Agenda de la", + "titleHighlight": "Conférence", + "subtitle": "Trois jours de Python, Ubuntu et open source. Du 17 au 19 septembre 2026", + "note": "Le programme est susceptible de changer. Tous les horaires sont locaux (WAT, UTC+1)." }, "financialAid": { "title": "Aide", @@ -538,7 +553,7 @@ "data": { "tracks": { "newTrack": "Nouveau Track", - "submitTalk": "Soumettre une conf\u00e9rence {{label}}", + "viewSpeakers": "Intervenants {{label}}", "attendTrack": "Participer au track {{label}}", "trackHighlights": "Points forts du track {{label}}", "ubucon": [ diff --git a/src/index.css b/src/index.css index bc09d34..962cb99 100644 --- a/src/index.css +++ b/src/index.css @@ -1514,6 +1514,7 @@ a { .page-header { padding: 120px var(--spacing-sm) var(--spacing-lg); min-height: 30vh; + margin-bottom: var(--spacing-sm); } .page-header h1 { @@ -2443,6 +2444,238 @@ img { font-weight: 500; } +/* =================================== + Agenda Day Toggle + =================================== */ +.agenda-toggle-wrap { + display: flex; + justify-content: center; + margin-bottom: var(--spacing-xl); +} + +.agenda-toggle { + display: inline-flex; + background: var(--color-dark-alt); + border-radius: 20px; + padding: 6px; + border: 1px solid var(--color-border); + gap: 4px; +} + +.agenda-toggle-btn { + padding: 0.65rem 0; + border-radius: 14px; + border: none; + cursor: pointer; + transition: all 0.3s ease; + background: transparent; + color: var(--color-text-secondary); + width: 200px; + font-weight: 700; + font-size: 1.05rem; + font-family: var(--font-ui); +} + +.agenda-toggle-btn.active { + background: var(--color-orange); + color: white; +} + +.agenda-slot { + display: grid; + grid-template-columns: 60px 1fr; + gap: var(--spacing-md); + align-items: start; +} + +.agenda-slot-time { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding-top: calc(var(--spacing-md) + 2px); + font-size: 0.82rem; + font-weight: 700; + color: var(--color-orange); + font-family: var(--font-ui); +} + +.agenda-slot-tracks { + display: grid; + grid-template-columns: 1fr; + gap: var(--spacing-sm); +} + +.agenda-slot-tracks.is-parallel { + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); +} + +.agenda-session-card { + height: 100%; +} + +@media (max-width: 768px) { + .agenda-slot { + grid-template-columns: 1fr; + gap: 0; + } + + .agenda-slot-time { + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 6px; + padding-top: 0; + padding-bottom: 8px; + font-size: 0.9rem; + } + + .agenda-slot-tracks { + gap: var(--spacing-sm); + } + + .agenda-slot-tracks.is-parallel { + grid-template-columns: 1fr; + } + + .agenda-toggle-wrap { + margin-bottom: var(--spacing-md); + } + + section.section.bg-dark { + padding-top: var(--spacing-lg); + } + + .agenda-toggle { + display: flex; + width: 100%; + gap: 2px; + } + + .agenda-toggle-btn { + flex: 1; + width: auto; + padding: 0.6rem 0.25rem; + font-size: 0.9rem; + } + + .agenda-session-card h4 { + font-size: 0.95rem; + } +} + +/* =================================== + Speaker Detail + =================================== */ +.speaker-detail-grid { + display: grid; + grid-template-columns: 1fr 300px; + gap: var(--spacing-xl); + align-items: start; +} + +@media (max-width: 768px) { + .speaker-detail-grid { + grid-template-columns: 1fr; + gap: var(--spacing-md); + } +} + +/* =================================== + Speaker Cards + =================================== */ +.speaker-card-link { + display: flex; + height: 100%; +} + +.speaker-card { + cursor: pointer; + text-align: center; + padding: var(--spacing-md); + transition: transform var(--transition-normal), box-shadow var(--transition-normal); + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; +} + +.speaker-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-lg); +} + +.speaker-card-photo { + width: 140px; + height: 140px; + border-radius: var(--radius-full); + object-fit: cover; + margin: 0 auto var(--spacing-sm); + border: 3px solid var(--color-orange); + display: block; +} + +.speaker-card-photo-placeholder { + width: 140px; + height: 140px; + border-radius: var(--radius-full); + background: var(--color-dark-alt); + border: 3px solid var(--color-border); + margin: 0 auto var(--spacing-sm); + display: flex; + align-items: center; + justify-content: center; + font-size: 2.5rem; + color: var(--color-orange); + font-family: var(--font-display); +} + +.speaker-card-name { + font-size: 1.05rem; + font-weight: 700; + margin-bottom: 4px; + color: var(--color-text-primary); +} + +.speaker-card-title { + font-size: 0.82rem; + color: var(--color-text-secondary); + font-family: var(--font-ui); + margin-bottom: 6px; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.speaker-card-talk { + font-size: 0.8rem; + color: var(--color-orange); + font-family: var(--font-ui); + font-weight: 600; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + margin-bottom: var(--spacing-sm); +} + +.speaker-card-tag { + display: inline-block; + font-size: 0.72rem; + font-weight: 700; + padding: 2px 10px; + border-radius: 50px; + background: var(--color-dark-alt); + color: var(--color-text-secondary); + border: 1px solid var(--color-border); + font-family: var(--font-ui); + margin-top: auto; + text-transform: uppercase; + letter-spacing: 0.04em; +} + /* =================================== Speakers Page Specific Styles =================================== */ diff --git a/src/pages/Agenda.jsx b/src/pages/Agenda.jsx new file mode 100644 index 0000000..495bfe0 --- /dev/null +++ b/src/pages/Agenda.jsx @@ -0,0 +1,226 @@ +import { useSearchParams, Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { Clock, MapPin, User, Languages } from 'lucide-react'; +import useScrollAnimation from '../hooks/useScrollAnimation'; +import { useLocalizedPath } from '../hooks/useLocalizedPath'; +import { DAYS, agenda, TYPE_STYLES, LANG_LABELS } from '../data/agenda'; +import { speakers } from '../data/speakers'; + +const normalizeName = (s) => + (s || '') + .normalize('NFKD') + .replace(/[̀-ͯ]/g, '') + .toLowerCase() + .replace(/[^a-z0-9]+/g, ' ') + .trim(); + +const NAME_ALIASES = { + 'kamdem ulrich': 'kamdem-yamen-ulrich-laress-ulrich', + 'tayo tate desmond': 'tayo-tate-desmond-corentin', +}; + +const speakerIdByName = {}; +speakers.forEach((sp) => { + speakerIdByName[normalizeName(sp.name)] = sp.id; +}); + +const resolveSpeakerId = (name) => { + const key = normalizeName(name); + if (NAME_ALIASES[key]) return NAME_ALIASES[key]; + if (speakerIdByName[key]) return speakerIdByName[key]; + const tokens = key.split(' ').filter(Boolean); + const match = speakers.find((sp) => { + const spKey = normalizeName(sp.name); + return tokens.length > 1 && tokens.every((t) => spKey.includes(t)); + }); + return match ? match.id : null; +}; + +const groupByTimeSlot = (sessions) => { + const slots = []; + const indexByTime = new Map(); + + sessions.forEach((session) => { + const isBreak = session.type === 'break' || session.type === 'social'; + if (isBreak) { + slots.push({ time: session.time, kind: 'break', sessions: [session] }); + return; + } + if (indexByTime.has(session.time)) { + slots[indexByTime.get(session.time)].sessions.push(session); + return; + } + indexByTime.set(session.time, slots.length); + slots.push({ time: session.time, kind: 'sessions', sessions: [session] }); + }); + + return slots; +}; + +const BreakRow = ({ session }) => ( +
+ + {session.time} + +
+ + {session.title}{session.room ? ` · ${session.room}` : ''} + +
+
+); + +const SessionCard = ({ session }) => { + const { l } = useLocalizedPath(); + const style = TYPE_STYLES[session.type] || TYPE_STYLES.talk; + const speakerId = session.speaker ? resolveSpeakerId(session.speaker) : null; + + return ( +
+
+

{session.title}

+ + {style.label} + +
+
+ {session.speaker && ( + speakerId ? ( + + {session.speaker} + + ) : ( + + {session.speaker} + + ) + )} + {session.room && ( + + {session.room}{session.track ? ` · ${session.track}` : ''} + + )} + {session.lang && ( + + {LANG_LABELS[session.lang]} + + )} +
+
+ ); +}; + +const TimeSlot = ({ slot }) => { + const parallel = slot.sessions.length > 1; + + return ( +
+
+ + {slot.time} +
+
+ {slot.sessions.map((session, i) => ( + + ))} +
+
+ ); +}; + +const Agenda = () => { + const { t } = useTranslation(); + useScrollAnimation(); + const [searchParams, setSearchParams] = useSearchParams(); + + const dayParam = searchParams.get('day'); + const activeDay = DAYS.find(d => d.key === dayParam) ? dayParam : 'day1'; + + const handleDayChange = (key) => { + setSearchParams({ day: key }, { replace: true }); + }; + + const sessions = agenda[activeDay] || []; + const slots = groupByTimeSlot(sessions); + const activeDayData = DAYS.find(d => d.key === activeDay); + + return ( + <> +
+
+

{t('agenda.title')} {t('agenda.titleHighlight')}

+

{t('agenda.subtitle')}

+
+
+ +
+
+
+
+ {DAYS.map((day) => { + const isActive = activeDay === day.key; + return ( + + ); + })} +
+
+ + {activeDayData?.key === 'day2' && ( +
+ UbuCon + UbuCon Cameroon Day +
+ )} + +
+ {slots.map((slot, i) => ( + slot.kind === 'break' + ? + : + ))} +
+
+
+ + + ); +}; + +export default Agenda; diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 9adeba5..2c36bf1 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { useLocalizedPath } from '../hooks/useLocalizedPath'; -import { Calendar, MapPin, Mic, Ticket } from 'lucide-react'; +import { Calendar, MapPin, CalendarDays, Ticket } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import useScrollAnimation from '../hooks/useScrollAnimation'; import LazyImage from '../components/LazyImage'; @@ -35,8 +35,8 @@ const Home = () => {
- - {t('home.callForSpeakers')} + + {t('home.viewAgenda')} {t('home.getYourTicket')} @@ -199,9 +199,6 @@ const Home = () => { {t('home.getYourTicket')} - - {t('home.submitATalk')} - {t('home.sponsorshipDeck')} diff --git a/src/pages/SpeakerDetail.jsx b/src/pages/SpeakerDetail.jsx new file mode 100644 index 0000000..61e233e --- /dev/null +++ b/src/pages/SpeakerDetail.jsx @@ -0,0 +1,153 @@ +import { useParams, Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { ArrowLeft, Globe, MapPin, Tag, Calendar } from 'lucide-react'; +import { useLocalizedPath } from '../hooks/useLocalizedPath'; +import useScrollAnimation from '../hooks/useScrollAnimation'; +import { speakers } from '../data/speakers'; +import { DAYS } from '../data/agenda'; + +const SpeakerDetail = () => { + useScrollAnimation(); + const { t } = useTranslation(); + const { l } = useLocalizedPath(); + const { speakerId } = useParams(); + + const speaker = speakers.find(s => s.id === speakerId); + + if (!speaker) { + return ( +
+
+

{t('speakers.notFound')}

+ + {t('speakers.backToSpeakers')} + +
+
+ ); + } + + const day = speaker.talk?.day ? DAYS.find(d => d.key === speaker.talk.day) : null; + + return ( + <> +
+
+ + {t('speakers.backToSpeakers')} + +
+ {speaker.photo ? ( + {speaker.name} + ) : ( +
+ {speaker.name.charAt(0)} +
+ )} +
+

{speaker.name}

+ {speaker.title && ( +

+ {speaker.title} +

+ )} + {speaker.country && ( +

+ {speaker.country} +

+ )} +
+ {speaker.linkedin && ( + + LinkedIn + + )} + {speaker.website && ( + + Website + + )} +
+
+
+
+
+ +
+
+
+ {speaker.talk && ( +
+
+

+ {t('speakers.talkDetails')} +

+

{speaker.talk.title}

+ +
+ {speaker.talk.category && ( + + {speaker.talk.category} + + )} + {day && ( + + {day.label} · {day.date} + + )} + {speaker.talk.track && ( + + {speaker.talk.track} + + )} +
+ + {speaker.talk.abstract && ( +

+ {speaker.talk.abstract} +

+ )} +
+
+ )} + + {speaker.bio && ( +
+

+ {t('speakers.aboutSpeaker')} +

+

{speaker.bio}

+
+ )} +
+
+
+ + ); +}; + +export default SpeakerDetail; diff --git a/src/pages/Speakers.jsx b/src/pages/Speakers.jsx index 87f2645..c4711cc 100644 --- a/src/pages/Speakers.jsx +++ b/src/pages/Speakers.jsx @@ -1,353 +1,70 @@ -import React, { useMemo } from 'react'; +import React from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Megaphone, Calendar, Target, AlertTriangle, ScrollText, HelpingHand } from 'lucide-react'; +import { User } from 'lucide-react'; import useScrollAnimation from '../hooks/useScrollAnimation'; import { useLocalizedPath } from '../hooks/useLocalizedPath'; - -const TIMELINE_MILESTONES = [ - { date: new Date('2026-03-30') }, - { date: new Date('2026-05-15T23:59:00+01:00') }, - { date: new Date('2026-05-30') }, - { date: new Date('2026-06-15') }, - { date: new Date('2026-09-19') }, -]; - -function getCfpStatus(now) { - const cfpOpen = TIMELINE_MILESTONES[0].date; - const cfpClose = TIMELINE_MILESTONES[1].date; - if (now < cfpOpen) return 'upcoming'; - if (now <= cfpClose) return 'open'; - return 'closed'; -} - -function getTimelineItemStatus(milestone, index, now) { - if (now >= milestone.date) return 'past'; - const prevMilestone = TIMELINE_MILESTONES[index - 1]; - if (!prevMilestone || now >= prevMilestone.date) return 'active'; - return 'upcoming'; -} +import { speakers } from '../data/speakers'; + +const SpeakerCard = ({ speaker, linkTo }) => ( + +
+ {speaker.photo ? ( + {speaker.name} + ) : ( +
+ +
+ )} +
{speaker.name}
+ {speaker.title &&
{speaker.title}
} + {speaker.talk?.title &&
{speaker.talk.title}
} + {speaker.talk?.category && {speaker.talk.category}} +
+ +); const Speakers = () => { useScrollAnimation(); const { t } = useTranslation(); const { l } = useLocalizedPath(); - const now = useMemo(() => new Date(), []); - const cfpStatus = getCfpStatus(now); - - const translatedMilestones = t('speakers.timelineMilestones', { returnObjects: true }); - - const milestones = TIMELINE_MILESTONES.map((m, i) => ({ - ...m, - label: translatedMilestones[i].label, - title: translatedMilestones[i].title, - description: translatedMilestones[i].description, - })); - - const bannerContent = { - upcoming: { text: t('speakers.cfpUpcoming', { date: milestones[0].label }) }, - open: { text: t('speakers.cfpOpen') }, - closed: { text: t('speakers.cfpClosed') }, - }[cfpStatus]; - - const ctaText = { - upcoming: t('speakers.ctaUpcoming', { date: milestones[0].label }), - open: t('speakers.ctaOpen', { date: milestones[1].label }), - closed: t('speakers.ctaClosed'), - }[cfpStatus]; + const confirmedSpeakers = speakers.filter(s => s.name !== 'TBA'); + const hasConfirmed = confirmedSpeakers.length > 0; return ( <> - {/* Page Header */}
-

{t('speakers.title')} {t('speakers.titleHighlight')}

-

{t('speakers.subtitle')}

+

{t('speakers.featuredTitle')} {t('speakers.featuredHighlight')}

+

{t('speakers.featuredSubtitle')}

- {/* Call for Speakers Banner */} -
-
-
-
- Speak -
-

{t('speakers.excitedTitle')}

-

- {t('speakers.excitedText')} -

- -

- {bannerContent.text}
- {t('speakers.bannerInvite')} -

- - {cfpStatus !== 'closed' && ( - {t('speakers.submitProposal')} - )} - -

- - {t('speakers.financialAidNote')}{' '} - - {t('speakers.financialAidLink')} - -

-
-
-
- - {/* Tribal Border */} -
- - {/* Proposal Guidelines */} -
+
-
-

{t('speakers.guidelinesTitle')} {t('speakers.guidelinesHighlight')}

-

{t('speakers.guidelinesSubtitle')}

-
- -
-

- {t('speakers.guidelinesIntro')} -

- -

- {t('speakers.guidelinesNote')} -

- - {/* Timeline */} -

- {t('speakers.timeline')} -

- -
- {milestones.map((milestone, index) => { - const status = getTimelineItemStatus(milestone, index, now); - return ( -
-
- {milestone.label} - {status === 'active' && {t('speakers.now')}} - {status === 'past' && {t('speakers.done')}} -
-
-

{milestone.title}

-

{milestone.description}

-
-
- ); - })} -
- - {/* Session Types */} -

- {t('speakers.sessionTypes')} -

- -
-
-

{t('speakers.regularTalks')}

-

- {t('speakers.regularTalksText')} -

-
- -
-

{t('speakers.lightningTalks')}

-

- {t('speakers.lightningTalksText')} -

-
- -
-

{t('speakers.workshopsSessions')}

-

- {t('speakers.workshopsSessionsText')} -

-
- -
-

{t('speakers.panelDiscussions')}

-

- {t('speakers.panelDiscussionsText')} -

-
-
- - {/* UbuCon Track */} -
-
- UbuCon - {t('speakers.ubuconTrackNew')} -
-

{t('speakers.ubuconTrackText')}

-
- - {/* Important Notes */} -

- {t('speakers.importantNotes')} -

- -
-

{t('speakers.aiPolicy')}

-

{t('speakers.aiPolicyText')}

-
- -
-

{t('speakers.inPersonOnly')}

-

{t('speakers.inPersonOnlyText')}

-
- -
-

{t('speakers.proposalLimit')}

-
    - {t('speakers.proposalLimitItems', { returnObjects: true }).map((item, index) => ( -
  • {item}
  • + {hasConfirmed ? ( + <> +
    + {confirmedSpeakers.map(speaker => ( + ))} -
-
- -
-

{t('speakers.recordingNotice')}

-

{t('speakers.recordingNoticeText')}

-
- -
-

{t('speakers.speakerSupport')}

-

{t('speakers.speakerSupportText')}

-
- - {/* Code of Conduct */} -

- {t('speakers.codeOfConduct')} -

- -

- {t('speakers.cocP1')} -

- -

- {t('speakers.cocP2')} -

- - -
-
-
- - {/* Featured Speakers Section */} - - - {/* Why Speak Section */} -
-
-
-

{t('speakers.whySpeakTitle')} {t('speakers.whySpeakHighlight')} {t('speakers.whySpeakSuffix')}

-
- -
-
-
- Travel -
-

{t('speakers.reachAudience')}

-

- {t('speakers.reachAudienceText')} -

-
- -
-
- Video
-

{t('speakers.recordedShared')}

-

- {t('speakers.recordedSharedText')} -

-
- -
-
- Ticket + + ) : ( +
+
+ Coming Soon
-

{t('speakers.freePass')}

-

- {t('speakers.freePassText')} +

{t('speakers.comingSoon')}

+

+ {t('speakers.comingSoonText')}

- -
-
- Networking -
-

{t('speakers.speakerNetworking')}

-

- {t('speakers.speakerNetworkingText')} -

-
- -
-
- Networking -
-

{t('speakers.buildBrand')}

-

- {t('speakers.buildBrandText')} -

-
- -
-
- Ideas -
-

{t('speakers.giveBack')}

-

- {t('speakers.giveBackText')} -

-
-
-
-
- - {/* CTA Section */} -
-
-

{t('speakers.readyToShare')}

-

- {ctaText} -

- {cfpStatus !== 'closed' && ( - - {t('speakers.submitProposalNow')} - )}
diff --git a/src/pages/UbuCon.jsx b/src/pages/UbuCon.jsx index c95072b..b070aba 100644 --- a/src/pages/UbuCon.jsx +++ b/src/pages/UbuCon.jsx @@ -3,6 +3,8 @@ import { Link } from 'react-router-dom'; import { useLocalizedPath } from '../hooks/useLocalizedPath'; import { useTranslation } from 'react-i18next'; import useScrollAnimation from '../hooks/useScrollAnimation'; +import { Clock, MapPin, User } from 'lucide-react'; +import { agenda, TYPE_STYLES } from '../data/agenda'; const UbuConMap = lazy(() => import('../components/UbuConMap')); @@ -60,12 +62,6 @@ const UbuCon = () => {

{t('ubucon.whatIsP2')}

-
{ {/* Tribal Border */}
+ {/* UbuCon Agenda */} +
+
+
+

{t('ubucon.agendaTitle')} {t('ubucon.agendaHighlight')}

+

September 18, 2026

+
+
+ {agenda.day2.map((session, i) => { + const style = TYPE_STYLES[session.type] || TYPE_STYLES.talk; + const isBreak = session.type === 'break' || session.type === 'social'; + return ( +
+
+ {session.time} +
+ {isBreak ? ( +
+ {session.title}{session.room ? `, ${session.room}` : ''} +
+ ) : ( +
+
+

{session.title}

+ + {style.label} + +
+
+ {session.speaker && ( + + {session.speaker} + + )} + {session.room && ( + + {session.room} + + )} +
+
+ )} +
+ ); + })} +
+
+
+ + {/* Tribal Border */} +
+ {/* CTA */}
@@ -150,12 +198,8 @@ const UbuCon = () => { {t('ubucon.ctaText')}

- - {t('ubucon.ctaSubmit')} - - {t('ubucon.ctaTicket')}