diff --git a/app.js b/app.js index f999689..4bb4168 100644 --- a/app.js +++ b/app.js @@ -1,25 +1,13 @@ -// Garage Templates Configuration -const garageTemplates = { - 'rs-auto': { - name: 'RS AUTO', - phone: '775 8999', - logo: { - type: 'svg', - content: ` - - - RS AUTO - S - - P:0085037 - MALDIVES - - ` - } - } - // Add more garage templates here as needed +// Template cache +const templateCache = { + html: {}, + css: {} }; +// Current template ID +let currentTemplateId = null; +let currentBarcodeNumber = null; + // DOM Elements const form = document.getElementById('stickerForm'); const garageSelect = document.getElementById('garage'); @@ -30,26 +18,114 @@ const modelNumberInput = document.getElementById('modelNumber'); const chassisNumberInput = document.getElementById('chassisNumber'); const engineSerialInput = document.getElementById('engineSerial'); const engineCapacityInput = document.getElementById('engineCapacity'); - -// Preview Elements -const previewRegNumber = document.getElementById('previewRegNumber'); -const previewBarcodeNumber = document.getElementById('previewBarcodeNumber'); -const previewBarcode = document.getElementById('previewBarcode'); -const previewFromDate = document.getElementById('previewFromDate'); -const previewToDate = document.getElementById('previewToDate'); -const previewModelNumber = document.getElementById('previewModelNumber'); -const previewChassisNumber = document.getElementById('previewChassisNumber'); -const previewEngineSerial = document.getElementById('previewEngineSerial'); -const previewEngineCapacity = document.getElementById('previewEngineCapacity'); -const previewGarageInfo = document.getElementById('previewGarageInfo'); -const garageLogo = document.getElementById('garageLogo'); +const stickerPreview = document.getElementById('stickerPreview'); // Initialize the application -function init() { +async function init() { setDefaultDates(); + await loadTemplateList(); setupEventListeners(); - updatePreview(); - generateBarcode(); + generateBarcodeNumber(); + await loadTemplate(garageSelect.value); +} + +// Load template list from index.json +async function loadTemplateList() { + try { + const response = await fetch('templates/index.json'); + const data = await response.json(); + + garageSelect.innerHTML = ''; + data.templates.forEach(template => { + const option = document.createElement('option'); + option.value = template.id; + option.textContent = template.name; + garageSelect.appendChild(option); + }); + } catch (error) { + console.error('Failed to load template list:', error); + showSnackbar('Failed to load templates', 'error'); + } +} + +// Load a specific template (HTML + CSS) +async function loadTemplate(templateId) { + if (currentTemplateId === templateId && templateCache.html[templateId]) { + renderTemplate(templateId); + return; + } + + try { + // Load HTML if not cached + if (!templateCache.html[templateId]) { + const htmlResponse = await fetch(`templates/${templateId}.html`); + templateCache.html[templateId] = await htmlResponse.text(); + } + + // Load CSS if not cached + if (!templateCache.css[templateId]) { + const cssResponse = await fetch(`templates/${templateId}.css`); + templateCache.css[templateId] = await cssResponse.text(); + + // Inject CSS into head + const styleId = `template-style-${templateId}`; + let styleEl = document.getElementById(styleId); + if (!styleEl) { + styleEl = document.createElement('style'); + styleEl.id = styleId; + document.head.appendChild(styleEl); + } + styleEl.textContent = templateCache.css[templateId]; + } + + currentTemplateId = templateId; + renderTemplate(templateId); + } catch (error) { + console.error('Failed to load template:', error); + showSnackbar('Failed to load template', 'error'); + } +} + +// Render template with current form values +function renderTemplate(templateId) { + const html = templateCache.html[templateId]; + if (!html) return; + + // Get current values - show empty placeholders if no input + const values = { + regNumber: regNumberInput.value ? formatRegNumber(regNumberInput.value) : 'X 0 X 0 0 0 0', + barcodeNumber: currentBarcodeNumber || 'XXXXXXXXXX', + fromDate: formatDateForDisplay(fromDateInput.value) || 'XX-XX-XXXX', + toDate: formatDateForDisplay(toDateInput.value) || 'XX-XX-XXXX', + modelNumber: modelNumberInput.value || 'XXX000-000X', + chassisNumber: chassisNumberInput.value || 'XXXXXXXXXXXXXXXXX', + engineSerial: engineSerialInput.value || 'XXX-XXXXXX', + engineCapacity: engineCapacityInput.value || 'XXX.XXX' + }; + + // Replace placeholders + let rendered = html; + for (const [key, value] of Object.entries(values)) { + rendered = rendered.replace(new RegExp(`{{${key}}}`, 'g'), value); + } + + stickerPreview.innerHTML = rendered; + + // Generate barcode + const barcodeEl = stickerPreview.querySelector('.barcode'); + if (barcodeEl && currentBarcodeNumber) { + try { + JsBarcode(barcodeEl, currentBarcodeNumber, { + format: 'CODE128', + width: 1.5, + height: 25, + displayValue: false, + margin: 0 + }); + } catch (e) { + console.error('Barcode generation failed:', e); + } + } } // Set default dates (today and 1 year from today) @@ -84,12 +160,19 @@ function formatRegNumber(regNumber) { // Generate a random barcode number (10 digits) function generateBarcodeNumber() { - return Math.floor(1000000000 + Math.random() * 9000000000).toString(); + currentBarcodeNumber = Math.floor(1000000000 + Math.random() * 9000000000).toString(); } // Setup event listeners function setupEventListeners() { + // Template change + garageSelect.addEventListener('change', async () => { + await loadTemplate(garageSelect.value); + }); + // Real-time preview updates + const updatePreview = () => renderTemplate(currentTemplateId); + regNumberInput.addEventListener('input', updatePreview); fromDateInput.addEventListener('change', updatePreview); toDateInput.addEventListener('change', updatePreview); @@ -97,7 +180,6 @@ function setupEventListeners() { chassisNumberInput.addEventListener('input', updatePreview); engineSerialInput.addEventListener('input', updatePreview); engineCapacityInput.addEventListener('input', updatePreview); - garageSelect.addEventListener('change', updatePreview); // Auto-uppercase for certain fields regNumberInput.addEventListener('input', (e) => { @@ -117,53 +199,10 @@ function setupEventListeners() { const toDate = new Date(fromDate); toDate.setFullYear(toDate.getFullYear() + 1); toDateInput.value = formatDateForInput(toDate); - updatePreview(); + renderTemplate(currentTemplateId); }); } -// Update the sticker preview -function updatePreview() { - const garage = garageTemplates[garageSelect.value]; - - // Update registration number - const regNumber = regNumberInput.value || 'X0X0000'; - previewRegNumber.textContent = formatRegNumber(regNumber); - - // Update dates - previewFromDate.textContent = formatDateForDisplay(fromDateInput.value) || 'DD-MM-YYYY'; - previewToDate.textContent = formatDateForDisplay(toDateInput.value) || 'DD-MM-YYYY'; - - // Update vehicle info - previewModelNumber.textContent = modelNumberInput.value || 'XXXX-XXXXX'; - previewChassisNumber.textContent = chassisNumberInput.value || 'XXXXXXXXXXXXXXXXX'; - previewEngineSerial.textContent = engineSerialInput.value || 'XXX-XXXXXX'; - previewEngineCapacity.textContent = engineCapacityInput.value || '000.000'; - - // Update garage info - if (garage) { - previewGarageInfo.textContent = `${garage.name}, TEL: ${garage.phone}`; - garageLogo.innerHTML = garage.logo.content; - } -} - -// Generate barcode -function generateBarcode() { - const barcodeNumber = generateBarcodeNumber(); - previewBarcodeNumber.textContent = barcodeNumber; - - try { - JsBarcode(previewBarcode, barcodeNumber, { - format: 'CODE128', - width: 1.5, - height: 30, - displayValue: false, - margin: 0 - }); - } catch (e) { - console.error('Barcode generation failed:', e); - } -} - // Handle form submission async function handleFormSubmit(e) { e.preventDefault(); @@ -173,6 +212,13 @@ async function handleFormSubmit(e) { submitBtn.querySelector('.material-icons').textContent = 'autorenew'; try { + // Generate new barcode for final PDF + generateBarcodeNumber(); + renderTemplate(currentTemplateId); + + // Wait for render + await new Promise(resolve => setTimeout(resolve, 100)); + await generatePDF(); showSnackbar('Sticker generated successfully!', 'success'); } catch (error) { @@ -187,17 +233,15 @@ async function handleFormSubmit(e) { // Generate PDF async function generatePDF() { const { jsPDF } = window.jspdf; - const stickerElement = document.querySelector('.sticker'); + const stickerElement = stickerPreview.querySelector('.sticker, [class^="sticker-"]'); - // Generate a new barcode number for this sticker - generateBarcode(); - - // Wait for barcode to render - await new Promise(resolve => setTimeout(resolve, 100)); + if (!stickerElement) { + throw new Error('No sticker element found'); + } // Capture the sticker as an image const canvas = await html2canvas(stickerElement, { - scale: 3, // Higher resolution + scale: 3, backgroundColor: '#ffffff', useCORS: true, logging: false @@ -206,7 +250,6 @@ async function generatePDF() { // Create PDF with sticker dimensions const imgData = canvas.toDataURL('image/png'); - // Calculate dimensions (sticker is approximately 280px wide) const pdfWidth = 100; // mm const pdfHeight = (canvas.height / canvas.width) * pdfWidth; @@ -216,7 +259,6 @@ async function generatePDF() { format: [pdfWidth + 10, pdfHeight + 10] }); - // Center the sticker const x = 5; const y = 5; @@ -226,7 +268,6 @@ async function generatePDF() { const regNumber = regNumberInput.value || 'sticker'; const filename = `roadworthiness-${regNumber.replace(/\s/g, '')}-${Date.now()}.pdf`; - // Save the PDF pdf.save(filename); } diff --git a/index.html b/index.html index 6861862..2c1115a 100644 --- a/index.html +++ b/index.html @@ -38,7 +38,7 @@
expand_more
@@ -48,7 +48,7 @@
Letters and numbers only
@@ -69,28 +69,28 @@
+ placeholder="XXX000-000X" required>
+ placeholder="XXXXXXXXXXXXXXXXX" required>
+ placeholder="XXX-XXXXXX" required>
+ placeholder="XXX.XXX" required>
@@ -109,45 +109,7 @@
- -
-
- X 0 X 0 0 0 0 -
-
- 0000000000 - -
-
-
-
- From: DD-MM-YYYY -
-
- To: DD-MM-YYYY -
-
XXXX-XXXXX
-
XXXXXXXXXXXXXXXXX
-
XXX-XXXXXX
-
000.000
-
- -
- -
+
diff --git a/styles.css b/styles.css index fb5474e..59b1e71 100644 --- a/styles.css +++ b/styles.css @@ -289,87 +289,14 @@ body { background-position: 0 0, 0 10px, 10px -10px, -10px 0px; } -/* Sticker Preview */ +/* Sticker Preview Container */ .sticker-preview { background-color: var(--md-sys-color-surface-container); padding: 8px; box-shadow: var(--md-sys-elevation-2); } -.sticker { - width: 280px; - border: 2px solid #000; - font-family: Arial, sans-serif; - font-size: 11px; - background: #fff; - color: #000; -} - -.sticker-header { - background-color: #000; - color: #fff; - text-align: center; - padding: 6px 8px; - font-size: 18px; - font-weight: bold; - letter-spacing: 4px; -} - -.sticker-barcode-row { - display: flex; - align-items: center; - border-bottom: 1px solid #000; - padding: 4px 8px; - gap: 8px; -} - -.sticker-barcode-row span { - font-size: 11px; - font-weight: 500; -} - -.sticker-barcode-row svg { - height: 30px; - flex: 1; -} - -.sticker-body { - display: flex; - border-bottom: 1px solid #000; -} - -.sticker-info { - flex: 1; - padding: 6px 8px; - font-size: 10px; - line-height: 1.4; -} - -.sticker-info .date-row { - margin-bottom: 2px; -} - -.sticker-logo { - width: 80px; - border-left: 1px solid #000; - display: flex; - align-items: center; - justify-content: center; - padding: 4px; -} - -.garage-logo .logo-svg { - width: 70px; - height: 70px; -} - -.sticker-footer { - text-align: center; - padding: 4px 8px; - font-size: 10px; - font-weight: 500; - border-top: 1px solid #000; -} +/* Sticker styles are loaded dynamically from templates/*.css */ /* Snackbar */ .snackbar { @@ -481,10 +408,6 @@ body { .preview-container { padding: 16px; } - - .sticker { - width: 260px; - } } /* Print Styles for PDF Export */ diff --git a/templates/index.json b/templates/index.json new file mode 100644 index 0000000..29cbb36 --- /dev/null +++ b/templates/index.json @@ -0,0 +1,12 @@ +{ + "templates": [ + { + "id": "rs-auto", + "name": "RS AUTO" + }, + { + "id": "motca", + "name": "Min. of Transport & Civil Aviation" + } + ] +} diff --git a/templates/motca.css b/templates/motca.css new file mode 100644 index 0000000..3f41869 --- /dev/null +++ b/templates/motca.css @@ -0,0 +1,73 @@ +/* Min. of Transport & Civil Aviation Template Styles */ +.sticker-motca { + width: 280px; + border: 2px solid #000; + font-family: Arial, sans-serif; + font-size: 11px; + background: #fff; + color: #000; +} + +.sticker-motca .sticker-header { + background-color: #000; + color: #fff; + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 8px; + font-size: 14px; + font-weight: bold; +} + +.sticker-motca .sticker-header .reg-number { + letter-spacing: 1px; +} + +.sticker-motca .sticker-header .barcode-number { + font-size: 12px; +} + +.sticker-motca .sticker-dates-row { + display: flex; + justify-content: space-between; + padding: 4px 8px; + font-size: 10px; + border-bottom: 1px solid #000; +} + +.sticker-motca .sticker-body { + display: flex; + border-bottom: 1px solid #000; +} + +.sticker-motca .sticker-info { + flex: 1; + padding: 4px 8px; + font-size: 10px; + line-height: 1.4; +} + +.sticker-motca .sticker-info .barcode { + height: 25px; + width: 100%; + margin-top: 4px; +} + +.sticker-motca .sticker-values { + width: 80px; + padding: 4px 8px; + font-size: 10px; + font-weight: bold; + text-align: right; + display: flex; + flex-direction: column; + justify-content: flex-start; + gap: 2px; +} + +.sticker-motca .sticker-footer { + text-align: center; + padding: 4px 8px; + font-size: 9px; + font-weight: 500; +} diff --git a/templates/motca.html b/templates/motca.html new file mode 100644 index 0000000..2061695 --- /dev/null +++ b/templates/motca.html @@ -0,0 +1,23 @@ +
+
+ {{regNumber}} + {{barcodeNumber}} +
+
+ From {{fromDate}} + To {{toDate}} +
+
+
+
{{modelNumber}}
+
{{chassisNumber}}
+
{{engineSerial}}
+ +
+
+
0 sc
+
{{engineCapacity}} cc
+
+
+ +
diff --git a/templates/rs-auto.css b/templates/rs-auto.css new file mode 100644 index 0000000..973e5ed --- /dev/null +++ b/templates/rs-auto.css @@ -0,0 +1,74 @@ +/* RS AUTO Template Styles */ +.sticker-rs-auto { + width: 280px; + border: 2px solid #000; + font-family: Arial, sans-serif; + font-size: 11px; + background: #fff; + color: #000; +} + +.sticker-rs-auto .sticker-header { + background-color: #000; + color: #fff; + text-align: center; + padding: 6px 8px; + font-size: 18px; + font-weight: bold; + letter-spacing: 4px; +} + +.sticker-rs-auto .sticker-barcode-row { + display: flex; + align-items: center; + border-bottom: 1px solid #000; + padding: 4px 8px; + gap: 8px; +} + +.sticker-rs-auto .sticker-barcode-row .barcode-number { + font-size: 11px; + font-weight: 500; +} + +.sticker-rs-auto .sticker-barcode-row .barcode { + height: 30px; + flex: 1; +} + +.sticker-rs-auto .sticker-body { + display: flex; + border-bottom: 1px solid #000; +} + +.sticker-rs-auto .sticker-info { + flex: 1; + padding: 6px 8px; + font-size: 10px; + line-height: 1.4; +} + +.sticker-rs-auto .sticker-info .date-row { + margin-bottom: 2px; +} + +.sticker-rs-auto .sticker-logo { + width: 80px; + border-left: 1px solid #000; + display: flex; + align-items: center; + justify-content: center; + padding: 4px; +} + +.sticker-rs-auto .sticker-logo .logo-svg { + width: 70px; + height: 70px; +} + +.sticker-rs-auto .sticker-footer { + text-align: center; + padding: 4px 8px; + font-size: 10px; + font-weight: 500; +} diff --git a/templates/rs-auto.html b/templates/rs-auto.html new file mode 100644 index 0000000..db49ce5 --- /dev/null +++ b/templates/rs-auto.html @@ -0,0 +1,30 @@ +
+
+ {{regNumber}} +
+
+ {{barcodeNumber}} + +
+
+
+
From: {{fromDate}}
+
To: {{toDate}}
+
{{modelNumber}}
+
{{chassisNumber}}
+
{{engineSerial}}
+
{{engineCapacity}}
+
+ +
+ +