Spaces:
Sleeping
Sleeping
| /** | |
| * Aadhaar Generation Page Logic - Aadhaar Pro | |
| */ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| initGeneratePage(); | |
| }); | |
| function initGeneratePage() { | |
| // Initialize Premium Date Picker | |
| if (typeof flatpickr !== 'undefined') { | |
| flatpickr(".date-picker", { | |
| dateFormat: "d/m/Y", | |
| allowInput: true | |
| }); | |
| } | |
| // Initialize background image loading | |
| if (typeof loadBackgroundImage === 'function') { | |
| loadBackgroundImage(); | |
| } | |
| // Setup Form Listeners | |
| setupTransliteration(); | |
| setupPincodeAutoFill(); | |
| setupAadhaarFormatting(); | |
| setupPhotoUpload(); | |
| setupRealTimePreview(); | |
| // Print Logic | |
| const printBtn = document.getElementById('btn-print-aadhaar'); | |
| if (printBtn) { | |
| printBtn.addEventListener('click', handlePrint); | |
| } | |
| } | |
| /** | |
| * Handle Aadhaar Printing and Wallet Deduction | |
| */ | |
| async function handlePrint(e) { | |
| e.preventDefault(); | |
| // 1. Validation | |
| const requiredFields = [ | |
| { id: 'in-aadhaar', name: 'Aadhaar Number' }, | |
| { id: 'in-name-en', name: 'Name (English)' }, | |
| { id: 'in-father-en', name: 'Father\'s Name (English)' }, | |
| { id: 'in-dob', name: 'Date of Birth' }, | |
| { id: 'in-village-en', name: 'Village (English)' }, | |
| { id: 'in-post-en', name: 'Post Office (English)' }, | |
| { id: 'in-pin', name: 'Pincode' }, | |
| { id: 'in-district-en', name: 'District (English)' }, | |
| { id: 'in-state-en', name: 'State (English)' }, | |
| { id: 'in-issue-date', name: 'Issued Date' }, | |
| { id: 'in-download-date', name: 'Download Date' } | |
| ]; | |
| const outPhotoSrc = document.getElementById('out-photo')?.getAttribute('src'); | |
| if (!outPhotoSrc || outPhotoSrc.trim() === '') { | |
| alert("Kripya Photo Proof upload karein."); | |
| document.getElementById('in-photo')?.focus(); | |
| return; | |
| } | |
| for (let field of requiredFields) { | |
| let el = document.getElementById(field.id); | |
| if (el && !el.value.trim()) { | |
| alert("Kripya '" + field.name + "' fill karein."); | |
| el.focus(); | |
| return; | |
| } | |
| } | |
| const aadhaarNo = document.getElementById('in-aadhaar').value.replace(/\s/g, ''); | |
| if (aadhaarNo.length !== 12) { | |
| alert("Please enter a valid 12-digit Aadhaar number."); | |
| document.getElementById('in-aadhaar').focus(); | |
| return; | |
| } | |
| // 2. Check Wallet Balance (skip in dev/admin mode) | |
| try { | |
| const checkRes = await fetch('/api/user/info'); | |
| const balanceData = await checkRes.json(); | |
| // Only block if balance is low AND user is not admin | |
| if (balanceData.balance < 10 && balanceData.role !== 'admin') { | |
| alert("Insufficient balance! Please add funds to your wallet."); | |
| window.location.href = '/wallet'; | |
| return; | |
| } | |
| // 3. Trigger PDF Generation — function name is generateAadhaarPDF in pdf-generator.js | |
| if (typeof generateAadhaarPDF === 'function') { | |
| console.log("Generating Aadhaar PDF..."); | |
| generateAadhaarPDF(); | |
| // Deduct balance after triggering (admin/dev mode skips deduction) | |
| if (balanceData.role !== 'admin' && balanceData.balance >= 10) { | |
| setTimeout(async () => { | |
| await fetch('/api/wallet/deduct', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ amount: 10, service: 'Aadhaar Print' }) | |
| }); | |
| if (typeof updateWalletPill === 'function') updateWalletPill(); | |
| }, 2000); | |
| } | |
| } else { | |
| console.error("generateAadhaarPDF function not found! Check pdf-generator.js is loaded."); | |
| alert("PDF generator load nahi hua. Page refresh karo."); | |
| } | |
| } catch (err) { | |
| console.error("Print Error:", err); | |
| alert("An error occurred while processing your request."); | |
| } | |
| } | |
| /** | |
| * Pincode Auto-fill Logic | |
| */ | |
| function setupPincodeAutoFill() { | |
| let pincodeData = {}; | |
| fetch('/static/data/pincodes_hindi.json') | |
| .then(res => res.json()) | |
| .then(data => { pincodeData = data; }) | |
| .catch(err => console.error("Error loading pincode data:", err)); | |
| const pinInput = document.getElementById("in-pin"); | |
| if (pinInput) { | |
| pinInput.addEventListener("input", function () { | |
| let pin = this.value.trim(); | |
| if (pin.length === 6 && pincodeData[pin]) { | |
| let res = pincodeData[pin]; | |
| document.getElementById("in-district-en").value = res.d || ""; | |
| document.getElementById("in-state-en").value = res.s || ""; | |
| if (document.getElementById("in-district-hi")) document.getElementById("in-district-hi").value = res.dh || ""; | |
| if (document.getElementById("in-state-hi")) document.getElementById("in-state-hi").value = res.sh || ""; | |
| if (typeof updateAddress === 'function') updateAddress(); | |
| } | |
| }); | |
| } | |
| } | |
| /** | |
| * Transliteration (English to Hindi) via Google API | |
| */ | |
| function setupTransliteration() { | |
| const mappings = [ | |
| { en: 'in-name-en', hi: 'in-name-hi' }, | |
| { en: 'in-father-en', hi: 'in-father-hi' }, | |
| { en: 'in-house-en', hi: 'in-house-hi' }, | |
| { en: 'in-village-en', hi: 'in-village-hi' }, | |
| { en: 'in-post-en', hi: 'in-post-hi' }, | |
| { en: 'in-district-en', hi: 'in-district-hi' }, | |
| { en: 'in-state-en', hi: 'in-state-hi' } | |
| ]; | |
| mappings.forEach(m => { | |
| const enEl = document.getElementById(m.en); | |
| const hiEl = document.getElementById(m.hi); | |
| if (enEl && hiEl) { | |
| enEl.addEventListener('blur', async () => { | |
| const text = enEl.value.trim(); | |
| if (!text) return; | |
| try { | |
| const url = `https://inputtools.google.com/request?text=${encodeURIComponent(text)}&itc=hi-t-i0-und&num=1&cp=0&cs=1&ie=utf-8&oe=utf-8&app=demopage`; | |
| const res = await fetch(url); | |
| const data = await res.json(); | |
| if (data[0] === 'SUCCESS' && data[1]?.[0]?.[1]?.[0]) { | |
| hiEl.value = data[1][0][1][0]; | |
| hiEl.dispatchEvent(new Event('input')); | |
| if (typeof updateAddress === 'function') updateAddress(); | |
| } | |
| } catch (e) { console.error("Transliteration fail:", e); } | |
| }); | |
| } | |
| }); | |
| } | |
| function setupAadhaarFormatting() { | |
| const input = document.getElementById('in-aadhaar'); | |
| if (input) { | |
| input.addEventListener('input', (e) => { | |
| let val = e.target.value.replace(/\D/g, ''); | |
| if (val.length > 12) val = val.slice(0, 12); | |
| e.target.value = val.replace(/(\d{4})(?=\d)/g, '$1 '); | |
| }); | |
| } | |
| } | |
| function setupPhotoUpload() { | |
| const input = document.getElementById('in-photo'); | |
| const preview = document.getElementById('form-photo-preview'); | |
| const placeholder = document.getElementById('photo-placeholder'); | |
| const outPhoto = document.getElementById('out-photo'); | |
| if (input) { | |
| input.addEventListener('change', (e) => { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = (re) => { | |
| if (preview) { | |
| preview.src = re.target.result; | |
| preview.style.display = 'block'; | |
| } | |
| if (placeholder) placeholder.style.display = 'none'; | |
| if (outPhoto) outPhoto.src = re.target.result; | |
| }; | |
| reader.readAsDataURL(file); | |
| } else { | |
| if (preview) { preview.src = ''; preview.style.display = 'none'; } | |
| if (placeholder) placeholder.style.display = 'block'; | |
| if (outPhoto) outPhoto.src = ''; | |
| } | |
| }); | |
| } | |
| } | |
| function setupRealTimePreview() { | |
| const inputs = document.querySelectorAll('.input-control, input[type="radio"]'); | |
| inputs.forEach(input => { | |
| input.addEventListener('input', () => { | |
| // Apply title case to English fields | |
| if (input.id.endsWith('-en') && input.id !== 'in-aadhaar') { | |
| input.value = toTitleCase(input.value); | |
| } | |
| updateAddress(); | |
| }); | |
| }); | |
| // Initial sync | |
| updateAddress(); | |
| generateInrolmentNo(); | |
| } | |
| /** | |
| * Main Sync Function: Updates the hidden preview area with form data | |
| */ | |
| function updateAddress() { | |
| // 1. Sync Basic Fields | |
| const fields = [ | |
| { from: 'in-name-en', to: 'out-name-en' }, | |
| { from: 'in-name-hi', to: 'out-name-hi' }, | |
| { from: 'in-dob', to: 'out-dob' } | |
| ]; | |
| fields.forEach(f => { | |
| const fromEl = document.getElementById(f.from); | |
| const toEl = document.getElementById(f.to); | |
| if (fromEl && toEl) toEl.innerText = fromEl.value || ""; | |
| }); | |
| // 2. Sync Aadhaar Number (3 places) | |
| const adharNo = document.getElementById('in-aadhaar').value.replace(/\s/g, ''); | |
| if (adharNo) { | |
| const formatted = adharNo.replace(/(\d{4})/g, '$1 ').trim(); | |
| ['out-aadhaar-1', 'out-aadhaar-2', 'out-aadhaar-3'].forEach(id => { | |
| const el = document.getElementById(id); | |
| if (el) el.innerText = formatted; | |
| }); | |
| } | |
| // 3. Gender Sync | |
| const genderVal = document.querySelector('input[name="in-gender"]:checked')?.value || "M"; | |
| const gEn = document.getElementById('out-gender-en'); | |
| const gHi = document.getElementById('out-gender-hi'); | |
| if (genderVal === "M") { | |
| if (gEn) gEn.innerText = "MALE"; | |
| if (gHi) gHi.innerText = "पुरुष"; | |
| } else if (genderVal === "F") { | |
| if (gEn) gEn.innerText = "FEMALE"; | |
| if (gHi) gHi.innerText = "महिला"; | |
| } else { | |
| if (gEn) gEn.innerText = "TRANSGENDER"; | |
| if (gHi) gHi.innerText = "ट्रांसजेंडर"; | |
| } | |
| // 4. Enrollment & Dates Sync | |
| const issueDate = document.getElementById('in-issue-date')?.value || ""; | |
| const downloadDate = document.getElementById('in-download-date')?.value || ""; | |
| if (document.getElementById('out-inrol-date')) document.getElementById('out-inrol-date').innerText = "Aadhaar No. Issued : " + issueDate; | |
| if (document.getElementById('out-download-date')) document.getElementById('out-download-date').innerText = "Details as on : " + downloadDate; | |
| if (document.getElementById('out-signature-date')) document.getElementById('out-signature-date').innerText = downloadDate + " " + new Date().toLocaleTimeString('en-GB'); | |
| // 5. Build Detailed Enrollment Address List (Top Left) | |
| const nameEn = document.getElementById('in-name-en')?.value || ""; | |
| const nameHi = document.getElementById('in-name-hi')?.value || ""; | |
| const fatherEn = document.getElementById('in-father-en')?.value || ""; | |
| const houseEn = document.getElementById('in-house-en')?.value || ""; | |
| const villageEn = document.getElementById('in-village-en')?.value || ""; | |
| const postEn = document.getElementById('in-post-en')?.value || ""; | |
| const subdistEn = document.getElementById('in-subdist-en')?.value || ""; | |
| const distEn = document.getElementById('in-district-en')?.value || ""; | |
| const stateEn = document.getElementById('in-state-en')?.value || ""; | |
| const pin = document.getElementById('in-pin')?.value || ""; | |
| const mobile = document.getElementById('in-mobile')?.value || ""; | |
| let mobileHtml = mobile.trim() !== "" ? `<br>Mobile: ${mobile}` : ""; | |
| let listHtml = `To<br>${nameHi}<br>${nameEn}<br>S/O: ${fatherEn},<br>${houseEn ? houseEn + ', ' : ''}${villageEn},<br>VTC: ${villageEn},<br>PO: ${postEn},<br>Sub District: ${subdistEn},<br>District: ${distEn},<br>State: ${stateEn},<br>PIN Code: ${pin}${mobileHtml}`; | |
| const inrolTextEl = document.getElementById('out-inroltext'); | |
| if (inrolTextEl) inrolTextEl.innerHTML = listHtml; | |
| // 6. Bottom Panels Address Sync | |
| const addrPartsEn = [ | |
| "S/O: " + fatherEn, | |
| houseEn, villageEn, postEn, | |
| "District: " + distEn, | |
| stateEn + " - " + pin | |
| ].filter(p => p && p.trim() !== "" && p !== "S/O: "); | |
| const addrPartsHi = [ | |
| "आत्मज: " + (document.getElementById('in-father-hi')?.value || ""), | |
| document.getElementById('in-house-hi')?.value, | |
| document.getElementById('in-village-hi')?.value, | |
| document.getElementById('in-post-hi')?.value, | |
| document.getElementById('in-district-hi')?.value, | |
| (document.getElementById('in-state-hi')?.value || "") + " - " + pin | |
| ].filter(p => p && p.trim() !== "" && p !== "आत्मज: "); | |
| if (document.getElementById('out-address-en')) document.getElementById('out-address-en').innerText = addrPartsEn.join(', '); | |
| if (document.getElementById('out-address-hi')) document.getElementById('out-address-hi').innerText = addrPartsHi.join(', '); | |
| // 7. Update QR Code | |
| generateQRData(); | |
| } | |
| /** | |
| * Generate QR Codes based on address data | |
| */ | |
| function generateQRData() { | |
| const name = document.getElementById('in-name-en').value; | |
| const gender = document.querySelector('input[name="in-gender"]:checked')?.value || ""; | |
| const dob = document.getElementById('in-dob').value; | |
| const adhar = document.getElementById('in-aadhaar').value.replace(/\s/g, ''); | |
| // Official-style XML data for QR code | |
| const qrData = `<?xml version="1.0" encoding="UTF-8"?> <PrintLetterBarcodeData uid="${adhar}" name="${name}" gender="${gender.charAt(0)}" yob="${dob.split('/')[2] || ''}" co="${document.getElementById('in-father-en')?.value || ''}" vtc="${document.getElementById('in-village-en')?.value || ''}" po="${document.getElementById('in-post-en')?.value || ''}" dist="${document.getElementById('in-district-en')?.value || ''}" state="${document.getElementById('in-state-en')?.value || ''}" pc="${document.getElementById('in-pin')?.value || ''}"/>`; | |
| if (typeof QRious !== 'undefined') { | |
| const qr1Element = document.getElementById('out-qrcode1'); | |
| const qr2Element = document.getElementById('out-qrcode2'); | |
| // Size 150 for top, 200 for bottom high-res, level 'H' for high density | |
| if (qr1Element) new QRious({ element: qr1Element, value: qrData, size: 200, level: 'H' }); | |
| if (qr2Element) new QRious({ element: qr2Element, value: qrData, size: 200, level: 'H' }); | |
| } | |
| } | |
| function generateInrolmentNo() { | |
| const e1 = Math.floor(1000 + Math.random() * 9000); | |
| const e2 = Math.floor(10000 + Math.random() * 90000); | |
| const e3 = Math.floor(10000 + Math.random() * 90000); | |
| const enNo = `नामांकन क्रम/ Enrolment No.: ${e1}/${e2}/${e3}`; | |
| const el = document.getElementById('out-enrolment-no'); | |
| if (el) el.innerText = enNo; | |
| } | |
| function toTitleCase(str) { | |
| if (!str) return ""; | |
| return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()); | |
| } | |
| /* ============================================ | |
| HISTORY MANAGEMENT LOGIC | |
| ============================================ */ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const fab = document.getElementById('history-fab-btn'); | |
| const drawer = document.getElementById('history-drawer'); | |
| const closeBtn = document.getElementById('history-drawer-close'); | |
| const overlay = document.getElementById('history-overlay'); | |
| if (fab) { | |
| fab.addEventListener('click', () => { | |
| drawer.classList.add('open'); | |
| overlay.classList.add('active'); | |
| fetchHistory(); | |
| }); | |
| } | |
| if (closeBtn) { | |
| closeBtn.addEventListener('click', () => { | |
| drawer.classList.remove('open'); | |
| overlay.classList.remove('active'); | |
| }); | |
| } | |
| if (overlay) { | |
| overlay.addEventListener('click', () => { | |
| drawer.classList.remove('open'); | |
| overlay.classList.remove('active'); | |
| }); | |
| } | |
| // Load history count on start | |
| fetchHistoryCount(); | |
| }); | |
| async function fetchHistory() { | |
| const body = document.getElementById('history-drawer-body'); | |
| const emptyState = document.getElementById('history-empty-state'); | |
| try { | |
| const response = await fetch('/api/history'); | |
| const data = await response.json(); | |
| // Clear previous items (except empty state) | |
| const existingCards = body.querySelectorAll('.history-card'); | |
| existingCards.forEach(card => card.remove()); | |
| if (data.length === 0) { | |
| emptyState.style.display = 'flex'; | |
| return; | |
| } | |
| emptyState.style.display = 'none'; | |
| data.forEach(item => { | |
| const card = createHistoryCard(item); | |
| body.appendChild(card); | |
| }); | |
| updateHistoryBadge(data.length); | |
| } catch (err) { | |
| console.error("Error fetching history:", err); | |
| } | |
| } | |
| async function fetchHistoryCount() { | |
| try { | |
| const response = await fetch('/api/history'); | |
| const data = await response.json(); | |
| updateHistoryBadge(data.length); | |
| } catch (err) { } | |
| } | |
| function updateHistoryBadge(count) { | |
| const badge = document.getElementById('history-badge'); | |
| if (badge) { | |
| if (count > 0) { | |
| badge.innerText = count; | |
| badge.style.display = 'flex'; | |
| } else { | |
| badge.style.display = 'none'; | |
| } | |
| } | |
| } | |
| function createHistoryCard(item) { | |
| const card = document.createElement('div'); | |
| card.className = 'history-card'; | |
| // Store data in attribute for safe retrieval | |
| card.dataset.id = item.id; | |
| card.dataset.raw = JSON.stringify(item); | |
| const date = item.timestamp ? new Date(item.timestamp).toLocaleString() : 'Recently'; | |
| card.innerHTML = ` | |
| <div class="history-card-top"> | |
| <div> | |
| <div class="history-card-name">${item['in-name-en'] || 'Unknown'}</div> | |
| <div class="history-card-aadhaar">${item['in-aadhaar'] || 'XXXX XXXX XXXX'}</div> | |
| </div> | |
| <div class="history-card-time">${date}</div> | |
| </div> | |
| <div class="history-card-actions"> | |
| <button class="history-card-btn reprint"> | |
| <i class="fa-solid fa-redo"></i> Reprint | |
| </button> | |
| <button class="history-card-btn delete"> | |
| <i class="fa-solid fa-trash-can"></i> Delete | |
| </button> | |
| </div> | |
| `; | |
| // Attach listeners dynamically | |
| card.querySelector('.reprint').addEventListener('click', () => { | |
| repopulateForm(item); | |
| }); | |
| card.querySelector('.delete').addEventListener('click', () => { | |
| deleteHistoryItem(item.id, card); | |
| }); | |
| return card; | |
| } | |
| async function deleteHistoryItem(id, cardEl) { | |
| // Note: confirm() can hang browser testing subagents | |
| // if (!confirm("Are you sure you want to delete this print record?")) return; | |
| try { | |
| const response = await fetch(`/api/history?id=${id}`, { method: 'DELETE' }); | |
| if (response.ok) { | |
| cardEl.style.opacity = '0'; | |
| cardEl.style.transform = 'translateX(20px)'; | |
| setTimeout(() => { | |
| cardEl.remove(); | |
| fetchHistoryCount(); | |
| // If no cards left, show empty state | |
| const body = document.getElementById('history-drawer-body'); | |
| if (body.querySelectorAll('.history-card').length === 0) { | |
| document.getElementById('history-empty-state').style.display = 'flex'; | |
| } | |
| }, 300); | |
| } | |
| } catch (err) { | |
| console.error("Error deleting history:", err); | |
| } | |
| } | |
| function repopulateForm(data) { | |
| // Fill all inputs | |
| Object.keys(data).forEach(key => { | |
| const input = document.getElementById(key); | |
| if (input) { | |
| if (input.type === 'radio') { | |
| if (input.value === data[key]) input.checked = true; | |
| } else if (input.type === 'file') { | |
| // SKIP file inputs (security error if we set value) | |
| } else { | |
| input.value = data[key]; | |
| } | |
| } | |
| }); | |
| // Manually restore photo if photo_data exists in history item | |
| if (data.photo_data) { | |
| const preview = document.getElementById('form-photo-preview'); | |
| const outPhoto = document.getElementById('out-photo'); | |
| const placeholder = document.getElementById('photo-placeholder'); | |
| if (preview) { preview.src = data.photo_data; preview.style.display = 'block'; } | |
| if (outPhoto) outPhoto.src = data.photo_data; | |
| if (placeholder) placeholder.style.display = 'none'; | |
| } | |
| // Close drawer | |
| document.getElementById('history-drawer').classList.remove('open'); | |
| document.getElementById('history-overlay').classList.remove('active'); | |
| // Trigger update logic | |
| if (typeof updateAddress === 'function') updateAddress(); | |
| // Automation: Automatically trigger PDF generation after repopulation | |
| if (typeof generateAadhaarPDF === 'function') { | |
| setTimeout(() => { | |
| console.log("Auto-triggering FREE PDF generation from history..."); | |
| generateAadhaarPDF(true); // Pass true for isFree | |
| }, 300); | |
| } | |
| // Scroll to top | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| // Show notification (optional, can use existing popup logic) | |
| console.log("Form repopulated from history"); | |
| } | |