2025-08-28 18:27:07 +02:00
|
|
|
import { add } from "date-fns";
|
2025-11-03 10:59:45 +01:00
|
|
|
import { Bug, Calculator, ClockAlert, Plus, ShieldUser } from "lucide-react";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { useState } from "react";
|
2025-08-04 17:45:44 +02:00
|
|
|
import toast from "react-hot-toast";
|
|
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Credenza,
|
|
|
|
|
CredenzaBody,
|
|
|
|
|
CredenzaContent,
|
|
|
|
|
CredenzaDescription,
|
|
|
|
|
CredenzaHeader,
|
|
|
|
|
CredenzaTitle,
|
|
|
|
|
CredenzaTrigger,
|
|
|
|
|
} from "~/components/custom_ui/credenza";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { MultiSelect } from "~/components/custom_ui/multiselect";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { LoadingPage } from "~/components/loading";
|
2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
AlertDialog,
|
|
|
|
|
AlertDialogAction,
|
|
|
|
|
AlertDialogCancel,
|
|
|
|
|
AlertDialogContent,
|
|
|
|
|
AlertDialogDescription,
|
|
|
|
|
AlertDialogFooter,
|
|
|
|
|
AlertDialogHeader,
|
|
|
|
|
AlertDialogTitle,
|
|
|
|
|
AlertDialogTrigger,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/components/ui/alert-dialog";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Button } from "~/components/ui/button";
|
2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Collapsible,
|
|
|
|
|
CollapsibleContent,
|
|
|
|
|
CollapsibleTrigger,
|
|
|
|
|
} from "~/components/ui/collapsible";
|
|
|
|
|
import {
|
|
|
|
|
Dialog,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogDescription,
|
|
|
|
|
DialogFooter,
|
|
|
|
|
DialogHeader,
|
|
|
|
|
DialogTitle,
|
|
|
|
|
DialogTrigger,
|
|
|
|
|
} from "~/components/ui/dialog";
|
|
|
|
|
import {
|
|
|
|
|
type FormValues as EditFormValues,
|
|
|
|
|
FormEditServizio,
|
|
|
|
|
} from "~/forms/FormEditServizioAdmin";
|
|
|
|
|
import { FormNewServizio, type FormValues } from "~/forms/FormNewServizio";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { useServizio } from "~/providers/ServizioProvider";
|
2025-08-28 18:27:07 +02:00
|
|
|
import type { AnnunciId } from "~/schemas/public/Annunci";
|
2025-08-19 16:44:38 +02:00
|
|
|
import type { ServizioServizioId } from "~/schemas/public/Servizio";
|
|
|
|
|
import type { UsersId } from "~/schemas/public/Users";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { api } from "~/utils/api";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-11-03 10:59:45 +01:00
|
|
|
export const ServizioActions = () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { servizio, isAdmin, userId } = useServizio();
|
2025-08-24 15:47:08 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const canUserEdit =
|
|
|
|
|
servizio.isOkAcconto &&
|
|
|
|
|
servizio.decorrenza !== null &&
|
|
|
|
|
!servizio.annunci.some((a) => a.user_confirmed_at !== null);
|
2025-08-24 15:47:08 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
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-col flex-wrap justify-between gap-3 sm:flex-row">
|
|
|
|
|
{(isAdmin || canUserEdit) && (
|
|
|
|
|
<div className="flex items-center gap-3">
|
2025-11-03 10:59:45 +01:00
|
|
|
{/* <span className="hidden sm:block">Azioni: </span> */}
|
2025-08-28 18:27:07 +02:00
|
|
|
<EditPreferenze />
|
|
|
|
|
<InterruzioneServizio />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{isAdmin && (
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<AddAnnuncio servizioId={servizio.servizio_id} userId={userId} />
|
|
|
|
|
<EditServizioAdmin />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-24 15:47:08 +02:00
|
|
|
};
|
2025-08-19 16:44:38 +02:00
|
|
|
export const AddAnnuncio = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
servizioId,
|
|
|
|
|
userId,
|
2025-08-19 16:44:38 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
servizioId: ServizioServizioId;
|
|
|
|
|
userId: UsersId;
|
2025-08-19 16:44:38 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { data: codici, isLoading: loading } =
|
|
|
|
|
api.servizio.getAnnunciAvailableToAdd.useQuery({
|
|
|
|
|
servizioId,
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const [selectedAnnunci, setSelectedAnnunci] = useState<
|
|
|
|
|
{ value: AnnunciId; label: string }[]
|
|
|
|
|
>([]);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const [openModal, setOpenModal] = useState(false);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const utils = api.useUtils();
|
|
|
|
|
const { mutate } = api.servizio.addServizioAnnunci.useMutation({
|
2025-08-29 16:18:32 +02:00
|
|
|
onError: (error) => {
|
|
|
|
|
console.error(error);
|
|
|
|
|
toast.error("Errore durante l'aggiunta dell'annuncio");
|
|
|
|
|
},
|
2025-08-28 18:27:07 +02:00
|
|
|
onSuccess: async () => {
|
|
|
|
|
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
|
|
|
|
|
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
|
|
|
|
|
servizioId,
|
|
|
|
|
});
|
|
|
|
|
await utils.servizio.getCompatibileAnnunci.invalidate({
|
|
|
|
|
servizioId,
|
|
|
|
|
});
|
|
|
|
|
await utils.servizio.getUserServizi.invalidate({ userId });
|
|
|
|
|
toast.success("Annuncio aggiunto");
|
|
|
|
|
setOpenModal(false);
|
|
|
|
|
setSelectedAnnunci([]);
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
2025-08-29 16:18:32 +02:00
|
|
|
<Dialog onOpenChange={setOpenModal} open={openModal}>
|
2025-08-28 18:27:07 +02:00
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
aria-label="Add Annuncio"
|
2025-11-03 10:59:45 +01:00
|
|
|
className=""
|
2025-08-28 18:27:07 +02:00
|
|
|
onClick={() => {
|
|
|
|
|
setSelectedAnnunci([]);
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
variant="outline"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<Plus />
|
|
|
|
|
Annuncio
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
<DialogContent
|
|
|
|
|
className="sm:max-w-[425px]"
|
|
|
|
|
onInteractOutside={(e) => {
|
|
|
|
|
if (selectedAnnunci.length !== 0) {
|
|
|
|
|
e.preventDefault(); // Prevent closing the dialog if there are selected announcements
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Aggiungi annuncio</DialogTitle>
|
|
|
|
|
<DialogDescription>
|
|
|
|
|
Salva un annuncio nella ricerca dell'utente
|
|
|
|
|
</DialogDescription>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
<div className="grid gap-4 py-4">
|
|
|
|
|
{loading ? (
|
|
|
|
|
<LoadingPage />
|
|
|
|
|
) : (
|
|
|
|
|
<MultiSelect
|
2025-08-29 16:18:32 +02:00
|
|
|
defaultValue={undefined}
|
2025-08-28 18:27:07 +02:00
|
|
|
elementId="select-annuncio"
|
|
|
|
|
elementName="annuncio"
|
|
|
|
|
isMulti={true}
|
|
|
|
|
onValueChange={(value) => {
|
|
|
|
|
setSelectedAnnunci(
|
|
|
|
|
value.map((v) => ({
|
|
|
|
|
label: v.label,
|
2025-08-29 16:18:32 +02:00
|
|
|
value: parseInt(v.value) as AnnunciId,
|
2025-08-28 18:27:07 +02:00
|
|
|
})),
|
|
|
|
|
);
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
options={
|
|
|
|
|
codici?.map((cod) => ({
|
|
|
|
|
label: cod.codice,
|
|
|
|
|
value: cod.id.toString(),
|
|
|
|
|
})) || []
|
|
|
|
|
}
|
|
|
|
|
placeholder=""
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<DialogFooter>
|
|
|
|
|
<Button
|
|
|
|
|
aria-label="Cancel Add Annuncio"
|
2025-08-29 16:18:32 +02:00
|
|
|
disabled={!selectedAnnunci.length}
|
2025-08-28 18:27:07 +02:00
|
|
|
onClick={() => {
|
|
|
|
|
if (!selectedAnnunci.length) return;
|
|
|
|
|
mutate({
|
|
|
|
|
annunci: selectedAnnunci.map((annuncio) => annuncio.value),
|
2025-08-29 16:18:32 +02:00
|
|
|
servizioId,
|
2025-08-28 18:27:07 +02:00
|
|
|
});
|
|
|
|
|
}}
|
2025-08-29 16:18:32 +02:00
|
|
|
type="button"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
Aggiungi
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
2025-08-19 16:44:38 +02:00
|
|
|
|
2025-08-04 17:45:44 +02:00
|
|
|
const InterruzioneServizio = () => {
|
2025-11-06 17:03:42 +01:00
|
|
|
const { servizio, userId, isAdmin } = useServizio();
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const beforeInterruzioneLimit =
|
|
|
|
|
servizio.decorrenza != null &&
|
|
|
|
|
new Date() <=
|
|
|
|
|
add(servizio.decorrenza, {
|
|
|
|
|
days: 14,
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const utils = api.useUtils();
|
|
|
|
|
const { mutate } = api.servizio.interruzioneServizio.useMutation({
|
|
|
|
|
onError: (error) => {
|
|
|
|
|
console.error(error);
|
|
|
|
|
toast.error("Errore durante la richiesta di interruzione");
|
|
|
|
|
},
|
2025-08-29 16:18:32 +02:00
|
|
|
onSuccess: async () => {
|
|
|
|
|
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
|
|
|
|
|
toast.success("Interruzione richiesta");
|
|
|
|
|
},
|
2025-08-28 18:27:07 +02:00
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<AlertDialog>
|
|
|
|
|
<AlertDialogTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
aria-label="Request Interruzione"
|
2025-11-03 10:59:45 +01:00
|
|
|
className=""
|
2025-11-06 17:03:42 +01:00
|
|
|
disabled={
|
|
|
|
|
!isAdmin &&
|
|
|
|
|
(!beforeInterruzioneLimit ||
|
|
|
|
|
servizio.isInterrotto ||
|
|
|
|
|
servizio.annunci.some((a) => a.user_confirmed_at !== null))
|
|
|
|
|
}
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
2025-11-06 17:03:42 +01:00
|
|
|
<ClockAlert /> <span>Interruzione Servizio</span>
|
2025-08-28 18:27:07 +02:00
|
|
|
</Button>
|
|
|
|
|
</AlertDialogTrigger>
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
<AlertDialogHeader>
|
|
|
|
|
<AlertDialogTitle>
|
|
|
|
|
Sei sicuro di voler richiedere l'interruzione?
|
|
|
|
|
</AlertDialogTitle>
|
|
|
|
|
<AlertDialogDescription>
|
|
|
|
|
Questa azione è irreversibile e comporta la disattivazione dei
|
|
|
|
|
servizi attivati. Confermando l'azione recedi dal servizio
|
|
|
|
|
secondo le condizioni contrattuali. Verrai notificato via email
|
|
|
|
|
della conferma dell'avvenuta interruzione.
|
|
|
|
|
</AlertDialogDescription>
|
|
|
|
|
</AlertDialogHeader>
|
|
|
|
|
<AlertDialogFooter>
|
|
|
|
|
<AlertDialogCancel>Annulla</AlertDialogCancel>
|
|
|
|
|
<AlertDialogAction
|
|
|
|
|
aria-label="Confirm Interruzione"
|
|
|
|
|
onClick={() => mutate({ servizioId: servizio.servizio_id })}
|
|
|
|
|
>
|
|
|
|
|
Continua
|
|
|
|
|
</AlertDialogAction>
|
|
|
|
|
</AlertDialogFooter>
|
|
|
|
|
</AlertDialogContent>
|
|
|
|
|
</AlertDialog>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const EditPreferenze = () => {
|
2025-11-06 17:03:42 +01:00
|
|
|
const { servizioId, userId, isAdmin, servizio } = useServizio();
|
2025-08-28 18:27:07 +02:00
|
|
|
const [openEditPreferenze, setOpenEditPreferenze] = useState(false);
|
|
|
|
|
const { data, isLoading } = api.servizio.getServizio.useQuery({ servizioId });
|
|
|
|
|
const utils = api.useUtils();
|
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.getAllServizioAnnunci.invalidate({ userId });
|
|
|
|
|
await utils.servizio.getServizio.invalidate({ servizioId });
|
|
|
|
|
await utils.servizio.getCompatibileAnnunci.invalidate({
|
|
|
|
|
servizioId,
|
|
|
|
|
});
|
|
|
|
|
setOpenEditPreferenze(false);
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
function onSubmit(fields: FormValues) {
|
|
|
|
|
update({
|
|
|
|
|
data: {
|
|
|
|
|
...fields,
|
|
|
|
|
},
|
2025-08-29 16:18:32 +02:00
|
|
|
servizioId,
|
2025-08-28 18:27:07 +02:00
|
|
|
});
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
2025-08-29 16:18:32 +02:00
|
|
|
<Credenza onOpenChange={setOpenEditPreferenze} open={openEditPreferenze}>
|
2025-08-28 18:27:07 +02:00
|
|
|
<CredenzaTrigger asChild>
|
2025-11-06 17:03:42 +01:00
|
|
|
<Button
|
|
|
|
|
aria-label="Preferenze di ricerca"
|
|
|
|
|
className=""
|
|
|
|
|
disabled={
|
|
|
|
|
!isAdmin &&
|
|
|
|
|
(servizio.isInterrotto ||
|
|
|
|
|
servizio.annunci.some((a) => a.hasConfermaAdmin))
|
|
|
|
|
}
|
|
|
|
|
>
|
2025-08-28 18:27:07 +02:00
|
|
|
<Calculator />
|
2025-11-03 10:59:45 +01:00
|
|
|
Modifica Preferenze
|
2025-08-28 18:27:07 +02:00
|
|
|
</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>
|
2025-11-04 19:18:13 +01:00
|
|
|
<CredenzaBody className="max-h-[80vh] w-full max-w-3xl overflow-y-auto pb-5 sm:max-w-5xl">
|
2025-08-28 18:27:07 +02:00
|
|
|
{isLoading ? (
|
|
|
|
|
<LoadingPage />
|
|
|
|
|
) : (
|
|
|
|
|
<FormNewServizio
|
|
|
|
|
initialData={data}
|
|
|
|
|
isLimited={!isAdmin}
|
2025-08-29 16:18:32 +02:00
|
|
|
onSubmit={onSubmit}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</CredenzaBody>
|
|
|
|
|
</CredenzaContent>
|
|
|
|
|
</Credenza>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
2025-08-23 17:13:43 +02:00
|
|
|
|
|
|
|
|
const EditServizioAdmin = () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { servizioId, userId, servizio } = useServizio();
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const { data, isLoading } = api.servizio.getServizio.useQuery({ servizioId });
|
|
|
|
|
const utils = api.useUtils();
|
2025-08-23 17:13:43 +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.getAllServizioAnnunci.invalidate({ userId });
|
|
|
|
|
await utils.servizio.getServizio.invalidate({ servizioId });
|
|
|
|
|
await utils.servizio.getCompatibileAnnunci.invalidate({
|
|
|
|
|
servizioId,
|
|
|
|
|
});
|
|
|
|
|
setOpen(false);
|
|
|
|
|
},
|
|
|
|
|
});
|
2025-08-23 17:13:43 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
function onSubmit(fields: EditFormValues) {
|
|
|
|
|
update({
|
|
|
|
|
data: {
|
|
|
|
|
...fields,
|
|
|
|
|
},
|
2025-08-29 16:18:32 +02:00
|
|
|
servizioId,
|
2025-08-28 18:27:07 +02:00
|
|
|
});
|
|
|
|
|
}
|
2025-08-23 17:13:43 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
2025-08-29 16:18:32 +02:00
|
|
|
<Credenza onOpenChange={setOpen} open={open}>
|
2025-08-28 18:27:07 +02:00
|
|
|
<CredenzaTrigger asChild>
|
2025-11-03 10:59:45 +01:00
|
|
|
<Button aria-label="Modifica Servizio Admin">
|
|
|
|
|
<ShieldUser />
|
2025-08-28 18:27:07 +02:00
|
|
|
Admin
|
|
|
|
|
</Button>
|
|
|
|
|
</CredenzaTrigger>
|
|
|
|
|
<CredenzaContent className="max-h-[90vh] w-full sm:max-w-5xl">
|
|
|
|
|
<CredenzaHeader>
|
|
|
|
|
<CredenzaTitle>Modifica Servizio Admin</CredenzaTitle>
|
|
|
|
|
<CredenzaDescription className="sr-only">
|
|
|
|
|
Modifica
|
|
|
|
|
</CredenzaDescription>
|
|
|
|
|
</CredenzaHeader>
|
|
|
|
|
<CredenzaBody className="max-h-[80vh] w-full space-y-2 overflow-y-auto pb-5">
|
|
|
|
|
{isLoading ? (
|
|
|
|
|
<LoadingPage />
|
|
|
|
|
) : (
|
|
|
|
|
<FormEditServizio initialData={data} onSubmit={onSubmit} />
|
|
|
|
|
)}
|
|
|
|
|
<Collapsible>
|
|
|
|
|
<CollapsibleTrigger asChild>
|
2025-11-03 10:59:45 +01:00
|
|
|
<Button variant="warning">
|
2025-08-28 18:27:07 +02:00
|
|
|
<Bug /> Debug Info Servizio
|
|
|
|
|
</Button>
|
|
|
|
|
</CollapsibleTrigger>
|
|
|
|
|
<CollapsibleContent>
|
|
|
|
|
{(() => {
|
|
|
|
|
// biome-ignore lint/correctness/noUnusedVariables: <need to extract>
|
|
|
|
|
const { annunci, ...rest } = servizio;
|
|
|
|
|
return <pre>{JSON.stringify(rest, null, 2)}</pre>;
|
|
|
|
|
})()}
|
|
|
|
|
</CollapsibleContent>
|
|
|
|
|
</Collapsible>
|
|
|
|
|
</CredenzaBody>
|
|
|
|
|
</CredenzaContent>
|
|
|
|
|
</Credenza>
|
|
|
|
|
);
|
2025-08-23 17:13:43 +02:00
|
|
|
};
|