function renderComponent(comp) { const el = document.createElement('div'); el.className = 'dropped-component'; el.id = comp.id; el.innerHTML = `
${renderComponentContent(comp)} `; el.addEventListener('click', (e) => {if (!e.target.closest('.component-controls')) selectComponent(comp.id);}); document.getElementById('drop-zone').appendChild(el); } function renderComponentContent(comp) { const d = comp.data; switch (comp.type) { case 'heading': return `
${d.text}
`; case 'text': return `
${(d.text || '').replace(/\n/g, '
')}
`; case 'logo': const logoUrl = d.url || Pro.data.store.logoUrl; return ``; case 'image': return ``; case 'divider': return `
`; case 'spacer': return `
`; case 'store-info': const s = Pro.data.store; return `
${s.name || ''}
${s.branch || ''}
${s.address || ''}
โทร: ${s.phone || ''} | Tax: ${s.taxId || ''}
`; case 'receipt-info': const r = Pro.data.receipt; return `
ใบเสร็จ: ${r.id || ''}
วันที่: ${r.date || ''} ${r.time || ''}
โต๊ะ: ${r.table || ''} | พนักงาน: ${r.staff || ''}
`; case 'items-table': const rows = (Pro.items.length ? Pro.items : [{name: 'ไม่มีรายการ', qty: 0, price: 0}]).map(it => { const t = (Number(it.qty) || 0) * (Number(it.price) || 0); return ` ${it.name}${it.note ? `
${it.note}` : ''} ${it.qty} ฿${(Number(it.price) || 0).toFixed(2)} ฿${t.toFixed(2)} `; }).join(''); return `${rows}
รายการจน.ราคารวม
`; case 'summary': const sum = Pro.calcSummary(); return `
ยอดรวม: ฿${sum.subtotal.toFixed(2)}
ภาษี ${Number(Pro.data.vatRate) || 0}%: ฿${sum.vat.toFixed(2)}
รวมสุทธิ: ฿${sum.total.toFixed(2)}
`; case 'payment-info': const sm = Pro.calcSummary(); const cash = parseFloat(d.cashReceived); const showCash = !isNaN(cash); const change = showCash ? cash - sm.total : 0; return `
ชำระโดย: ${d.method || 'เงินสด'}
${showCash ? `รับเงิน: ฿${cash.toFixed(2)}
` : ''} ${showCash ? `เงินทอน: ฿${Math.max(change, 0).toFixed(2)}` : ''}
`; case 'barcode': return `
`; case 'promptpay': const id = Pro.data.store.promptpayId || ''; const sum2 = Pro.calcSummary(); const amt = (d.useTotal ? sum2.total : parseFloat(d.amount || '')); const amtStr = (!isNaN(amt) && amt > 0) ? `/${amt.toFixed(2)}` : ''; const src = id ? `https://promptpay.io/${encodeURIComponent(id)}${amtStr}.png` : ''; return `
${src ? `\"PromptPay\"` : '
ตั้งค่า PromptPay ID ในข้อมูลร้าน
'}
`; case 'footer': return `
${(d.text || '').replace(/\n/g, '
')}
`; case 'box': const boxStyle = ` padding: ${d.padding || 10}px; margin: ${d.margin || 5}px 0; border: ${d.borderWidth || 1}px ${d.borderStyle || 'solid'} ${d.borderColor || '#ddd'}; border-radius: ${d.borderRadius || 4}px; background: ${d.backgroundColor || 'transparent'}; ${d.width ? `width: ${d.width}px;` : ''} ${d.height ? `height: ${d.height}px;` : ''} display: ${d.layout || 'block'}; ${d.layout === 'flex' ? ` flex-direction: ${d.flexDirection || 'row'}; justify-content: ${d.justifyContent || 'flex-start'}; align-items: ${d.alignItems || 'stretch'}; gap: ${d.gap || 10}px; ` : ''} ${d.layout === 'grid' ? ` grid-template-columns: ${d.gridColumns || 'repeat(2, 1fr)'}; gap: ${d.gap || 10}px; ` : ''} `; const childrenHtml = (comp.children || []).map(child => { const childComp = Pro.components.find(c => c.id === child.id); return childComp ? `
${renderComponentContent(childComp)}
` : ''; }).join(''); return `
${childrenHtml}
ลาก component มาวางในกล่อง
`; default: return `
Unknown: ${comp.type}
`; } } function selectComponent(id) { document.querySelectorAll('.dropped-component').forEach(el => el.classList.remove('selected')); const el = document.getElementById(id); if (el) el.classList.add('selected'); Pro.selected = Pro.components.find(c => c.id === id) || null; showProperties(Pro.selected); } function deleteComponent(id) { // Remove from any box that contains this component Pro.components.forEach(comp => { if (comp.type === 'box' && comp.children) { comp.children = comp.children.filter(child => child.id !== id); } }); // Remove the component itself Pro.components = Pro.components.filter(c => c.id !== id); document.getElementById(id)?.remove(); if (!Pro.components.length) showEmptyMessage(); if (Pro.selected?.id === id) {Pro.selected = null; showProperties(null);} // Refresh all box components refreshAll(); } function refreshAll() { Pro.components.forEach(c => { const el = document.getElementById(c.id); if (el) { const ctrl = el.querySelector('.component-controls')?.outerHTML || ''; el.innerHTML = ctrl + renderComponentContent(c); } }); setTimeout(() => {renderBarcodes(); bindInlineEditors(); setupBoxDropZones();}, 20); } function renderBarcodes() { document.querySelectorAll('.barcode-svg').forEach(svg => { const value = svg.dataset.value; const height = parseInt(svg.dataset.height) || 50; const width = Math.max(0.5, Math.min(2, parseFloat(svg.dataset.width) || 1)); const finalValue = value || (Pro?.data?.receipt?.id || ''); if (finalValue) {try {JsBarcode(svg, finalValue, {format: 'CODE128', width, height, displayValue: true, fontSize: 12});} catch (e) {} } }); } // removed qrcode renderer; using PromptPay image instead function bindInlineEditors() { document.querySelectorAll('.editable[contenteditable="true"]').forEach(el => { el.onblur = (e) => { const compEl = e.target.closest('.dropped-component'); if (!compEl) return; const comp = Pro.components.find(c => c.id === compEl.id); if (!comp) return; const scope = e.target.dataset.scope; const key = e.target.dataset.key; const text = e.target.innerText.trim(); if (scope) { Pro.data[scope][key] = text; if (scope === 'store') { try {localStorage.setItem('pro.store', JSON.stringify(Pro.data.store));} catch (e) {} } } else if (key) { comp.data[key] = text; } showProperties(Pro.selected); refreshAll(); }; }); }