import React, { useEffect, useState, useCallback } from "react"; import { api } from "../api/client"; import { useAuth } from "../context/AuthContext"; import { PageHeader } from "../components/PageHeader"; import { VendorFilter } from "../components/VendorFilter"; import { Table } from "../components/Table"; import { Modal } from "../components/Modal"; import { FormField, Btn, inputStyle } from "../components/FormField"; // ─── Types ────────────────────────────────────────────────────────────────── interface Vendor { id: string; name: string; } interface EventTaxOverride { id: string; taxId: string; rate: number; tax: { name: string; rate: number }; } interface EventProductItem { id: string; productId: string; product: { id: string; name: string; price: number; sku?: string }; } interface Event { id: string; name: string; description?: string; startsAt: string; endsAt: string; isActive: boolean; vendorId: string; vendor?: Vendor; taxOverrides?: EventTaxOverride[]; products?: EventProductItem[]; _count?: { products: number; taxOverrides: number; transactions: number }; } interface Tax { id: string; name: string; rate: number; } interface Product { id: string; name: string; price: number; sku?: string; } interface ApiList { data: T[]; pagination: { total: number } } interface EventSummary { event: { id: string; name: string; startsAt: string; endsAt: string }; totals: { revenue: number; tax: number; discounts: number; transactionCount: number; averageTransaction: number }; byPaymentMethod: { method: string; revenue: number; count: number }[]; topProducts: { productId: string; productName: string; revenue: number; unitsSold: number }[]; } // ─── Main Page ────────────────────────────────────────────────────────────── export default function EventsPage() { const { user } = useAuth(); const [vendorId, setVendorId] = useState(user?.vendorId ?? ""); const [events, setEvents] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editing, setEditing] = useState(null); const [detail, setDetail] = useState(null); const [detailView, setDetailView] = useState<"config" | "report">("config"); const load = useCallback(async () => { setLoading(true); try { const q = vendorId && user?.role === "admin" ? `?vendorId=${encodeURIComponent(vendorId)}&limit=50` : "?limit=50"; const res = await api.get>(`/events${q}`); setEvents(res.data); setTotal(res.pagination.total); } catch (err) { console.error(err); } finally { setLoading(false); } }, [vendorId, user?.role]); useEffect(() => { load(); }, [load]); const openDetail = async (ev: Event) => { try { const full = await api.get(`/events/${ev.id}`); setDetail(full); setDetailView("config"); } catch (err) { console.error(err); } }; const handleDelete = async (id: string) => { if (!confirm("Delete this event? This cannot be undone.")) return; await api.delete(`/events/${id}`); load(); }; const columns = [ { key: "name", header: "Name", render: (ev: Event) => ev.name }, { key: "dates", header: "Dates", render: (ev: Event) => `${fmtDate(ev.startsAt)} → ${fmtDate(ev.endsAt)}` }, { key: "vendor", header: "Vendor", render: (ev: Event) => ev.vendor?.name ?? ev.vendorId }, { key: "status", header: "Status", render: (ev: Event) => ( {ev.isActive ? "Active" : "Inactive"} ) }, { key: "counts", header: "Products / Taxes / Txns", render: (ev: Event) => `${ev._count?.products ?? 0} / ${ev._count?.taxOverrides ?? 0} / ${ev._count?.transactions ?? 0}` }, { key: "actions", header: "", render: (ev: Event) => (
openDetail(ev)}>View { setEditing(ev); setShowForm(true); }}>Edit handleDelete(ev.id)}>Delete
) }, ]; return (
{ setEditing(null); setShowForm(true); }}>+ New Event} />
{showForm && ( setShowForm(false)} onSaved={() => { setShowForm(false); load(); }} /> )} {detail && ( setDetail(null)} onRefresh={() => openDetail(detail)} /> )} ); } // ─── Event Form Modal ──────────────────────────────────────────────────────── function EventFormModal({ event, defaultVendorId, onClose, onSaved }: { event: Event | null; defaultVendorId?: string; onClose: () => void; onSaved: () => void; }) { const { user } = useAuth(); const isAdmin = user?.role === "admin"; const [name, setName] = useState(event?.name ?? ""); const [description, setDescription] = useState(event?.description ?? ""); const [startsAt, setStartsAt] = useState(event ? toDatetimeLocal(event.startsAt) : ""); const [endsAt, setEndsAt] = useState(event ? toDatetimeLocal(event.endsAt) : ""); const [isActive, setIsActive] = useState(event?.isActive ?? true); const [selectedVendorId, setSelectedVendorId] = useState( event?.vendorId ?? defaultVendorId ?? "" ); const [vendors, setVendors] = useState([]); const [error, setError] = useState(""); const [saving, setSaving] = useState(false); useEffect(() => { if (isAdmin && !event) { api.get<{ data: Vendor[] }>("/vendors?limit=200") .then((r) => setVendors(r.data)) .catch(console.error); } }, [isAdmin, event]); const save = async () => { if (isAdmin && !event && !selectedVendorId) { setError("Please select a vendor for this event."); return; } setSaving(true); setError(""); try { const body = { name, description: description || undefined, startsAt: new Date(startsAt).toISOString(), endsAt: new Date(endsAt).toISOString(), isActive, }; if (event) { await api.put(`/events/${event.id}`, body); } else { const q = isAdmin && selectedVendorId ? `?vendorId=${encodeURIComponent(selectedVendorId)}` : ""; await api.post(`/events${q}`, body); } onSaved(); } catch (err) { setError(err instanceof Error ? err.message : "Save failed"); } finally { setSaving(false); } }; return ( {isAdmin && !event && ( )} setName(e.target.value)} />