infoalloggi-monorepo/apps/infoalloggi/src/components/tables/servizio-table.tsx

344 lines
10 KiB
TypeScript
Raw Normal View History

2025-08-28 18:27:07 +02:00
import {
Copy,
EllipsisVertical,
ExternalLink,
Mail,
Trash2,
Wrench,
} from "lucide-react";
import Link from "next/link";
2025-08-04 17:45:44 +02:00
import toast from "react-hot-toast";
2025-08-28 18:27:07 +02:00
import { Confirm } from "~/components/confirm";
2025-08-04 17:45:44 +02:00
import { LoadingPage } from "~/components/loading";
2025-08-28 18:27:07 +02:00
import { AddAnnuncio } from "~/components/servizio/servizio_actions";
import { Button } from "~/components/ui/button";
2025-08-04 17:45:44 +02:00
import {
2025-08-28 18:27:07 +02:00
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
2025-08-04 17:45:44 +02:00
} from "~/components/ui/dropdown-menu";
import {
2025-08-28 18:27:07 +02:00
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "~/components/ui/table";
import {
2025-08-28 18:27:07 +02:00
Tooltip,
TooltipContent,
TooltipTrigger,
} from "~/components/ui/tooltip";
2025-08-28 18:27:07 +02:00
import { env } from "~/env.mjs";
import type { AnnunciId } from "~/schemas/public/Annunci";
import type { Servizio, ServizioServizioId } from "~/schemas/public/Servizio";
import type { UsersId } from "~/schemas/public/Users";
import { api } from "~/utils/api";
2025-08-04 17:45:44 +02:00
export const TabServizio = ({
2025-08-28 18:27:07 +02:00
userId,
setEditData,
setOpen,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
userId: UsersId;
setEditData: (data: Servizio | undefined) => void;
setOpen: (open: boolean) => void;
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
const { data } = api.servizio.getUserServizi.useQuery({ userId });
const utils = api.useUtils();
const { mutate: remove } = api.servizio.deleteServizio.useMutation({
2025-08-29 16:18:32 +02:00
onError: (error) => {
toast.error(error.message);
},
2025-08-28 18:27:07 +02:00
onSuccess: async () => {
toast.success("Servizio rimosso con successo");
await utils.servizio.getUserServizi.invalidate();
},
2025-08-29 16:18:32 +02:00
});
const { mutate: sendEmail } = api.servizio.sendServizioEmail.useMutation({
2025-08-28 18:27:07 +02:00
onError: (error) => {
toast.error(error.message);
},
onSuccess: async () => {
toast.success("Servizio rimosso con successo");
await utils.servizio.getUserServizi.invalidate();
},
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const { mutate: update } = api.servizio.updateServizio.useMutation({
2025-08-29 16:18:32 +02:00
onError: (error) => {
toast.error(error.message);
},
2025-08-28 18:27:07 +02:00
onSuccess: async () => {
toast.success("Servizio modificato con successo");
await utils.servizio.getUserServizi.invalidate();
},
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
if (!data) return <LoadingPage />;
return (
<Table>
<TableCaption>
{data.length > 0
? "Lista dai servizi dell'utente"
: "Nessun servizio attualmente inserito"}
</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Id</TableHead>
<TableHead>Tipologia</TableHead>
<TableHead>Creato Il</TableHead>
<TableHead>Inviato</TableHead>
<TableHead>Status</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((servizio, idx) => {
const onboardDone =
servizio.isSent ||
servizio.isInterrotto ||
servizio.isOkAcconto ||
servizio.decorrenza !== null;
return (
<TableRow key={idx}>
<TableCell className="max-w-20 truncate font-medium">
{servizio.servizio_id}
</TableCell>
<TableCell>{servizio.tipologia}</TableCell>
<TableCell>
{new Date(servizio.created_at).toLocaleDateString("it-IT", {
day: "2-digit",
2025-08-29 16:18:32 +02:00
month: "2-digit",
year: "numeric",
2025-08-28 18:27:07 +02:00
})}
</TableCell>
<TableCell>
{servizio.isSent ? (
<span className="text-green-500">Inviato</span>
) : (
<span className="text-neutral-500">Non inviato</span>
)}
</TableCell>
<TableCell>
{servizio.isInterrotto ? (
<span className="text-red-500">Interrotto</span>
) : servizio.isOkAcconto && servizio.decorrenza !== null ? (
<span className="text-green-500">Attivo</span>
) : (
<span className="text-neutral-500">Inattivo</span>
)}
</TableCell>
<TableCell>
<AnnunciSection
selectedAnnunci={servizio.annunci}
servizioId={servizio.servizio_id}
userId={userId}
/>
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger asChild>
2025-08-29 16:18:32 +02:00
<Button aria-label="Servizio Actions" variant="outline">
2025-08-28 18:27:07 +02:00
<EllipsisVertical />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem asChild>
<Confirm
description="Sei sicuro di voler inviare l'email?"
onConfirm={() => {
sendEmail({
servizioId: servizio.servizio_id,
userId: userId,
});
}}
2025-08-29 16:18:32 +02:00
title="Invia email"
2025-08-28 18:27:07 +02:00
>
<Button
aria-label="Invia Email Servizio"
className="flex w-full justify-start gap-3 px-2 font-normal"
2025-08-29 16:18:32 +02:00
disabled={onboardDone}
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant="ghost"
2025-08-28 18:27:07 +02:00
>
<Mail className="stroke-foreground size-5" />
Invia all&apos;utente
</Button>
</Confirm>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link
aria-label="Apri Onboarding Servizio"
className="!p-0"
2025-08-29 16:18:32 +02:00
href={`/area-riservata/admin/user-view/onboard/${servizio.servizio_id}`}
2025-08-28 18:27:07 +02:00
>
<Button
className="flex w-full justify-start gap-3 px-2 font-normal"
disabled={onboardDone}
2025-08-29 16:18:32 +02:00
size="sm"
variant="ghost"
2025-08-28 18:27:07 +02:00
>
<ExternalLink className="stroke-foreground size-5" />
Apri Onboarding
</Button>
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Button
aria-label="Invia Email Servizio"
className="flex w-full justify-start gap-3 px-2 font-normal"
disabled={onboardDone}
onClick={async () => {
await navigator.clipboard.writeText(
`${env.NEXT_PUBLIC_BASE_URL}/servizio/pre-onboard/${servizio.servizio_id}`,
);
toast.success("Copiato negli appunti", {
icon: "📋",
});
}}
2025-08-29 16:18:32 +02:00
size="sm"
variant="ghost"
2025-08-28 18:27:07 +02:00
>
<Copy className="stroke-foreground size-5" />
Copia Link
</Button>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger className="flex items-center gap-3 hover:cursor-pointer">
<Wrench className="stroke-foreground size-5" />
<span className="text-foreground">Status Servizio</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuRadioGroup
onValueChange={(v) => {
console.log(v);
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
update({
data: {
decorrenza: v === "true" ? new Date() : null,
2025-08-29 16:18:32 +02:00
isOkAcconto: v === "true",
2025-08-28 18:27:07 +02:00
},
2025-08-29 16:18:32 +02:00
servizioId: servizio.servizio_id,
2025-08-28 18:27:07 +02:00
});
}}
2025-08-29 16:18:32 +02:00
value={String(servizio.isOkAcconto)}
2025-08-28 18:27:07 +02:00
>
<DropdownMenuRadioItem value="true">
Attivo
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="false">
Disattiva
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSub>
<DropdownMenuSubTrigger className="flex items-center gap-3 hover:cursor-pointer">
<Wrench className="stroke-foreground size-5" />
<span className="text-foreground">Interruzione</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuRadioGroup
onValueChange={(v) => {
update({
data: {
isInterrotto: v === "true",
},
2025-08-29 16:18:32 +02:00
servizioId: servizio.servizio_id,
2025-08-28 18:27:07 +02:00
});
}}
2025-08-29 16:18:32 +02:00
value={servizio.isInterrotto.toString()}
2025-08-28 18:27:07 +02:00
>
<DropdownMenuRadioItem value="true">
Interrotto
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="false">
Non Interrotto
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<DropdownMenuSeparator />
<DropdownMenuItem
aria-label="Modifica Parametri Servizio"
className="flex items-center gap-3 hover:cursor-pointer"
onClick={() => {
setEditData(servizio);
setOpen(true);
}}
>
<Wrench className="stroke-foreground size-5" />
Modifica parametri
</DropdownMenuItem>
<DropdownMenuItem
aria-label="Elimina Servizio"
className="flex items-center gap-3 hover:cursor-pointer"
onClick={() =>
confirm("Sei sicuro di voler eliminare il servizio?") &&
remove({ servizioId: servizio.servizio_id })
}
>
<Trash2 className="stroke-foreground size-5" />
Elimina Servizio
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
2025-08-04 17:45:44 +02:00
};
const AnnunciSection = ({
2025-08-28 18:27:07 +02:00
selectedAnnunci,
servizioId,
userId,
}: {
2025-08-28 18:27:07 +02:00
selectedAnnunci: { codice: string; annunci_id: AnnunciId }[];
servizioId: ServizioServizioId;
userId: UsersId;
}) => {
2025-08-28 18:27:07 +02:00
return (
<div className="flex items-center gap-3">
<Tooltip>
<TooltipTrigger className="w-5">
{selectedAnnunci?.length}
</TooltipTrigger>
<TooltipContent>
{selectedAnnunci?.length > 0 ? (
<ul>
{selectedAnnunci?.map((annuncio) => (
<li key={annuncio.annunci_id}>{annuncio.codice}</li>
))}
</ul>
) : (
<span>Nessun annuncio associato</span>
)}
</TooltipContent>
</Tooltip>
<AddAnnuncio servizioId={servizioId} userId={userId} />
</div>
);
};