"use client"; import { useState, useTransition } from "react"; import { useRouter } from "next/navigation"; import { ArrowUp, ArrowDown, ArrowUpDown, GripVertical } from "lucide-react"; import { Input } from "@/components/ui/input"; import { EditableCell } from "@/components/ui/editable-cell"; import { OptionSelect } from "@/components/ui/option-select"; import { OptionMultiSelect } from "@/components/ui/option-multi-select"; import { updateServiceField, quickAddService, addServiceOption, removeServiceOption, renameServiceOption, } from "@/app/admin/catalog/actions"; import type { ServiceWithTags, CatalogFieldOptions } from "@/lib/admin-queries"; export type ColumnKey = | "nome" | "descrizione" | "fase" | "offerta" | "pacchetto" | "prezzo" | "stato"; export const COLUMN_DEFS: Record = { nome: { header: "Nome", sortable: true, minWidth: "160px" }, descrizione: { header: "Descrizione", sortable: false, minWidth: "200px" }, fase: { header: "Fase", sortable: true, minWidth: "140px" }, offerta: { header: "Offerta", sortable: true, minWidth: "180px" }, pacchetto: { header: "Pacchetto", sortable: true, minWidth: "180px" }, prezzo: { header: "Prezzo", sortable: true, minWidth: "100px" }, stato: { header: "Stato", sortable: false, minWidth: "100px" }, }; export const DEFAULT_COL_ORDER: ColumnKey[] = [ "nome", "descrizione", "fase", "offerta", "pacchetto", "prezzo", "stato", ]; function formatPrice(raw: string): string { const num = parseFloat(raw); return `€${(isNaN(num) ? 0 : num).toLocaleString("it-IT", { minimumFractionDigits: 2 })}`; } function ServiceRow({ service, options, colOrder, }: { service: ServiceWithTags; options: CatalogFieldOptions; colOrder: ColumnKey[]; }) { const router = useRouter(); const [, startTransition] = useTransition(); const [error, setError] = useState(null); function run(fn: () => Promise) { setError(null); startTransition(async () => { try { await fn(); router.refresh(); } catch (e) { setError(e instanceof Error ? e.message : "Errore nel salvataggio"); } }); } function renderCell(key: ColumnKey) { switch (key) { case "nome": return ( run(() => updateServiceField(service.id, "name", v))} /> ); case "descrizione": return ( run(() => updateServiceField(service.id, "description", v))} /> ); case "fase": return ( run(() => updateServiceField(service.id, "fase", v ?? ""))} onRename={(o, n) => run(() => renameServiceOption("fase", o, n))} /> ); case "offerta": return ( run(() => addServiceOption("tag", service.id, v))} onRemove={(v) => run(() => removeServiceOption("tag", service.id, v))} onRename={(o, n) => run(() => renameServiceOption("tag", o, n))} /> ); case "pacchetto": return ( run(() => addServiceOption("pacchetto", service.id, v))} onRemove={(v) => run(() => removeServiceOption("pacchetto", service.id, v))} onRename={(o, n) => run(() => renameServiceOption("pacchetto", o, n))} /> ); case "prezzo": return ( run(() => updateServiceField(service.id, "unit_price", v))} /> ); case "stato": return ( run(() => updateServiceField(service.id, "active", v))} /> ); } } return ( <> {colOrder.map((key) => renderCell(key))} {error && ( {error} )} ); } function QuickAddRow({ options, colOrder, }: { options: CatalogFieldOptions; colOrder: ColumnKey[]; }) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [fase, setFase] = useState(""); const [price, setPrice] = useState(""); const [, startTransition] = useTransition(); const [error, setError] = useState(null); const router = useRouter(); function submit() { const trimmed = name.trim(); if (!trimmed) return; setError(null); startTransition(async () => { try { await quickAddService({ name: trimmed, description: description || undefined, fase: fase || undefined, unit_price: price || undefined, }); setName(""); setDescription(""); setFase(""); setPrice(""); router.refresh(); } catch (err) { setError(err instanceof Error ? err.message : "Errore nel salvataggio"); } }); } function onKeyDown(e: React.KeyboardEvent) { if (e.key === "Enter") { e.preventDefault(); submit(); } } const cellInput = "border-0 bg-transparent shadow-none h-8 text-sm placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-primary"; function renderCell(key: ColumnKey) { switch (key) { case "nome": return ( setName(e.target.value)} onKeyDown={onKeyDown} placeholder="+ Aggiungi servizio" className={cellInput} /> ); case "descrizione": return ( setDescription(e.target.value)} onKeyDown={onKeyDown} placeholder="Descrizione..." className={cellInput} /> ); case "fase": return ( setFase(e.target.value)} onKeyDown={onKeyDown} placeholder="Fase..." list="catalog-fase-pool" className={cellInput} /> {options.fase.map((f) => ( ); case "prezzo": return ( setPrice(e.target.value)} onKeyDown={onKeyDown} placeholder="0,00" inputMode="decimal" className={`${cellInput} tabular-nums`} /> ); case "stato": return ( Invio ↵ ); default: return ; } } return ( <> {colOrder.map((key) => renderCell(key))} {error && ( {error} )} ); } export function ServiceTable({ activeServices, inactiveServices, options, colOrder, sortKey, sortDir, onSortClick, onColDragStart, onColDragOver, onColDrop, }: { activeServices: ServiceWithTags[]; inactiveServices: ServiceWithTags[]; options: CatalogFieldOptions; colOrder: ColumnKey[]; sortKey: ColumnKey | null; sortDir: "asc" | "desc"; onSortClick: (key: ColumnKey) => void; onColDragStart: (key: ColumnKey) => void; onColDragOver: (e: React.DragEvent, key: ColumnKey) => void; onColDrop: (key: ColumnKey) => void; }) { function SortIcon({ colKey }: { colKey: ColumnKey }) { if (!COLUMN_DEFS[colKey].sortable) return null; if (sortKey !== colKey) return ; return sortDir === "asc" ? : ; } const totalCount = activeServices.length + inactiveServices.length; return (
{colOrder.map((key) => { const def = COLUMN_DEFS[key]; return ( ); })} {activeServices.map((service) => ( ))} {inactiveServices.length > 0 && ( <> {inactiveServices.map((service) => ( ))} )}
onColDragStart(key)} onDragOver={(e) => onColDragOver(e, key)} onDrop={() => onColDrop(key)} onClick={() => def.sortable && onSortClick(key)} className={`text-left py-3 px-3 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground select-none whitespace-nowrap group ${ def.sortable ? "cursor-pointer hover:text-foreground" : "cursor-grab" }`} style={{ minWidth: def.minWidth }} > {def.header}
Servizi disattivati
{totalCount === 1 ? "Visualizzazione di 1 servizio" : `Visualizzazione di ${totalCount} servizi`}
); }