208 lines
7.5 KiB
TypeScript
208 lines
7.5 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import Link from 'next/link';
|
|
import Card from '@/components/ui/Card';
|
|
import Button from '@/components/ui/Button';
|
|
import { UserTicket } from '@/lib/api';
|
|
|
|
interface TicketsTabProps {
|
|
tickets: UserTicket[];
|
|
language: string;
|
|
}
|
|
|
|
export default function TicketsTab({ tickets, language }: TicketsTabProps) {
|
|
const [filter, setFilter] = useState<'all' | 'upcoming' | 'past'>('all');
|
|
|
|
const now = new Date();
|
|
const filteredTickets = tickets.filter((ticket) => {
|
|
if (filter === 'all') return true;
|
|
const eventDate = ticket.event?.startDatetime
|
|
? new Date(ticket.event.startDatetime)
|
|
: null;
|
|
if (filter === 'upcoming') return eventDate && eventDate > now;
|
|
if (filter === 'past') return eventDate && eventDate <= now;
|
|
return true;
|
|
});
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
return new Date(dateStr).toLocaleDateString(language === 'es' ? 'es-ES' : 'en-US', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
});
|
|
};
|
|
|
|
const formatCurrency = (amount: number, currency: string = 'PYG') => {
|
|
if (currency === 'PYG') {
|
|
return `${amount.toLocaleString('es-PY')} PYG`;
|
|
}
|
|
return `$${amount.toFixed(2)} ${currency}`;
|
|
};
|
|
|
|
const getStatusBadge = (status: string) => {
|
|
const styles: Record<string, string> = {
|
|
confirmed: 'bg-green-100 text-green-800',
|
|
checked_in: 'bg-blue-100 text-blue-800',
|
|
pending: 'bg-yellow-100 text-yellow-800',
|
|
cancelled: 'bg-red-100 text-red-800',
|
|
};
|
|
const labels: Record<string, Record<string, string>> = {
|
|
en: {
|
|
confirmed: 'Confirmed',
|
|
checked_in: 'Checked In',
|
|
pending: 'Pending',
|
|
cancelled: 'Cancelled',
|
|
},
|
|
es: {
|
|
confirmed: 'Confirmado',
|
|
checked_in: 'Registrado',
|
|
pending: 'Pendiente',
|
|
cancelled: 'Cancelado',
|
|
},
|
|
};
|
|
return (
|
|
<span className={`px-2 py-1 text-xs rounded-full ${styles[status] || 'bg-gray-100 text-gray-800'}`}>
|
|
{labels[language]?.[status] || status}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Filter Buttons */}
|
|
<div className="flex gap-2">
|
|
{(['all', 'upcoming', 'past'] as const).map((f) => (
|
|
<button
|
|
key={f}
|
|
onClick={() => setFilter(f)}
|
|
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
filter === f
|
|
? 'bg-secondary-blue text-white'
|
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
|
}`}
|
|
>
|
|
{f === 'all' && (language === 'es' ? 'Todas' : 'All')}
|
|
{f === 'upcoming' && (language === 'es' ? 'Próximas' : 'Upcoming')}
|
|
{f === 'past' && (language === 'es' ? 'Pasadas' : 'Past')}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Tickets List */}
|
|
{filteredTickets.length === 0 ? (
|
|
<Card className="p-8 text-center">
|
|
<p className="text-gray-600 mb-4">
|
|
{language === 'es' ? 'No tienes entradas' : 'You have no tickets'}
|
|
</p>
|
|
<Link href="/events">
|
|
<Button>
|
|
{language === 'es' ? 'Explorar Eventos' : 'Explore Events'}
|
|
</Button>
|
|
</Link>
|
|
</Card>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{filteredTickets.map((ticket) => (
|
|
<Card key={ticket.id} className="p-4">
|
|
<div className="flex flex-col md:flex-row md:items-center gap-4">
|
|
{/* Event Image */}
|
|
{ticket.event?.bannerUrl && (
|
|
<div className="w-full md:w-32 h-24 rounded-lg overflow-hidden flex-shrink-0">
|
|
<img
|
|
src={ticket.event.bannerUrl}
|
|
alt={ticket.event.title}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Ticket Info */}
|
|
<div className="flex-1">
|
|
<div className="flex items-start justify-between gap-2">
|
|
<h3 className="font-semibold">
|
|
{language === 'es' && ticket.event?.titleEs
|
|
? ticket.event.titleEs
|
|
: ticket.event?.title || 'Event'}
|
|
</h3>
|
|
{getStatusBadge(ticket.status)}
|
|
</div>
|
|
|
|
<div className="mt-2 space-y-1 text-sm text-gray-600">
|
|
{ticket.event?.startDatetime && (
|
|
<p>
|
|
<span className="font-medium">
|
|
{language === 'es' ? 'Fecha:' : 'Date:'}
|
|
</span>{' '}
|
|
{formatDate(ticket.event.startDatetime)}
|
|
</p>
|
|
)}
|
|
{ticket.event?.location && (
|
|
<p>
|
|
<span className="font-medium">
|
|
{language === 'es' ? 'Lugar:' : 'Location:'}
|
|
</span>{' '}
|
|
{ticket.event.location}
|
|
</p>
|
|
)}
|
|
{ticket.payment && (
|
|
<p>
|
|
<span className="font-medium">
|
|
{language === 'es' ? 'Pago:' : 'Payment:'}
|
|
</span>{' '}
|
|
{formatCurrency(ticket.payment.amount, ticket.payment.currency)} -
|
|
<span className={`ml-1 ${
|
|
ticket.payment.status === 'paid' ? 'text-green-600' : 'text-yellow-600'
|
|
}`}>
|
|
{ticket.payment.status === 'paid'
|
|
? (language === 'es' ? 'Pagado' : 'Paid')
|
|
: (language === 'es' ? 'Pendiente' : 'Pending')}
|
|
</span>
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex flex-col gap-2">
|
|
<Link href={`/booking/success/${ticket.id}`}>
|
|
<Button size="sm" className="w-full">
|
|
{language === 'es' ? 'Ver Entrada' : 'View Ticket'}
|
|
</Button>
|
|
</Link>
|
|
{(ticket.status === 'confirmed' || ticket.status === 'checked_in') && (
|
|
<a
|
|
href={ticket.bookingId
|
|
? `/api/tickets/booking/${ticket.bookingId}/pdf`
|
|
: `/api/tickets/${ticket.id}/pdf`
|
|
}
|
|
download
|
|
className="text-center"
|
|
>
|
|
<Button variant="outline" size="sm" className="w-full">
|
|
{language === 'es' ? 'Descargar Ticket(s)' : 'Download Ticket(s)'}
|
|
</Button>
|
|
</a>
|
|
)}
|
|
{ticket.invoice && (
|
|
<a
|
|
href={ticket.invoice.pdfUrl || '#'}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-center"
|
|
>
|
|
<Button variant="outline" size="sm" className="w-full">
|
|
{language === 'es' ? 'Descargar Factura' : 'Download Invoice'}
|
|
</Button>
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|