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

1209 lines
30 KiB
TypeScript

import {
Check,
ChevronsUpDown,
CircleCheck,
ClipboardPaste,
ExternalLink,
FolderKanban,
Pen,
ShieldUser,
Trash2,
Wrench,
} from "lucide-react";
import Link from "next/link";
import { useState } from "react";
import toast from "react-hot-toast";
import { z } from "zod/v4";
import { ExtIcon } from "~/components/area-riservata/allegati";
import { Confirm } from "~/components/confirm";
import {
Credenza,
CredenzaBody,
CredenzaClose,
CredenzaContent,
CredenzaDescription,
CredenzaFooter,
CredenzaHeader,
CredenzaTitle,
CredenzaTrigger,
} from "~/components/custom_ui/credenza";
import { LoadingPage } from "~/components/loading";
import { Button, buttonVariants } from "~/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "~/components/ui/command";
import { Label } from "~/components/ui/label";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "~/components/ui/popover";
import { Separator } from "~/components/ui/separator";
import { Switch } from "~/components/ui/switch";
import { UploadModal } from "~/components/upload_modal";
import { cn, formatCurrency } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider";
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import type { FileMetadataWithAdmin } from "~/server/services/storage.service";
import { api } from "~/utils/api";
export const AnnuncioActions = () => {
const { servizioId, userId, isAdmin } = useServizio();
const { data, annuncioId } = useServizioAnnuncio();
const utils = api.useUtils();
const { mutate } = api.servizio.removeAnnuncioServizio.useMutation({
onError: (error) => {
console.error(error);
toast.error("Errore durante la rimozione dell'annuncio");
},
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getCompatibileAnnunci.invalidate({
servizioId,
});
toast.success("Annuncio rimosso");
},
});
if ((!isAdmin && data.open_contatti_at == null) || isAdmin) {
return (
<div className="flex w-full max-w-fit flex-wrap justify-start gap-3 sm:justify-end">
{isAdmin && data.user_confirmed_at != null && <AdminLavoraConferma />}
{isAdmin && data.accettato_conferma_at != null && (
<>
<AdminContratto />
{data.gestionale_id && (
<Link
aria-label="Open Gestionale"
href={`https://win-jk822fggr6b:480/id/${data.gestionale_id}`}
target="_blank"
>
<Button>
<FolderKanban className="size-5" /> Gestionale
</Button>
</Link>
)}
</>
)}
{isAdmin && (
<Popover>
<PopoverTrigger asChild>
<Button>
<ShieldUser />
<span>Admin</span>
</Button>
</PopoverTrigger>
<PopoverContent
align="end"
className="flex w-fit flex-col items-center gap-4"
>
<Link
href={`/area-riservata/admin/edit-annuncio/${annuncioId}`}
target="_blank"
>
<Button
aria-label="Edit Annuncio"
className="w-full"
size="sm"
variant="secondary"
>
<Wrench />
<span>Modifica Annuncio</span>
</Button>
</Link>
<Confirm
description="Sei sicuro di voler eliminare l'annuncio?"
onConfirm={() => {
mutate({
annuncioId,
servizioId,
});
}}
title="Elimina annuncio"
>
<Button
aria-label="Delete Annuncio"
className="w-full"
size="sm"
variant="destructive"
>
<Trash2 />
<span>Elimina Annuncio</span>
</Button>
</Confirm>
</PopoverContent>
</Popover>
)}
</div>
);
}
return null;
};
const ContrattoImportSchema = z.object({
contratto_decorrenza: z.string().nullable(),
contratto_scadenza: z.string().nullable(),
contratto_tipo: z
.enum([
"Transitorio",
"Agevolato",
"4 + 4",
"30 Giorni",
"Transi.Age",
"Foresteria",
"Commerciale",
])
.nullable(),
gestionale_id: z.number().nullable(),
});
interface ContrattoImportObj
extends Omit<
z.infer<typeof ContrattoImportSchema>,
"contratto_decorrenza" | "contratto_scadenza"
> {
contratto_decorrenza: Date | null;
contratto_scadenza: Date | null;
}
const parseContrattoImport = (ingest: string) => {
let parsedJson: unknown;
try {
parsedJson = JSON.parse(ingest);
} catch {
toast.error(
'Stringa non valida, clicca "copia stringa" nel gestionale e poi questo pulsante',
);
return undefined;
}
const parsed = ContrattoImportSchema.safeParse(parsedJson);
if (parsed.success) {
return {
...parsed.data,
contratto_decorrenza: parsed.data.contratto_decorrenza
? new Date(parsed.data.contratto_decorrenza)
: null,
contratto_scadenza: parsed.data.contratto_scadenza
? new Date(parsed.data.contratto_scadenza)
: null,
};
}
console.error("Invalid format:", parsed.error);
return undefined;
};
const ContrattoImport = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { data } = useServizioAnnuncio();
const [contratto, setContratto] = useState<ContrattoImportObj | undefined>({
contratto_decorrenza: data.contratto_decorrenza
? new Date(data.contratto_decorrenza)
: null,
contratto_scadenza: data.contratto_scadenza
? new Date(data.contratto_scadenza)
: null,
contratto_tipo: data.contratto_tipo as ContrattoImportObj["contratto_tipo"],
gestionale_id: data.gestionale_id,
});
const handleSave = () => {
handleUpdate({
...contratto,
});
};
return (
<Card>
<CardHeader>
<CardTitle>Dati Contratto</CardTitle>
<CardDescription className="sr-only">desc</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center gap-3">
<Button
aria-label="Import Contratto"
onClick={async () => {
const clipboardText = await navigator.clipboard.readText();
setContratto(parseContrattoImport(clipboardText));
}}
variant="info"
>
<ClipboardPaste className="size-4" />
Clicca qui per importare la stringa dal gestionale
</Button>
<Button
aria-label="Reset Contratto"
onClick={async () => {
setContratto({
contratto_decorrenza: null,
contratto_scadenza: null,
contratto_tipo: null,
gestionale_id: null,
});
}}
variant="destructive"
>
<Trash2 className="size-4" />
Reset
</Button>
</div>
{contratto ? (
<div className="flex flex-col gap-2">
<p>
<span className="font-semibold">ID Gestionale: </span>
{contratto.gestionale_id}{" "}
<Button
aria-label="Edit Gestionale ID"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica l'ID gestionale del contratto:",
contratto.gestionale_id?.toString() || "",
);
if (newValue !== null) {
setContratto((prev) =>
prev
? { ...prev, gestionale_id: parseInt(newValue) }
: undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
<p>
<span className="font-semibold">Decorrenza: </span>
{contratto.contratto_decorrenza &&
new Date(contratto.contratto_decorrenza).toLocaleDateString(
"it-IT",
)}{" "}
<Button
aria-label="Edit Decorrenza"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica la decorrenza del contratto (DD/MM/YYYY):",
contratto.contratto_decorrenza
? new Date(
contratto.contratto_decorrenza,
).toLocaleDateString("it-IT")
: "",
);
if (newValue !== null) {
setContratto((prev) =>
prev
? {
...prev,
contratto_decorrenza: new Date(newValue),
}
: undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
<p>
<span className="font-semibold">Scadenza: </span>
{contratto.contratto_scadenza &&
new Date(contratto.contratto_scadenza).toLocaleDateString(
"it-IT",
)}{" "}
<Button
aria-label="Edit Scadenza"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica la scadenza del contratto (DD/MM/YYYY):",
contratto.contratto_scadenza
? new Date(
contratto.contratto_scadenza,
).toLocaleDateString("it-IT")
: "",
);
if (newValue !== null) {
setContratto((prev) =>
prev
? {
...prev,
contratto_scadenza: new Date(newValue),
}
: undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
<p>
<span className="font-semibold">Tipo di contratto: </span>
{contratto.contratto_tipo}{" "}
<Button
aria-label="Edit Tipo di contratto"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica il tipo di contratto:",
contratto.contratto_tipo || "",
);
if (
newValue !== null &&
ContrattoImportSchema.shape.contratto_tipo.safeParse(
newValue,
).success
) {
setContratto((prev) =>
prev
? {
...prev,
contratto_tipo:
newValue as ContrattoImportObj["contratto_tipo"],
}
: undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
</div>
) : (
<p>Inserisci la stringa dal gestionale e elabora</p>
)}
</CardContent>
<CardFooter>
<Button
aria-label="Save Contratto"
onClick={() => {
handleSave();
}}
variant="success"
>
Salva
</Button>
</CardFooter>
</Card>
);
};
const AdminContratto = () => {
const { userId, servizioId } = useServizio();
const { data, annuncioId } = useServizioAnnuncio();
const [open, setOpen] = useState(false);
const utils = api.useUtils();
const { mutate: updateServizio } =
api.servizio.updateServizioAnnunci.useMutation({
onError: (error) => {
toast.error(error.message);
},
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getServizio.invalidate({
servizioId,
});
toast.success("Dati salvati con successo");
},
});
const handleUpdate = (upd: ServizioAnnunciUpdate) => {
updateServizio({
annuncioId,
data: {
...upd,
},
servizioId,
});
};
return (
<Credenza onOpenChange={setOpen} open={open}>
<CredenzaTrigger asChild>
<Button
aria-label="Open Contratto Section"
className="w-fit"
variant={data.doc_contratto_added ? "success" : "secondary"}
>
{data.doc_contratto_added ? (
<CircleCheck className="size-6" />
) : (
<Wrench className="size-6" />
)}
Contratto
</Button>
</CredenzaTrigger>
<CredenzaContent className="max-h-[90vh] max-w-4xl px-3 sm:max-w-5xl">
<CredenzaHeader>
<CredenzaTitle>Sezione Contratto</CredenzaTitle>
<CredenzaDescription className="sr-only">
Contratto modal
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody className="max-h-[80vh] overflow-auto pb-5">
<div className="flex flex-col gap-8">
<ContrattoSection handleUpdate={handleUpdate} />
<Separator />
<ContrattoImport handleUpdate={handleUpdate} />
<Separator />
<RicevutaSection handleUpdate={handleUpdate} />
</div>
</CredenzaBody>
<CredenzaFooter>
<CredenzaClose asChild>
<Button aria-label="Close Credenza" variant="outline">
Chiudi
</Button>
</CredenzaClose>
</CredenzaFooter>
</CredenzaContent>
</Credenza>
);
};
const ContrattoSection = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { userId } = useServizio();
const { data } = useServizioAnnuncio();
const [selectedDoc, setSelectedDoc] = useState<string | null>(
data.doc_contratto_ref,
);
const utils = api.useUtils();
const { data: files, isLoading } = api.storage.getAll.useQuery();
const { mutate: setUserStorage } =
api.storage.addUserStorageEntry.useMutation({
onSuccess: async () => {
await utils.storage.retrieveUserFileData.invalidate({
userId,
});
await utils.storage.getAll.invalidate();
},
});
const handleSave = () => {
handleUpdate({
doc_contratto_added: selectedDoc != null,
doc_contratto_ref: selectedDoc,
});
if (selectedDoc) {
setUserStorage({
storageId: selectedDoc,
userId,
});
}
};
if (isLoading) return <LoadingPage />;
if (files === undefined) {
return <p>Errore nel caricamento dei documenti</p>;
}
return (
<Card>
<CardHeader>
<CardTitle>Documento del Contratto</CardTitle>
<CardDescription>contratto di locazione</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{selectedDoc && (
<div className="flex flex-wrap items-center gap-3">
<DocInfo storageId={selectedDoc}>
<Button
aria-label="Reset Contratto"
className="flex items-center gap-3"
onClick={() => {
handleUpdate({
doc_contratto_added: false,
doc_contratto_ref: null,
});
setSelectedDoc(null);
}}
variant="destructive"
>
<Trash2 className="size-6" />
</Button>
</DocInfo>
</div>
)}
<DocCombo
current={selectedDoc}
files={files}
onSelect={(f) => {
setSelectedDoc(f.id);
}}
/>
<UploadModal isAdmin userId={userId} />
</CardContent>
<CardFooter>
<Button
aria-label="Save Contratto"
onClick={() => {
handleSave();
}}
variant="success"
>
Salva
</Button>
</CardFooter>
</Card>
);
};
const RicevutaSection = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { userId } = useServizio();
const { data } = useServizioAnnuncio();
const [selectedDoc, setSelectedDoc] = useState<string | null>(
data.doc_registrazione_ref,
);
const utils = api.useUtils();
const { data: files, isLoading } = api.storage.getAll.useQuery();
const { mutate: setUserStorage } =
api.storage.addUserStorageEntry.useMutation({
onSuccess: async () => {
await utils.storage.retrieveUserFileData.invalidate({
userId,
});
await utils.storage.getAll.invalidate();
},
});
const handleSave = () => {
handleUpdate({
doc_registrazione_added: selectedDoc != null,
doc_registrazione_ref: selectedDoc,
});
if (selectedDoc) {
setUserStorage({
storageId: selectedDoc,
userId,
});
}
};
if (isLoading) return <LoadingPage />;
if (files === undefined) {
return <p>Errore nel caricamento dei documenti</p>;
}
return (
<Card>
<CardHeader>
<CardTitle>Ricevuta di Registrazione</CardTitle>
<CardDescription className="sr-only">desc</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{selectedDoc && (
<div className="flex flex-wrap items-center gap-3">
<DocInfo storageId={selectedDoc}>
<Button
aria-label="Reset Ricevuta"
className="flex items-center gap-3"
onClick={() => {
handleUpdate({
doc_registrazione_added: false,
doc_registrazione_ref: null,
});
setSelectedDoc(null);
}}
variant="destructive"
>
<Trash2 className="size-6" />
</Button>
</DocInfo>
</div>
)}
<DocCombo
current={selectedDoc}
files={files}
onSelect={(f) => {
setSelectedDoc(f.id);
}}
/>
<UploadModal isAdmin userId={userId} />
</CardContent>
<CardFooter>
<Button
aria-label="Save Ricevuta"
onClick={() => {
handleSave();
}}
variant="success"
>
Salva
</Button>
</CardFooter>
</Card>
);
};
const AdminLavoraConferma = () => {
const { userId, servizioId } = useServizio();
const { data, annuncioId } = useServizioAnnuncio();
const [open, setOpen] = useState(false);
const utils = api.useUtils();
const { mutate: updateServizio } =
api.servizio.updateServizioAnnunci.useMutation({
onError: (error) => {
toast.error(error.message);
},
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getServizio.invalidate({
servizioId,
});
toast.success("Dati salvati con successo");
},
});
const handleUpdate = (upd: ServizioAnnunciUpdate) => {
updateServizio({
annuncioId,
data: {
...upd,
},
servizioId,
});
};
return (
<Credenza onOpenChange={setOpen} open={open}>
<CredenzaTrigger asChild>
<Button
aria-label="Open Lavora Conferma Section"
className="w-fit"
variant={data.hasConfermaAdmin ? "success" : "secondary"}
>
{data.hasConfermaAdmin ? (
<CircleCheck className="size-6" />
) : (
<Wrench className="size-6" />
)}
Conferma
</Button>
</CredenzaTrigger>
<CredenzaContent className="max-h-[90vh] w-full px-3 sm:max-w-4xl">
<CredenzaHeader>
<CredenzaTitle>Lavorazione conferma</CredenzaTitle>
<CredenzaDescription className="sr-only">
Consegna status modal
</CredenzaDescription>
</CredenzaHeader>
<CredenzaBody className="max-h-[80vh] w-full overflow-y-auto pb-5">
<div className="flex flex-col gap-8">
<DocSection handleUpdate={handleUpdate} />
<Caparra handleUpdate={handleUpdate} />
<SendConferma handleUpdate={handleUpdate} />
</div>
</CredenzaBody>
<CredenzaFooter>
<CredenzaClose asChild>
<Button aria-label="Close Credenza" variant="outline">
Chiudi
</Button>
</CredenzaClose>
</CredenzaFooter>
</CredenzaContent>
</Credenza>
);
};
const DocSection = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { userId } = useServizio();
const { data } = useServizioAnnuncio();
const utils = api.useUtils();
const { data: files, isLoading } = api.storage.getAll.useQuery();
const { mutate: setUserStorage } =
api.storage.addUserStorageEntry.useMutation({
onSuccess: async () => {
await utils.storage.retrieveUserFileData.invalidate({
userId,
});
await utils.storage.getAll.invalidate();
},
});
if (isLoading) return <LoadingPage />;
if (files === undefined) {
return <p>Errore nel caricamento dei documenti</p>;
}
return (
<Card>
<CardHeader>
<CardTitle>Documento di Conferma</CardTitle>
<CardDescription>
documento che l&apos;utente vedra alla conferma
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{data.doc_conferma_ref && (
<div className="flex flex-wrap items-start gap-3">
<DocInfo storageId={data.doc_conferma_ref}>
<Button
aria-label="Reset Conferma"
className="flex items-center gap-3"
onClick={() => {
handleUpdate({
doc_conferma_added: false,
doc_conferma_ref: null,
});
}}
variant="destructive"
>
<Trash2 />
</Button>
</DocInfo>
</div>
)}
<div className="flex flex-wrap items-center gap-3">
<DocCombo
current={data.doc_conferma_ref}
files={files}
onSelect={(f) => {
handleUpdate({
doc_conferma_added: true,
doc_conferma_ref: f.id,
});
setUserStorage({
storageId: f.id,
userId,
});
}}
/>
<UploadModal isAdmin userId={userId} />
</div>
</CardContent>
</Card>
);
};
const DocInfo = ({
storageId,
children,
}: {
storageId: string;
children: React.ReactNode;
}) => {
const { data: file_info } = api.storage.getFileInfos.useQuery({
storageId,
});
if (!file_info) return null;
return (
<div className="flex w-full flex-col gap-2">
<Label htmlFor="selected-file">File Selezionato: </Label>
<div className="flex w-full items-center gap-2 sm:max-w-[30rem]">
<div className="flex w-full flex-1">
<Link
aria-label="Open Selected File"
className={cn(
buttonVariants({
variant: "outline",
}),
"flex w-fit grow items-center gap-2",
)}
href={`/area-riservata/allegato-view/${storageId}`}
id="selected-file"
target="_blank"
>
<ExtIcon mimeType={file_info.mimeType} />
<span className="max-w-40 truncate sm:max-w-80">
{file_info.originalName}
</span>
<ExternalLink className="size-4" />
</Link>
</div>
{children}
</div>
</div>
);
};
const DocCombo = ({
files,
onSelect,
current,
}: {
files: FileMetadataWithAdmin[];
onSelect: (file: FileMetadataWithAdmin) => void;
current: string | null;
}) => {
const [open, setOpen] = useState(false);
return (
<Popover onOpenChange={setOpen} open={open}>
<PopoverTrigger asChild>
<Button
aria-expanded={open}
aria-label="Select Existing File"
className="w-auto justify-between"
role="combobox"
variant="outline"
>
Seleziona file esistente...
<ChevronsUpDown className="ml-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Command>
<CommandInput placeholder="Cerca Documento..." />
<CommandList>
<CommandEmpty>Nessun file.</CommandEmpty>
<CommandGroup>
{files.map((file) => (
<CommandItem
key={file.id}
onSelect={(currentValue) => {
if (currentValue !== current) {
// biome-ignore lint/style/noNonNullAssertion: <exists>
onSelect(files.find((f) => f.id === currentValue)!);
}
setOpen(false);
}}
value={file.id}
>
<span className="flex items-center gap-2">
<ExtIcon mimeType={file.mimeType} />
<span className="block font-medium">
{file.originalName}
</span>
{current === file.id && (
<Check className="size-4 text-accent-foreground" />
)}
</span>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};
const CaparraSchema = z.object({
caparra_causale: z.string().nullable(),
caparra_iban: z.string().nullable(),
caparra_importo: z.string().nullable(),
caparra_intestazione: z.string().nullable(),
});
type CaparraObj = z.infer<typeof CaparraSchema>;
const parseCaparra = (ingest: string) => {
let parsedJson: unknown;
try {
parsedJson = JSON.parse(ingest);
} catch {
toast.error(
'Stringa non valida, clicca "copia stringa" nel gestionale e poi questo pulsante',
);
return undefined;
}
const parsed = CaparraSchema.safeParse(parsedJson);
if (parsed.success) {
return parsed.data;
}
console.error("Invalid caparra format:", parsed.error);
return undefined;
};
const Caparra = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { data } = useServizioAnnuncio();
const [caparra, setCaparra] = useState<CaparraObj | undefined>({
caparra_causale: data.caparra_causale,
caparra_iban: data.caparra_iban,
caparra_importo: data.caparra_importo,
caparra_intestazione: data.caparra_intestazione,
});
const handleSave = () => {
handleUpdate({
...caparra,
});
};
return (
<Card>
<CardHeader>
<CardTitle>Dati Pagamento Caparra</CardTitle>
<CardDescription>
dati per il pagamento della caparra, da mostrare all&apos;utente
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-wrap items-center gap-3">
<Button
aria-label="Import Caparra"
onClick={async () => {
const clipboardText = await navigator.clipboard.readText();
setCaparra(parseCaparra(clipboardText));
}}
variant="info"
>
<ClipboardPaste className="size-4" />
Dal gestionale
</Button>
<Button
aria-label="Reset Caparra"
onClick={async () => {
setCaparra({
caparra_causale: null,
caparra_iban: null,
caparra_importo: null,
caparra_intestazione: null,
});
}}
variant="destructive"
>
<Trash2 className="size-4" />
</Button>
</div>
{caparra ? (
<div className="flex flex-col gap-2">
<p>
<span className="font-semibold">Intestazione: </span>
{caparra.caparra_intestazione}{" "}
<Button
aria-label="Edit Intestazione"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica l'intestazione della caparra:",
caparra.caparra_intestazione || "",
);
if (newValue !== null) {
setCaparra((prev) =>
prev
? { ...prev, caparra_intestazione: newValue }
: undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
<p>
<span className="font-semibold">IBAN: </span>
{caparra.caparra_iban}{" "}
<Button
aria-label="Edit IBAN"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica l'IBAN della caparra:",
caparra.caparra_iban || "",
);
if (newValue !== null) {
setCaparra((prev) =>
prev ? { ...prev, caparra_iban: newValue } : undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
<p>
<span className="font-semibold">Importo: </span>
{caparra.caparra_importo}{" "}
<Button
aria-label="Edit Importo"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica l'importo della caparra:",
caparra.caparra_importo || "",
);
if (newValue !== null) {
setCaparra((prev) =>
prev ? { ...prev, caparra_importo: newValue } : undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
<p>
<span className="font-semibold">Causale: </span>
{caparra.caparra_causale}{" "}
<Button
aria-label="Edit Causale"
className="h-6 px-1.5 [&_svg]:size-4"
onClick={() => {
const newValue = prompt(
"Modifica la causale della caparra:",
caparra.caparra_causale || "",
);
if (newValue !== null) {
setCaparra((prev) =>
prev ? { ...prev, caparra_causale: newValue } : undefined,
);
}
}}
size="sm"
>
<Pen />
</Button>
</p>
</div>
) : (
<p>Inserisci la stringa dal gestionale e elabora</p>
)}
</CardContent>
<CardFooter>
<Button
aria-label="Save Caparra"
onClick={() => {
handleSave();
}}
variant="success"
>
Salva
</Button>
</CardFooter>
</Card>
);
};
const SendConferma = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { servizio } = useServizio();
const { data } = useServizioAnnuncio();
const { t } = useTranslation();
const [check, setCheck] = useState(data.hasConfermaAdmin);
return (
<Card>
<CardHeader>
<CardTitle>Lavorazione Conferma</CardTitle>
<CardDescription>
controlla i parametri di ricerca prima!
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-col gap-2">
<p>Tipologia servizio: {servizio.tipologia}</p>
{(() => {
if (servizio.tipologia === TipologiaPosizioneEnum.Transitorio) {
return (
<>
<p>
Permanenza (da preferenze):{" "}
{servizio.permanenza
? t.preferenze.permanenza[servizio.permanenza]
: "non inserita"}
</p>
<p>
Permanenza (da descrizione): &quot;
{(() => {
const match = data.desc_it?.match(/Permanenza:.*$/m);
return match ? match[0] : "";
})()}
&quot;
</p>
</>
);
}
return (
<>
<p>Budget (da preferenze): {formatCurrency(servizio.budget)}</p>
<p>
Canone Mensile (da descrizione):{" "}
{formatCurrency(data.prezzo / 100)}
</p>
</>
);
})()}
</div>
<div className="flex items-center gap-3">
<Switch
aria-label="Conferma Lavorata"
checked={check}
id="conferma"
onCheckedChange={(c) => {
setCheck(c);
}}
/>
<Label htmlFor="conferma">Conferma lavorata</Label>
</div>
</CardContent>
<CardFooter>
<Button
aria-label="Save Conferma Status"
onClick={() => {
handleUpdate({
hasConfermaAdmin: check,
});
}}
variant="success"
>
Salva
</Button>
</CardFooter>
</Card>
);
};