First Commit

This commit is contained in:
2026-03-10 22:29:56 -04:00
commit c4977e4b5e
22 changed files with 5947 additions and 0 deletions

View File

@ -0,0 +1,115 @@
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>
);
}