/* Icons (lucide-style, currentColor) + Sunburst + small helpers — shared via window */ const I = ({ d, children, sw = 2 }) => ( ); const ICONS = { target: , chart: , monitor: , users: , doctor: , clinic: , building: , rocket: , doc: , key: , compass: , tools: , calendar: , clock: , video: , gift: , check: , shield: , spark: , arrow: , download: , mail: , phone: , pin: , bell: , layers: , zap: , }; function Icon({ name, className }) { return {ICONS[name] || ICONS.spark}; } function Sunburst({ size = 22, color = '' }) { // Pick the official single-color mark by intended tone (CSS color can't recolor an SVG). const c = String(color).toLowerCase(); const src = c.includes('amber') ? 'brand-assets/mark-sun-amber.svg' : (c.includes('white') || c.includes('#fff')) ? 'brand-assets/mark-sun-white.svg' : 'brand-assets/mark-sun-navy.svg'; return ; } // ---- validation helpers ---- const isEmail = (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(v || '').trim()); const isPhoneVN = (v) => /^(0|\+84)(\s?\d){8,10}$/.test(String(v || '').replace(/[.\-()]/g, '').trim()); function validateField(f, value) { const empty = value == null || value === '' || (Array.isArray(value) && value.length === 0) || (f.type === 'matrix' && !Object.keys(value || {}).length); if (f.required && empty) return 'Vui lòng hoàn tất mục này.'; if (empty) return ''; if (f.type === 'email' && !isEmail(value)) return 'Email chưa hợp lệ.'; if (f.type === 'tel' && !isPhoneVN(value)) return 'Số điện thoại chưa hợp lệ (VD: 0901 234 567).'; return ''; } // ---- Submit form to webinar backend (CSV + Google Sheet append) ---- // Backend strips honeypot/password/type/captcha server-side, but rejecting locally // when the honeypot is filled avoids a network round-trip for obvious bots. async function submitForm(formName, payload) { if (String(payload?.__honeypot || '').length) throw new Error('spam'); const res = await fetch('/api/submit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ form: formName, submittedAt: new Date().toISOString(), data: payload, }), }); if (!res.ok) { const body = await res.text().catch(() => ''); throw new Error('submit failed: ' + res.status + ' ' + body); } return res.json(); } Object.assign(window, { Icon, Sunburst, validateField, isEmail, isPhoneVN, submitForm });