2025-11-06 17:03:42 +01:00
import { CircleCheck , Handshake } from "lucide-react" ;
2025-10-24 16:10:59 +02:00
import Link from "next/link" ;
2025-11-06 17:03:42 +01:00
import { useState } from "react" ;
import toast from "react-hot-toast" ;
2025-08-04 17:45:44 +02:00
import { AllegatoIframe } from "~/components/allegato-iframe" ;
2025-08-28 18:27:07 +02:00
import { LoadingPage } from "~/components/loading" ;
2025-11-28 15:11:14 +01:00
import { getStorageUrl } from "~/lib/storage_utils" ;
2025-08-13 17:29:26 +02:00
import { useTranslation } from "~/providers/I18nProvider" ;
2025-11-06 17:03:42 +01:00
import { useServizio , useServizioAnnuncio } from "~/providers/ServizioProvider" ;
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum" ;
import type { UsersStorageUserStorageId } from "~/schemas/public/UsersStorage" ;
2025-08-28 18:27:07 +02:00
import { api } from "~/utils/api" ;
2025-11-11 15:41:20 +01:00
import { DocumentiUploadSection } from "../area-riservata/documenti_personali" ;
2025-11-06 17:03:42 +01:00
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" ;
2025-08-04 17:45:44 +02:00
2025-10-24 16:10:59 +02:00
export const FileSection = ( { storageId } : { storageId : string } ) = > {
const { data : fileInfos , isLoading } = api . storage . getFileInfos . useQuery ( {
2025-08-28 18:27:07 +02:00
storageId ,
} ) ;
const { t } = useTranslation ( ) ;
if ( isLoading ) return < LoadingPage / > ;
if ( ! fileInfos ) return < p > { t . file_section . error } < / p > ;
return (
< >
< AllegatoIframe
2025-10-24 16:10:59 +02:00
allegato = { fileInfos }
2025-08-28 18:27:07 +02:00
className = "h-full max-h-[70vh] min-h-[60vh]"
/ >
2025-08-04 17:45:44 +02:00
2025-10-24 16:10:59 +02:00
< Link
2025-08-29 16:18:32 +02:00
className = "underline underline-offset-1 after:content-['_↗']"
2025-11-28 15:11:14 +01:00
href = { getStorageUrl ( {
storageId : fileInfos.id ,
params : { mode : "download" } ,
} ) }
2025-08-28 18:27:07 +02:00
>
{ t . file_section . download }
2025-10-24 16:10:59 +02:00
< / Link >
2025-08-28 18:27:07 +02:00
< / >
) ;
2025-08-04 17:45:44 +02:00
} ;
2025-11-06 17:03:42 +01:00
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 (
2025-11-12 17:05:05 +01:00
< >
< Credenza onOpenChange = { setOpen } open = { open } >
< CredenzaTrigger asChild >
2025-12-16 12:16:16 +01:00
< Button aria-label = "Conferma immobile" className = "w-full sm:w-fit" >
2026-01-16 15:47:38 +01:00
< Handshake / > { t . richieste . cta }
2025-11-12 17:05:05 +01:00
< / Button >
< / CredenzaTrigger >
< CredenzaContent className = "max-h-[90vh]" >
< CredenzaHeader >
< CredenzaTitle > { t . richieste . conf_title } < / CredenzaTitle >
< CredenzaDescription className = "sr-only" >
{ t . richieste . conf_desc }
< / CredenzaDescription >
< / CredenzaHeader >
{ needsDocuments ? (
< DocCheckpoint / >
) : (
< InvioConferma closeModal = { ( ) = > setOpen ( false ) } / >
) }
< / CredenzaContent >
< / Credenza >
< / >
2025-11-06 17:03:42 +01:00
) ;
} ;
2025-11-12 17:05:05 +01:00
const InvioConferma = ( { closeModal } : { closeModal : ( ) = > void } ) = > {
2025-11-06 17:03:42 +01:00
const { userId , 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 . getAllServizioAnnunci . invalidate ( { userId } ) ;
2026-03-06 16:15:47 +01:00
await utils . servizio . getServizioData . invalidate ( { servizioId } ) ;
2025-11-12 17:05:05 +01:00
closeModal ( ) ;
2025-11-06 17:03:42 +01:00
} ,
} ,
) ;
return (
< >
2025-11-12 17:05:05 +01:00
< CredenzaBody className = "flex flex-col gap-4" >
2025-11-06 17:03:42 +01:00
< p > { t . richieste . conferma_txt } < / p >
2025-11-12 17:05:05 +01:00
< p className = "text-sm" > { t . richieste . conf_alert } < / p >
2025-11-06 17:03:42 +01:00
< / CredenzaBody >
< CredenzaFooter >
< LoadingButton
aria - label = "Conferma immobile"
loading = { isPending }
onClick = { ( ) = > {
mutate ( {
annuncioId ,
servizioId ,
} ) ;
} }
variant = "success"
>
{ t . conferma }
< / LoadingButton >
< CredenzaClose asChild >
2025-11-10 11:52:36 +01:00
< Button aria-label = "Chiudi modal credenza" > { t . chiudi } < / Button >
2025-11-06 17:03:42 +01:00
< / CredenzaClose >
< / CredenzaFooter >
< / >
) ;
} ;
const DocCheckpoint = ( ) = > {
2026-02-25 15:30:53 +01:00
const { t , locale } = useTranslation ( ) ;
2025-12-18 12:18:24 +01:00
const { userId , servizio , isAdmin , servizioId } = useServizio ( ) ;
2025-11-06 17:03:42 +01:00
const utils = api . useUtils ( ) ;
const { mutate , isPending } = api . servizio . updateServizio . useMutation ( {
onSuccess : async ( ) = > {
await utils . servizio . getAllServizioAnnunci . invalidate ( {
userId ,
} ) ;
2026-03-06 16:15:47 +01:00
await utils . servizio . getServizioData . invalidate ( { servizioId } ) ;
2025-11-06 17:03:42 +01:00
await utils . storage . retrieveUserFileData . invalidate ( { userId } ) ;
await utils . servizio . getServizio . invalidate ( {
servizioId : servizio.servizio_id ,
} ) ;
2025-12-18 12:18:24 +01:00
await utils . servizio . getPacksSolution . invalidate ( { servizioId } ) ;
2025-11-06 17:03:42 +01:00
} ,
onError : ( err ) = > {
toast . error ( ` Errore aggiornamento servizio: ${ err . message } ` ) ;
} ,
} ) ;
const handleUpload = ( id : UsersStorageUserStorageId ) = > {
mutate ( {
servizioId : servizio.servizio_id ,
data : { doc_motivazione_ref : id } ,
} ) ;
} ;
return (
< >
< CredenzaBody className = "max-h-[80vh] overflow-auto pb-5" >
< div className = "space-y-5" >
< p >
2026-02-25 15:30:53 +01:00
{ 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" }
2025-11-06 17:03:42 +01:00
< / p >
2025-11-11 15:41:20 +01:00
< DocumentiUploadSection
caricatoNote
document = { servizio . doc_motivazione }
isAdmin = { isAdmin }
isPending = { isPending }
onUpload = { handleUpload }
2026-02-25 15:30:53 +01:00
title = { locale === "it" ? "Documento:" : "Document:" }
2025-11-11 15:41:20 +01:00
userId = { userId }
/ >
2025-11-06 17:03:42 +01:00
< br / >
< br / >
< p className = "text-muted-foreground text-sm" >
2026-02-25 15:30:53 +01:00
{ 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 " }
2025-11-06 17:03:42 +01:00
< Link className = "underline" href = "/privacy-policy" target = "_blank" >
2026-02-25 15:30:53 +01:00
{ locale === "it" ? "Informativa sulla Privacy" : "Privacy Policy" }
2025-11-06 17:03:42 +01:00
< / Link >
< / p >
< / div >
< / CredenzaBody >
< CredenzaFooter >
< CredenzaClose asChild >
< Button aria-label = "Chiudi" disabled = { isPending } variant = "outline" >
{ t . chiudi }
< / Button >
< / CredenzaClose >
< / CredenzaFooter >
< / >
) ;
} ;
export const ConfermaInLavorazioneModal = ( ) = > {
2026-02-25 15:30:53 +01:00
const { t , locale } = useTranslation ( ) ;
2025-11-06 17:03:42 +01:00
const { data } = useServizioAnnuncio ( ) ;
return (
< Credenza >
< CredenzaTrigger asChild >
2025-12-16 12:16:16 +01:00
< Button
aria - label = "Conferma in lavorazione"
className = "w-full sm:w-fit"
>
2026-01-16 15:47:38 +01:00
< CircleCheck / >
2026-02-25 15:30:53 +01:00
{ locale === "it"
? "Conferma in lavorazione"
: "Confirmation in progress" }
2025-11-06 17:03:42 +01:00
< / Button >
< / CredenzaTrigger >
< CredenzaContent className = "max-h-[90vh]" >
< CredenzaHeader >
2026-02-25 15:30:53 +01:00
< CredenzaTitle >
{ locale === "it"
? "In attesa di conferma"
: "Awaiting confirmation" }
< / CredenzaTitle >
2025-11-06 17:03:42 +01:00
< CredenzaDescription className = "sr-only" >
Consegna status modal
< / CredenzaDescription >
< / CredenzaHeader >
2026-01-16 12:23:18 +01:00
< CredenzaBody className = "prose dark:prose-invert max-h-[80vh] overflow-auto pb-5" >
2025-11-06 17:03:42 +01:00
< p >
2026-02-25 15:30:53 +01:00
{ locale === "it"
? "Richiesta di conferma inviata il:"
: "Confirmation request sent on:" } { " " }
2025-11-06 17:03:42 +01:00
{ 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" ,
} ) }
< / p >
< p >
2026-02-25 15:30:53 +01:00
{ 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." }
2025-11-06 17:03:42 +01:00
< / p >
< / CredenzaBody >
< CredenzaFooter >
< CredenzaClose asChild >
< Button aria-label = "Chiudi modal credenza" variant = "outline" >
{ t . chiudi }
< / Button >
< / CredenzaClose >
< / CredenzaFooter >
< / CredenzaContent >
< / Credenza >
) ;
} ;