import React, { useState, useEffect, useRef } from "react";
// Simple GST Billing React component (single-file prototype)
// - Uses Tailwind CSS classes for styling
// - Features: seller/buyer details, add items with HSN, qty, rate, discount, GST rate
// - Auto-calculates taxable value, CGST/SGST or IGST based on states
// - Save/Load invoices to localStorage, Print/Export using window.print()
// - Default export: React functional component
export default function GstBillingApp() {
const [seller, setSeller] = useState({ name: "Your Business", address: "", state: "Maharashtra", gstin: "" });
const [buyer, setBuyer] = useState({ name: "Customer", address: "", state: "Karnataka", gstin: "" });
const [items, setItems] = useState([
{ id: 1, desc: "Sample Item", hsn: "1001", qty: 1, rate: 100.0, discount: 0, gst: 18 }
]);
const [invoiceNo, setInvoiceNo] = useState(() => `INV-${Date.now().toString().slice(-6)}`);
const [date, setDate] = useState(() => new Date().toISOString().slice(0, 10));
const printRef = useRef();
// helper: compute line totals
function lineTotal(it) {
const amount = it.qty * it.rate;
const disc = (it.discount || 0) / 100 * amount;
const taxable = Math.max(0, amount - disc);
const gstAmt = taxable * (it.gst || 0) / 100;
return { amount, disc, taxable, gstAmt };
}
function isInterState() {
return seller.state && buyer.state && seller.state.trim().toLowerCase() !== buyer.state.trim().toLowerCase();
}
function invoiceTotals() {
let totalTaxable = 0, totalGst = 0;
items.forEach(it => {
const { taxable, gstAmt } = lineTotal(it);
totalTaxable += taxable;
totalGst += gstAmt;
});
return { totalTaxable, totalGst, grandTotal: totalTaxable + totalGst };
}
function updateItem(id, key, value) {
setItems(prev => prev.map(it => it.id === id ? { ...it, [key]: value } : it));
}
function addItem() {
setItems(prev => [...prev, { id: Date.now(), desc: "", hsn: "", qty: 1, rate: 0, discount: 0, gst: 18 }]);
}
function removeItem(id) {
setItems(prev => prev.filter(it => it.id !== id));
}
function saveDraft() {
const payload = { seller, buyer, items, invoiceNo, date };
localStorage.setItem(`gst-invoice-${invoiceNo}`, JSON.stringify(payload));
localStorage.setItem("gst-invoice-latest", JSON.stringify(payload));
alert("Invoice saved to localStorage (draft)");
}
function loadLatest() {
const data = localStorage.getItem("gst-invoice-latest");
if (!data) return alert("No saved invoice found.");
const p = JSON.parse(data);
setSeller(p.seller || seller);
setBuyer(p.buyer || buyer);
setItems(p.items || items);
setInvoiceNo(p.invoiceNo || invoiceNo);
setDate(p.date || date);
alert("Loaded latest saved invoice.");
}
function exportAsJSON() {
const payload = { seller, buyer, items, invoiceNo, date };
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `${invoiceNo}.json`;
a.click();
URL.revokeObjectURL(url);
}
function printInvoice() {
window.print();
}
// small UI
const totals = invoiceTotals();
return (
);
}
GST Billing — Prototype
Seller (From)
setSeller({ ...seller, name: e.target.value })} placeholder="Seller name" /> setSeller({ ...seller, address: e.target.value })} placeholder="Address" /> setSeller({ ...seller, state: e.target.value })} placeholder="State" /> setSeller({ ...seller, gstin: e.target.value })} placeholder="GSTIN" />Buyer (To)
setBuyer({ ...buyer, name: e.target.value })} placeholder="Buyer name" /> setBuyer({ ...buyer, address: e.target.value })} placeholder="Address" /> setBuyer({ ...buyer, state: e.target.value })} placeholder="State" /> setBuyer({ ...buyer, gstin: e.target.value })} placeholder="GSTIN" />
setInvoiceNo(e.target.value)} />
setDate(e.target.value)} />
{isInterState() ? Interstate → IGST : Intrastate → CGST+SGST}
| # | Description | HSN | Qty | Rate | Discount % | GST % | Taxable | Tax | Total | Action |
|---|---|---|---|---|---|---|---|---|---|---|
| {idx + 1} | updateItem(it.id, 'desc', e.target.value)} /> | updateItem(it.id, 'hsn', e.target.value)} /> | updateItem(it.id, 'qty', parseFloat(e.target.value || 0))} /> | updateItem(it.id, 'rate', parseFloat(e.target.value || 0))} /> | updateItem(it.id, 'discount', parseFloat(e.target.value || 0))} /> | {lt.taxable.toFixed(2)} | {lt.gstAmt.toFixed(2)} | {(lt.taxable + lt.gstAmt).toFixed(2)} |
Taxable Total: {totals.totalTaxable.toFixed(2)}
GST Total: {totals.totalGst.toFixed(2)}
Grand Total: {totals.grandTotal.toFixed(2)}
Comments
Post a Comment