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";
|
2025-08-19 16:44:38 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
2025-08-19 16:44:38 +02:00
|
|
|
} from "~/components/ui/tooltip";
|
2025-10-20 16:22:20 +02:00
|
|
|
import { env } from "~/env";
|
2025-08-28 18:27:07 +02:00
|
|
|
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 () => {
|
2025-10-20 16:22:20 +02:00
|
|
|
toast.success("Email onboarding inviata con successo");
|
2025-08-28 18:27:07 +02:00
|
|
|
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.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>
|
2025-09-01 16:48:03 +02:00
|
|
|
{(() => {
|
|
|
|
|
if (servizio.isInterrotto)
|
|
|
|
|
return <span className="text-red-500">Interrotto</span>;
|
|
|
|
|
if (servizio.isOkAcconto && servizio.decorrenza !== null)
|
|
|
|
|
return <span className="text-green-500">Attivo</span>;
|
|
|
|
|
return <span className="text-neutral-500">Inattivo</span>;
|
|
|
|
|
})()}
|
2025-08-28 18:27:07 +02:00
|
|
|
</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-10-20 12:33:49 +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
|
|
|
>
|
2025-10-10 16:18:43 +02:00
|
|
|
<Mail className="size-5 stroke-foreground" />
|
|
|
|
|
{servizio.isSent
|
|
|
|
|
? "Re-Invia all'utente"
|
|
|
|
|
: "Invia all'utente"}
|
2025-08-28 18:27:07 +02:00
|
|
|
</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
|
|
|
>
|
2025-10-10 16:18:43 +02:00
|
|
|
<ExternalLink className="size-5 stroke-foreground" />
|
2025-08-28 18:27:07 +02:00
|
|
|
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
|
|
|
>
|
2025-10-10 16:18:43 +02:00
|
|
|
<Copy className="size-5 stroke-foreground" />
|
2025-08-28 18:27:07 +02:00
|
|
|
Copia Link
|
|
|
|
|
</Button>
|
|
|
|
|
</DropdownMenuItem>
|
|
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
<DropdownMenuSub>
|
|
|
|
|
<DropdownMenuSubTrigger className="flex items-center gap-3 hover:cursor-pointer">
|
2025-10-10 16:18:43 +02:00
|
|
|
<Wrench className="size-5 stroke-foreground" />
|
2025-08-28 18:27:07 +02:00
|
|
|
<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">
|
2025-10-10 16:18:43 +02:00
|
|
|
<Wrench className="size-5 stroke-foreground" />
|
2025-08-28 18:27:07 +02:00
|
|
|
<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);
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-10-10 16:18:43 +02:00
|
|
|
<Wrench className="size-5 stroke-foreground" />
|
2025-08-28 18:27:07 +02:00
|
|
|
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 })
|
|
|
|
|
}
|
|
|
|
|
>
|
2025-10-10 16:18:43 +02:00
|
|
|
<Trash2 className="size-5 stroke-foreground" />
|
2025-08-28 18:27:07 +02:00
|
|
|
Elimina Servizio
|
|
|
|
|
</DropdownMenuItem>
|
|
|
|
|
</DropdownMenuContent>
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
</TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
2025-08-19 16:44:38 +02:00
|
|
|
|
|
|
|
|
const AnnunciSection = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
selectedAnnunci,
|
|
|
|
|
servizioId,
|
|
|
|
|
userId,
|
2025-08-19 16:44:38 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
selectedAnnunci: { codice: string; annunci_id: AnnunciId }[];
|
|
|
|
|
servizioId: ServizioServizioId;
|
|
|
|
|
userId: UsersId;
|
2025-08-19 16:44:38 +02:00
|
|
|
}) => {
|
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>
|
|
|
|
|
);
|
2025-08-19 16:44:38 +02:00
|
|
|
};
|