feat: update Servizio component to handle interruzioneDays and acceptedInterr logic refactor: simplify servizio_actions by removing unused InterruzioneServizio and EditParametri components feat: enhance FormEditServizioAdmin to include interruzioneDays and acceptedInterr fields fix: adjust email service to delete obsolete emails and handle invalid email types gracefully chore: update stripe controller to utilize interruzioneDays for lock expiration logic fix: ensure correct handling of servizio expiration and interruzione logic in service controller
249 lines
7.8 KiB
TypeScript
249 lines
7.8 KiB
TypeScript
import { add, differenceInDays, isBefore } from "date-fns";
|
|
import { ArrowRight, Clock, ClockAlert, XCircle } from "lucide-react";
|
|
import toast from "react-hot-toast";
|
|
import { Confirm } from "~/components/confirm";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from "~/components/ui/alert-dialog";
|
|
import { Button } from "~/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardFooter,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "~/components/ui/card";
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from "~/components/ui/tooltip";
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
import { useServizio } from "~/providers/ServizioProvider";
|
|
import { api } from "~/utils/api";
|
|
|
|
export const DisclaimerInterruzioneServizio = () => {
|
|
const { servizio, servizioId } = useServizio();
|
|
const { t } = useTranslation();
|
|
const utils = api.useUtils();
|
|
const { mutate: interrompiServizio } =
|
|
api.servizio.interruzioneServizio.useMutation({
|
|
onError: (error) => {
|
|
console.error(error);
|
|
toast.error("Errore durante la richiesta di interruzione");
|
|
},
|
|
onSuccess: async () => {
|
|
await utils.servizio.invalidate();
|
|
toast.success("Interruzione richiesta");
|
|
},
|
|
});
|
|
const { mutate: updateServizio } = api.servizio.updateServizio.useMutation({
|
|
onError: (error) => {
|
|
console.error(error);
|
|
toast.error("Errore");
|
|
},
|
|
onSuccess: async () => {
|
|
await utils.servizio.invalidate();
|
|
},
|
|
});
|
|
|
|
if (!servizio.decorrenza) return null;
|
|
|
|
return (
|
|
<main className="mx-auto max-w-4xl px-4 py-6 md:py-8">
|
|
<div className="mb-10 text-center">
|
|
<div className="mx-auto mb-6 flex size-16 items-center justify-center rounded-full bg-secondary">
|
|
<Clock className="size-8 text-primary" />
|
|
</div>
|
|
<h1 className="mb-4 text-balance font-bold text-2xl text-foreground tracking-tight md:text-4xl">
|
|
Sono passati {differenceInDays(new Date(), servizio.decorrenza)}{" "}
|
|
giorni dall'inizio della tua ricerca
|
|
</h1>
|
|
<p className="mx-auto max-w-xl text-pretty text-lg text-muted-foreground leading-relaxed">
|
|
Hai trovato qualche immobile interessante? <br /> Ricordati che{" "}
|
|
<b>se non intendi confermare</b> nessun immobile, puoi interrompere la
|
|
ricerca <b>entro 14 giorni dalla decorrenza</b>, evitando così di
|
|
dover pagare il saldo del servizio acquistato.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
<Card className="border-2 border-primary shadow-lg">
|
|
<CardHeader className="pb-2 text-center">
|
|
<div className="mx-auto mb-3 flex size-14 items-center justify-center rounded-full bg-primary/10">
|
|
<ArrowRight className="size-7 text-primary" />
|
|
</div>
|
|
<CardTitle className="text-foreground text-xl">Continua</CardTitle>
|
|
<CardDescription className="text-base">
|
|
Continua a cercare casa
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<div className="flex h-full w-full flex-col justify-between">
|
|
<CardContent className="">
|
|
<p className="mb-4 text-muted-foreground text-sm">
|
|
Avrai sempre modo di interrompere il servizio usando il pulsante
|
|
relativo.
|
|
</p>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button
|
|
className="h-12 w-full bg-primary font-medium text-base text-primary-foreground hover:bg-primary/90"
|
|
onClick={() =>
|
|
updateServizio({ servizioId, data: { acceptedInterr: true } })
|
|
}
|
|
>
|
|
Continua con la ricerca
|
|
</Button>
|
|
</CardFooter>
|
|
</div>
|
|
</Card>
|
|
<Card className="border-border">
|
|
<CardHeader className="pb-2 text-center">
|
|
<div className="mx-auto mb-3 flex size-14 items-center justify-center rounded-full bg-muted">
|
|
<XCircle className="size-7 text-muted-foreground" />
|
|
</div>
|
|
<CardTitle className="text-foreground text-xl">
|
|
Interrompi
|
|
</CardTitle>
|
|
<CardDescription className="text-base">
|
|
Termina la tua ricerca ora
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<div className="flex h-full w-full flex-col justify-between">
|
|
<CardContent className="">
|
|
<ul className="mb-6 space-y-2 text-muted-foreground text-sm">
|
|
<li className="flex items-center gap-2">
|
|
<span className="size-1.5 rounded-full bg-muted-foreground" />
|
|
Terminazione immediata del servizio
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<span className="size-1.5 rounded-full bg-muted-foreground" />
|
|
Nessun addebito aggiuntivo applicato
|
|
</li>
|
|
</ul>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Confirm
|
|
description={t.servizio.interruzione.description}
|
|
onConfirm={() => {
|
|
interrompiServizio({ servizioId });
|
|
}}
|
|
title={t.servizio.interruzione.title}
|
|
>
|
|
<Button
|
|
className="h-12 w-full border-border font-medium text-base text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
variant="outline"
|
|
>
|
|
Interrompi il mio servizio ora
|
|
</Button>
|
|
</Confirm>
|
|
</CardFooter>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
<p className="mt-8 text-center text-muted-foreground text-sm">
|
|
Hai fino al{" "}
|
|
{add(servizio.decorrenza, {
|
|
days: servizio.interruzioneDays,
|
|
}).toLocaleDateString("it-IT")}{" "}
|
|
per interrompere il servizio senza costi aggiuntivi.
|
|
<br />
|
|
Se hai bisogno di assistenza, contattaci.
|
|
</p>
|
|
</main>
|
|
);
|
|
};
|
|
|
|
export const InterruzioneServizio = () => {
|
|
const { servizio, isAdmin } = useServizio();
|
|
const { t } = useTranslation();
|
|
const utils = api.useUtils();
|
|
const { mutate } = api.servizio.interruzioneServizio.useMutation({
|
|
onError: (error) => {
|
|
console.error(error);
|
|
toast.error("Errore durante la richiesta di interruzione");
|
|
},
|
|
onSuccess: async () => {
|
|
await utils.servizio.invalidate();
|
|
toast.success("Interruzione richiesta");
|
|
},
|
|
});
|
|
if (!servizio.decorrenza) return null;
|
|
const fineInterruzione = add(servizio.decorrenza, {
|
|
days: servizio.interruzioneDays,
|
|
});
|
|
|
|
const canInterrompere =
|
|
!servizio.isInterrotto &&
|
|
(isAdmin ||
|
|
(isBefore(new Date(), fineInterruzione) &&
|
|
servizio.annunci.every((a) => a.accettato_conferma_at === null)));
|
|
|
|
return (
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>
|
|
{canInterrompere ? (
|
|
<Button
|
|
aria-label="Request Interruzione"
|
|
className="w-full"
|
|
disabled={!canInterrompere}
|
|
>
|
|
<ClockAlert /> <span>{t.servizio.interruzione.btn}</span>
|
|
</Button>
|
|
) : (
|
|
<Tooltip delayDuration={0}>
|
|
<TooltipTrigger asChild>
|
|
<span className="inline-block w-full">
|
|
<Button
|
|
aria-label="Request Interruzione"
|
|
className="w-full"
|
|
disabled={!canInterrompere}
|
|
>
|
|
<ClockAlert /> <span>{t.servizio.interruzione.btn}</span>
|
|
</Button>
|
|
</span>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>Periodo di interruzione scaduto, contattaci.</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
</AlertDialogTrigger>
|
|
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{t.servizio.interruzione.title}</AlertDialogTitle>
|
|
<AlertDialogDescription className="space-y-2">
|
|
<p>
|
|
<b>
|
|
Fine periodo di interruzione:{" "}
|
|
{fineInterruzione.toLocaleDateString("it-IT")}
|
|
</b>
|
|
</p>
|
|
<p className="whitespace-pre-line">
|
|
{t.servizio.interruzione.description}
|
|
</p>
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>{t.annulla}</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
aria-label="Confirm Interruzione"
|
|
onClick={() => mutate({ servizioId: servizio.servizio_id })}
|
|
>
|
|
{t.conferma}
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
);
|
|
};
|