Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<title>PyCon Cameroon 2026 | Cameroon's Premier Python Conference</title>

<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/images/branding/preview.jpg">
<link rel="apple-touch-icon" href="/images/branding/preview.jpg">
<link rel="icon" type="image/webp" href="/images/branding/python-cameroon-logo.webp">
<link rel="apple-touch-icon" href="/images/branding/python-cameroon-logo.webp">

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
Expand Down
99 changes: 99 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
"deploy:preview": "npm run build && firebase hosting:channel:deploy preview"
},
"dependencies": {
"i18next": "^26.0.3",
"leaflet": "^1.9.4",
"lucide-react": "^1.7.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-i18next": "^17.0.2",
"react-leaflet": "^5.0.0",
"react-router-dom": "^7.13.0"
},
Expand Down
47 changes: 23 additions & 24 deletions src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';

const Footer = () => {
const { t } = useTranslation();

return (
<footer className="footer">
<div className="container">
Expand All @@ -13,8 +16,7 @@ const Footer = () => {
</div>

<p className="footer-description">
Cameroon's first Python conference, bringing together developers, enthusiasts,
and innovators from across Africa and beyond.
{t('footer.description')}
</p>
<div className="footer-social mt-md">
<a href="https://x.com/PythonCameroon" aria-label="X" data-tooltip="Follow us on X"
Expand All @@ -26,8 +28,7 @@ const Footer = () => {
</a>
<a href="https://linkedin.com/company/PythonCameroon" aria-label="LinkedIn" className="social-link">
<svg className="social-icon-svg" viewBox="0 0 24 24">
<path
d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
</svg>
</a>
<a href="https://discord.gg/mC5zzqGmQ5" aria-label="Discord" className="social-link">
Expand All @@ -38,8 +39,7 @@ const Footer = () => {
</a>
<a href="https://github.com/PythonCameroon" aria-label="GitHub" className="social-link">
<svg className="social-icon-svg" viewBox="0 0 24 24">
<path
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
</a>
<a href="https://chat.whatsapp.com/Ckc80ophGEH0NJFmZAzDMr" aria-label="WhatsApp"
Expand All @@ -53,48 +53,47 @@ const Footer = () => {
</div>

<div>
<h4 className="footer-title">About</h4>
<h4 className="footer-title">{t('footer.about')}</h4>
<div className="footer-links">
<Link to="/about">Overview</Link>
<Link to="/about#team">Team</Link>
<Link to="/code-of-conduct">Code of Conduct</Link>
<a href="https://www.python.org/psf-landing/">Python Software Foundation</a>
<Link to="/about">{t('footer.overview')}</Link>
<Link to="/about#team">{t('footer.team')}</Link>
<Link to="/code-of-conduct">{t('footer.codeOfConduct')}</Link>
<a href="https://www.python.org/psf-landing/">{t('footer.psf')}</a>
</div>
</div>

<div>
<h4 className="footer-title">Program</h4>
<h4 className="footer-title">{t('footer.program')}</h4>
<div className="footer-links">
<Link to="/speakers">Speakers</Link>
<Link to="/speakers#guidelines">Proposal Guidelines</Link>
{/* <Link to="/attend">Attend</Link> */}
<Link to="/venue">Venue</Link>
<Link to="/speakers">{t('footer.speakers')}</Link>
<Link to="/speakers#guidelines">{t('footer.proposalGuidelines')}</Link>
<Link to="/venue">{t('footer.venue')}</Link>
</div>
</div>

<div>
<h4 className="footer-title">Contact</h4>
<h4 className="footer-title">{t('footer.contact')}</h4>
<div className="footer-links">
<a href="mailto:organizers@pythoncameroon.org">organizers@pythoncameroon.org</a>
<Link to="/sponsor">Sponsorship</Link>
<Link to="/sponsor">Support</Link>
<Link to="/sponsor">{t('footer.sponsorship')}</Link>
<Link to="/sponsor">{t('footer.support')}</Link>
</div>
</div>
</div>

<div className="footer-bottom">
<div>
<p>© 2026 PyCon Cameroon. All rights reserved.</p>
<p>{t('footer.copyright')}</p>
<p style={{ fontSize: '0.8rem', marginTop: '5px' }}>
Built by <a href="https://github.com/azilmuluh" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--color-orange)' }}>Muluh Azinwi Success</a>
{t('footer.builtBy')} <a href="https://github.com/azilmuluh" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--color-orange)' }}>Muluh Azinwi Success</a>
</p>
</div>
<div>
<Link to="/terms">Terms and Conditions</Link>
<Link to="/terms">{t('footer.terms')}</Link>
<span style={{ margin: '0 var(--spacing-sm)' }}>|</span>
<Link to="/privacy">Privacy Policy</Link>
<Link to="/privacy">{t('footer.privacy')}</Link>
<span style={{ margin: '0 var(--spacing-sm)' }}>|</span>
<Link to="/health-safety">Health & Safety</Link>
<Link to="/health-safety">{t('footer.healthSafety')}</Link>
</div>
</div>
</div>
Expand Down
41 changes: 27 additions & 14 deletions src/components/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import React, { useState, useEffect } from 'react';
import { NavLink, Link, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Languages } from 'lucide-react';

const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const location = useLocation();
const { t, i18n } = useTranslation();

const toggleLanguage = () => {
i18n.changeLanguage(i18n.language === 'en' ? 'fr' : 'en');
};

useEffect(() => {
const handleScroll = () => {
Expand Down Expand Up @@ -41,16 +48,19 @@ const Navbar = () => {
</Link>

<div className={`nav-links ${isOpen ? 'active' : ''}`} id="navLinks">
<NavLink to="/" className={({ isActive }) => isActive ? "active" : ""}>Home</NavLink>
<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>About</NavLink>
<NavLink to="/speakers" className={({ isActive }) => isActive ? "active" : ""}>Speakers</NavLink>
<NavLink to="/sponsor" className={({ isActive }) => isActive ? "active" : ""}>Sponsor</NavLink>
<NavLink to="/attend" className={({ isActive }) => isActive ? "active" : ""}>Attend</NavLink>
<NavLink to="/venue" className={({ isActive }) => isActive ? "active" : ""}>Venue</NavLink>
<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.about')}</NavLink>
<NavLink to="/speakers" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.speakers')}</NavLink>
<NavLink to="/sponsor" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.sponsor')}</NavLink>
<NavLink to="/attend" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.attend')}</NavLink>
<NavLink to="/venue" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.venue')}</NavLink>
<NavLink to="/ubucon" className={({ isActive }) => isActive ? "active" : ""} style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<img src="/images/partners/canonical-cm.webp" alt="" style={{ width: '18px', height: '18px', objectFit: 'contain', borderRadius: '50%' }} />
UbuCon
{t('nav.ubucon')}
</NavLink>
<button onClick={toggleLanguage} className="lang-toggle" aria-label="Toggle language">
<Languages size={16} />
{i18n.language === 'en' ? 'FR' : 'EN'}
</button>
</div>

{/* Mobile drawer overlay */}
Expand All @@ -66,16 +76,19 @@ const Navbar = () => {
&times;
</button>
<div className="nav-drawer-links">
<NavLink to="/" className={({ isActive }) => isActive ? "active" : ""}>Home</NavLink>
<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>About</NavLink>
<NavLink to="/speakers" className={({ isActive }) => isActive ? "active" : ""}>Speakers</NavLink>
<NavLink to="/sponsor" className={({ isActive }) => isActive ? "active" : ""}>Sponsor</NavLink>
<NavLink to="/attend" className={({ isActive }) => isActive ? "active" : ""}>Attend</NavLink>
<NavLink to="/venue" className={({ isActive }) => isActive ? "active" : ""}>Venue</NavLink>
<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.about')}</NavLink>
<NavLink to="/speakers" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.speakers')}</NavLink>
<NavLink to="/sponsor" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.sponsor')}</NavLink>
<NavLink to="/attend" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.attend')}</NavLink>
<NavLink to="/venue" className={({ isActive }) => isActive ? "active" : ""}>{t('nav.venue')}</NavLink>
<NavLink to="/ubucon" className={({ isActive }) => isActive ? "active" : ""} style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
<img src="/images/partners/canonical-cm.webp" alt="" style={{ width: '22px', height: '22px', objectFit: 'contain', borderRadius: '50%' }} />
UbuCon
{t('nav.ubucon')}
</NavLink>
<button onClick={toggleLanguage} className="lang-toggle" aria-label="Toggle language">
<Languages size={16} />
{i18n.language === 'en' ? 'FR' : 'EN'}
</button>
</div>
</div>

Expand Down
Loading
Loading