use client
import { useState, useCallback } from 'react'
interface InvoiceLineItem {
id: string
description: string
quantity: number
unitPrice: number
}
interface InvoiceState {
lineItems: InvoiceLineItem[]
discountPercent: number
taxPercent: number
}
export default function InvoiceTable() {
const [invoice, setInvoice] = useState<InvoiceState>({
lineItems: [
{ id: '1', description: 'Professional Consulting Services', quantity: 10, unitPrice: 150 },
{ id: '2', description: 'Software Development - Frontend', quantity: 40, unitPrice: 120 },
{ id: '3', description: 'UI/UX Design Package', quantity: 20, unitPrice: 100 },
{ id: '4', description: 'QA Testing & Deployment', quantity: 15, unitPrice: 85 },
],
discountPercent: 5,
taxPercent: 10,
})
const calculateSubtotal = useCallback((quantity: number, unitPrice: number) => {
return quantity * unitPrice
}, [])
const subtotal = invoice.lineItems.reduce((sum, item) => {
return sum + calculateSubtotal(item.quantity, item.unitPrice)
}, 0)
const discountAmount = (subtotal * invoice.discountPercent) / 100
const taxableAmount = subtotal - discountAmount
const taxAmount = (taxableAmount * invoice.taxPercent) / 100
const total = taxableAmount + taxAmount
const addLineItem = useCallback(() => {
const newItem: InvoiceLineItem = {
id: Date.now().toString(),
description: 'New Line Item',
quantity: 1,
unitPrice: 0,
}
setInvoice((prev) => ({
...prev,
lineItems: [...prev.lineItems, newItem],
}))
}, [])
const removeLineItem = useCallback((id: string) => {
setInvoice((prev) => ({
...prev,
lineItems: prev.lineItems.filter((item) => item.id !== id),
}))
}, [])
const updateLineItem = useCallback(
(id: string, field: keyof InvoiceLineItem, value: string | number) => {
setInvoice((prev) => ({
...prev,
lineItems: prev.lineItems.map((item) =>
item.id === id
? {
...item,
[field]: field === 'description' ? value : Number(value),
}
: item
),
}))
},
[]
)
const updateDiscount = useCallback((value: string) => {
setInvoice((prev) => ({
...prev,
discountPercent: Number(value),
}))
}, [])
const updateTax = useCallback((value: string) => {
setInvoice((prev) => ({
...prev,
taxPercent: Number(value),
}))
}, [])
return (
<div className="min-h-screen bg-[#0A0A0A] p-8">
<div className="max-w-6xl mx-auto">
<h1 className="text-3xl font-bold text-[#F5F5F0] mb-2">Invoice</h1>
<p className="text-[#999999] mb-8">INV-2024-001</p>
<div className="bg-[#0F0F0F] rounded-lg border border-[#1A1A1A] overflow-hidden">
<table className="w-full">
<thead>
<tr className="border-b border-[#1A1A1A] bg-[#141414]">
<th className="px-6 py-4 text-left text-[#F5F5F0] font-semibold text-sm">
Description
</th>
<th className="px-6 py-4 text-right text-[#F5F5F0] font-semibold text-sm w-24">
Qty
</th>
<th className="px-6 py-4 text-right text-[#F5F5F0] font-semibold text-sm w-32">
Unit Price
</th>
<th className="px-6 py-4 text-right text-[#F5F5F0] font-semibold text-sm w-32">
Subtotal
</th>
<th className="px-6 py-4 text-center text-[#F5F5F0] font-semibold text-sm w-20">
Action
</th>
</tr>
</thead>
<tbody>
{invoice.lineItems.map((item) => (
<tr key={item.id} className="border-b border-[#1A1A1A] hover:bg-[#141414] transition">
<td className="px-6 py-4">
<input
type="text"
value={item.description}
onChange={(e) => updateLineItem(item.id, 'description', e.target.value)}
className="w-full bg-[#0A0A0A] text-[#F5F5F0] border border-[#2A2A2A] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#C9A84C]"
/>
</td>
<td className="px-6 py-4">
<input
type="number"
value={item.quantity}
onChange={(e) => updateLineItem(item.id, 'quantity', e.target.value)}
className="w-full bg-[#0A0A0A] text-[#F5F5F0] border border-[#2A2A2A] rounded px-3 py-2 text-sm text-right focus:outline-none focus:border-[#C9A84C]"
/>
</td>
<td className="px-6 py-4">
<input
type="number"
step="0.01"
value={item.unitPrice}
onChange={(e) => updateLineItem(item.id, 'unitPrice', e.target.value)}
className="w-full bg-[#0A0A0A] text-[#F5F5F0] border border-[#2A2A2A] rounded px-3 py-2 text-sm text-right focus:outline-none focus:border-[#C9A84C]"
/>
</td>
<td className="px-6 py-4 text-right text-[#F5F5F0] font-medium">
${calculateSubtotal(item.quantity, item.unitPrice).toFixed(2)}
</td>
<td className="px-6 py-4 text-center">
<button
onClick={() => removeLineItem(item.id)}
className="text-[#C9A84C] hover:text-[#E8C547] transition font-medium text-sm"
>
Remove
</button>
</td>
</tr>
))}
</tbody>
</table>
<div className="px-6 py-4 border-t border-[#1A1A1A] bg-[#0F0F0F]">
<button
onClick={addLineItem}
className="px-4 py-2 bg-[#1A1A1A] text-[#C9A84C] rounded border border-[#C9A84C] hover:bg-[#C9A84C] hover:text-[#0A0A0A] transition font-medium text-sm"
>
+ Add Line Item
</button>
</div>
<div className="px-6 py-6 bg-[#0A0A0A] border-t border-[#1A1A1A]">
<div className="flex justify-end max-w-md ml-auto space-y-4">
<div className="w-full">
<div className="flex justify-between items-center py-3 border-b border-[#1A1A1A]">
<span className="text-[#999999] text-sm">Subtotal</span>
<span className="text-[#F5F5F0] font-medium">${subtotal.toFixed(2)}</span>
</div>
<div className="flex justify-between items-center py-3 border-b border-[#1A1A1A]">
<label className="text-[#999999] text-sm flex items-center gap-2">
Discount
<input
type="number"
value={invoice.discountPercent}
onChange={(e) => updateDiscount(e.target.value)}
className="w-16 bg-[#0A0A0A] text-[#F5F5F0] border border-[#2A2A2A] rounded px-2 py-1 text-xs focus:outline-none focus:border-[#C9A84C]"
/>
%
</label>
<span className="text-[#F5F5F0] font-medium">-${discountAmount.toFixed(2)}</span>
</div>
<div className="flex justify-between items-center py-3 border-b border-[#1A1A1A]">
<label className="text-[#999999] text-sm flex items-center gap-2">
Tax
<input
type="number"
value={invoice.taxPercent}
onChange={(e) => updateTax(e.target.value)}
className="w-16 bg-[#0A0A0A] text-[#F5F5F0] border border-[#2A2A2A] rounded px-2 py-1 text-xs focus:outline-none focus:border-[#C9A84C]"
/>
%
</label>
<span className="text-[#F5F5F0] font-medium">+${taxAmount.toFixed(2)}</span>
</div>
<div className="flex justify-between items-center py-4 mt-2">
<span className="text-[#C9A84C] text-lg font-bold">Total</span>
<span className="text-[#C9A84C] text-2xl font-bold">${total.toFixed(2)}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}