diff --git a/apps/db/migrations/43_conferma_enum.up.sql b/apps/db/migrations/43_conferma_enum.up.sql new file mode 100644 index 0000000..57cef45 --- /dev/null +++ b/apps/db/migrations/43_conferma_enum.up.sql @@ -0,0 +1,53 @@ +DO $$ BEGIN + CREATE TYPE public."StatusConfermaEnum" AS ENUM ( + 'Inviato conferma', + 'Conferma accettata', + 'Caparra versata' + ); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +ALTER TABLE IF EXISTS public.servizio_annunci +ADD COLUMN IF NOT EXISTS status_conferma public."StatusConfermaEnum"; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'servizio_annunci' + AND column_name = 'hasConfermaAdmin' + ) THEN + UPDATE public.servizio_annunci + SET status_conferma = 'Caparra versata' + WHERE "hasConfermaAdmin" = true; + END IF; +END $$; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS doc_conferma_ref; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS user_confirmed_at; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS accettato_conferma_at; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS caparra_causale; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS caparra_iban; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS caparra_importo; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS caparra_intestazione; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS "hasConfermaAdmin"; + +ALTER TABLE IF EXISTS public.servizio_annunci +DROP COLUMN IF EXISTS doc_conferma_added; diff --git a/apps/infoalloggi/src/components/servizio/admin_conferma.tsx b/apps/infoalloggi/src/components/servizio/admin_conferma.tsx deleted file mode 100644 index c31f47d..0000000 --- a/apps/infoalloggi/src/components/servizio/admin_conferma.tsx +++ /dev/null @@ -1,893 +0,0 @@ -import { format } from "date-fns"; -import { - CircleAlert, - CircleCheck, - ClipboardPaste, - FlaskConical, - Pen, - Trash2, - Wrench, -} from "lucide-react"; -import Link from "next/link"; -import { useState } from "react"; -import toast from "react-hot-toast"; -import { useDebounce } from "react-use"; -import z from "zod"; -import { it } from "~/i18n/it"; -import { handleConsegna } from "~/lib/annuncio_details"; -import { formatCurrency } from "~/lib/utils"; -import { useTranslation } from "~/providers/I18nProvider"; -import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider"; -import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; -import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage"; -import { api } from "~/utils/api"; -import { Confirm } from "../confirm"; -import { Counter } from "../counter"; -import { - Credenza, - CredenzaBody, - CredenzaClose, - CredenzaContent, - CredenzaDescription, - CredenzaFooter, - CredenzaHeader, - CredenzaTitle, - CredenzaTrigger, -} from "../custom_ui/credenza"; -import { NumberInput } from "../custom_ui/InputNumber"; -import { - type ListOption, - MultiSelect, - UnpackOptions, -} from "../custom_ui/multiselect"; -import { LoadingPage } from "../loading"; -import { Button } from "../ui/button"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "../ui/card"; -import { Label } from "../ui/label"; -import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; -import { UploadModal } from "../upload_modal"; -import { ServizioPacksInfos } from "./servizio_actions"; -import { DocCombo, DocInfo } from "./shared"; - -export const AdminLavoraConferma = () => { - const { servizio } = useServizio(); - const { data } = useServizioAnnuncio(); - - return ( - - - - - - - Lavorazione conferma - - Consegna status modal - - - -
- - - {servizio.tipologia === TipologiaPosizioneEnum.Transitorio && ( - - )} - -
-
- - - - - - - -
-
- ); -}; - -const DebugActions = () => { - const { servizioId } = useServizio(); - const { annuncioId } = useServizioAnnuncio(); - const utils = api.useUtils(); - const { mutate } = api.servizio.updateServizioAnnunci.useMutation({ - onError: (error) => { - toast.error(error.message); - }, - onSuccess: async () => { - await utils.servizio.invalidate(); - - toast.success("Dati salvati con successo"); - }, - }); - - return ( - - - - - - { - mutate({ - annuncioId, - servizioId, - data: { - user_confirmed_at: null, - }, - }); - }} - title="Elimina annuncio" - > - - - - - ); -}; - -const DocSection = () => { - const { userId } = useServizio(); - const { data, updateServizioAnnunci } = useServizioAnnuncio(); - const initialDoc = data.doc_conferma_ref; - const [selectedDoc, setSelectedDoc] = useState( - data.doc_conferma_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 = async () => { - await updateServizioAnnunci({ - doc_conferma_added: selectedDoc != null, - doc_conferma_ref: selectedDoc, - }); - if (selectedDoc) { - setUserStorage({ - storageId: selectedDoc, - userId, - }); - } - }; - if (isLoading) return ; - if (files === undefined) { - return

Errore nel caricamento dei documenti

; - } - return ( - - - Documento di Conferma - - documento che l'utente vedra alla conferma - - - - {selectedDoc && ( -
- - - -
- )} - { - setSelectedDoc(f.id); - }} - /> - { - if (storageIds.length === 1 && storageIds[0]) { - setSelectedDoc(storageIds[0]); - } - }} - isAdmin - userId={userId} - /> -
- - - {selectedDoc !== initialDoc ? ( -
- - Salva modifiche -
- ) : ( - initialDoc && - selectedDoc === initialDoc && ( -
- - Dati salvati -
- ) - )} -
-
- ); -}; - -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 = () => { - const { data, updateServizioAnnunci } = useServizioAnnuncio(); - const initialCaparra = { - caparra_causale: data.caparra_causale, - caparra_iban: data.caparra_iban, - caparra_importo: data.caparra_importo, - caparra_intestazione: data.caparra_intestazione, - }; - - const [caparra, setCaparra] = useState( - initialCaparra, - ); - const handleSave = async () => { - await updateServizioAnnunci({ - caparra_causale: caparra?.caparra_causale || null, - caparra_iban: caparra?.caparra_iban || null, - caparra_importo: caparra?.caparra_importo || null, - caparra_intestazione: caparra?.caparra_intestazione || null, - }); - }; - 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

- )} -
- - - {JSON.stringify(caparra) !== JSON.stringify(initialCaparra) ? ( -
- - Salva modifiche -
- ) : ( - Object.values(initialCaparra).every((v) => v !== null) && ( -
- - Dati salvati -
- ) - )} -
-
- ); -}; - -const MotivazioneSection = () => { - const { servizio } = useServizio(); - const { t } = useTranslation(); - - if (!servizio) return null; - return ( - - - Motivazione di transitorietà - - Informazioni relative alla motivazione di transitorietà inserite - dall'utente - - - -
-

- Motivazione specificata: - {servizio.motivazione_transitorio ? ( - - { - t.parametri.motivazioni_mappings.find( - (m) => m.id === servizio.motivazione_transitorio, - )?.title - } - - ) : ( - Non inserita - )} -

-

- - Data scadenza motivazione inserita:{" "} - - {servizio.scadenza_motivazione_transitoria ? ( - format(servizio.scadenza_motivazione_transitoria, "dd/MM/yyyy") - ) : ( - Non inserita - )} -

- {!servizio.skipDocMotivazione && ( -

- Documento caricato: - {servizio.doc_motivazione_ref ? ( - - ) : ( - Non caricato - )} -

- )} -
-
-
- ); -}; - -const FileLink = ({ - userStorageId, -}: { - userStorageId: UsersStorageUserStorageId; -}) => { - const { data: storageId } = - api.storage.getStorageIdFromUserStorageId.useQuery({ - userStorageId, - }); - if (!storageId) return null; - return ( - - Vedi documento - - ); -}; - -const TransitorioSection = () => { - const { servizio } = useServizio(); - const { data } = useServizioAnnuncio(); - const { t } = useTranslation(); - const { servizioId } = 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.invalidate(); - }, - }); - - const handlePermanenzaChange = (value: ListOption) => { - update({ - servizioId, - data: { - permanenza: value.value !== "" ? parseInt(value.value) : null, - }, - }); - }; - const preferenzeDescrizione = data.desc_it?.match(/Permanenza:.*$/m); - - const permanenzaAnnuncio = - data.permanenza !== null - ? `${data.permanenza.join(" - ")} mesi` - : (preferenzeDescrizione?.[0] ?? - "errore: testo non trovato in descrizione"); - - return ( -
-
- - - -
-
-

Permanenza in annuncio:

-

{permanenzaAnnuncio}

-
-
- ); -}; - -const StabileSection = () => { - const { servizio, servizioId } = useServizio(); - const { data } = useServizioAnnuncio(); - const [value, setValue] = useState(servizio.budget); - 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.invalidate(); - }, - }); - - useDebounce( - () => { - if (value === servizio.budget) return; - update({ - servizioId, - data: { - budget: value, - }, - }); - }, - 1000, - [value], - ); - - return ( -
-
- Budget (da preferenze servizio): - { - if (val !== undefined) { - setValue(val); - } - }} - stepper={50} - suffix=" €" - /> -
-
-

Prezzo in descrizione:

-

{formatCurrency(data.prezzo / 100)}

-
-
- ); -}; - -const GeneralSection = () => { - const { data } = useServizioAnnuncio(); - const { t } = useTranslation(); - - const { servizio, servizioId } = 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.invalidate(); - }, - }); - - 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.parametri.mesi, - subito: it.card.consegna_subito, - }); - - return ( - <> -
-
- Persone: -
-
- - handleAdultiChange(Number(v))} - value={servizio.n_adulti || 0} - /> -
- -
- - handleMinoriChange(Number(v))} - value={servizio.n_minori || 0} - /> -
-
-
-
-

Persone in annuncio:

-

{personeAnnuncio}

-
-
-
-
- - - label: t.parametri.mesi[servizio.entromese]!, - value: String(servizio.entromese), - } - : undefined - } - elementId="select-mesi" - elementName="entro_mese" - isMulti={false} - onValueChange={handleDisponibilitàChange} - options={UnpackOptions({ - options: t.parametri.mesi, - })} - placeholder="Seleziona..." - /> -
-
-

Disponibilità in annuncio:

-

{dispAnnuncio}

-
-
- - ); -}; - -const SendConferma = () => { - const { servizio } = useServizio(); - const { data } = useServizioAnnuncio(); - const utils = api.useUtils(); - const { mutate: handleUpdate } = - api.servizio.adminUpdateConfermaStatus.useMutation({ - onSuccess: async () => { - await utils.servizio.invalidate(); - - toast.success("Status aggiornato con successo"); - }, - onError: (error) => { - toast.error(`Errore durante l'aggiornamento: ${error.message}`); - }, - }); - - const docOk = data.doc_conferma_added && data.doc_conferma_ref; - const caparraOk = - data.caparra_iban && - data.caparra_importo && - data.caparra_intestazione && - data.caparra_causale; - - return ( - - - Lavorazione Conferma - - controlla i parametri di ricerca prima! - - - -
-

- Tipologia servizio: - {servizio.tipologia} -

- {servizio.tipologia === TipologiaPosizioneEnum.Transitorio ? ( - - ) : ( - - )} - - -
-
- {!docOk && ( -

- Attenzione: il documento di conferma non è stato ancora caricato -

- )} - {!caparraOk && ( -

- Attenzione: i dati per il pagamento della caparra non sono - completi -

- )} -

- ATTENZIONE: Il saldo verrà aggiunto (se necessario), in base ai - parametri, all'invio della conferma -

- -
- -
-
-
-
- ); -}; diff --git a/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx b/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx index 4b3c658..fb4710c 100644 --- a/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx +++ b/apps/infoalloggi/src/components/servizio/annuncio_actions.tsx @@ -1,12 +1,14 @@ import { + Circle, + CircleCheck, FileBadge, FileText, FolderKanban, Info, PackageCheck, - Pointer, Printer, Trash2, + Workflow, Wrench, } from "lucide-react"; import Link from "next/link"; @@ -22,6 +24,14 @@ import { AlertDialogTrigger, } from "~/components/ui/alert-dialog"; import { Button, type ButtonConfigs } from "~/components/ui/button"; +import { + Popover, + PopoverContent, + PopoverDescription, + PopoverHeader, + PopoverTitle, + PopoverTrigger, +} from "~/components/ui/popover"; import { Tooltip, TooltipContent, @@ -33,11 +43,10 @@ import { useTranslation } from "~/providers/I18nProvider"; import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider"; import type { AnnunciId } from "~/schemas/public/Annunci"; import OrderTypeEnum from "~/schemas/public/OrderTypeEnum"; +import StatusConfermaEnum from "~/schemas/public/StatusConfermaEnum"; import { api } from "~/utils/api"; import { Confirm } from "../confirm"; -import { AdminLavoraConferma } from "./admin_conferma"; import { AdminContratto } from "./admin_contratto"; -import { ConfermaAnnuncioModal, ConfermaInLavorazioneModal } from "./conferma"; import { ContattiProprietarioModal } from "./modale_contatti"; export const AdminActions = () => { @@ -62,8 +71,9 @@ export const AdminActions = () => {

Azioni Admin:

- {data.user_confirmed_at != null && } - {data.accettato_conferma_at != null && ( + + + {data.status_conferma != null && ( <> {data.gestionale_id && ( @@ -134,86 +144,79 @@ export const AdminActions = () => { }; export const UserActions = () => { - const { servizioId } = useServizio(); - const { data, annuncioId } = useServizioAnnuncio(); - const { locale } = useTranslation(); - const ContattiSbloccati = data.open_contatti_at !== null; - const ConfermaUtente = data.user_confirmed_at !== null; - const ConfermaLavorata = data.doc_conferma_added && data.hasConfermaAdmin; - const AccettatoConferma = data.accettato_conferma_at !== null; + const { data } = useServizioAnnuncio(); return ( <>
- {ConfermaUtente - ? !data.hasConfermaAdmin && - : ContattiSbloccati && } - - {AccettatoConferma ? ( - - - - ) : ( - ConfermaLavorata && - (data.doc_conferma_ref ? ( - - - - ) : ( - - - - - - -

- {locale === "it" - ? "Il documento non è più disponibile" - : "The document is no longer available"} -

-

- {locale === "it" - ? "Se hai bisogno di una copia, contattaci via email o chat." - : "If you need a copy, contact us via email or chat."} -

-
-
-
- )) - )}
- - {data.accettato_conferma_at != null && } + {data.status_conferma != null && } ); }; + +const AdminAnnuncioConfermaPopover = () => { + const { servizioId } = useServizio(); + const { data } = useServizioAnnuncio(); + const utils = api.useUtils(); + const { mutate } = api.servizio.updateServizioAnnunci.useMutation({ + onSuccess: async () => { + await utils.servizio.invalidate(); + }, + onError: (error) => { + console.error(error); + toast.error("Errore durante l'aggiornamento dell'annuncio"); + }, + }); + const handleStatusChange = (status: StatusConfermaEnum | null) => { + mutate({ + annuncioId: data.id, + servizioId, + data: { status_conferma: status }, + }); + }; + return ( + + + + + + + Status Conferma: + + Description + + + +
+ + {Object.values(StatusConfermaEnum).map((status) => ( + + ))} +
+
+
+ ); +}; + const AdminAnnuncioContatti = () => { const { data } = useServizioAnnuncio(); const { data: annuncio, isLoading } = api.annunci.getAnnuncioFull.useQuery({ diff --git a/apps/infoalloggi/src/components/servizio/conferma.tsx b/apps/infoalloggi/src/components/servizio/conferma.tsx deleted file mode 100644 index f31b06d..0000000 --- a/apps/infoalloggi/src/components/servizio/conferma.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import { CircleCheck, Handshake } from "lucide-react"; -import Link from "next/link"; -import { useState } from "react"; -import toast from "react-hot-toast"; -import { AllegatoIframe } from "~/components/allegato-iframe"; -import { LoadingPage } from "~/components/loading"; -import { getStorageUrl } from "~/lib/storage_utils"; -import { useTranslation } from "~/providers/I18nProvider"; -import { useServizio, useServizioAnnuncio } from "~/providers/ServizioProvider"; -import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; -import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage"; -import { api } from "~/utils/api"; -import { DocumentiUploadSection } from "../area-riservata/documenti_personali"; -import { - Credenza, - CredenzaBody, - CredenzaClose, - CredenzaContent, - CredenzaDescription, - CredenzaFooter, - CredenzaHeader, - CredenzaTitle, - CredenzaTrigger, -} from "../custom_ui/credenza"; -import LoadingButton from "../custom_ui/loading-button"; -import { Button } from "../ui/button"; - -export const FileSection = ({ storageId }: { storageId: string }) => { - const { data: fileInfos, isLoading } = api.storage.getFileInfos.useQuery({ - storageId, - }); - const { t } = useTranslation(); - if (isLoading) return ; - if (!fileInfos) return

{t.file_section.error}

; - return ( - <> - - - - {t.file_section.download} - - - ); -}; - -export const ConfermaAnnuncioModal = () => { - const { servizio } = useServizio(); - - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - - const needsDocuments = - !servizio.skipDocMotivazione && - !servizio.doc_motivazione && - servizio.tipologia === TipologiaPosizioneEnum.Transitorio; - return ( - <> - - - - - - - {t.richieste.conf_title} - - {t.richieste.conf_desc} - - - - {needsDocuments ? ( - - ) : ( - setOpen(false)} /> - )} - - - - ); -}; - -const InvioConferma = ({ closeModal }: { closeModal: () => void }) => { - const { servizioId } = useServizio(); - const { annuncioId } = useServizioAnnuncio(); - const { t } = useTranslation(); - const utils = api.useUtils(); - const { mutate, isPending } = api.servizio.userSendConfermaIntent.useMutation( - { - onError: (err) => { - toast.error(err.message, { id: "updateRichiestaToast" }); - }, - onMutate: () => { - toast.loading("Conferma in corso", { id: "updateRichiestaToast" }); - }, - onSuccess: async () => { - toast.success("Richiesta confermata con successo", { - id: "updateRichiestaToast", - }); - - await utils.servizio.invalidate(); - closeModal(); - }, - }, - ); - - return ( - <> - -

- Hai fatto la visita? Se no contatta il proprietario! -

-

{t.richieste.conferma_txt}

- -

{t.richieste.conf_alert}

-
- - { - mutate({ - annuncioId, - servizioId, - }); - }} - variant="info" - > - {t.procedi} - - - - - - - - ); -}; - -const DocCheckpoint = () => { - const { t, locale } = useTranslation(); - const { userId, servizio, isAdmin } = useServizio(); - const utils = api.useUtils(); - const { mutate, isPending } = api.servizio.updateServizio.useMutation({ - onSuccess: async () => { - await utils.servizio.invalidate(); - }, - onError: (err) => { - toast.error(`Errore aggiornamento servizio: ${err.message}`); - }, - }); - const handleUpload = (id: UsersStorageUserStorageId) => { - mutate({ - servizioId: servizio.servizio_id, - data: { doc_motivazione_ref: id }, - }); - }; - return ( - <> - -
-

- {locale === "it" - ? "Devi ancora caricare un tuo documento legato alla permanenza e motivazione di transitorietà" - : "You still need to upload a document related to your stay and reason for transience"} -

- - - -
-
-

- {locale === "it" - ? "I tuoi dati saranno conservati solamente per la durata del servizio, in conformità con la nostra " - : "Your data will be stored only for the duration of the service, in accordance with our "} - - {locale === "it" ? "Informativa sulla Privacy" : "Privacy Policy"} - -

-
-
- - - - - - - ); -}; - -export const ConfermaInLavorazioneModal = () => { - const { t, locale } = useTranslation(); - const { data } = useServizioAnnuncio(); - return ( - - - - - - - - {locale === "it" - ? "In attesa di conferma" - : "Awaiting confirmation"} - - - Consegna status modal - - - -

- {locale === "it" - ? "Richiesta di conferma inviata il:" - : "Confirmation request sent on:"}{" "} - {data.user_confirmed_at && - new Date(data.user_confirmed_at).toLocaleString("it", { - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - month: "2-digit", - year: "numeric", - })} -

-

- {locale === "it" - ? "La conferma è ancora in lavorazione. Stiamo verificando con i proprietari le condizioni e dettagli della conferma per riferirteli. Attendi una comunicazione." - : "The confirmation is still in progress. We are checking with the owners the conditions and details of the confirmation to report them to you. Please wait for a communication."} -

-
- - - - - - -
-
- ); -}; diff --git a/apps/infoalloggi/src/components/servizio/interruzione.tsx b/apps/infoalloggi/src/components/servizio/interruzione.tsx index 5a327e3..1b0dceb 100644 --- a/apps/infoalloggi/src/components/servizio/interruzione.tsx +++ b/apps/infoalloggi/src/components/servizio/interruzione.tsx @@ -195,7 +195,7 @@ export const InterruzioneServizio = () => { !servizio.isInterrotto && (isAdmin || (isBefore(new Date(), fineInterruzione) && - servizio.annunci.every((a) => a.accettato_conferma_at === null))); + servizio.annunci.every((a) => a.status_conferma === null))); return ( diff --git a/apps/infoalloggi/src/components/servizio/parametri_ricerca.tsx b/apps/infoalloggi/src/components/servizio/parametri_ricerca.tsx index 7c08218..3d34a8f 100644 --- a/apps/infoalloggi/src/components/servizio/parametri_ricerca.tsx +++ b/apps/infoalloggi/src/components/servizio/parametri_ricerca.tsx @@ -71,7 +71,7 @@ const Schema = z pianoterra: z.boolean(), reddito: z.string().nullable(), terrazzo: z.boolean(), - tipologia: z.custom(), + tipologia: z.enum(TipologiaPosizioneEnum), scadenza_motivazione_transitoria: z.date().nullable(), skipPayment: z.boolean(), skipControlloDoc: z.boolean(), @@ -832,7 +832,7 @@ export const EditServizioModal = () => { disabled={ !isAdmin && (servizio.isInterrotto || - servizio.annunci.some((a) => a.hasConfermaAdmin)) + servizio.annunci.some((a) => a.status_conferma !== null)) } variant="secondary" > diff --git a/apps/infoalloggi/src/components/servizio/servizio.tsx b/apps/infoalloggi/src/components/servizio/servizio.tsx index 376644d..74dc666 100644 --- a/apps/infoalloggi/src/components/servizio/servizio.tsx +++ b/apps/infoalloggi/src/components/servizio/servizio.tsx @@ -567,15 +567,13 @@ const Main = () => { ); } - const annuncioConfermato = servizio.annunci.filter( - (a) => a.accettato_conferma_at !== null, - ); + const InConferma = servizio.annunci.filter((a) => a.status_conferma !== null); // in conferma, mostra solo annunci confermati - if (servizio.isOkSaldo && annuncioConfermato.length > 0) { + if (servizio.isOkSaldo && InConferma.length > 0) { return ( <> - {annuncioConfermato.map((data) => { + {InConferma.map((data) => { return ( {

Altri annunci:

{servizio.annunci - .filter((a) => !a.accettato_conferma_at) + .filter((a) => a.status_conferma === null) .map((data) => { return ( { // normal case return ( <> - {annuncioConfermato.length > 0 && ( + {InConferma.length > 0 && (
{t.servizio.saldi_in_attesa}
- {annuncioConfermato.map((a) => ( + {InConferma.map((a) => (
-

- {a.codice} -{" "} - {a.accettato_conferma_at && - new Date(a.accettato_conferma_at).toLocaleDateString("it", { - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - month: "2-digit", - year: "numeric", - })} -

+

{a.codice}

void }) => { ); }; -export const ServizioPacksInfos = () => { - const { servizio } = useServizio(); - const { t } = useTranslation(); - const { data: packs } = api.servizio.getPacksSolution.useQuery({ - servizioId: servizio.servizio_id, - }); - if (!packs) return null; - - return ( - - - - - - - {t.servizio.prezzi.sub} - -
-
- {packs.acconto.success ? ( - <> - - {packs.acconto.pack.nome_it} - - - {packs.acconto.pack.desc_it} - - - {formatCurrency(packs.acconto.pack.prezzo_cent / 100)} - - - COD: {packs.acconto.pack.idprezziario} - - - ) : ( - {packs.acconto.message} - )} -
- -
- {packs.saldo.success ? ( - <> - {packs.saldo.pack.nome_it} - - {packs.saldo.pack.desc_it} - - - {formatCurrency(packs.saldo.pack.prezzo_cent / 100)} - - - COD: {packs.saldo.pack.idprezziario} - - - ) : ( - {packs.saldo.message} - )} -
- {servizio.tipologia === TipologiaPosizioneEnum.Stabile && ( -
- <> - - {t.servizio.prezzi.consulenza} - - - {t.servizio.prezzi.consulenza_desc} - - - - {t.servizio.prezzi.gotoprezzi} - - - - -
- )} -
-
-
- ); -}; - const AdminActivationUfficio = () => { const { servizio } = useServizio(); const utils = api.useUtils(); diff --git a/apps/infoalloggi/src/components/tables/richieste-table.tsx b/apps/infoalloggi/src/components/tables/richieste-table.tsx index f5a3b17..93acf24 100644 --- a/apps/infoalloggi/src/components/tables/richieste-table.tsx +++ b/apps/infoalloggi/src/components/tables/richieste-table.tsx @@ -56,6 +56,7 @@ import { UserAvatar } from "~/components/user_avatar"; import { MutationPageRestore } from "~/lib/tableUtils"; import { cn } from "~/lib/utils"; import OrderTypeEnum from "~/schemas/public/OrderTypeEnum"; +import StatusConfermaEnum from "~/schemas/public/StatusConfermaEnum"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import type { RichiesteData } from "~/server/controllers/servizio.controller"; import { api } from "~/utils/api"; @@ -383,10 +384,10 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => { } } - const annuncioConfermato = row.original.annunci_servizio.filter( - (a) => a.accettato_conferma_at !== null, + const annuncioCaparraVersata = row.original.annunci_servizio.filter( + (a) => a.status_conferma === StatusConfermaEnum["Caparra versata"], ); - if (annuncioConfermato.length > 0) { + if (annuncioCaparraVersata.length > 0) { if (row.original.isOkSaldo) { badgeClass = "border-blue-500 bg-blue-500/10 text-blue-500"; contents = ( @@ -407,9 +408,24 @@ export const RichiesteTable = ({ data }: RichiesteTableProps) => { return; } + const annuncioConfermato = row.original.annunci_servizio.filter( + (a) => + a.status_conferma === StatusConfermaEnum["Conferma accettata"], + ); + if (annuncioConfermato.length > 0) { + badgeClass = "border-yellow-500 bg-yellow-500/10 text-yellow-600"; + contents = ( + <> + + Attesa di caparra + + ); + + return; + } const annuncioAttesaConferma = row.original.annunci_servizio.filter( - (a) => a.accettato_conferma_at == null && a.doc_conferma_added, + (a) => a.status_conferma === StatusConfermaEnum["Inviato conferma"], ); if (annuncioAttesaConferma.length > 0) { badgeClass = "border-blue-500 bg-blue-500/10 text-blue-500"; diff --git a/apps/infoalloggi/src/pages/servizio/conferma-immobile/[...slug].tsx b/apps/infoalloggi/src/pages/servizio/conferma-immobile/[...slug].tsx deleted file mode 100644 index 5d31bf4..0000000 --- a/apps/infoalloggi/src/pages/servizio/conferma-immobile/[...slug].tsx +++ /dev/null @@ -1,219 +0,0 @@ -import type { GetServerSideProps } from "next"; -import { useRouter } from "next/router"; -import { useState } from "react"; -import toast from "react-hot-toast"; -import { DocNotFoundPage } from "~/components/doc_not_found"; -import { AreaRiservataLayout } from "~/components/Layout"; -import { LoadingPage } from "~/components/loading"; -import { FileSection } from "~/components/servizio/conferma"; -import { Status500 } from "~/components/status-page"; -import { Button } from "~/components/ui/button"; -import { Checkbox } from "~/components/ui/checkbox"; -import { redirectTo500 } from "~/lib/utils"; -import type { NextPageWithLayout } from "~/pages/_app"; -import { useTranslation } from "~/providers/I18nProvider"; -import { useEnforcedSession } from "~/providers/SessionProvider"; -import type { AnnunciId } from "~/schemas/public/Annunci"; -import type { ServizioServizioId } from "~/schemas/public/Servizio"; -import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper"; -import { zAnnuncioId, zServizioId } from "~/server/utils/zod_types"; -import { api } from "~/utils/api"; - -type ConfermaImmobileProps = { - servizioId: ServizioServizioId; - annuncioId: AnnunciId; -}; -/** - * Pagina di conferma immobile: /servizio/conferma-immobile/[servizioId]/[annuncioId] - */ -const ConfermaImmobile: NextPageWithLayout = ({ - servizioId, - annuncioId, -}: ConfermaImmobileProps) => { - const { data, isLoading } = api.servizio.getServizioAnnuncio.useQuery({ - annuncioId, - servizioId, - }); - const { t } = useTranslation(); - if (isLoading) return ; - if (!data) return ; - if (!data.doc_conferma_ref) return ; - return ( -
-
-
-

{t.servizio.conferma.titolo}

-

{t.servizio.conferma.descrizione}

- - - -
-

{t.servizio.conferma.cta}

-

{t.servizio.conferma.coordinate}

-

- {t.servizio.conferma.intestazione}{" "} - {data.caparra_intestazione || ""} -

-

- {t.servizio.conferma.iban} {data.caparra_iban || ""} -

-

- {t.servizio.conferma.importo} {data.caparra_importo || ""} -

-

- {t.servizio.conferma.causale} {data.caparra_causale || ""} -

-

- {t.servizio.conferma.coord_footer} -

-
- - -
-
-
- ); -}; - -const ConfirmSection = ({ - servizioId, - annuncioId, -}: { - servizioId: ServizioServizioId; - annuncioId: AnnunciId; -}) => { - const { t } = useTranslation(); - const router = useRouter(); - const [letto, setLetto] = useState(false); - const [accettato, setAccettato] = useState(false); - const { user } = useEnforcedSession(); - const utils = api.useUtils(); - const { mutate: conferma } = - api.servizio.userAcceptConfermaImmobile.useMutation({ - onMutate: () => { - const toastId = toast.loading(t.caricamento, { - icon: "🔄", - }); - return { toastId }; - }, - onSuccess: async (data, _variables, context) => { - await utils.storage.retrieveUserFileData.invalidate(); - await utils.servizio.invalidate(); - - toast.success("Confermato", { - icon: "👍", - id: context?.toastId, - }); - if (user.isAdmin) { - await router.push( - `/area-riservata/admin/user-view/servizio/${data.userId}/${servizioId}`, - ); - } else { - await router.push(`/area-riservata/servizio/${servizioId}`); - } - }, - }); - return ( - <> -
-
- v !== "indeterminate" && setLetto(v)} - /> -
- -
-
-
- v !== "indeterminate" && setAccettato(v)} - /> -
- -

- {t.servizio.conferma.confermo_termini} -

-
-
-
- - - ); -}; - -ConfermaImmobile.getLayout = function getLayout(page) { - return {page}; -}; - -export default ConfermaImmobile; - -export const getServerSideProps = (async (context) => { - const slug = context.params?.slug; - if (!slug || slug.length !== 2) { - return { - notFound: true, - }; - } - const [servizioId, annuncioId] = slug; - if (!servizioId || !annuncioId) { - return { - notFound: true, - }; - } - - const parsedServizioId = zServizioId.safeParse(servizioId); - const parsedAnnuncioId = zAnnuncioId.safeParse(parseInt(annuncioId)); - - if (!parsedServizioId.success || !parsedAnnuncioId.success) { - return { - notFound: true, - }; - } - - const access_token = context.req.cookies.access_token; - - const helper = await TrpcAuthedFetchingIstance({ access_token }); - if (helper) { - await helper.trpc.servizio.getServizioAnnuncio.prefetch({ - annuncioId: parsedAnnuncioId.data, - servizioId: parsedServizioId.data, - }); - - return { - props: { - annuncioId: parsedAnnuncioId.data, - servizioId: parsedServizioId.data, - trpcState: helper.trpc.dehydrate(), - }, - }; - } - return redirectTo500; -}) satisfies GetServerSideProps; diff --git a/apps/infoalloggi/src/pages/servizio/riapri-conferma/[...slug].tsx b/apps/infoalloggi/src/pages/servizio/riapri-conferma/[...slug].tsx deleted file mode 100644 index a6ba60e..0000000 --- a/apps/infoalloggi/src/pages/servizio/riapri-conferma/[...slug].tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { ArrowLeft } from "lucide-react"; -import type { GetServerSideProps } from "next"; -import { useRouter } from "next/router"; -import { DocNotFoundPage } from "~/components/doc_not_found"; -import { AreaRiservataLayout } from "~/components/Layout"; -import { LoadingPage } from "~/components/loading"; -import { FileSection } from "~/components/servizio/conferma"; -import { Status500 } from "~/components/status-page"; -import { Button } from "~/components/ui/button"; -import { redirectTo500 } from "~/lib/utils"; -import type { NextPageWithLayout } from "~/pages/_app"; -import { useTranslation } from "~/providers/I18nProvider"; -import type { AnnunciId } from "~/schemas/public/Annunci"; -import type { ServizioServizioId } from "~/schemas/public/Servizio"; -import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper"; -import { zAnnuncioId, zServizioId } from "~/server/utils/zod_types"; -import { api } from "~/utils/api"; - -type RiapriConfermaProps = { - servizioId: ServizioServizioId; - annuncioId: AnnunciId; -}; -/** - * Pagina di conferma di riapertura servizio: /servizio/riapri-conferma/[servizioId]/[annuncioId] - */ -const RiapriConferma: NextPageWithLayout = ({ - servizioId, - annuncioId, -}: RiapriConfermaProps) => { - const { data, isLoading } = api.servizio.getServizioAnnuncio.useQuery({ - annuncioId, - servizioId, - }); - const router = useRouter(); - const { t } = useTranslation(); - - if (isLoading) return ; - if (!data) return ; - if (!data.doc_conferma_ref) return ; - return ( -
-
-
-

{t.servizio.conferma.titolo}

-

{t.servizio.conferma.descrizione}

- - - -
-

{t.servizio.conferma.cta}

-

{t.servizio.conferma.coordinate}

-

- {t.servizio.conferma.intestazione}{" "} - {data.caparra_intestazione || ""} -

-

- {t.servizio.conferma.iban} {data.caparra_iban || ""} -

-

- {t.servizio.conferma.importo} {data.caparra_importo || ""} -

-

- {t.servizio.conferma.causale} {data.caparra_causale || ""} -

-

- {t.servizio.conferma.coord_footer} -

-
- - -
-
-
- ); -}; - -RiapriConferma.getLayout = function getLayout(page) { - return {page}; -}; - -export default RiapriConferma; - -export const getServerSideProps = (async (context) => { - const slug = context.params?.slug; - if (!slug || slug.length !== 2) { - return { - notFound: true, - }; - } - const [servizioId, annuncioId] = slug; - if (!servizioId || !annuncioId) { - return { - notFound: true, - }; - } - - const parsedServizioId = zServizioId.safeParse(servizioId); - const parsedAnnuncioId = zAnnuncioId.safeParse(parseInt(annuncioId)); - - if (!parsedServizioId.success || !parsedAnnuncioId.success) { - return { - notFound: true, - }; - } - - const access_token = context.req.cookies.access_token; - - const helper = await TrpcAuthedFetchingIstance({ access_token }); - if (helper) { - await helper.trpc.servizio.getServizioAnnuncio.prefetch({ - annuncioId: parsedAnnuncioId.data, - servizioId: parsedServizioId.data, - }); - - return { - props: { - annuncioId: parsedAnnuncioId.data, - servizioId: parsedServizioId.data, - trpcState: helper.trpc.dehydrate(), - }, - }; - } - return redirectTo500; -}) satisfies GetServerSideProps; diff --git a/apps/infoalloggi/src/providers/SessionProvider.tsx b/apps/infoalloggi/src/providers/SessionProvider.tsx index 5dc6114..442d565 100644 --- a/apps/infoalloggi/src/providers/SessionProvider.tsx +++ b/apps/infoalloggi/src/providers/SessionProvider.tsx @@ -10,18 +10,18 @@ import { LoadingPage } from "~/components/loading"; import type { Session, SessionStatus } from "~/server/api/trpc"; import { api } from "~/utils/api"; -export type ValidSession = { +type ValidSession = { user: Session; status: Extract; }; type InvalidStatus = Exclude | "LOADING"; -export type InvalidSession = { +type InvalidSession = { status: InvalidStatus; user: null; }; -export type SessionContextType = ValidSession | InvalidSession; +type SessionContextType = ValidSession | InvalidSession; const SessionContext = createContext({ status: "LOADING", diff --git a/apps/infoalloggi/src/schemas/public/ServizioAnnunci.ts b/apps/infoalloggi/src/schemas/public/ServizioAnnunci.ts index f810a8d..1cfa799 100644 --- a/apps/infoalloggi/src/schemas/public/ServizioAnnunci.ts +++ b/apps/infoalloggi/src/schemas/public/ServizioAnnunci.ts @@ -3,6 +3,7 @@ import type { ServizioServizioId } from './Servizio'; import type { AnnunciId } from './Annunci'; +import type { default as StatusConfermaEnum } from './StatusConfermaEnum'; import type { ColumnType, Selectable, Insertable, Updateable } from 'kysely'; /** Identifier type for public.servizio_annunci */ @@ -20,22 +21,6 @@ export default interface ServizioAnnunciTable { open_contatti_at: ColumnType; - doc_conferma_ref: ColumnType; - - user_confirmed_at: ColumnType; - - accettato_conferma_at: ColumnType; - - caparra_causale: ColumnType; - - caparra_iban: ColumnType; - - caparra_importo: ColumnType; - - caparra_intestazione: ColumnType; - - hasConfermaAdmin: ColumnType; - doc_contratto_ref: ColumnType; gestionale_id: ColumnType; @@ -48,13 +33,13 @@ export default interface ServizioAnnunciTable { doc_registrazione_ref: ColumnType; - doc_conferma_added: ColumnType; - doc_contratto_added: ColumnType; doc_registrazione_added: ColumnType; note: ColumnType; + + status_conferma: ColumnType; } export type ServizioAnnunci = Selectable; diff --git a/apps/infoalloggi/src/schemas/public/StatusConfermaEnum.ts b/apps/infoalloggi/src/schemas/public/StatusConfermaEnum.ts new file mode 100644 index 0000000..3364a0a --- /dev/null +++ b/apps/infoalloggi/src/schemas/public/StatusConfermaEnum.ts @@ -0,0 +1,11 @@ +// @generated +// This file is automatically generated by Kanel. Do not modify manually. + +/** Represents the enum public.StatusConfermaEnum */ +enum StatusConfermaEnum { + 'Inviato conferma' = 'Inviato conferma', + 'Conferma accettata' = 'Conferma accettata', + 'Caparra versata' = 'Caparra versata', +}; + +export default StatusConfermaEnum; diff --git a/apps/infoalloggi/src/server/api/routers/servizio.ts b/apps/infoalloggi/src/server/api/routers/servizio.ts index d8b2ae3..6cdd97b 100644 --- a/apps/infoalloggi/src/server/api/routers/servizio.ts +++ b/apps/infoalloggi/src/server/api/routers/servizio.ts @@ -18,22 +18,18 @@ import { getPacksPerServizio } from "~/server/controllers/pagamenti.controller"; import { AdminAttivaServizio_Ufficio, addServizioAnnunci, - adminUpdateConfermaStatus, attivaServizio_SaltaPagamentoAcconto, getAllServizioAnnunci, getAnnunciAvailableToAdd, getCompatibileAnnunci, getDataPerAcquisto, getRichieste, - getServiziByUserId, getServizioDataById, InteractionLogicTipologia, interruzioneServizio, processServizioOnboard, SbloccaContatti, type ServizioData, - userAccettaConferma, - userSendConfermaIntent, } from "~/server/controllers/servizio.controller"; import { createOrdine, @@ -376,47 +372,7 @@ export const servizioRouter = createTRPCRouter({ servizioId: input.servizioId, }); }), - adminUpdateConfermaStatus: adminProcedure - .input( - z.object({ - annuncioId: zAnnuncioId, - servizioId: zServizioId, - status: z.boolean(), - }), - ) - .mutation(async ({ input }) => { - return await adminUpdateConfermaStatus({ - annuncioId: input.annuncioId, - servizioId: input.servizioId, - status: input.status, - }); - }), - userSendConfermaIntent: protectedProcedure - .input( - z.object({ - annuncioId: zAnnuncioId, - servizioId: zServizioId, - }), - ) - .mutation(async ({ input }) => { - return await userSendConfermaIntent({ - annuncioId: input.annuncioId, - servizioId: input.servizioId, - }); - }), - userAcceptConfermaImmobile: protectedProcedure - .input( - z.object({ - annuncioId: zAnnuncioId, - servizioId: zServizioId, - }), - ) - .mutation(async ({ input }) => { - return await userAccettaConferma({ - annuncioId: input.annuncioId, - servizioId: input.servizioId, - }); - }), + getAllServiziData: protectedProcedure .input( z.object({ diff --git a/apps/infoalloggi/src/server/api/routers/storage.ts b/apps/infoalloggi/src/server/api/routers/storage.ts index c5eb687..39292eb 100644 --- a/apps/infoalloggi/src/server/api/routers/storage.ts +++ b/apps/infoalloggi/src/server/api/routers/storage.ts @@ -1,9 +1,6 @@ import { TRPCError } from "@trpc/server"; import z from "zod"; -import type { - NewUsersStorage, - UsersStorageUserStorageId, -} from "~/schemas/public/UsersStorage"; +import type { NewUsersStorage } from "~/schemas/public/UsersStorage"; import { adminProcedure, createTRPCRouter, @@ -14,7 +11,6 @@ import { deleteUserStorageEntry, getAllWithFromAdmin, getMultipleWithRelations, - getStorageIdFromUserStorageId, purgeFileFromUserStorages, retrieveUserFiles, } from "~/server/controllers/storage.controller"; @@ -87,11 +83,4 @@ export const storageRouter = createTRPCRouter({ .mutation(async ({ input }) => { return await editFile(input.fileId, input.name); }), - getStorageIdFromUserStorageId: protectedProcedure - .input(z.object({ userStorageId: z.custom() })) - .query(async ({ input }) => { - return await getStorageIdFromUserStorageId({ - userStorageId: input.userStorageId, - }); - }), }); diff --git a/apps/infoalloggi/src/server/controllers/servizio.controller.ts b/apps/infoalloggi/src/server/controllers/servizio.controller.ts index 0bd7efc..d81869c 100644 --- a/apps/infoalloggi/src/server/controllers/servizio.controller.ts +++ b/apps/infoalloggi/src/server/controllers/servizio.controller.ts @@ -455,7 +455,7 @@ export const getAllServizioAnnunci = async ( //ignora annunci che non sono disponibili //.where("annunci.web", "=", true) //.where("annunci.stato", "!=", "Sospeso") - .orderBy("servizio_annunci.user_confirmed_at", (ob) => + .orderBy("servizio_annunci.status_conferma", (ob) => ob.asc().nullsLast(), ) .orderBy("servizio_annunci.open_contatti_at", (ob) => @@ -593,7 +593,7 @@ export const getServizioDataById = async ( //ignora annunci che non sono disponibili //.where("annunci.web", "=", true) //.where("annunci.stato", "!=", "Sospeso") - .orderBy("servizio_annunci.user_confirmed_at", (ob) => + .orderBy("servizio_annunci.status_conferma", (ob) => ob.asc().nullsLast(), ) .orderBy("servizio_annunci.open_contatti_at", (ob) => @@ -631,10 +631,7 @@ export const getServizioDataById = async ( ...annuncio, created_at: new Date(annuncio.created_at), open_contatti_at: parseNullableDateString(annuncio.open_contatti_at), - user_confirmed_at: parseNullableDateString(annuncio.user_confirmed_at), - accettato_conferma_at: parseNullableDateString( - annuncio.accettato_conferma_at, - ), + contratto_decorrenza: parseNullableDateString( annuncio.contratto_decorrenza, ), @@ -1006,222 +1003,6 @@ export const InteractionLogicTipologia = async ({ }; }; -export const userSendConfermaIntent = async ({ - servizioId, - annuncioId, -}: { - servizioId: ServizioServizioId; - annuncioId: AnnunciId; -}) => { - try { - const servizio = await getServizioById(servizioId); - if ( - !servizio || - servizio.isInterrotto || - !servizio.isOkAcconto || - servizio.decorrenza == null - ) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Servizio not found", - }); - } - await db - .updateTable("servizio_annunci") - .set({ - user_confirmed_at: new Date(), - }) - .where("servizio_id", "=", servizioId) - .where("annunci_id", "=", annuncioId) - .execute(); - - // NOTIFICHE EMAIL CONFERMA IMMOBILE - - const utente = await getUser({ db, userId: servizio.user_id }); - if (!utente.comms_enabled) { - return true; - } - - const annuncio_codice = await db - .selectFrom("annunci") - .select("codice") - .where("id", "=", annuncioId) - .executeTakeFirstOrThrow( - () => - new Error( - `Annuncio not found - servizioId: ${servizioId}, annunciId: ${annuncioId}`, - ), - ); - - await NewMail({ - template: { - mailType: "generic", - props: { - link: { - href: `${env.BASE_URL}/area-riservata/admin/user-view/ricerca/${servizio.user_id}`, - label: "Visualizza Annuncio", - }, - noreply: true, - testo: `L'utente ${utente.username} ha notificato interesse per l'immobile con codice ${annuncio_codice.codice}.`, - title: `Nuovo interesse conferma immobile ${annuncio_codice.codice}`, - }, - }, - mail: { - subject: `Nuovo interesse conferma immobile ${annuncio_codice.codice}`, - to: "web@infoalloggi.it", - }, - }); - - await NewMail({ - template: { - mailType: "generic", - props: { - noreply: true, - testo: `La tua notifica di interesse per l'immobile con codice ${annuncio_codice.codice} è stata inviata con successo. Contatteremo i proprietari per raccogliere le informazioni necessarie. Riceverai una notifica via email per procedere.`, - title: `Notifica interesse immobile ${annuncio_codice.codice} inviata con successo`, - }, - }, - mail: { - subject: `Notifica interesse immobile ${annuncio_codice.codice} inviata con successo`, - to: utente.email, - }, - userId: utente.id, - }); - return true; - } catch (e) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: `Error confirming immobile: ${(e as Error).message}`, - }); - } -}; - -export const adminUpdateConfermaStatus = async ({ - servizioId, - annuncioId, - status, -}: { - servizioId: ServizioServizioId; - annuncioId: AnnunciId; - status: boolean; -}) => { - try { - await db - .updateTable("servizio_annunci") - .set({ - hasConfermaAdmin: status, - }) - .where("servizio_id", "=", servizioId) - .where("annunci_id", "=", annuncioId) - .execute(); - - const servizio = await getServizioById(servizioId); - const utente = await getUser({ db, userId: servizio.user_id }); - - const saldo = await SaldoSolver({ - tipologia: servizio.tipologia, - permanenza: servizio.permanenza, - budget: servizio.budget, - }); - if (saldo.success) { - await createOrdine({ - servizio_id: servizioId, - type: OrderTypeEnum.Saldo, - amount_cent: saldo.pack.prezzo_cent, - packid: saldo.pack.idprezziario, - userid: servizio.user_id, - }); - } else { - console.error( - `Errore nel calcolo del saldo per il servizio ${servizioId}: ${saldo.message}, procedura invio conferma`, - ); - } - - //ADMIN CONFERMA LAVORATA EMAIL - if (!utente.comms_enabled) { - return true; - } - if (status) { - await NewMail({ - template: { - mailType: "generic", - props: { - link: { - href: `${env.BASE_URL}/area-riservata/servizio/${servizioId}`, - label: "Vai al servizio", - }, - noreply: true, - testo: `La conferma è stata lavorata con successo. Puoi procedere con le prossime azioni.`, - title: "Conferma lavorata per il servizio di ricerca affitto", - }, - }, - mail: { - subject: "Conferma lavorata per il servizio di ricerca affitto", - to: utente.email, - }, - userId: utente.id, - }); - } - - return true; - } catch (e) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: `Error updating conferma status from annuncio: ${(e as Error).message}`, - }); - } -}; - -export const userAccettaConferma = async ({ - servizioId, - annuncioId, -}: { - servizioId: ServizioServizioId; - annuncioId: AnnunciId; -}) => { - try { - await db - .updateTable("servizio_annunci") - .set({ - accettato_conferma_at: new Date(), - }) - .where("servizio_id", "=", servizioId) - .where("annunci_id", "=", annuncioId) - .execute(); - - const servizio = await getServizioById(servizioId); - const utente = await getUser({ db, userId: servizio.user_id }); - - // USER ACCETTA CONFERMA EMAIL - - await NewMail({ - template: { - mailType: "generic", - props: { - link: { - href: `${env.BASE_URL}/area-riservata/admin/user-view/servizio/${servizio.user_id}/${servizioId}`, - label: "Vai al servizio", - }, - noreply: true, - testo: `L'utente ha accettato le condizioni di conferma per la locazione. Puoi procedere con le prossime azioni.`, - title: `Utente ${utente.username} ha accettato le condizioni di conferma`, - }, - }, - mail: { - subject: `Utente ${utente.username} ha accettato le condizioni di conferma`, - to: "web@infoalloggi.it", - }, - }); - - return { success: true, userId: utente.id }; - } catch (e) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: `Error accepting conferma from annuncio: ${(e as Error).message}`, - }); - } -}; - export const UtenteInteressatoAdAnnuncioEmail = async ({ codice, nome, @@ -1414,7 +1195,7 @@ export const getRichieste = async (): Promise => { withImages(), withVideos(), ]) - .orderBy("servizio_annunci.user_confirmed_at", (ob) => + .orderBy("servizio_annunci.status_conferma", (ob) => ob.asc().nullsLast(), ) .orderBy("servizio_annunci.open_contatti_at", (ob) =>