"use client"; /* eslint-disable react-hooks/set-state-in-effect */ import { useState, useTransition, useEffect, useMemo } from "react"; import { useRouter } from "next/navigation"; import { deleteLocation } from "@/actions/locations"; import AddLocationModal from "@/components/admin/AddLocationModal"; import EditLocationModal, { type LocationForEdit } from "@/components/admin/EditLocationModal"; import { AdminSearchInput, AdminFilterTabs, AdminButton, AdminIconButton, useToast, Skeleton, } from "@/components/admin/design-system"; export type AdminLocation = { id: string; brand_id: string; name: string; address: string | null; city: string | null; state: string | null; zip: string | null; phone: string | null; contact_name: string | null; contact_email: string | null; notes: string | null; active: boolean; deleted_at: string | null; created_at: string; updated_at: string; slug: string | null; stop_count: number; }; type Props = { locations: AdminLocation[]; brandId: string; }; type StatusFilter = "all" | "active" | "inactive"; export default function LocationsTab({ locations, brandId }: Props) { const router = useRouter(); const { success: showSuccess, error: showError } = useToast(); const [, startTransition] = useTransition(); const [search, setSearch] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const [page, setPage] = useState(0); const [isLoading, setIsLoading] = useState(false); const [deleteError, setDeleteError] = useState(null); const [showAdd, setShowAdd] = useState(false); const [editing, setEditing] = useState(null); const [pendingDeleteId, setPendingDeleteId] = useState(null); const PAGE_SIZE = 50; useEffect(() => { setIsLoading(true); const t = setTimeout(() => setIsLoading(false), 250); return () => clearTimeout(t); }, [page, statusFilter, search]); const counts = useMemo( () => ({ all: locations.length, active: locations.filter((l) => l.active).length, inactive: locations.filter((l) => !l.active).length, }), [locations] ); const filtered = useMemo(() => { const q = search.trim().toLowerCase(); return locations.filter((l) => { const matchesSearch = !q || l.name.toLowerCase().includes(q) || (l.address ?? "").toLowerCase().includes(q) || (l.city ?? "").toLowerCase().includes(q) || (l.contact_name ?? "").toLowerCase().includes(q); const matchesStatus = statusFilter === "all" || (statusFilter === "active" && l.active) || (statusFilter === "inactive" && !l.active); return matchesSearch && matchesStatus; }); }, [locations, search, statusFilter]); const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE)); const paginated = filtered.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE); const tabs = [ { value: "all", label: "All", count: counts.all }, { value: "active", label: "Active", count: counts.active }, { value: "inactive", label: "Inactive", count: counts.inactive }, ]; function handleAdded() { setShowAdd(false); showSuccess("Venue created"); startTransition(() => router.refresh()); } function handleEdited() { setEditing(null); showSuccess("Venue updated"); startTransition(() => router.refresh()); } async function handleDelete(loc: AdminLocation) { if (!confirm(`Delete venue "${loc.name}"? This cannot be undone.`)) return; setPendingDeleteId(loc.id); setDeleteError(null); const res = await deleteLocation(loc.id, brandId); setPendingDeleteId(null); if (res.success) { showSuccess("Venue deleted"); startTransition(() => router.refresh()); } else { setDeleteError(res.error ?? "Delete failed"); showError("Delete failed", res.error ?? "Unknown error"); } } return ( <> {/* Filter bar */}
{ setSearch(e.target.value); setPage(0); }} onClear={() => { setSearch(""); setPage(0); }} showClear className="flex-1 min-w-48 max-w-72" /> { setStatusFilter(value as StatusFilter); setPage(0); }} tabs={tabs} size="sm" showCounts /> {filtered.length} venue{filtered.length !== 1 ? "s" : ""} {totalPages > 1 && (
setPage((p) => Math.max(0, p - 1))} disabled={page === 0 || isLoading} className="!rounded-lg border border-[var(--admin-border)]" > {page + 1}/{totalPages} setPage((p) => Math.min(totalPages - 1, p + 1))} disabled={page >= totalPages - 1 || isLoading} className="!rounded-lg border border-[var(--admin-border)]" >
)} setShowAdd(true)} icon={ } > Add Venue
{/* Delete error */} {deleteError && (
{deleteError}{" "}
)} {/* Table */} {isLoading ? ( Array.from({ length: 6 }).map((_, i) => ( )) ) : filtered.length === 0 ? ( ) : ( paginated.map((loc) => ( )) )}
Venue Address Contact Stops Status

{search || statusFilter !== "all" ? "No venues match your filters." : "No venues yet. Add your first venue to get started."}

{loc.name}
{(loc.city || loc.state) && (
{[loc.city, loc.state].filter(Boolean).join(", ")}
)}
{loc.address ? (
{loc.address}
{loc.zip && (
{loc.zip}
)}
) : ( )}
{loc.contact_name || loc.phone || loc.contact_email ? (
{loc.contact_name && (
{loc.contact_name}
)} {loc.phone && (
{loc.phone}
)} {loc.contact_email && (
{loc.contact_email}
)}
) : ( )}
{loc.stop_count > 0 ? ( {loc.stop_count} ) : ( 0 )} {loc.active ? ( Active ) : ( Inactive )}
{showAdd && ( setShowAdd(false)} brandId={brandId} onSuccess={handleAdded} /> )} {editing && ( setEditing(null)} location={editing} brandId={brandId} onSuccess={handleEdited} /> )} ); }