feat: enhance document upload functionality and add save state indicators in Admin sections

This commit is contained in:
Marco Pedone 2026-02-19 10:48:31 +01:00
parent 8f3802ede3
commit 890f0572f3
2 changed files with 119 additions and 14 deletions

View file

@ -1,5 +1,6 @@
import { format } from "date-fns"; import { format } from "date-fns";
import { import {
CircleAlert,
CircleCheck, CircleCheck,
ClipboardPaste, ClipboardPaste,
FlaskConical, FlaskConical,
@ -230,7 +231,18 @@ const DocSection = () => {
}); });
}} }}
/> />
<UploadModal isAdmin userId={userId} /> <UploadModal
cb_onUpload={async ({ storageIds }) => {
if (storageIds.length === 1 && storageIds[0]) {
await updateServizioAnnunci({
doc_conferma_added: true,
doc_conferma_ref: storageIds[0],
});
}
}}
isAdmin
userId={userId}
/>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -266,13 +278,16 @@ const parseCaparra = (ingest: string) => {
const Caparra = () => { const Caparra = () => {
const { data, updateServizioAnnunci } = useServizioAnnuncio(); const { data, updateServizioAnnunci } = useServizioAnnuncio();
const initialCaparra = {
const [caparra, setCaparra] = useState<CaparraObj | undefined>({
caparra_causale: data.caparra_causale, caparra_causale: data.caparra_causale,
caparra_iban: data.caparra_iban, caparra_iban: data.caparra_iban,
caparra_importo: data.caparra_importo, caparra_importo: data.caparra_importo,
caparra_intestazione: data.caparra_intestazione, caparra_intestazione: data.caparra_intestazione,
}); };
const [caparra, setCaparra] = useState<CaparraObj | undefined>(
initialCaparra,
);
const handleSave = async () => { const handleSave = async () => {
await updateServizioAnnunci({ await updateServizioAnnunci({
caparra_causale: caparra?.caparra_causale || null, caparra_causale: caparra?.caparra_causale || null,
@ -415,9 +430,10 @@ const Caparra = () => {
<p>Inserisci la stringa dal gestionale e elabora</p> <p>Inserisci la stringa dal gestionale e elabora</p>
)} )}
</CardContent> </CardContent>
<CardFooter> <CardFooter className="gap-4">
<Button <Button
aria-label="Save Caparra" aria-label="Save Caparra"
disabled={JSON.stringify(caparra) === JSON.stringify(initialCaparra)}
onClick={async () => { onClick={async () => {
await handleSave(); await handleSave();
}} }}
@ -425,6 +441,19 @@ const Caparra = () => {
> >
Salva Salva
</Button> </Button>
{JSON.stringify(caparra) !== JSON.stringify(initialCaparra) ? (
<div className="flex items-center gap-2">
<CircleAlert className="size-5 text-orange-500" />
<span className="text-orange-500">Salva modifiche</span>
</div>
) : (
Object.values(initialCaparra).every((v) => v !== null) && (
<div className="flex items-center gap-2">
<CircleCheck className="size-5 text-green-500" />
<span className="text-green-500">Dati salvati</span>
</div>
)
)}
</CardFooter> </CardFooter>
</Card> </Card>
); );

View file

@ -1,4 +1,11 @@
import { CircleCheck, ClipboardPaste, Pen, Trash2, Wrench } from "lucide-react"; import {
CircleAlert,
CircleCheck,
ClipboardPaste,
Pen,
Trash2,
Wrench,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import z from "zod"; import z from "zod";
@ -77,6 +84,7 @@ export const AdminContratto = () => {
const ContrattoSection = () => { const ContrattoSection = () => {
const { userId } = useServizio(); const { userId } = useServizio();
const { data, updateServizioAnnunci } = useServizioAnnuncio(); const { data, updateServizioAnnunci } = useServizioAnnuncio();
const initialDoc = data.doc_contratto_ref;
const [selectedDoc, setSelectedDoc] = useState<string | null>( const [selectedDoc, setSelectedDoc] = useState<string | null>(
data.doc_contratto_ref, data.doc_contratto_ref,
); );
@ -112,6 +120,7 @@ const ContrattoSection = () => {
if (files === undefined) { if (files === undefined) {
return <p>Errore nel caricamento dei documenti</p>; return <p>Errore nel caricamento dei documenti</p>;
} }
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
@ -146,11 +155,20 @@ const ContrattoSection = () => {
setSelectedDoc(f.id); setSelectedDoc(f.id);
}} }}
/> />
<UploadModal isAdmin userId={userId} /> <UploadModal
cb_onUpload={({ storageIds }) => {
if (storageIds.length === 1 && storageIds[0]) {
setSelectedDoc(storageIds[0]);
}
}}
isAdmin
userId={userId}
/>
</CardContent> </CardContent>
<CardFooter> <CardFooter className="gap-4">
<Button <Button
aria-label="Save Contratto" aria-label="Save Contratto"
disabled={selectedDoc === initialDoc}
onClick={async () => { onClick={async () => {
await handleSave(); await handleSave();
}} }}
@ -158,6 +176,20 @@ const ContrattoSection = () => {
> >
Salva Salva
</Button> </Button>
{selectedDoc !== initialDoc ? (
<div className="flex items-center gap-2">
<CircleAlert className="size-5 text-orange-500" />
<span className="text-orange-500">Salva modifiche</span>
</div>
) : (
initialDoc &&
selectedDoc === initialDoc && (
<div className="flex items-center gap-2">
<CircleCheck className="size-5 text-green-500" />
<span className="text-green-500">Dati salvati</span>
</div>
)
)}
</CardFooter> </CardFooter>
</Card> </Card>
); );
@ -217,8 +249,7 @@ const parseContrattoImport = (ingest: string) => {
const ContrattoImport = () => { const ContrattoImport = () => {
const { data, updateServizioAnnunci } = useServizioAnnuncio(); const { data, updateServizioAnnunci } = useServizioAnnuncio();
const initialContratto = {
const [contratto, setContratto] = useState<ContrattoImportObj | undefined>({
contratto_decorrenza: data.contratto_decorrenza contratto_decorrenza: data.contratto_decorrenza
? new Date(data.contratto_decorrenza) ? new Date(data.contratto_decorrenza)
: null, : null,
@ -227,7 +258,11 @@ const ContrattoImport = () => {
: null, : null,
contratto_tipo: data.contratto_tipo as ContrattoImportObj["contratto_tipo"], contratto_tipo: data.contratto_tipo as ContrattoImportObj["contratto_tipo"],
gestionale_id: data.gestionale_id, gestionale_id: data.gestionale_id,
}); };
const [contratto, setContratto] = useState<ContrattoImportObj | undefined>(
initialContratto,
);
const handleSave = async () => { const handleSave = async () => {
await updateServizioAnnunci({ await updateServizioAnnunci({
contratto_decorrenza: contratto?.contratto_decorrenza || null, contratto_decorrenza: contratto?.contratto_decorrenza || null,
@ -404,9 +439,12 @@ const ContrattoImport = () => {
<p>Inserisci la stringa dal gestionale e elabora</p> <p>Inserisci la stringa dal gestionale e elabora</p>
)} )}
</CardContent> </CardContent>
<CardFooter> <CardFooter className="gap-4">
<Button <Button
aria-label="Save Contratto" aria-label="Save Contratto"
disabled={
JSON.stringify(contratto) === JSON.stringify(initialContratto)
}
onClick={async () => { onClick={async () => {
await handleSave(); await handleSave();
}} }}
@ -414,6 +452,19 @@ const ContrattoImport = () => {
> >
Salva Salva
</Button> </Button>
{JSON.stringify(contratto) !== JSON.stringify(initialContratto) ? (
<div className="flex items-center gap-2">
<CircleAlert className="size-5 text-orange-500" />
<span className="text-orange-500">Salva modifiche</span>
</div>
) : (
Object.values(initialContratto).every((v) => v !== null) && (
<div className="flex items-center gap-2">
<CircleCheck className="size-5 text-green-500" />
<span className="text-green-500">Dati salvati</span>
</div>
)
)}
</CardFooter> </CardFooter>
</Card> </Card>
); );
@ -422,6 +473,7 @@ const ContrattoImport = () => {
const RicevutaSection = () => { const RicevutaSection = () => {
const { userId } = useServizio(); const { userId } = useServizio();
const { data, updateServizioAnnunci } = useServizioAnnuncio(); const { data, updateServizioAnnunci } = useServizioAnnuncio();
const initialDoc = data.doc_registrazione_ref;
const [selectedDoc, setSelectedDoc] = useState<string | null>( const [selectedDoc, setSelectedDoc] = useState<string | null>(
data.doc_registrazione_ref, data.doc_registrazione_ref,
); );
@ -457,6 +509,7 @@ const RicevutaSection = () => {
if (files === undefined) { if (files === undefined) {
return <p>Errore nel caricamento dei documenti</p>; return <p>Errore nel caricamento dei documenti</p>;
} }
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
@ -491,11 +544,20 @@ const RicevutaSection = () => {
setSelectedDoc(f.id); setSelectedDoc(f.id);
}} }}
/> />
<UploadModal isAdmin userId={userId} /> <UploadModal
cb_onUpload={({ storageIds }) => {
if (storageIds.length === 1 && storageIds[0]) {
setSelectedDoc(storageIds[0]);
}
}}
isAdmin
userId={userId}
/>
</CardContent> </CardContent>
<CardFooter> <CardFooter className="gap-4">
<Button <Button
aria-label="Save Ricevuta" aria-label="Save Ricevuta"
disabled={selectedDoc === initialDoc}
onClick={async () => { onClick={async () => {
await handleSave(); await handleSave();
}} }}
@ -503,6 +565,20 @@ const RicevutaSection = () => {
> >
Salva Salva
</Button> </Button>
{selectedDoc !== initialDoc ? (
<div className="flex items-center gap-2">
<CircleAlert className="size-5 text-orange-500" />
<span className="text-orange-500">Salva modifiche</span>
</div>
) : (
initialDoc &&
selectedDoc === initialDoc && (
<div className="flex items-center gap-2">
<CircleCheck className="size-5 text-green-500" />
<span className="text-green-500">Dati salvati</span>
</div>
)
)}
</CardFooter> </CardFooter>
</Card> </Card>
); );