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 |
|