import {
Check,
ChevronsUpDown,
CircleCheck,
ClipboardPaste,
ExternalLink,
FolderKanban,
Pen,
Plus,
Trash2,
Wrench,
} from "lucide-react";
import Link from "next/link";
import { useRef, 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 {
AnimatedButton,
type AnimatedButtonRef,
} from "~/components/custom_ui/animated-button";
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 { AnnunciId } from "~/schemas/public/Annunci";
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 (
{isAdmin && data.user_confirmed_at != null &&
}
{isAdmin && data.accettato_conferma_at != null && (
<>
{data.gestionale_id && (
)}
>
)}
{isAdmin && (
<>
{
mutate({
annuncioId,
servizioId,
});
}}
title="Elimina annuncio"
>
>
)}
);
}
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,
"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({
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 (
Dati Contratto
desc
{contratto ? (
ID Gestionale:
{contratto.gestionale_id}{" "}
Decorrenza:
{contratto.contratto_decorrenza &&
new Date(contratto.contratto_decorrenza).toLocaleDateString(
"it-IT",
)}{" "}
Scadenza:
{contratto.contratto_scadenza &&
new Date(contratto.contratto_scadenza).toLocaleDateString(
"it-IT",
)}{" "}
Tipo di contratto:
{contratto.contratto_tipo}{" "}
) : (
Inserisci la stringa dal gestionale e elabora
)}
);
};
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 (
Sezione Contratto
Contratto modal
);
};
const ContrattoSection = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { userId } = useServizio();
const { data } = useServizioAnnuncio();
const [selectedDoc, setSelectedDoc] = useState(
data.doc_contratto_ref,
);
const utils = api.useUtils();
const { data: files, isLoading } = api.storage.getAll.useQuery();
const { mutate: setUserStorage } = api.storage.addUserStorage.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 ;
if (files === undefined) {
return Errore nel caricamento dei documenti
;
}
return (
Documento del Contratto
contratto di locazione
{selectedDoc && (
)}
{
setSelectedDoc(f.id);
}}
/>
);
};
const RicevutaSection = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { userId } = useServizio();
const { data } = useServizioAnnuncio();
const [selectedDoc, setSelectedDoc] = useState(
data.doc_registrazione_ref,
);
const utils = api.useUtils();
const { data: files, isLoading } = api.storage.getAll.useQuery();
const { mutate: setUserStorage } = api.storage.addUserStorage.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 ;
if (files === undefined) {
return Errore nel caricamento dei documenti
;
}
return (
Ricevuta di Registrazione
desc
{selectedDoc && (
)}
{
setSelectedDoc(f.id);
}}
/>
);
};
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 (
Lavorazione conferma
Consegna status modal
);
};
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.addUserStorage.useMutation({
onSuccess: async () => {
await utils.storage.retrieveUserFileData.invalidate({
userId,
});
await utils.storage.getAll.invalidate();
},
});
if (isLoading) return ;
if (files === undefined) {
return Errore nel caricamento dei documenti
;
}
return (
Documento di Conferma
documento che l'utente vedra alla conferma
{data.doc_conferma_ref && (
)}
{
handleUpdate({
doc_conferma_added: true,
doc_conferma_ref: f.id,
});
setUserStorage({
storageId: f.id,
userId,
});
}}
/>
);
};
const DocInfo = ({
storageId,
children,
}: {
storageId: string;
children: React.ReactNode;
}) => {
const { data: file_info } = api.storage.getFileInfos.useQuery({
storageId,
});
if (!file_info) return null;
return (
{file_info.originalName}
{children}
);
};
const DocCombo = ({
files,
onSelect,
current,
}: {
files: FileMetadataWithAdmin[];
onSelect: (file: FileMetadataWithAdmin) => void;
current: string | null;
}) => {
const [open, setOpen] = useState(false);
return (
Nessun file.
{files.map((file) => (
{
if (currentValue !== current) {
// biome-ignore lint/style/noNonNullAssertion:
onSelect(files.find((f) => f.id === currentValue)!);
}
setOpen(false);
}}
value={file.id}
>
{file.originalName}
{current === file.id && (
)}
))}
);
};
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;
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({
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 (
Dati Pagamento Caparra
dati per il pagamento della caparra, da mostrare all'utente
{caparra ? (
Intestazione:
{caparra.caparra_intestazione}{" "}
IBAN:
{caparra.caparra_iban}{" "}
Importo:
{caparra.caparra_importo}{" "}
Causale:
{caparra.caparra_causale}{" "}
) : (
Inserisci la stringa dal gestionale e elabora
)}
);
};
const SendConferma = ({
handleUpdate,
}: {
handleUpdate: (upd: ServizioAnnunciUpdate) => void;
}) => {
const { servizio } = useServizio();
const { data } = useServizioAnnuncio();
const { t } = useTranslation();
const [check, setCheck] = useState(data.hasConfermaAdmin);
return (
Lavorazione Conferma
controlla i parametri di ricerca prima!
Tipologia servizio: {servizio.tipologia}
{(() => {
if (servizio.tipologia === TipologiaPosizioneEnum.Transitorio) {
return (
<>
Permanenza (da preferenze):{" "}
{servizio.permanenza
? t.preferenze.permanenza[servizio.permanenza]
: "non inserita"}
Permanenza (da descrizione): "
{(() => {
const match = data.desc_it?.match(/Permanenza:.*$/m);
return match ? match[0] : "";
})()}
"
>
);
}
return (
<>
Budget (da preferenze): {formatCurrency(servizio.budget)}
Canone Mensile (da descrizione):{" "}
{formatCurrency(data.prezzo / 100)}
>
);
})()}
{
setCheck(c);
}}
/>
);
};
export const AddButton = ({ annuncioId }: { annuncioId: AnnunciId }) => {
const { servizioId, userId } = useServizio();
const utils = api.useUtils();
const { mutate } = api.servizio.addServizioAnnunci.useMutation({
onError: (error) => {
console.error(error);
toast.error("Errore durante l'aggiunta dell'annuncio");
},
onSuccess: async () => {
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
await utils.servizio.getAnnunciAvailableToAdd.invalidate({
servizioId,
});
await utils.servizio.AnnuncioServizioTipologiaMatch.invalidate({
userId,
});
await utils.servizio.getCompatibileAnnunci.invalidate({
servizioId,
});
toast.success("Annuncio aggiunto alla ricerca");
},
});
const buttonRef = useRef(null);
return (
{
buttonRef.current?.sparkle();
//wait for the animation to finish
await new Promise((resolve) => setTimeout(resolve, 1000));
mutate({ annunci: [annuncioId], servizioId });
}}
ref={buttonRef}
sparklesClassName="fill-yellow-500 stroke-transparent"
sparklesIcon="thumbs-up"
sparklesNumber={15}
sparklesRadius={60}
sparklesScale={[1, 1.5]}
variant="info"
>
Aggiungi agli annunci da contattare
);
};