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

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">