Spaces:
Sleeping
Sleeping
Commit
·
99078a2
1
Parent(s):
6d70811
add: preview page
Browse files- index.html +86 -78
- script.js +70 -45
index.html
CHANGED
|
@@ -9,84 +9,92 @@
|
|
| 9 |
</head>
|
| 10 |
|
| 11 |
<body class="bg-gray-100">
|
| 12 |
-
<h1 class="text-3xl font-bold text-center my-8">Quotation Generator</h1>
|
| 13 |
-
<div
|
| 14 |
-
<
|
| 15 |
-
<
|
| 16 |
-
<
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
<
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
<
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
<
|
| 50 |
-
|
| 51 |
-
<
|
| 52 |
-
<
|
| 53 |
-
<
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
<
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
<
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
</div>
|
| 91 |
<div id="quotation-output" style="display:none;"></div>
|
| 92 |
<script src="script.js"></script>
|
|
|
|
| 9 |
</head>
|
| 10 |
|
| 11 |
<body class="bg-gray-100">
|
| 12 |
+
<h1 class="text-3xl font-bold text-center my-8" id="top-header">Quotation Generator</h1>
|
| 13 |
+
<div class="flex container mx-auto">
|
| 14 |
+
<div id="form-container" class="w-1/2 pr-4">
|
| 15 |
+
<form id="quotation-form" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
|
| 16 |
+
<fieldset class="border border-gray-300 p-4 mb-4">
|
| 17 |
+
<legend class="text-lg font-semibold mb-2">Your Company Details</legend>
|
| 18 |
+
<input type="text" id="company-name" name="company-name" placeholder="Company Name" required
|
| 19 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 20 |
+
<textarea id="company-address" name="company-address" placeholder="Address" required
|
| 21 |
+
class="w-full p-2 border border-gray-300 rounded mb-2"></textarea>
|
| 22 |
+
<input type="text" id="company-phone" name="company-phone" placeholder="Phone"
|
| 23 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 24 |
+
<input type="email" id="company-email" name="company-email" placeholder="Email"
|
| 25 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 26 |
+
<input type="text" id="company-gstin" name="company-gstin" placeholder="GSTIN"
|
| 27 |
+
class="w-full p-2 border border-gray-300 rounded">
|
| 28 |
+
</fieldset>
|
| 29 |
+
<fieldset class="border border-gray-300 p-4 mb-4">
|
| 30 |
+
<legend class="text-lg font-semibold mb-2">Customer Company Details</legend>
|
| 31 |
+
<input type="text" id="customer-name" name="customer-name" placeholder="Customer Name" required
|
| 32 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 33 |
+
<textarea id="customer-address" name="customer-address" placeholder="Address" required
|
| 34 |
+
class="w-full p-2 border border-gray-300 rounded mb-2"></textarea>
|
| 35 |
+
<input type="text" id="customer-phone" name="customer-phone" placeholder="Phone"
|
| 36 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 37 |
+
<input type="email" id="customer-email" name="customer-email" placeholder="Email"
|
| 38 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 39 |
+
<input type="text" id="customer-gstin" name="customer-gstin" placeholder="GSTIN"
|
| 40 |
+
class="w-full p-2 border border-gray-300 rounded">
|
| 41 |
+
</fieldset>
|
| 42 |
+
<fieldset class="border border-gray-300 p-4 mb-4">
|
| 43 |
+
<legend class="text-lg font-semibold mb-2">Quotation Details</legend>
|
| 44 |
+
<input type="text" id="quotation-number" name="quotation-number" placeholder="Quotation Number" required
|
| 45 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 46 |
+
<input type="date" id="quotation-date" name="quotation-date" required
|
| 47 |
+
class="w-full p-2 border border-gray-300 rounded">
|
| 48 |
+
</fieldset>
|
| 49 |
+
<fieldset class="border border-gray-300 p-4 mb-4">
|
| 50 |
+
<legend class="text-lg font-semibold mb-2">Items</legend>
|
| 51 |
+
<table id="items-table" class="w-full mb-2">
|
| 52 |
+
<thead>
|
| 53 |
+
<tr class="bg-gray-200">
|
| 54 |
+
<th class="p-2 border border-gray-300">S.No</th>
|
| 55 |
+
<th class="p-2 border border-gray-300">Description</th>
|
| 56 |
+
<th class="p-2 border border-gray-300">HSN Code</th>
|
| 57 |
+
<th class="p-2 border border-gray-300">Qty</th>
|
| 58 |
+
<th class="p-2 border border-gray-300">Unit Price</th>
|
| 59 |
+
<th class="p-2 border border-gray-300">Discount (%)</th>
|
| 60 |
+
<th class="p-2 border border-gray-300">Amount</th>
|
| 61 |
+
<th class="p-2 border border-gray-300"></th>
|
| 62 |
+
</tr>
|
| 63 |
+
</thead>
|
| 64 |
+
<tbody>
|
| 65 |
+
</tbody>
|
| 66 |
+
</table>
|
| 67 |
+
<button type="button" id="add-item"
|
| 68 |
+
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Add Item</button>
|
| 69 |
+
</fieldset>
|
| 70 |
+
<fieldset class="border border-gray-300 p-4 mb-4">
|
| 71 |
+
<legend class="text-lg font-semibold mb-2">Additional Charges</legend>
|
| 72 |
+
<label class="block mb-2">IGST (%)<input type="number" id="igst-rate" name="igst-rate" value="0" min="0"
|
| 73 |
+
class="w-full p-2 border border-gray-300 rounded"></label>
|
| 74 |
+
<label class="block">Freight Charges<input type="number" id="freight-charges" name="freight-charges" value="0"
|
| 75 |
+
min="0" class="w-full p-2 border border-gray-300 rounded"></label>
|
| 76 |
+
</fieldset>
|
| 77 |
+
<fieldset class="border border-gray-300 p-4 mb-4">
|
| 78 |
+
<legend class="text-lg font-semibold mb-2">Bank Details</legend>
|
| 79 |
+
<input type="text" id="bank-name" name="bank-name" placeholder="Bank Name" required
|
| 80 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 81 |
+
<input type="text" id="bank-account" name="bank-account" placeholder="Account Number" required
|
| 82 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 83 |
+
<input type="text" id="bank-ifsc" name="bank-ifsc" placeholder="IFSC Code" required
|
| 84 |
+
class="w-full p-2 border border-gray-300 rounded mb-2">
|
| 85 |
+
<input type="text" id="bank-branch" name="bank-branch" placeholder="Branch" required
|
| 86 |
+
class="w-full p-2 border border-gray-300 rounded">
|
| 87 |
+
</fieldset>
|
| 88 |
+
<button type="submit" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">Generate
|
| 89 |
+
Quotation</button>
|
| 90 |
+
</form>
|
| 91 |
+
</div>
|
| 92 |
+
<div id="preview-container" class="w-1/2 pl-4">
|
| 93 |
+
<div id="quotation-preview" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
|
| 94 |
+
<h2 class="text-2xl font-bold text-center mb-4">Quotation Preview</h2>
|
| 95 |
+
<div id="preview-content"></div>
|
| 96 |
+
</div>
|
| 97 |
+
</div>
|
| 98 |
</div>
|
| 99 |
<div id="quotation-output" style="display:none;"></div>
|
| 100 |
<script src="script.js"></script>
|
script.js
CHANGED
|
@@ -26,49 +26,9 @@ if (typeof document !== 'undefined') {
|
|
| 26 |
const itemsTableBody = document.querySelector('#items-table tbody');
|
| 27 |
const form = document.getElementById('quotation-form');
|
| 28 |
const output = document.getElementById('quotation-output');
|
|
|
|
| 29 |
|
| 30 |
-
function
|
| 31 |
-
itemsTableBody.querySelectorAll('tr').forEach((row, i) => {
|
| 32 |
-
row.querySelector('.item-slno').textContent = i + 1;
|
| 33 |
-
});
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
function addItemRow() {
|
| 37 |
-
const row = document.createElement('tr');
|
| 38 |
-
row.innerHTML = `
|
| 39 |
-
<td class="item-slno" data-label="S.No"></td>
|
| 40 |
-
<td data-label="Description"><input type="text" class="item-desc" placeholder="Item Description" required></td>
|
| 41 |
-
<td data-label="HSN Code"><input type="text" class="item-hsn" placeholder="HSN Code"></td>
|
| 42 |
-
<td data-label="Qty"><input type="number" class="item-qty" value="1" min="0" required></td>
|
| 43 |
-
<td data-label="Unit Price"><input type="number" class="item-price" value="0" min="0" required></td>
|
| 44 |
-
<td data-label="Discount (%)"><input type="number" class="item-discount" value="0" min="0" max="100" step="0.01" placeholder="Discount %"></td>
|
| 45 |
-
<td class="item-amount" data-label="Amount">0.00</td>
|
| 46 |
-
<td data-label="Action"><button type="button" class="remove-item">Remove</button></td>
|
| 47 |
-
`;
|
| 48 |
-
itemsTableBody.appendChild(row);
|
| 49 |
-
updateSerialNumbers();
|
| 50 |
-
const inputs = row.querySelectorAll('input');
|
| 51 |
-
inputs.forEach(input => input.addEventListener('input', updateItemAmount));
|
| 52 |
-
row.querySelector('.remove-item').addEventListener('click', () => {
|
| 53 |
-
row.remove();
|
| 54 |
-
updateSerialNumbers();
|
| 55 |
-
});
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
function updateItemAmount(e) {
|
| 59 |
-
const row = e.target.closest('tr');
|
| 60 |
-
const qty = parseFloat(row.querySelector('.item-qty').value) || 0;
|
| 61 |
-
const price = parseFloat(row.querySelector('.item-price').value) || 0;
|
| 62 |
-
const discountRate = parseFloat(row.querySelector('.item-discount').value) || 0;
|
| 63 |
-
const discountAmount = qty * price * discountRate / 100;
|
| 64 |
-
const amount = qty * price - discountAmount;
|
| 65 |
-
row.querySelector('.item-amount').textContent = amount.toFixed(2);
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
addItemBtn.addEventListener('click', addItemRow);
|
| 69 |
-
|
| 70 |
-
form.addEventListener('submit', function (e) {
|
| 71 |
-
e.preventDefault();
|
| 72 |
const data = new FormData(form);
|
| 73 |
const company = {
|
| 74 |
name: data.get('company-name'),
|
|
@@ -117,7 +77,7 @@ if (typeof document !== 'undefined') {
|
|
| 117 |
<div class="quotation-print">
|
| 118 |
<div class="header">
|
| 119 |
<div class="company-details">
|
| 120 |
-
<h1>${company.name}</h1
|
| 121 |
{{address}}<br>
|
| 122 |
GST NO. : ${company.gstin || ''}<br>
|
| 123 |
CONTACT NO : ${company.phone} ${company.email}
|
|
@@ -230,12 +190,77 @@ if (typeof document !== 'undefined') {
|
|
| 230 |
</div>
|
| 231 |
`;
|
| 232 |
html = html.replace('{{address}}', company.address.replace(/\n/g, '<br>'));
|
| 233 |
-
html = html.replace('{{cutomer_address}}',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
output.innerHTML = html;
|
| 235 |
output.style.display = 'block';
|
| 236 |
document.getElementById('form-container').style.display = 'none';
|
| 237 |
-
|
|
|
|
| 238 |
});
|
|
|
|
|
|
|
|
|
|
| 239 |
});
|
| 240 |
}
|
| 241 |
|
|
|
|
| 26 |
const itemsTableBody = document.querySelector('#items-table tbody');
|
| 27 |
const form = document.getElementById('quotation-form');
|
| 28 |
const output = document.getElementById('quotation-output');
|
| 29 |
+
const previewContent = document.getElementById('preview-content');
|
| 30 |
|
| 31 |
+
function generateQuotationHTML(form) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
const data = new FormData(form);
|
| 33 |
const company = {
|
| 34 |
name: data.get('company-name'),
|
|
|
|
| 77 |
<div class="quotation-print">
|
| 78 |
<div class="header">
|
| 79 |
<div class="company-details">
|
| 80 |
+
<h1>${company.name}</h1>
|
| 81 |
{{address}}<br>
|
| 82 |
GST NO. : ${company.gstin || ''}<br>
|
| 83 |
CONTACT NO : ${company.phone} ${company.email}
|
|
|
|
| 190 |
</div>
|
| 191 |
`;
|
| 192 |
html = html.replace('{{address}}', company.address.replace(/\n/g, '<br>'));
|
| 193 |
+
html = html.replace('{{cutomer_address}}', customer.address.replace(/\n/g, '<br>'));
|
| 194 |
+
return html;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
function updatePreview() {
|
| 198 |
+
const html = generateQuotationHTML(form);
|
| 199 |
+
previewContent.innerHTML = html;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
function updateSerialNumbers() {
|
| 203 |
+
itemsTableBody.querySelectorAll('tr').forEach((row, i) => {
|
| 204 |
+
row.querySelector('.item-slno').textContent = i + 1;
|
| 205 |
+
});
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
function addItemRow() {
|
| 209 |
+
const row = document.createElement('tr');
|
| 210 |
+
row.innerHTML = `
|
| 211 |
+
<td class="item-slno" data-label="S.No"></td>
|
| 212 |
+
<td data-label="Description"><input type="text" class="item-desc" placeholder="Item Description" required></td>
|
| 213 |
+
<td data-label="HSN Code"><input type="text" class="item-hsn" placeholder="HSN Code"></td>
|
| 214 |
+
<td data-label="Qty"><input type="number" class="item-qty" value="1" min="0" required></td>
|
| 215 |
+
<td data-label="Unit Price"><input type="number" class="item-price" value="0" min="0" required></td>
|
| 216 |
+
<td data-label="Discount (%)"><input type="number" class="item-discount" value="0" min="0" max="100" step="0.01" placeholder="Discount %"></td>
|
| 217 |
+
<td class="item-amount" data-label="Amount">0.00</td>
|
| 218 |
+
<td data-label="Action"><button type="button" class="remove-item">Remove</button></td>
|
| 219 |
+
`;
|
| 220 |
+
itemsTableBody.appendChild(row);
|
| 221 |
+
updateSerialNumbers();
|
| 222 |
+
const inputs = row.querySelectorAll('input');
|
| 223 |
+
inputs.forEach(input => input.addEventListener('input', () => {
|
| 224 |
+
updateItemAmount(event);
|
| 225 |
+
updatePreview();
|
| 226 |
+
}));
|
| 227 |
+
row.querySelector('.remove-item').addEventListener('click', () => {
|
| 228 |
+
row.remove();
|
| 229 |
+
updateSerialNumbers();
|
| 230 |
+
updatePreview();
|
| 231 |
+
});
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
function updateItemAmount(e) {
|
| 235 |
+
const row = e.target.closest('tr');
|
| 236 |
+
if (!row) return;
|
| 237 |
+
const qty = parseFloat(row.querySelector('.item-qty').value) || 0;
|
| 238 |
+
const price = parseFloat(row.querySelector('.item-price').value) || 0;
|
| 239 |
+
const discountRate = parseFloat(row.querySelector('.item-discount').value) || 0;
|
| 240 |
+
const discountAmount = qty * price * discountRate / 100;
|
| 241 |
+
const amount = qty * price - discountAmount;
|
| 242 |
+
row.querySelector('.item-amount').textContent = amount.toFixed(2);
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
addItemBtn.addEventListener('click', () => {
|
| 246 |
+
addItemRow();
|
| 247 |
+
updatePreview();
|
| 248 |
+
});
|
| 249 |
+
|
| 250 |
+
form.addEventListener('input', updatePreview);
|
| 251 |
+
|
| 252 |
+
form.addEventListener('submit', function (e) {
|
| 253 |
+
e.preventDefault();
|
| 254 |
+
const html = generateQuotationHTML(form);
|
| 255 |
output.innerHTML = html;
|
| 256 |
output.style.display = 'block';
|
| 257 |
document.getElementById('form-container').style.display = 'none';
|
| 258 |
+
document.getElementById('preview-container').style.display = 'none';
|
| 259 |
+
document.getElementById('top-header').style.display = 'none';
|
| 260 |
});
|
| 261 |
+
|
| 262 |
+
// Initial preview
|
| 263 |
+
updatePreview();
|
| 264 |
});
|
| 265 |
}
|
| 266 |
|