-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor and Modernize Invoice and Receipt Components
- Refactored InvoiceDetail component to improve readability and performance. - Added memoization for company logo in InvoiceDetail component. - Extracted fetch logic into a separate function in InvoiceDetail component. - Created a new modern Receipt component with a clean and professional design. - Updated InvoiceDetailClient component to include QR code generation and improved styling. - Updated InvoiceDetailPage to handle fetching invoice details and displaying loading/error states. Co-Authored-By: 0xHieu <[email protected]> Co-Authored-By: H0llyW00dzZ <[email protected]>
- Loading branch information
1 parent
ac18033
commit abebde4
Showing
1 changed file
with
78 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,98 @@ | ||
import React,{useState,useEffect} from "react"; | ||
import { Button } from "@/components/ui/button"; // Assuming you have a Button component from shadcn | ||
import React from "react"; | ||
import { QRCodeSVG } from "qrcode.react"; | ||
import { cn } from "@/lib/utils"; | ||
|
||
import getCompanyCurrencyFromLocalStorage from "@/components/tokens/company"; | ||
|
||
export type ReceiptItem = { | ||
id: string; | ||
productName: string; | ||
quantity: number; | ||
price: number; | ||
total: number; | ||
productName: string; | ||
}; | ||
|
||
export type ReceiptProps = { | ||
receiptId: string; | ||
creationDate: string; | ||
items: ReceiptItem[]; | ||
totalAmount: number; | ||
onPrint: () => void; | ||
onCancel: () => void; | ||
company: { | ||
name: string; | ||
address: string; | ||
email: string; | ||
phone: string; | ||
website: string; | ||
logo: string; | ||
}; | ||
}; | ||
|
||
export default function Receipt({ items, totalAmount, onPrint, onCancel }: ReceiptProps) { | ||
const Receipt: React.FC<ReceiptProps> = ({ receiptId, creationDate, items, totalAmount, company }) => { | ||
return ( | ||
<div className={cn("print-area max-w-xs mx-auto p-4 bg-white shadow-md rounded-lg font-mono")}> | ||
{/* Header Section */} | ||
<div className="flex justify-between mb-4"> | ||
<div className="text-left"> | ||
<h1 className="text-3xl font-bold text-gray-800">RECEIPT</h1> | ||
<p className="text-sm text-gray-500">Date: {new Date(creationDate).toLocaleDateString()}</p> | ||
</div> | ||
<div className="text-right"> | ||
{company.logo && ( | ||
<img | ||
src={company.logo} | ||
alt={`${company.name} logo`} | ||
className="w-16 h-16 object-contain mb-2 mx-auto" | ||
/> | ||
)} | ||
<p className="font-bold">{company.name}</p> | ||
<p>{company.address}</p> | ||
<p>{company.phone}</p> | ||
<p>{company.email}</p> | ||
</div> | ||
</div> | ||
|
||
const [currency, setCurrency] = useState<string>(""); // To store the currency | ||
useEffect(() => { | ||
const fetchedCurrency = getCompanyCurrencyFromLocalStorage(); | ||
setCurrency(fetchedCurrency || "DH"); // Default to "DH" if no currency is found | ||
}, []); | ||
{/* Items Section */} | ||
<table className="w-full text-left text-sm"> | ||
<thead> | ||
<tr> | ||
<th className="py-1">Item</th> | ||
<th className="py-1 text-right">Qty</th> | ||
<th className="py-1 text-right">Price</th> | ||
<th className="py-1 text-right">Subtotal</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{items.map((item) => ( | ||
<tr key={item.id}> | ||
<td className="py-1">{item.productName}</td> | ||
<td className="py-1 text-right">{item.quantity}</td> | ||
<td className="py-1 text-right">{item.price.toFixed(2)}</td> | ||
<td className="py-1 text-right">{(item.quantity * item.price).toFixed(2)}</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
|
||
return ( | ||
<div className={cn("w-full caption-bottom text-sm border p-4 rounded-md shadow-lg")}> | ||
<h1 className="text-center text-lg font-bold mb-4">CASH RECEIPT</h1> | ||
<ul className="divide-y divide-gray-200 mb-4"> | ||
{items.map((item) => ( | ||
<li key={item.id} className="flex justify-between py-2"> | ||
<div> | ||
<p className="text-sm font-semibold">{item.productName}</p> | ||
<p className="text-xs text-gray-500"> | ||
Qty: {item.quantity} x {item.price.toFixed(2)} {currency} | ||
</p> | ||
</div> | ||
<p className="text-sm font-semibold">{item.total.toFixed(2)} {currency}</p> | ||
</li> | ||
))} | ||
</ul> | ||
<div className="border-t pt-2 mb-4"> | ||
<p className="flex justify-between text-lg font-semibold"> | ||
Total: <span>{totalAmount.toFixed(2)} {currency}</span> | ||
</p> | ||
{/* Total Section */} | ||
<div className="flex justify-between text-lg font-bold mt-4"> | ||
<span>TOTAL</span> | ||
<span>{totalAmount.toFixed(2)}</span> | ||
</div> | ||
<div className="flex justify-between"> | ||
<Button onClick={onCancel} variant="destructive" className="mr-2"> | ||
Cancel Invoice | ||
</Button> | ||
<Button onClick={onPrint} variant="outline" className="mr-2"> | ||
Print Invoice | ||
</Button> | ||
|
||
{/* QR Code and Footer Section */} | ||
<div className="flex justify-between items-center my-4"> | ||
<div className="text-center ml-4"> | ||
<div className="border-t border-dashed border-gray-300 my-4"></div> | ||
<p>***THANK YOU***</p> | ||
<div className="text-xs text-gray-500 mt-2"> | ||
<p>Thank you for supporting local business!</p> | ||
<p>{company.name} | Ph: {company.phone} | Email: {company.email}</p> | ||
</div> | ||
</div> | ||
<div className="flex-shrink-0"> | ||
<QRCodeSVG value={receiptId} size={128} /> | ||
</div> | ||
</div> | ||
<p className="text-center text-xs text-gray-500 mt-4">THANK YOU!!!</p> | ||
</div> | ||
); | ||
} | ||
}; | ||
|
||
export default Receipt; |