116 lines
4.6 KiB
JavaScript
116 lines
4.6 KiB
JavaScript
import React from 'react';
|
|
import { Trash2 } from 'lucide-react';
|
|
|
|
export default function QuoteSummary({ items, customer, shippingCost, onShippingChange, onRemoveItem }) {
|
|
const provinceTaxRates = {
|
|
'ON': 0.13, // Ontario (HST)
|
|
'QC': 0.05, // Quebec (GST)
|
|
'NS': 0.15, // Nova Scotia (HST)
|
|
'NB': 0.15, // New Brunswick (HST)
|
|
'MB': 0.05, // Manitoba (GST)
|
|
'BC': 0.05, // British Columbia (GST)
|
|
'PE': 0.15, // Prince Edward Island (HST)
|
|
'SK': 0.05, // Saskatchewan (GST)
|
|
'AB': 0.05, // Alberta (GST)
|
|
'NL': 0.15, // Newfoundland and Labrador (HST)
|
|
'NT': 0.05, // Northwest Territories (GST)
|
|
'YT': 0.05, // Yukon (GST)
|
|
'NU': 0.05 // Nunavut (GST)
|
|
};
|
|
|
|
const getTaxRate = () => {
|
|
const province = customer.province?.toUpperCase().trim();
|
|
return provinceTaxRates[province] || 0.13; // Default to 13% if unknown
|
|
};
|
|
|
|
const calculateSubtotal = () => {
|
|
return items.reduce((total, item) => total + (item.Price * item.quantity), 0);
|
|
};
|
|
|
|
const subtotal = calculateSubtotal();
|
|
const shipping = parseFloat(shippingCost) || 0;
|
|
const taxRate = getTaxRate();
|
|
const tax = (subtotal + shipping) * taxRate;
|
|
const total = subtotal + shipping + tax;
|
|
|
|
const formatCurrency = (amount) => {
|
|
return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount);
|
|
};
|
|
|
|
if (items.length === 0) {
|
|
return (
|
|
<div className="glass-card" style={{ textAlign: 'center', color: 'var(--text-muted)' }}>
|
|
<p>No items added to the quote yet.</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="glass-card">
|
|
<h2>Quote Summary</h2>
|
|
<div className="table-container">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Item ID</th>
|
|
<th>Description</th>
|
|
<th>Unit Price</th>
|
|
<th>Quantity</th>
|
|
<th>Total</th>
|
|
<th className="no-print">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{items.map((item, index) => (
|
|
<tr key={index}>
|
|
<td>{item['Item ID']}</td>
|
|
<td>{item.Description}</td>
|
|
<td>{formatCurrency(item.Price)}</td>
|
|
<td>{item.quantity}</td>
|
|
<td>{formatCurrency(item.Price * item.quantity)}</td>
|
|
<td className="no-print">
|
|
<button
|
|
className="btn btn-icon btn-danger"
|
|
onClick={() => onRemoveItem(index)}
|
|
title="Remove item"
|
|
>
|
|
<Trash2 size={16} />
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div className="totals">
|
|
<div className="total-row">
|
|
<span>Subtotal:</span>
|
|
<span>{formatCurrency(subtotal)}</span>
|
|
</div>
|
|
<div className="total-row">
|
|
<span>Shipping:</span>
|
|
<div className="no-print" style={{ display: 'flex', alignItems: 'center' }}>
|
|
<span style={{ marginRight: '0.25rem' }}>$</span>
|
|
<input
|
|
type="number"
|
|
value={shippingCost}
|
|
onChange={(e) => onShippingChange(e.target.value)}
|
|
style={{ width: '80px', padding: '0.25rem', fontSize: '1rem', textAlign: 'right' }}
|
|
/>
|
|
</div>
|
|
<span className="print-only-block" style={{ display: 'none' }}>{formatCurrency(shipping)}</span>
|
|
</div>
|
|
<div className="total-row">
|
|
<span>Tax ({(taxRate * 100).toFixed(1)}%):</span>
|
|
<span>{formatCurrency(tax)}</span>
|
|
</div>
|
|
<div className="total-row grand-total">
|
|
<span>Total:</span>
|
|
<span>{formatCurrency(total)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|