refactor: enhance AdminLavoraConferma component with GeneralSection and improve form handling in AnnuncioEditForm

This commit is contained in:
Marco Pedone 2025-12-16 16:43:42 +01:00
parent 4ce60fd827
commit c783dfed03
4 changed files with 194 additions and 35 deletions

View file

@ -12,6 +12,8 @@ import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useDebounce } from "react-use"; import { useDebounce } from "react-use";
import z from "zod"; import z from "zod";
import { it } from "~/i18n/it";
import { handleConsegna } from "~/lib/annuncio_details";
import { formatCurrency } from "~/lib/utils"; import { formatCurrency } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider"; import { useTranslation } from "~/providers/I18nProvider";
import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider"; import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider";
@ -19,6 +21,7 @@ import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage"; import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import { Confirm } from "../confirm"; import { Confirm } from "../confirm";
import { Counter } from "../counter";
import { import {
Credenza, Credenza,
CredenzaBody, CredenzaBody,
@ -548,6 +551,12 @@ const TransitorioSection = () => {
}); });
}; };
const preferenzeDescrizione = data.desc_it?.match(/Permanenza:.*$/m); const preferenzeDescrizione = data.desc_it?.match(/Permanenza:.*$/m);
const permanenzaAnnuncio = data.permanenza
? `${data.permanenza.join(" - ")} mesi`
: (preferenzeDescrizione?.[0] ??
"errore: testo non trovato in descrizione");
return ( return (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="max-w-sm"> <div className="max-w-sm">
@ -575,10 +584,8 @@ const TransitorioSection = () => {
/> />
</div> </div>
<p> <p>
<span className="font-semibold">Permanenza in descrizione: </span> <span className="font-semibold">Permanenza in annuncio: </span>
{preferenzeDescrizione {permanenzaAnnuncio}
? preferenzeDescrizione[0]
: "errore: testo non trovato in descrizione"}
</p> </p>
</div> </div>
); );
@ -639,6 +646,131 @@ const StabileSection = () => {
); );
}; };
const GeneralSection = () => {
const { servizio } = useServizio();
const { data } = useServizioAnnuncio();
const { t } = useTranslation();
const { servizioId, userId } = useServizio();
const utils = api.useUtils();
const { mutate: update } = api.servizio.updateServizio.useMutation({
onError: (error) => {
toast.error(error.message);
},
onSuccess: async () => {
toast.success("Servizio modificato con successo");
await utils.servizio.getUserServizi.invalidate();
await utils.servizio.getAllServizioAnnunci.invalidate({ userId });
},
});
const handleDisponibilitàChange = (value: ListOption) => {
update({
servizioId,
data: {
entromese: value.value !== "" ? parseInt(value.value) : null,
},
});
};
const handleAdultiChange = (value: number) => {
update({
servizioId,
data: {
n_adulti: value,
},
});
};
const handleMinoriChange = (value: number) => {
update({
servizioId,
data: {
n_minori: value,
},
});
};
const personeDescrizione = data.desc_it?.match(/Preferenza:.*$/m);
const personeAnnuncio = data.persone
? data.persone.join(", ").toUpperCase()
: (personeDescrizione?.[0] ?? "errore: testo non trovato in descrizione");
const dispAnnuncio = data.disponibile_da
? format(new Date(data.disponibile_da), "dd/MM/yyyy")
: handleConsegna({
consegna: data.consegna,
aggiornamento: it.card.in_aggiornamento,
consegna_da: it.card.consegna_da,
mesi: it.preferenze.mesi,
subito: it.card.consegna_subito,
});
return (
<>
<div className="flex flex-col gap-2">
<span className="font-semibold">Persone:</span>
<div className="flex w-full gap-4">
<div className="flex w-1/2 flex-col gap-2">
<Label htmlFor="n_adulti">{t.preferenze.nadulti_label}</Label>
<Counter
name="n_adulti"
onlyPositive
setValue={(v) => handleAdultiChange(Number(v))}
value={servizio.n_adulti || 0}
/>
</div>
<div className="flex w-1/2 flex-col gap-2">
<Label htmlFor="n_minori">{t.preferenze.nminori_label}</Label>
<Counter
name="n_minori"
onlyPositive
setValue={(v) => handleMinoriChange(Number(v))}
value={servizio.n_minori || 0}
/>
</div>
</div>
<p>
<span className="font-semibold">Persone in annuncio: </span>
{personeAnnuncio}
</p>
</div>
<div className="flex flex-col gap-2">
<span className="font-semibold">Entro mese:</span>
<div className="max-w-sm">
<Label htmlFor="select-mesi">{t.preferenze.dalmese_label}</Label>
<MultiSelect
defaultValue={
servizio.entromese !== null
? {
// biome-ignore lint/style/noNonNullAssertion: <known size>
label: t.preferenze.mesi[servizio.entromese]!,
value: String(servizio.entromese),
}
: undefined
}
elementId="select-mesi"
elementName="entro_mese"
isMulti={false}
onValueChange={handleDisponibilitàChange}
options={UnpackOptions({
options: t.preferenze.mesi,
})}
placeholder="Seleziona..."
/>
</div>
<p>
<span className="font-semibold">Disponibilità in annuncio: </span>
{dispAnnuncio}
</p>
</div>
</>
);
};
const SendConferma = () => { const SendConferma = () => {
const { servizio } = useServizio(); const { servizio } = useServizio();
const { data } = useServizioAnnuncio(); const { data } = useServizioAnnuncio();
@ -667,7 +799,7 @@ const SendConferma = () => {
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-10"> <CardContent className="space-y-10">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-4">
<p> <p>
<span className="font-semibold">Tipologia servizio: </span> <span className="font-semibold">Tipologia servizio: </span>
{servizio.tipologia} {servizio.tipologia}
@ -677,6 +809,7 @@ const SendConferma = () => {
) : ( ) : (
<StabileSection /> <StabileSection />
)} )}
<GeneralSection />
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Switch <Switch

View file

@ -1041,11 +1041,25 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
name="disponibile_da" name="disponibile_da"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className="flex flex-wrap items-center gap-x-2"> <div className="relative flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="disponibile_da"> <FormLabel htmlFor="disponibile_da">
Disponibile Da Disponibile Da
</FormLabel> </FormLabel>
<FormMessage /> <FormMessage />
{field.value !== null && (
<Button
className="-top-2 absolute right-0 flex items-center gap-1"
onClick={() => {
form.setValue("disponibile_da", null);
}}
size="sm"
type="button"
variant="link"
>
<RotateCw className="size-4" />
Reset
</Button>
)}
</div> </div>
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
@ -1096,36 +1110,38 @@ export const AnnuncioEditForm = ({ data }: { data: AnnunciWithMedia }) => {
<FormField <FormField
control={form.control} control={form.control}
name="consegna" name="consegna"
render={({ field }) => ( render={({ field: { value, onChange } }) => {
<FormItem> const currVal =
<div className="flex flex-wrap items-center gap-x-2"> value !== null
<FormLabel htmlFor="select-consegna"> ? consegnaOptions.filter(
Consegna (o) => value.toString() === o.value,
</FormLabel> )[0]
<FormMessage /> : undefined;
</div> return (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="select-consegna">
Consegna
</FormLabel>
<FormMessage />
</div>
<FormControl> <FormControl>
<MultiSelect <MultiSelect
defaultValue={ defaultValue={currVal}
field.value elementId="select-consegna"
? consegnaOptions.filter((o) => elementName="consegna"
field.value?.toString().includes(o.value), isMulti={false}
) onValueChange={async (value) => {
: undefined onChange(parseInt(value.value));
} }}
elementId="select-consegna" options={consegnaOptions}
elementName="consegna" placeholder="Seleziona..."
isMulti={false} />
onValueChange={async (value) => { </FormControl>
field.onChange(parseInt(value.value)); </FormItem>
}} );
options={consegnaOptions} }}
placeholder="Seleziona..."
/>
</FormControl>
</FormItem>
)}
/> />
} }
children2={ children2={

View file

@ -14,6 +14,10 @@ export const handleConsegna = ({
aggiornamento: string; aggiornamento: string;
}) => { }) => {
const currentMonth = new Date().getMonth() + 1; const currentMonth = new Date().getMonth() + 1;
console.log("handleConsegna", {
consegna: consegna,
currentMonth: currentMonth,
});
if (consegna !== null) { if (consegna !== null) {
if (consegna !== currentMonth) { if (consegna !== currentMonth) {
return `${mesi[consegna]}`; return `${mesi[consegna]}`;

View file

@ -635,6 +635,9 @@ export const getAllServizioAnnunci = async (userId: UsersId) => {
"servizio.skipPayment", "servizio.skipPayment",
"servizio.skipControlloDoc", "servizio.skipControlloDoc",
"servizio.skipDocMotivazione", "servizio.skipDocMotivazione",
"servizio.n_adulti",
"servizio.n_minori",
"servizio.entromese",
jsonObjectFrom( jsonObjectFrom(
eb eb
.selectFrom("users_storage") .selectFrom("users_storage")
@ -699,6 +702,9 @@ export const getAllServizioAnnunci = async (userId: UsersId) => {
"annunci.media_updated_at", "annunci.media_updated_at",
"annunci.homepage", "annunci.homepage",
"annunci.web", "annunci.web",
"annunci.disponibile_da",
"annunci.persone",
"annunci.permanenza",
withImages(), withImages(),
withVideos(), withVideos(),
]) ])