Add ticket system with QR scanner and PDF generation

- Add ticket validation and check-in API endpoints
- Add PDF ticket generation with QR codes (pdfkit)
- Add admin QR scanner page with camera support
- Add admin site settings page
- Update email templates with PDF ticket download link
- Add checked_in_by_admin_id field for audit tracking
- Update booking success page with ticket download
- Various UI improvements to events and booking pages
This commit is contained in:
Michilis
2026-02-02 00:45:12 +00:00
parent b0cbaa60f0
commit 9410e83b89
28 changed files with 1930 additions and 85 deletions
+5 -8
View File
@@ -31,13 +31,9 @@ export default function Footer() {
className="h-10 w-auto"
/>
</Link>
<p className="mt-3 text-gray-600 max-w-md">
<p className="mt-3 max-w-md" style={{ color: '#002F44' }}>
{t('footer.tagline')}
</p>
{/* Local SEO text */}
<p className="mt-2 text-sm text-gray-500">
Language Exchange Events in Asunción, Paraguay
</p>
</div>
{/* Quick Links */}
@@ -84,7 +80,7 @@ export default function Footer() {
{/* Social */}
{socialLinks.length > 0 && (
<div>
<h3 className="font-semibold text-primary-dark mb-4">
<h3 className="font-semibold mb-4" style={{ color: '#002F44' }}>
{t('footer.social')}
</h3>
<div className="flex flex-wrap gap-3">
@@ -112,13 +108,14 @@ export default function Footer() {
<Link
key={link.slug}
href={`/legal/${link.slug}`}
className="text-gray-500 hover:text-primary-dark transition-colors text-sm"
className="hover:opacity-70 transition-colors text-sm"
style={{ color: '#002F44' }}
>
{locale === 'es' ? link.es : link.en}
</Link>
))}
</div>
<div className="text-center text-gray-500 text-sm">
<div className="text-center text-sm" style={{ color: '#002F44' }}>
{t('footer.copyright', { year: currentYear })}
</div>
</div>
+36 -9
View File
@@ -3,6 +3,7 @@
import Link from 'next/link';
import Image from 'next/image';
import { useState } from 'react';
import { usePathname } from 'next/navigation';
import { useLanguage } from '@/context/LanguageContext';
import { useAuth } from '@/context/AuthContext';
import LanguageToggle from '@/components/LanguageToggle';
@@ -10,6 +11,37 @@ import Button from '@/components/ui/Button';
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
const pathname = usePathname();
const isActive = pathname === href || (href !== '/' && pathname.startsWith(href));
return (
<Link
href={href}
className="font-medium transition-colors"
style={{ color: isActive ? '#FBB82B' : '#002F44' }}
>
{children}
</Link>
);
}
function MobileNavLink({ href, children, onClick }: { href: string; children: React.ReactNode; onClick: () => void }) {
const pathname = usePathname();
const isActive = pathname === href || (href !== '/' && pathname.startsWith(href));
return (
<Link
href={href}
className="px-4 py-2 hover:bg-gray-50 rounded-lg font-medium"
style={{ color: isActive ? '#FBB82B' : '#002F44' }}
onClick={onClick}
>
{children}
</Link>
);
}
export default function Header() {
const { t } = useLanguage();
const { user, isAdmin, logout } = useAuth();
@@ -41,13 +73,9 @@ export default function Header() {
{/* Desktop Navigation */}
<div className="hidden md:flex items-center gap-6">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-gray-700 hover:text-primary-dark font-medium transition-colors"
>
<NavLink key={link.href} href={link.href}>
{link.label}
</Link>
</NavLink>
))}
</div>
@@ -115,14 +143,13 @@ export default function Header() {
>
<div className="flex flex-col gap-2 pt-4">
{navLinks.map((link) => (
<Link
<MobileNavLink
key={link.href}
href={link.href}
className="px-4 py-2 text-gray-700 hover:bg-gray-50 rounded-lg font-medium"
onClick={() => setMobileMenuOpen(false)}
>
{link.label}
</Link>
</MobileNavLink>
))}
<div className="border-t border-gray-100 mt-2 pt-4 px-4">