Files
event-qr/client/src/pages/Home.tsx
2025-11-29 13:50:40 +01:00

144 lines
6.9 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { Plus, Users, QrCode, Trash2 } from 'lucide-react';
import { getTickets, deleteTicket } from '../utils/storage';
import type { Ticket } from '../types';
export const Home: React.FC = () => {
const [tickets, setTickets] = useState<Ticket[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchTickets = async () => {
try {
setLoading(true);
const fetchedTickets = await getTickets('default-event'); // For now, single event
setTickets(fetchedTickets);
} catch (err) {
setError('Failed to load tickets.');
console.error(err);
} finally {
setLoading(false);
}
};
fetchTickets();
}, []);
const handleDelete = async (id: string) => {
if (window.confirm('Are you sure you want to delete this ticket?')) {
try {
await deleteTicket(id);
setTickets(tickets.filter(t => t.id !== id));
} catch (err) {
alert('Failed to delete ticket.');
console.error(err);
}
}
};
return (
<div className="space-y-6 md:space-y-8">
<header className="flex flex-col sm:flex-row justify-between items-start sm:items-end gap-4">
<div>
<h1 className="text-3xl md:text-4xl font-bold mb-2">Dashboard</h1>
<p className="text-slate-400 text-sm md:text-base">Manage your events and attendees</p>
</div>
<Link to="/generate" className="btn-primary whitespace-nowrap">
<Plus size={20} />
<span className="hidden sm:inline">New Ticket</span>
<span className="sm:hidden">New</span>
</Link>
</header>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="glass-panel p-6">
<div className="flex items-center gap-4 mb-4">
<div className="p-3 bg-indigo-500/20 rounded-lg text-indigo-400">
<Users size={24} />
</div>
<div>
<p className="text-sm text-slate-400">Total Attendees</p>
<p className="text-2xl font-bold">{tickets.length}</p>
</div>
</div>
</div>
<div className="glass-panel p-6">
<div className="flex items-center gap-4 mb-4">
<div className="p-3 bg-purple-500/20 rounded-lg text-purple-400">
<QrCode size={24} />
</div>
<div>
<p className="text-sm text-slate-400">Scanned Tickets</p>
<p className="text-2xl font-bold">{tickets.filter(t => t.status === 'used').length}</p>
</div>
</div>
</div>
</div>
<div className="glass-panel p-4 md:p-6">
<h2 className="text-lg md:text-xl font-bold mb-4">Recent Tickets</h2>
{loading ? (
<div className="text-center py-8 text-slate-500">
Loading tickets...
</div>
) : error ? (
<div className="text-center py-8 text-red-500">
{error}
</div>
) : tickets.length === 0 ? (
<div className="text-center py-8 text-slate-500">
No tickets generated yet.
</div>
) : (
<div className="overflow-x-auto -mx-4 md:mx-0">
<table className="w-full text-left border-collapse min-w-[600px]">
<thead>
<tr className="text-slate-400 border-b border-slate-700 text-sm">
<th className="p-3 md:p-4">Name</th>
<th className="p-3 md:p-4 hidden sm:table-cell">Email</th>
<th className="p-3 md:p-4">Type</th>
<th className="p-3 md:p-4">Status</th>
<th className="p-3 md:p-4 text-right">Actions</th>
</tr>
</thead>
<tbody>
{tickets.slice().reverse().map(ticket => (
<tr key={ticket.id} className="border-b border-slate-800 hover:bg-slate-800/50 text-sm">
<td className="p-3 md:p-4 font-medium">{ticket.attendeeName}</td>
<td className="p-3 md:p-4 text-slate-400 hidden sm:table-cell">{ticket.attendeeEmail}</td>
<td className="p-3 md:p-4">
<span className={`px-2 py-1 rounded-full text-xs whitespace-nowrap ${ticket.ticketType === 'Partyborner' ? 'bg-amber-500/20 text-amber-400' : 'bg-blue-500/20 text-blue-400'
}`}>
{ticket.ticketType}
</span>
</td>
<td className="p-3 md:p-4">
<span className={`px-2 py-1 rounded-full text-xs whitespace-nowrap ${ticket.status === 'valid' ? 'bg-green-500/20 text-green-400' : 'bg-slate-500/20 text-slate-400'
}`}>
{ticket.status}
</span>
</td>
<td className="p-3 md:p-4 text-right">
<button
onClick={() => handleDelete(ticket.id)}
className="text-slate-500 hover:text-red-400 transition-colors p-2"
title="Delete Ticket"
>
<Trash2 size={18} />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
);
};