- Implemented FormEditOrder component for editing orders with validation and submission handling. - Created SchedaAnnuncioPage for displaying printable announcement details. - Added BonificoManualePage for manual bank transfer payment instructions. - Developed PagamentoPage for Stripe payment processing with PaymentIntent creation. - Introduced PreviewPurchasePage for preparing payment with service details. - Added pagamenti.controller for handling payment preparation and retrieval of payment data.
221 lines
6.3 KiB
TypeScript
221 lines
6.3 KiB
TypeScript
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 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;
|
|
};
|
|
|
|
const ConfermaImmobile: NextPageWithLayout<ConfermaImmobileProps> = ({
|
|
servizioId,
|
|
annuncioId,
|
|
}: ConfermaImmobileProps) => {
|
|
const { data, isLoading } = api.servizio.getServizioAnnuncio.useQuery({
|
|
annuncioId,
|
|
servizioId,
|
|
});
|
|
const { t } = useTranslation();
|
|
if (isLoading) return <LoadingPage />;
|
|
if (!data) return <Status500 />;
|
|
if (!data.doc_conferma_ref) return <DocNotFoundPage />;
|
|
return (
|
|
<div className="mx-2 flex flex-1 overflow-auto">
|
|
<div className="mx-auto flex grow sm:grow-0">
|
|
<div className="flex flex-col items-center space-y-4 py-4">
|
|
<h1 className="font-bold text-xl">{t.servizio.conferma.titolo}</h1>
|
|
<p className="text-center">{t.servizio.conferma.descrizione}</p>
|
|
|
|
<FileSection storageId={data.doc_conferma_ref} />
|
|
|
|
<div className="flex max-w-3xl flex-col gap-2 rounded-md bg-green-400 p-4 text-left">
|
|
<p className="font-semibold text-xl">{t.servizio.conferma.cta}</p>
|
|
<p>{t.servizio.conferma.coordinate}</p>
|
|
<p>
|
|
{t.servizio.conferma.intestazione}{" "}
|
|
{data.caparra_intestazione || ""}
|
|
</p>
|
|
<p>
|
|
{t.servizio.conferma.iban} {data.caparra_iban || ""}
|
|
</p>
|
|
<p>
|
|
{t.servizio.conferma.importo} {data.caparra_importo || ""}
|
|
</p>
|
|
<p>
|
|
{t.servizio.conferma.causale} {data.caparra_causale || ""}
|
|
</p>
|
|
<p className="font-semibold text-xl">
|
|
{t.servizio.conferma.coord_footer}
|
|
</p>
|
|
</div>
|
|
|
|
<ConfirmSection annuncioId={annuncioId} servizioId={servizioId} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
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/ricerca/${data.userId}`,
|
|
);
|
|
} else {
|
|
await router.push(`/area-riservata/servizio/${servizioId}`);
|
|
}
|
|
},
|
|
});
|
|
return (
|
|
<>
|
|
<div className="flex flex-col items-start space-y-3 px-2">
|
|
<div className="flex items-center space-x-2">
|
|
<Checkbox
|
|
checked={letto}
|
|
className="size-6"
|
|
id="letto"
|
|
onCheckedChange={(v) => v !== "indeterminate" && setLetto(v)}
|
|
/>
|
|
<div className="grid gap-1.5 leading-none">
|
|
<label
|
|
className="font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
htmlFor="letto"
|
|
>
|
|
{t.servizio.conferma.ho_letto}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<Checkbox
|
|
checked={accettato}
|
|
className="size-6"
|
|
id="termini"
|
|
onCheckedChange={(v) => v !== "indeterminate" && setAccettato(v)}
|
|
/>
|
|
<div className="grid gap-1.5 leading-none">
|
|
<label
|
|
className="font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
htmlFor="termini"
|
|
>
|
|
{t.servizio.conferma.accetto}
|
|
</label>
|
|
<p className="text-muted-foreground text-sm">
|
|
{t.servizio.conferma.confermo_termini}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
className="h-fit whitespace-normal text-pretty"
|
|
disabled={!letto || !accettato}
|
|
onClick={() => {
|
|
// Handle confirmation logic here
|
|
conferma({
|
|
annuncioId,
|
|
servizioId,
|
|
});
|
|
}}
|
|
>
|
|
{t.servizio.conferma.conferma}
|
|
</Button>
|
|
</>
|
|
);
|
|
};
|
|
|
|
ConfermaImmobile.getLayout = function getLayout(page) {
|
|
return <AreaRiservataLayout noSidebar>{page}</AreaRiservataLayout>;
|
|
};
|
|
|
|
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 {
|
|
redirect: {
|
|
destination: "/500",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}) satisfies GetServerSideProps<ConfermaImmobileProps>;
|