infoalloggi-monorepo/apps/infoalloggi/src/components/servizio/servizio_actions.tsx

292 lines
8.4 KiB
TypeScript
Raw Normal View History

2025-08-04 17:45:44 +02:00
import toast from "react-hot-toast";
import type { AnnunciId } from "~/schemas/public/Annunci";
import { api } from "~/utils/api";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";
import { Button } from "~/components/ui/button";
import { LoadingPage } from "~/components/loading";
import { MultiSelect } from "~/components/custom_ui/multiselect";
import { add } from "date-fns";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "~/components/ui/alert-dialog";
import { Calculator, ClockAlert, Plus } from "lucide-react";
import { FormNewServizio, type FormValues } from "~/forms/FormNewServizio";
import {
Credenza,
CredenzaBody,
CredenzaContent,
CredenzaDescription,
CredenzaHeader,
CredenzaTitle,
CredenzaTrigger,
} from "~/components/custom_ui/credenza";
import { useState } from "react";
import { useServizio } from "~/providers/ServizioProvider";
export const ServizioActions = () => {
const { servizio, isAdmin } = useServizio();
const canUserEdit =
servizio.isOkAcconto &&
servizio.decorrenza !== null &&
!servizio.annunci.some((a) => a.user_confirmed_at !== null);
if (!isAdmin && !canUserEdit) {
return null; // If the user is not an admin and cannot edit, do not render the actions
}
return (
<div className="flex w-full flex-wrap justify-start gap-3 sm:justify-end">
{(isAdmin || canUserEdit) && (
<>
<EditPreferenze />
<InterruzioneServizio />
</>
)}
{isAdmin && <AddAnnuncio />}
</div>
);
};
const AddAnnuncio = () => {
const { servizioId, userId } = useServizio();
const { data: codici, isLoading: loading } =
api.servizio.getAnnunciAvailableToAdd.useQuery({
servizioId,
});
const [selectedAnnunci, setSelectedAnnunci] = useState<
{ value: AnnunciId; label: string }[]
>([]);
const [openModal, setOpenModal] = useState(false);
const utils = api.useUtils();
const { mutate } = api.servizio.addServizioAnnunci.useMutation({
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
servizioId,
});
await utils.servizio.getCompatibileAnnunci.invalidate({
servizioId,
});
toast.success("Annuncio aggiunto");
setOpenModal(false);
setSelectedAnnunci([]);
},
onError: (error) => {
console.error(error);
toast.error("Errore durante l'aggiunta dell'annuncio");
},
});
return (
<Dialog open={openModal} onOpenChange={setOpenModal}>
<DialogTrigger asChild>
<Button
aria-label="Add Annuncio"
variant="outline"
onClick={() => {
setSelectedAnnunci([]);
}}
className="items-center gap-2"
>
<Plus />
Annuncio
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Aggiungi annuncio</DialogTitle>
<DialogDescription>
Salva un annuncio nella ricerca dell&apos;utente
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
{loading ? (
<LoadingPage />
) : (
<MultiSelect
elementId="select-annuncio"
elementName="annuncio"
isMulti={true}
placeholder=""
defaultValue={undefined}
options={
codici?.map((cod) => ({
label: cod.codice,
value: cod.id.toString(),
})) || []
}
onValueChange={(value) => {
setSelectedAnnunci(
value.map((v) => ({
value: parseInt(v.value) as AnnunciId,
label: v.label,
})),
);
}}
/>
)}
</div>
<DialogFooter>
<Button
aria-label="Cancel Add Annuncio"
type="button"
onClick={() => {
if (!selectedAnnunci.length) return;
mutate({
servizioId,
annunci: selectedAnnunci.map((annuncio) => annuncio.value),
});
}}
disabled={!selectedAnnunci.length}
>
Aggiungi
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
const InterruzioneServizio = () => {
const { servizio, userId } = useServizio();
const beforeInterruzioneLimit =
servizio.decorrenza != null &&
new Date() <=
add(servizio.decorrenza, {
days: 14,
});
const utils = api.useUtils();
const { mutate } = api.servizio.interruzioneServizio.useMutation({
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
toast.success("Interruzione richiesta");
},
onError: (error) => {
console.error(error);
toast.error("Errore durante la richiesta di interruzione");
},
});
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
aria-label="Request Interruzione"
variant="outline"
className="items-center gap-2"
disabled={!beforeInterruzioneLimit}
>
<ClockAlert /> <span>Interruzione</span>
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Sei sicuro di voler richiedere l&apos;interruzione?
</AlertDialogTitle>
<AlertDialogDescription>
Questa azione è irreversibile e comporta la disattivazione dei
servizi attivati. Confermando l&apos;azione recedi dal servizio
secondo le condizioni contrattuali. Verrai notificato via email
della conferma dell&apos;avvenuta interruzione.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Annulla</AlertDialogCancel>
<AlertDialogAction
aria-label="Confirm Interruzione"
onClick={() => mutate({ servizioId: servizio.servizio_id })}
>
Continua
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};
const EditPreferenze = () => {
const { servizioId, userId, isAdmin } = useServizio();
const [openEditPreferenze, setOpenEditPreferenze] = useState(false);
const { data, isLoading } = api.servizio.getServizio.useQuery({ servizioId });
const utils = api.useUtils();
const { mutate: update } = api.servizio.updateServizio.useMutation({
onSuccess: async () => {
toast.success("Servizio modificato con successo");
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getServizio.invalidate({ servizioId });
await utils.servizio.getCompatibileAnnunci.invalidate({
servizioId,
});
setOpenEditPreferenze(false);
},
onError: (error) => {
toast.error(error.message);
},
});
function onSubmit(fields: FormValues) {
update({
servizioId,
data: {
...fields,
},
});
}
return (
<Credenza open={openEditPreferenze} onOpenChange={setOpenEditPreferenze}>
<CredenzaTrigger asChild>
<Button
aria-label="Preferenze di ricerca"
className="items-center gap-2"
>
<Calculator />
Preferenze
</Button>
</CredenzaTrigger>
<CredenzaContent className="max-h-[90vh] w-full sm:max-w-5xl">
<CredenzaHeader>
<CredenzaTitle>Preferenze di ricerca</CredenzaTitle>
<CredenzaDescription className="sr-only">
Preferenze
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody className="max-h-[80vh] w-full overflow-y-auto pb-5">
{isLoading ? (
<LoadingPage />
) : (
<FormNewServizio
initialData={data}
onSubmit={onSubmit}
isLimited={!isAdmin}
/>
)}
</CredenzaBody>
</CredenzaContent>
</Credenza>
);
};