135 lines
6.7 KiB
TypeScript
135 lines
6.7 KiB
TypeScript
'use client';
|
|
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Separator } from '@/components/ui/separator';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Download, Printer, Copy, CreditCard } from 'lucide-react';
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
import { formatCurrency } from '@/data/mockData';
|
|
import { toast } from 'sonner';
|
|
|
|
interface ReceiptViewerProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
booking: any; // Using any for now, ideally UserBooking
|
|
}
|
|
|
|
export function ReceiptViewer({
|
|
open,
|
|
onOpenChange,
|
|
booking
|
|
}: ReceiptViewerProps) {
|
|
if (!booking) return null;
|
|
|
|
const copyToClipboard = (text: string) => {
|
|
navigator.clipboard.writeText(text);
|
|
toast.success("Copied to clipboard");
|
|
};
|
|
|
|
// Mock calculations for the receipt
|
|
const basePrice = booking.amount * 0.9;
|
|
const tax = booking.amount * 0.1;
|
|
const fee = 2.50; // Mock processing fee
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="sm:max-w-[480px] p-0 overflow-hidden flex flex-col bg-white">
|
|
<div className="bg-slate-900 p-6 text-white text-center rounded-t-lg">
|
|
<div className="w-12 h-12 bg-white/10 rounded-full flex items-center justify-center mx-auto mb-3">
|
|
<CreditCard className="h-6 w-6 text-white" />
|
|
</div>
|
|
<h2 className="text-xl font-bold tracking-tight">Receipt from Eventify</h2>
|
|
<p className="text-slate-400 text-sm mt-1">Thank you for your business.</p>
|
|
</div>
|
|
|
|
<ScrollArea className="max-h-[60vh]">
|
|
<div className="p-6 space-y-6">
|
|
{/* Meta Data */}
|
|
<div className="grid grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<p className="text-muted-foreground text-xs uppercase tracking-wider">Order ID</p>
|
|
<div className="flex items-center gap-1 font-mono font-medium hover:text-primary cursor-pointer" onClick={() => copyToClipboard(booking.id)}>
|
|
{booking.id.substring(0, 12)}...
|
|
<Copy className="h-3 w-3 opacity-50" />
|
|
</div>
|
|
</div>
|
|
<div className="text-right">
|
|
<p className="text-muted-foreground text-xs uppercase tracking-wider">Date</p>
|
|
<p className="font-medium">{new Date(booking.createdAt).toLocaleDateString()}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator className="bg-slate-100" />
|
|
|
|
{/* Customer Info */}
|
|
<div>
|
|
<p className="text-muted-foreground text-xs uppercase tracking-wider mb-2">Billed To</p>
|
|
<p className="font-semibold text-sm">User ID: {booking.userId}</p>
|
|
<p className="text-sm text-slate-500">Payment Method: Visa •••• 4242</p>
|
|
</div>
|
|
|
|
{/* Line Items */}
|
|
<div className="bg-slate-50 p-4 rounded-lg border border-slate-100">
|
|
<table className="w-full text-sm">
|
|
<thead>
|
|
<tr className="text-xs text-muted-foreground text-left border-b border-slate-200">
|
|
<th className="pb-2 font-medium">Description</th>
|
|
<th className="pb-2 font-medium text-right">Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-slate-100">
|
|
<tr>
|
|
<td className="py-3">
|
|
<p className="font-medium text-slate-900">{booking.eventName}</p>
|
|
<p className="text-xs text-slate-500">{booking.ticketType} x {booking.quantity}</p>
|
|
</td>
|
|
<td className="py-3 text-right tabular-nums text-slate-900">
|
|
{formatCurrency(basePrice)}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="py-2 text-slate-500">Processing Fees</td>
|
|
<td className="py-2 text-right tabular-nums text-slate-500">{formatCurrency(fee)}</td>
|
|
</tr>
|
|
<tr>
|
|
<td className="py-2 text-slate-500">Tax (10%)</td>
|
|
<td className="py-2 text-right tabular-nums text-slate-500">{formatCurrency(tax)}</td>
|
|
</tr>
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td className="pt-3 font-bold text-slate-900">Total</td>
|
|
<td className="pt-3 font-bold text-right tabular-nums text-slate-900">{formatCurrency(booking.amount + fee)}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
{/* Status */}
|
|
<div className="flex justify-center">
|
|
<Badge variant="outline" className="px-3 py-1 font-normal text-slate-500 border-slate-200 bg-slate-50">
|
|
Status: <span className="font-semibold text-slate-900 ml-1">{booking.status}</span>
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
</ScrollArea>
|
|
|
|
<div className="p-4 border-t border-slate-100 bg-slate-50 flex gap-2 justify-end">
|
|
<Button variant="outline" size="sm" onClick={() => window.print()}>
|
|
<Printer className="mr-2 h-4 w-4" /> Print
|
|
</Button>
|
|
<Button variant="default" size="sm">
|
|
<Download className="mr-2 h-4 w-4" /> Download PDF
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|