226 lines
6.4 KiB
TypeScript
226 lines
6.4 KiB
TypeScript
"use client";
|
|
|
|
import { format } from "date-fns";
|
|
import { it } from "date-fns/locale";
|
|
import Image from "next/image";
|
|
import toast from "react-hot-toast";
|
|
import { Button } from "~/components/ui/button";
|
|
import { Card } from "~/components/ui/card";
|
|
import { PaymentMethodToString, type PaymentType } from "~/i18n/stripe";
|
|
import { formatCurrency } from "~/lib/utils";
|
|
import type { OrdiniOrdineId } from "~/schemas/public/Ordini";
|
|
import type { Prezziario } from "~/schemas/public/Prezziario";
|
|
import { api } from "~/utils/api";
|
|
|
|
export function ReceiptGenerator({
|
|
data,
|
|
raw,
|
|
}: {
|
|
data: PurchaseData;
|
|
raw: boolean;
|
|
}) {
|
|
const { mutate: generatePdf } =
|
|
api.pagamenti.getRicevutaCortesiaPdf.useMutation({
|
|
onMutate: () => {
|
|
toast.loading("Generazione PDF in corso...", {
|
|
id: "generate-pdf",
|
|
});
|
|
},
|
|
onSuccess: (res) => {
|
|
toast.success("PDF generato con successo!", {
|
|
id: "generate-pdf",
|
|
});
|
|
|
|
const blob = new Blob([res as unknown as ArrayBuffer], {
|
|
type: "application/pdf",
|
|
});
|
|
const url = window.URL.createObjectURL(blob);
|
|
const link = document.createElement("a");
|
|
link.href = url;
|
|
link.download = `ricevuta-cortesia-${data.clienteNome.replace(/\s+/g, "-")}-${format(data.date, "dd-MM-yyyy")}.pdf`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
link.remove();
|
|
window.URL.revokeObjectURL(url);
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Errore durante la generazione del PDF: ${error.message}`, {
|
|
id: "generate-pdf",
|
|
});
|
|
},
|
|
});
|
|
if (raw) {
|
|
return <Receipt data={data} />;
|
|
}
|
|
return (
|
|
<div className="mx-auto max-w-4xl">
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="font-bold text-2xl">Ricevuta di cortesia</h1>
|
|
|
|
<Button onClick={() => generatePdf({ ordineId: data.ordineId })}>
|
|
Stampa
|
|
</Button>
|
|
</div>
|
|
<div>
|
|
<Card className="p-4">
|
|
<Receipt data={data} />
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export type PurchaseData = {
|
|
ordineId: OrdiniOrdineId;
|
|
date: Date;
|
|
|
|
clienteNome: string;
|
|
codiceFiscale: string | null;
|
|
clienteIndirizzo: string;
|
|
paymentMethod: PaymentType;
|
|
packName: string;
|
|
sconto: number;
|
|
|
|
amount_cent: number;
|
|
previewSaldo: Prezziario | null;
|
|
};
|
|
|
|
interface ReceiptProps {
|
|
data: PurchaseData;
|
|
}
|
|
|
|
function Receipt({ data }: ReceiptProps) {
|
|
const companyName = "Arcenia S.r.l.";
|
|
|
|
const companyAddress =
|
|
"Via Beata Giovanna 1\nBassano del Grappa (VI) 36061\nItalia";
|
|
|
|
const note =
|
|
"La presente non costituisce ricevuta o fattura fiscale ai sensi dell'Art.21, DPR 633/72.\nSeguirà fattura elettronica direttamente al tuo indirizzo di posta.";
|
|
const footer =
|
|
"Arcenia Srl. | Tel. +39 0424529869 | Email: arca@infoalloggi.it";
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<h1 className="font-bold text-2xl">{companyName}</h1>
|
|
<p className="whitespace-pre-line text-sm">{companyAddress}</p>
|
|
</div>
|
|
|
|
<div className="relative size-24">
|
|
<Image
|
|
alt={`${companyName} logo`}
|
|
className="object-contain"
|
|
fill
|
|
src={"/favicon/favicon.png"}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="border-b pb-4">
|
|
<h2 className="text-center font-semibold text-xl">
|
|
Ricevuta di cortesia
|
|
</h2>
|
|
<div className="mt-2 flex justify-between">
|
|
<div>
|
|
<p className="text-muted-foreground text-sm">ID Ordine</p>
|
|
<p className="text-sm">{data.ordineId}</p>
|
|
</div>
|
|
<div className="text-right">
|
|
<p className="text-muted-foreground text-sm">Data</p>
|
|
<p className="font-medium">
|
|
{format(data.date, "PPP", {
|
|
locale: it,
|
|
})}
|
|
</p>
|
|
<p className="text-sm">
|
|
{format(data.date, "p", {
|
|
locale: it,
|
|
})}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-6">
|
|
<div>
|
|
<h3 className="mb-1 font-semibold">Intestatario:</h3>
|
|
<p className="font-medium">{data.clienteNome}</p>
|
|
<p className="text-sm">{data.codiceFiscale}</p>
|
|
<p className="whitespace-pre-line text-sm">{data.clienteIndirizzo}</p>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<table className="w-full table-auto">
|
|
<thead className="flex w-full">
|
|
<tr className="grid w-full grow grid-cols-[1fr_auto_auto] gap-2 border-b">
|
|
<th className="py-2 text-left">Oggetto</th>
|
|
|
|
<th className="py-2 text-right">Sconto applicato</th>
|
|
<th className="py-2 text-right">Importo</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr className="grid w-full grow grid-cols-[1fr_auto_auto] gap-2 border-b">
|
|
<td className="py-2">{data.packName}</td>
|
|
|
|
<td className="py-2 text-right">
|
|
{data.sconto > 0 && `${data.sconto}%`}
|
|
</td>
|
|
<td className="py-2 text-right">
|
|
{formatCurrency(data.amount_cent / 100)}
|
|
</td>
|
|
</tr>
|
|
{data.previewSaldo && (
|
|
<tr className="grid w-full grow grid-cols-[1fr_auto_auto] gap-2 border-b">
|
|
<td className="py-2">
|
|
{data.previewSaldo.nome_it}{" "}
|
|
<span className="text-sm">
|
|
pagamento differito, non è dovuto in questo momento
|
|
</span>
|
|
</td>
|
|
|
|
<td className="py-2 text-right"> </td>
|
|
<td className="py-2 text-right text-muted-foreground line-through">
|
|
{formatCurrency(data.previewSaldo.prezzo_cent / 100)}
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div className="flex justify-end">
|
|
<div className="w-64">
|
|
<div className="mt-1 flex justify-between pt-2 font-bold">
|
|
<span>Totale:</span>
|
|
<span>{formatCurrency(data.amount_cent / 100)}</span>
|
|
</div>
|
|
<div className="flex justify-between py-1">
|
|
<span>di cui IVA (22%):</span>
|
|
<span>{formatCurrency((data.amount_cent / 100) * 0.22)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="border-t pt-4">
|
|
<div className="flex justify-between">
|
|
<div>
|
|
<h3 className="mb-1 font-semibold">Metodo di pagamento</h3>
|
|
<p>{PaymentMethodToString(data.paymentMethod)}</p>
|
|
</div>
|
|
<div className="text-right">
|
|
<h3 className="mb-1 font-semibold">Status pagamento</h3>
|
|
<p className="font-medium text-green-600">Pagato</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="border-t pt-4">
|
|
<h3 className="mb-1 font-semibold">Note</h3>
|
|
<p className="whitespace-pre-line text-sm">{note}</p>
|
|
</div>
|
|
|
|
<div className="pt-6 text-center text-muted-foreground text-sm">
|
|
<p>{footer}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|