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:
@@ -13,10 +13,10 @@ export default function HeroSection() {
|
||||
<div className="container-page py-16 md:py-24">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold text-primary-dark leading-tight text-balance">
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight text-balance" style={{ color: '#002F44' }}>
|
||||
{t('home.hero.title')}
|
||||
</h1>
|
||||
<p className="mt-6 text-xl text-gray-600">
|
||||
<p className="mt-6 text-xl" style={{ color: '#002F44' }}>
|
||||
{t('home.hero.subtitle')}
|
||||
</p>
|
||||
<div className="mt-8 flex flex-wrap gap-4">
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function NewsletterSection() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section className="section-padding bg-primary-dark text-white">
|
||||
<section className="section-padding text-white" style={{ backgroundColor: '#002F44' }}>
|
||||
<div className="container-page">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<SparklesIcon className="w-12 h-12 mx-auto text-primary-yellow" />
|
||||
|
||||
@@ -62,10 +62,10 @@ export default function NextEventSection() {
|
||||
<h3 className="text-2xl font-bold text-primary-dark">
|
||||
{locale === 'es' && nextEvent.titleEs ? nextEvent.titleEs : nextEvent.title}
|
||||
</h3>
|
||||
<p className="mt-3 text-gray-600">
|
||||
{locale === 'es' && nextEvent.descriptionEs
|
||||
? nextEvent.descriptionEs
|
||||
: nextEvent.description}
|
||||
<p className="mt-3 text-gray-600 whitespace-pre-line">
|
||||
{locale === 'es'
|
||||
? (nextEvent.shortDescriptionEs || nextEvent.descriptionEs || nextEvent.shortDescription || nextEvent.description)
|
||||
: (nextEvent.shortDescription || nextEvent.description)}
|
||||
</p>
|
||||
|
||||
<div className="mt-6 space-y-3">
|
||||
@@ -93,9 +93,11 @@ export default function NextEventSection() {
|
||||
? t('events.details.free')
|
||||
: `${nextEvent.price.toLocaleString()} ${nextEvent.currency}`}
|
||||
</span>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
{nextEvent.availableSeats} {t('events.details.spotsLeft')}
|
||||
</p>
|
||||
{!nextEvent.externalBookingEnabled && (
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
{nextEvent.availableSeats} {t('events.details.spotsLeft')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<Button size="lg" className="mt-6">
|
||||
{t('common.moreInfo')}
|
||||
|
||||
Reference in New Issue
Block a user