billi / index.html
ar3843's picture
Create a best bill generating web app , in which shopkeeper can save all bills and can also print them - Initial Deployment
fb134dc verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SmartBill - Invoice Generator</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Poppins', sans-serif;
}
.invoice-paper {
background: white;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.divider {
border-bottom: 1px dashed #e2e8f0;
}
.print-only {
display: none;
}
@media print {
.no-print {
display: none !important;
}
.print-only {
display: block !important;
}
body {
background: white !important;
}
.invoice-paper {
box-shadow: none !important;
margin: 0 !important;
padding: 0 !important;
}
}
</style>
</head>
<body class="bg-gray-50">
<div class="min-h-screen">
<!-- Header -->
<header class="bg-indigo-600 text-white shadow-lg no-print">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i data-feather="file-text" class="w-8 h-8"></i>
<h1 class="text-2xl font-bold">SmartBill</h1>
</div>
<nav>
<ul class="flex space-x-6">
<li><a href="#" class="hover:text-indigo-200 transition">Dashboard</a></li>
<li><a href="#bills" class="hover:text-indigo-200 transition">Bills</a></li>
<li><a href="#customers" class="hover:text-indigo-200 transition">Customers</a></li>
</ul>
</nav>
<button class="bg-white text-indigo-600 px-4 py-2 rounded-lg font-medium hover:bg-indigo-50 transition">
<i data-feather="user" class="inline mr-2"></i> Account
</button>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<div class="flex flex-col lg:flex-row gap-8">
<!-- Invoice Form -->
<div class="lg:w-2/3 no-print" data-aos="fade-right">
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
<h2 class="text-xl font-bold text-gray-800 mb-4">Create New Bill</h2>
<!-- Customer Info -->
<div class="mb-6">
<h3 class="text-lg font-semibold text-gray-700 mb-3">Customer Information</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Name</label>
<input type="text" id="customer-name" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Phone</label>
<input type="tel" id="customer-phone" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-gray-700 mb-1">Address</label>
<textarea id="customer-address" rows="2" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"></textarea>
</div>
</div>
</div>
<!-- Items Table -->
<div class="mb-6">
<div class="flex justify-between items-center mb-3">
<h3 class="text-lg font-semibold text-gray-700">Items</h3>
<button id="add-item" class="bg-indigo-100 text-indigo-600 px-3 py-1 rounded-lg text-sm font-medium hover:bg-indigo-200 transition">
<i data-feather="plus" class="w-4 h-4 inline mr-1"></i> Add Item
</button>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Item</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Quantity</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Price</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th>
</tr>
</thead>
<tbody id="items-table" class="bg-white divide-y divide-gray-200">
<!-- Items will be added here dynamically -->
</tbody>
</table>
</div>
</div>
<!-- Payment Info -->
<div class="mb-6">
<h3 class="text-lg font-semibold text-gray-700 mb-3">Payment Information</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Subtotal</label>
<input type="text" id="subtotal" readonly class="w-full px-4 py-2 bg-gray-100 border border-gray-300 rounded-lg">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Tax (%)</label>
<input type="number" id="tax" value="0" min="0" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Discount</label>
<input type="number" id="discount" value="0" min="0" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
</div>
<div class="md:col-span-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Total Amount</label>
<input type="text" id="total-amount" readonly class="w-full px-4 py-3 bg-gray-100 border border-gray-300 rounded-lg text-xl font-bold">
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex justify-end space-x-4">
<button id="clear-form" class="px-6 py-2 border border-gray-300 rounded-lg font-medium text-gray-700 hover:bg-gray-50 transition">
Clear
</button>
<button id="save-bill" class="px-6 py-2 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition">
Save Bill
</button>
<button id="generate-bill" class="px-6 py-2 bg-green-600 text-white rounded-lg font-medium hover:bg-green-700 transition">
Generate Bill
</button>
</div>
</div>
</div>
<!-- Invoice Preview -->
<div class="lg:w-1/3 no-print" data-aos="fade-left">
<div class="sticky top-4">
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
<h2 class="text-xl font-bold text-gray-800 mb-4">Bill Preview</h2>
<div id="invoice-preview" class="invoice-paper p-6">
<!-- Preview will be rendered here -->
<div class="text-center py-12 text-gray-400">
<i data-feather="file-text" class="w-12 h-12 mx-auto mb-4"></i>
<p>Your bill preview will appear here</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Saved Bills Section -->
<section id="bills" class="mt-12 no-print" data-aos="fade-up">
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800">Saved Bills</h2>
<div class="relative">
<input type="text" placeholder="Search bills..." class="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<i data-feather="search" class="absolute left-3 top-2.5 text-gray-400"></i>
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Bill No.</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Customer</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody id="saved-bills" class="bg-white divide-y divide-gray-200">
<!-- Saved bills will be loaded here -->
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">No bills saved yet</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</main>
</div>
<!-- Invoice Template (Hidden) -->
<div id="invoice-template" class="hidden">
<div class="invoice-paper p-8 max-w-3xl mx-auto">
<div class="flex justify-between items-start mb-8">
<div>
<h1 class="text-3xl font-bold text-indigo-600">SMARTBILL</h1>
<p class="text-gray-600">123 Business Street, City</p>
<p class="text-gray-600">Phone: (123) 456-7890</p>
<p class="text-gray-600">Email: info@smartbill.com</p>
</div>
<div class="text-right">
<h2 class="text-2xl font-bold text-gray-800">INVOICE</h2>
<p class="text-gray-600">#<span id="invoice-number">0001</span></p>
<p class="text-gray-600">Date: <span id="invoice-date">01 Jan 2023</span></p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
<div>
<h3 class="text-lg font-semibold text-gray-800 mb-2">Bill To:</h3>
<p class="font-medium" id="invoice-customer-name">Customer Name</p>
<p id="invoice-customer-phone">Phone: </p>
<p id="invoice-customer-address">Address: </p>
</div>
<div class="text-right md:text-left">
<h3 class="text-lg font-semibold text-gray-800 mb-2">Payment Details:</h3>
<p>Subtotal: <span id="invoice-subtotal" class="font-medium">$0.00</span></p>
<p>Tax (<span id="invoice-tax-rate">0</span>%): <span id="invoice-tax" class="font-medium">$0.00</span></p>
<p>Discount: <span id="invoice-discount" class="font-medium">$0.00</span></p>
<p class="text-xl font-bold mt-2">Total: <span id="invoice-total">$0.00</span></p>
</div>
</div>
<div class="mb-8">
<table class="min-w-full">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-2 font-medium text-gray-700">Item</th>
<th class="text-right py-2 font-medium text-gray-700">Qty</th>
<th class="text-right py-2 font-medium text-gray-700">Price</th>
<th class="text-right py-2 font-medium text-gray-700">Total</th>
</tr>
</thead>
<tbody id="invoice-items">
<!-- Items will be added here -->
</tbody>
</table>
</div>
<div class="border-t border-gray-200 pt-6">
<p class="text-gray-600 text-sm">Thank you for your business!</p>
<div class="print-only mt-8 pt-6 border-t border-gray-200">
<p class="text-sm text-gray-500">This is a computer generated invoice. No signature required.</p>
</div>
</div>
</div>
</div>
<script>
// Initialize libraries
document.addEventListener('DOMContentLoaded', function() {
AOS.init();
feather.replace();
// Initialize variables
let items = [];
let savedBills = JSON.parse(localStorage.getItem('savedBills')) || [];
let currentBillNumber = parseInt(localStorage.getItem('lastBillNumber')) || 1;
// DOM Elements
const itemsTable = document.getElementById('items-table');
const addItemBtn = document.getElementById('add-item');
const saveBillBtn = document.getElementById('save-bill');
const generateBillBtn = document.getElementById('generate-bill');
const clearFormBtn = document.getElementById('clear-form');
const subtotalInput = document.getElementById('subtotal');
const taxInput = document.getElementById('tax');
const discountInput = document.getElementById('discount');
const totalAmountInput = document.getElementById('total-amount');
const invoicePreview = document.getElementById('invoice-preview');
const savedBillsTable = document.getElementById('saved-bills');
// Add item to the bill
addItemBtn.addEventListener('click', function() {
const itemId = Date.now();
items.push({
id: itemId,
name: '',
quantity: 1,
price: 0,
total: 0
});
renderItemsTable();
updateCalculations();
});
// Render items table
function renderItemsTable() {
itemsTable.innerHTML = '';
if (items.length === 0) {
itemsTable.innerHTML = `
<tr>
<td colspan="5" class="px-4 py-4 text-center text-gray-500">No items added yet</td>
</tr>
`;
return;
}
items.forEach((item, index) => {
const row = document.createElement('tr');
row.className = index % 2 === 0 ? 'bg-white' : 'bg-gray-50';
row.innerHTML = `
<td class="px-4 py-2 whitespace-nowrap">
<input type="text" value="${item.name}" data-id="${item.id}" data-field="name" class="w-full px-2 py-1 border border-gray-300 rounded focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
</td>
<td class="px-4 py-2 whitespace-nowrap">
<input type="number" value="${item.quantity}" min="1" data-id="${item.id}" data-field="quantity" class="w-full px-2 py-1 border border-gray-300 rounded focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
</td>
<td class="px-4 py-2 whitespace-nowrap">
<input type="number" value="${item.price}" min="0" step="0.01" data-id="${item.id}" data-field="price" class="w-full px-2 py-1 border border-gray-300 rounded focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500">
</td>
<td class="px-4 py-2 whitespace-nowrap font-medium">$${item.total.toFixed(2)}</td>
<td class="px-4 py-2 whitespace-nowrap">
<button data-id="${item.id}" class="delete-item text-red-600 hover:text-red-800">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</td>
`;
itemsTable.appendChild(row);
// Add event listeners to the new inputs
const inputs = row.querySelectorAll('input');
inputs.forEach(input => {
input.addEventListener('change', function() {
const id = parseInt(this.dataset.id);
const field = this.dataset.field;
const value = field === 'name' ? this.value : parseFloat(this.value);
updateItem(id, field, value);
});
});
// Add event listener to delete button
const deleteBtn = row.querySelector('.delete-item');
deleteBtn.addEventListener('click', function() {
const id = parseInt(this.dataset.id);
deleteItem(id);
});
});
feather.replace();
}
// Update item
function updateItem(id, field, value) {
const itemIndex = items.findIndex(item => item.id === id);
if (itemIndex !== -1) {
items[itemIndex][field] = value;
// Recalculate total if quantity or price changes
if (field === 'quantity' || field === 'price') {
items[itemIndex].total = items[itemIndex].quantity * items[itemIndex].price;
}
renderItemsTable();
updateCalculations();
updateInvoicePreview();
}
}
// Delete item
function deleteItem(id) {
items = items.filter(item => item.id !== id);
renderItemsTable();
updateCalculations();
updateInvoicePreview();
}
// Calculate totals
function updateCalculations() {
const subtotal = items.reduce((sum, item) => sum + item.total, 0);
const taxRate = parseFloat(taxInput.value) || 0;
const discountAmount = parseFloat(discountInput.value) || 0;
const taxAmount = subtotal * (taxRate / 100);
const total = subtotal + taxAmount - discountAmount;
subtotalInput.value = subtotal.toFixed(2);
totalAmountInput.value = total.toFixed(2);
}
// Update invoice preview
function updateInvoicePreview() {
const customerName = document.getElementById('customer-name').value || 'Customer Name';
const customerPhone = document.getElementById('customer-phone').value || '';
const customerAddress = document.getElementById('customer-address').value || '';
const subtotal = parseFloat(subtotalInput.value) || 0;
const taxRate = parseFloat(taxInput.value) || 0;
const discountAmount = parseFloat(discountInput.value) || 0;
const taxAmount = subtotal * (taxRate / 100);
const total = subtotal + taxAmount - discountAmount;
// Format today's date
const today = new Date();
const options = { year: 'numeric', month: 'short', day: 'numeric' };
const formattedDate = today.toLocaleDateString('en-US', options);
// Generate invoice HTML
let invoiceHTML = document.getElementById('invoice-template').innerHTML;
// Replace placeholders with actual values
invoiceHTML = invoiceHTML
.replace('0001', currentBillNumber.toString().padStart(4, '0'))
.replace('01 Jan 2023', formattedDate)
.replace('Customer Name', customerName)
.replace('Phone: ', customerPhone ? `Phone: ${customerPhone}` : '')
.replace('Address: ', customerAddress ? `Address: ${customerAddress}` : '')
.replace('$0.00', `$${subtotal.toFixed(2)}`)
.replace('0', taxRate.toString())
.replace('$0.00', `$${taxAmount.toFixed(2)}`)
.replace('$0.00', `$${discountAmount.toFixed(2)}`)
.replace('$0.00', `$${total.toFixed(2)}`);
// Add items to invoice
const itemsStart = invoiceHTML.indexOf('<tbody id="invoice-items">') + 26;
const itemsEnd = invoiceHTML.indexOf('</tbody>', itemsStart);
let itemsHTML = '';
items.forEach(item => {
itemsHTML += `
<tr class="border-b border-gray-100">
<td class="py-2">${item.name || 'Item'}</td>
<td class="py-2 text-right">${item.quantity}</td>
<td class="py-2 text-right">$${item.price.toFixed(2)}</td>
<td class="py-2 text-right">$${item.total.toFixed(2)}</td>
</tr>
`;
});
invoiceHTML = invoiceHTML.slice(0, itemsStart) + itemsHTML + invoiceHTML.slice(itemsEnd);
// Update preview
invoicePreview.innerHTML = invoiceHTML;
feather.replace();
}
// Save bill
saveBillBtn.addEventListener('click', function() {
if (items.length === 0) {
alert('Please add at least one item to the bill');
return;
}
const customerName = document.getElementById('customer-name').value || 'Customer';
const subtotal = parseFloat(subtotalInput.value) || 0;
const total = parseFloat(totalAmountInput.value) || 0;
const today = new Date();
const bill = {
id: Date.now(),
number: currentBillNumber,
date: today.toISOString(),
customer: customerName,
items: [...items],
subtotal: subtotal,
tax: parseFloat(taxInput.value) || 0,
discount: parseFloat(discountInput.value) || 0,
total: total,
status: 'unpaid'
};
savedBills.unshift(bill);
localStorage.setItem('savedBills', JSON.stringify(savedBills));
currentBillNumber++;
localStorage.setItem('lastBillNumber', currentBillNumber.toString());
renderSavedBills();
alert('Bill saved successfully!');
});
// Generate/Print bill
generateBillBtn.addEventListener('click', function() {
if (items.length === 0) {
alert('Please add at least one item to the bill');
return;
}
updateInvoicePreview();
// Clone the invoice preview
const invoiceClone = invoicePreview.cloneNode(true);
invoiceClone.classList.remove('no-print');
// Open print window
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>Invoice #${currentBillNumber.toString().padStart(4, '0')}</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.invoice-paper { max-width: 800px; margin: 0 auto; background: white; padding: 20px; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
.text-right { text-align: right; }
.font-bold { font-weight: bold; }
</style>
</head>
<body>
`);
printWindow.document.write(invoiceClone.innerHTML);
printWindow.document.write('</body></html>');
printWindow.document.close();
// Auto print after a short delay
setTimeout(() => {
printWindow.print();
}, 500);
});
// Clear form
clearFormBtn.addEventListener('click', function() {
if (confirm('Are you sure you want to clear the current bill?')) {
items = [];
document.getElementById('customer-name').value = '';
document.getElementById('customer-phone').value = '';
document.getElementById('customer-address').value = '';
taxInput.value = '0';
discountInput.value = '0';
renderItemsTable();
updateCalculations();
updateInvoicePreview();
}
});
// Render saved bills
function renderSavedBills() {
savedBillsTable.innerHTML = '';
if (savedBills.length === 0) {
savedBillsTable.innerHTML = `
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">No bills saved yet</td>
</tr>
`;
return;
}
savedBills.forEach(bill => {
const date = new Date(bill.date);
const formattedDate = date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">${bill.number.toString().padStart(4, '0')}</td>
<td class="px-6 py-4 whitespace-nowrap">${formattedDate}</td>
<td class="px-6 py-4 whitespace-nowrap">${bill.customer}</td>
<td class="px-6 py-4 whitespace-nowrap">$${bill.total.toFixed(2)}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs rounded-full ${bill.status === 'paid' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'}">
${bill.status}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button data-id="${bill.id}" class="view-bill text-indigo-600 hover:text-indigo-900 mr-3">
<i data-feather="eye" class="w-4 h-4"></i>
</button>
<button data-id="${bill.id}" class="print-bill text-gray-600 hover:text-gray-900 mr-3">
<i data-feather="printer" class="w-4 h-4"></i>
</button>
<button data-id="${bill.id}" class="delete-bill text-red-600 hover:text-red-900">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</td>
`;
savedBillsTable.appendChild(row);
// Add event listeners to action buttons
const viewBtn = row.querySelector('.view-bill');
const printBtn = row.querySelector('.print-bill');
const deleteBtn = row.querySelector('.delete-bill');
viewBtn.addEventListener('click', () => viewBill(bill.id));
printBtn.addEventListener('click', () => printBill(bill.id));
deleteBtn.addEventListener('click', () => deleteSavedBill(bill.id));
});
feather.replace();
}
// View saved bill
function viewBill(id) {
const bill = savedBills.find(b => b.id === id);
if (!bill) return;
// Clear current form
items = [];
document.getElementById('customer-name').value = bill.customer;
taxInput.value = bill.tax;
discountInput.value = bill.discount;
// Add items
bill.items.forEach(item => {
items.push({...item});
});
renderItemsTable();
updateCalculations();
updateInvoicePreview();
// Scroll to form
document.querySelector('main').scrollIntoView({ behavior: 'smooth' });
}
// Print saved bill
function printBill(id) {
const bill = savedBills.find(b => b.id === id);
if (!bill) return;
// Format date
const date = new Date(bill.date);
const formattedDate = date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
// Generate invoice HTML
let invoiceHTML = document.getElementById('invoice-template').innerHTML;
// Replace placeholders with actual values
invoiceHTML = invoiceHTML
.replace('0001', bill.number.toString().padStart(4, '0'))
.replace('01 Jan 2023', formattedDate)
.replace('Customer Name', bill.customer)
.replace('$0.00', `$${bill.subtotal.toFixed(2)}`)
.replace('0', bill.tax.toString())
.replace('$0.00', `$${(bill.subtotal * (bill.tax / 100)).toFixed(2)}`)
.replace('$0.00', `$${bill.discount.toFixed(2)}`)
.replace('$0.00', `$${bill.total.toFixed(2)}`);
// Add items to invoice
const itemsStart = invoiceHTML.indexOf('<tbody id="invoice-items">') + 26;
const itemsEnd = invoiceHTML.indexOf('</tbody>', itemsStart);
let itemsHTML = '';
bill.items.forEach(item => {
itemsHTML += `
<tr class="border-b border-gray-100">
<td class="py-2">${item.name || 'Item'}</td>
<td class="py-2 text-right">${item.quantity}</td>
<td class="py-2 text-right">$${item.price.toFixed(2)}</td>
<td class="py-2 text-right">$${item.total.toFixed(2)}</td>
</tr>
`;
});
invoiceHTML = invoiceHTML.slice(0, itemsStart) + itemsHTML + invoiceHTML.slice(itemsEnd);
// Open print window
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>Invoice #${bill.number.toString().padStart(4, '0')}</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.invoice-paper { max-width: 800px; margin: 0 auto; background: white; padding: 20px; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
.text-right { text-align: right; }
.font-bold { font-weight: bold; }
</style>
</head>
<body>
${invoiceHTML}
</body>
</html>
`);
printWindow.document.close();
// Auto print after a short delay
setTimeout(() => {
printWindow.print();
}, 500);
}
// Delete saved bill
function deleteSavedBill(id) {
if (confirm('Are you sure you want to delete this bill?')) {
savedBills = savedBills.filter(bill => bill.id !== id);
localStorage.setItem('savedBills', JSON.stringify(savedBills));
renderSavedBills();
}
}
// Event listeners for tax and discount inputs
taxInput.addEventListener('change', updateCalculations);
discountInput.addEventListener('change', updateCalculations);
// Initial render
renderItemsTable();
renderSavedBills();
// Update invoice preview when customer info changes
const customerInputs = ['customer-name', 'customer-phone', 'customer-address'];
customerInputs.forEach(id => {
document.getElementById(id).addEventListener('input', updateInvoicePreview);
});
});
</script>
</body>
</html>