import type { ColumnDef, Table } from "@tanstack/react-table"; import { add, format, formatDuration, intervalToDuration, isBefore, } from "date-fns"; import { it } from "date-fns/locale"; import { BadgeEuro, CircleCheck, CircleCheckBig, ClipboardPen, ClockFading, ExternalLink, Logs, ReceiptEuro, SearchSlash, SearchX, UserCog, } from "lucide-react"; import Link from "next/link"; import { useRef } from "react"; import toast from "react-hot-toast"; import { NotePopover } from "~/components/notePopover"; import { AnnuncioCardRichieste } from "~/components/servizio/annuncio_card"; import { OrdersModal } from "~/components/servizio/servizio_actions"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, } from "~/components/ui/context-menu"; import { DataTable } from "~/components/ui/data-table"; import { DataTableColumnHeader } from "~/components/ui/dataTable-header"; import type { PinnedFiltro, SearchFiltro, } from "~/components/ui/dataTable-toolbar"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from "~/components/ui/sheet"; import { Tooltip, TooltipContent, TooltipTrigger, } from "~/components/ui/tooltip"; import { UserAvatar } from "~/components/user_avatar"; import { WhatsAppIcon2 } from "~/components/whatsapp-svg"; import { MutationPageRestore } from "~/lib/tableUtils"; import { cn } from "~/lib/utils"; import { orderTypeEnum } from "~/schemas/public/OrderTypeEnum"; import { statusConfermaEnum } from "~/schemas/public/StatusConfermaEnum"; import { tipologiaPosizioneEnum } from "~/schemas/public/TipologiaPosizioneEnum"; import type { RichiesteData } from "~/server/controllers/servizio.controller"; import { api } from "~/utils/api"; import { DEFAULT_SERVIZIO_DURATION_DAYS } from "~/utils/config"; type RichiesteTableProps = { data: RichiesteData[]; }; const StatusMapping = { attesa_attivazione: "Attesa di attivazione", interrotto: "Interrotto", disabled: "Disattivato", onboarding: "Onboarding", scaduto: "Scaduto", consulenza_da_pagare: "Consulenza da pagare", consulenza_da_inserire: "Consulenza da inserire", attesa_saldo: "Attesa pagamento saldo", attesa_caparra: "Attesa versamento caparra", attesa_conferma: "Attesa accettazione conferma", attivo: "Attivo", attesa_pagamento_acconto: "Attesa pagamento acconto", confermato: "Confermato", }; export type StatusKey = keyof typeof StatusMapping; function statusParser(data: RichiesteData): keyof typeof StatusMapping { if (data.isInterrotto) { return "interrotto"; } if (data.isDisabled) { return "disabled"; } if (!data.onboardOk) { return "onboarding"; } if (!data.isOkAcconto || !data.decorrenza) { const acconti = data.ordini_servizio.filter( (o) => o.type === orderTypeEnum.enum.Acconto, ); const activeAcconto = acconti.find((a) => a.isActive); //hasActiveAcconto if (activeAcconto) { return "attesa_attivazione"; } return "attesa_pagamento_acconto"; } if ( isBefore( add(data.decorrenza, { days: DEFAULT_SERVIZIO_DURATION_DAYS, }), new Date(), ) && !data.isOkSaldo ) { return "scaduto"; } const contrattoInserito = data.annunci_servizio.filter( (a) => a.doc_contratto_added, ); if (contrattoInserito.length > 0) { if (data.isOkSaldo) { if (data.isOkConsulenza) { return "confermato"; } if ( //hasConsulanzaDaPagare data.ordini_servizio.some( (o) => o.type === orderTypeEnum.enum.Consulenza && !o.isActive, ) ) { return "consulenza_da_pagare"; } return "consulenza_da_inserire"; } } const annuncioCaparraVersata = data.annunci_servizio.filter( (a) => a.status_conferma === statusConfermaEnum.enum["Caparra versata"], ); if (annuncioCaparraVersata.length > 0) { if (data.isOkSaldo) { return "confermato"; } return "attesa_saldo"; } const annuncioConfermato = data.annunci_servizio.filter( (a) => a.status_conferma === statusConfermaEnum.enum["Conferma accettata"], ); if (annuncioConfermato.length > 0) { return "attesa_caparra"; } const annuncioAttesaConferma = data.annunci_servizio.filter( (a) => a.status_conferma === statusConfermaEnum.enum["Inviato conferma"], ); if (annuncioAttesaConferma.length > 0) { return "attesa_conferma"; } return "attivo"; } export const RichiesteTable = ({ data }: RichiesteTableProps) => { const tabledata = data.map((d) => ({ ...d, status: statusParser(d), })); const columns_titles = { id: "ID Ordine", created_at: "Creato il", username: "Utente", note: "Note", decorrenza: "Decorrenza", status: "Status", tipologia: "Tipo", annunci: "Annunci", ordini: "Ordini", interruzione_expires_at: "Scad. interr.", actions: "Apri", }; const statusOptions = Object.entries(StatusMapping).map(([key, label]) => ({ label, value: key, })); const type_options = [ { label: "Transitorio", value: tipologiaPosizioneEnum.enum.Transitorio }, { label: "Stabile", value: tipologiaPosizioneEnum.enum.Stabile }, ]; type Column = (typeof tabledata)[number]; const tableRef = useRef | null>(null); const utils = api.useUtils(); const { onMutate, onSettled } = MutationPageRestore(tableRef); const { mutate, isPending } = api.users.editUser.useMutation({ onMutate, onSettled, onSuccess: async () => { toast.success("Note aggiornate con successo"); await utils.users.invalidate(); await utils.servizio.invalidate(); }, onError: (error) => { toast.error(error.message); }, }); const searchFiltro: SearchFiltro = { columnName: "username", placeholder: "Cerca...", }; const columns: ColumnDef[] = [ { cell: ({ row }) => { return ( ); }, header: ({ column }) => ( ), enableHiding: false, id: "actions", }, { accessorKey: "id", cell: () => null, enableHiding: false, header: () => null, }, { accessorKey: "created_at", sortingFn: "datetime", cell: ({ row }) => { return

{format(row.original.created_at, "dd/MM")}

; }, header: ({ column }) => ( ), }, { accessorKey: "username", cell: ({ row }) => { const data = row.original; return ( {data.username} {data.username} Profilo {data.extra_telefono_numero ? ( WhatsApp WhatsApp ({data.nome}) WhatsApp ({data.extra_telefono_titolo}) ) : ( WhatsApp )} ); }, enableHiding: false, header: ({ column }) => ( ), }, { accessorKey: "note", cell: ({ row }) => { const note = row.original.note; return ( ); }, header: ({ column }) => ( ), enableSorting: false, }, { accessorKey: "decorrenza", cell: ({ row }) => { let decorrenzaColor = ""; let decorrenzaTooltip = ""; if (!row.original.isOkAcconto || !row.original.decorrenza) { decorrenzaColor = "text-muted-foreground"; decorrenzaTooltip = "Attesa di attivazione"; } else if (row.original.isInterrotto) { decorrenzaColor = "text-destructive"; decorrenzaTooltip = "Servizio interrotto"; } else if (row.original.isDisabled) { decorrenzaColor = "text-muted-foreground"; decorrenzaTooltip = "Servizio disattivato"; } else if (row.original.isOkAcconto && !row.original.isOkSaldo) { decorrenzaColor = "text-warning"; decorrenzaTooltip = "Attesa di saldo"; } return ( {row.original.decorrenza ? format(row.original.decorrenza, "dd/MM") : "---"}

{decorrenzaTooltip}

); }, enableHiding: false, header: ({ column }) => ( ), sortingFn: "datetime", }, { accessorKey: "tipologia", cell: ({ row }) => { const { tipologia } = row.original; return ( {tipologia.toString()} ); }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)); }, header: ({ column }) => ( ), }, { accessorKey: "status", cell: ({ row }) => { let badgeClass = ""; let icon: React.ReactNode; const statusText = StatusMapping[row.original.status as StatusKey] || row.original.status; switch (row.original.status) { case "interrotto": badgeClass = "border-destructive bg-destructive/10 text-destructive"; icon = ; break; case "disabled": badgeClass = "border-gray-500 bg-muted/10 text-muted-foreground"; icon = ; break; case "onboarding": badgeClass = "border-pink-500 bg-pink-500/10 text-pink-600"; icon = ( ); break; case "attesa_attivazione": badgeClass = "border-yellow-500 bg-yellow-500/10 text-yellow-600"; icon = ( ); break; case "attesa_pagamento_acconto": badgeClass = "border-orange-500 bg-orange-500/10 text-orange-600"; icon = ( ); break; case "scaduto": badgeClass = "border-red-500 bg-red-500/10 text-red-500"; icon = ; break; case "confermato": badgeClass = "border-blue-500 bg-blue-500/10 text-blue-500"; icon = ( ); break; case "consulenza_da_pagare": badgeClass = "border-orange-500 bg-orange-500/10 text-orange-600"; icon = ( ); break; case "consulenza_da_inserire": badgeClass = "border-yellow-500 bg-yellow-500/10 text-yellow-600"; icon = ( ); break; case "attesa_saldo": badgeClass = "border-orange-500 bg-orange-500/10 text-orange-600"; icon = ( ); break; case "attesa_caparra": badgeClass = "border-yellow-500 bg-yellow-500/10 text-yellow-600"; icon = ( ); break; case "attesa_conferma": badgeClass = "border-yellow-500 bg-yellow-500/10 text-yellow-600"; icon = ( ); break; case "attivo": badgeClass = "border-green-500 bg-green-500/10 text-green-500"; icon = ( ); break; } return ( svg]:size-4", badgeClass, )} variant="outline" > {icon} {statusText} ); }, filterFn: (row, id, value) => { return value.includes(row.getValue(id)); }, header: ({ column }) => ( ), }, { accessorKey: "interruzione_expires_at", cell: ({ row }) => { if (row.original.isInterrotto) return "---"; if (row.original.isDisabled) return "---"; if (!row.original.decorrenza) return "---"; const expiresAt = add(row.original.decorrenza, { days: row.original.interruzioneDays, }); const timeLeft = intervalToDuration({ start: new Date(), end: expiresAt, }); const daysLeft = timeLeft.days || 0; let badgeColor = ""; if (daysLeft > 5) { badgeColor = "border-green-500 bg-green-500/10 text-green-500"; } else if (daysLeft > 0) { badgeColor = "border-yellow-500 bg-yellow-500/10 text-yellow-500"; } else { badgeColor = "border-red-500 bg-red-500/10 text-red-500"; } return ( {format(expiresAt, "dd/MM")} {daysLeft > 0 ? (

{formatDuration(timeLeft, { locale: it, format: ["days"] })}{" "} rimanenti

) : (

Interruzione scaduta

)}
); }, enableSorting: false, header: ({ column }) => ( ), }, { accessorKey: "annunci_servizio", cell: ({ row }) => { const annunci = row.original.annunci_servizio; return ( Annunci Inseriti Desc
{annunci.map((a) => ( ))}
); }, header: ({ column }) => ( ), enableSorting: false, }, { accessorKey: "ordini", cell: ({ row }) => { //paidOrdiniCount const paidOrders = row.original.ordini_servizio.filter( (o) => o.isActive, ).length; //unpaidOrdiniCount const unpaidOrders = row.original.ordini_servizio.length - paidOrders; return (
{unpaidOrders > 0 && ( {unpaidOrders} )} {paidOrders > 0 && ( {paidOrders} )}
); }, header: ({ column }) => ( ), enableSorting: false, }, ]; const pinnedFiltri: PinnedFiltro[] = [ { columnName: "status", options: statusOptions, title: "Stato Ordine", }, { columnName: "tipologia", options: type_options, title: "Tipo Ordine", }, ]; return (
(tableRef.current = t)} pinnedFiltri={pinnedFiltri} searchColumn={searchFiltro} />
); };