Files
Spanglish/frontend/src/app/(public)/dashboard/components/TicketsTab.tsx

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>
);
}