// New patient — 3-step wizard. const WZ_VT = window.VT || {}; const WZ_DANGER = WZ_VT.danger || '#b91c1c'; // ───────────────────────────────────────────────────────────── // Validation — au blur uniquement. // Corriger quelqu'un pendant qu'il tape est hostile : on attend qu'il ait // fini le champ. L'erreur s'efface dès la première frappe suivante. // Les champs facultatifs ne sont contrôlés que s'ils sont remplis. // ───────────────────────────────────────────────────────────── function wzValidate(field, raw) { const v = String(raw || '').trim(); if (field === 'ddn') { if (!v) return 'Date de naissance requise.'; const m = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(v); if (!m) return 'Format attendu : JJ/MM/AAAA.'; const [, dd, mm, yyyy] = m.map(Number); const d = new Date(yyyy, mm - 1, dd); // getMonth() rattrape les 31/02 : si le mois a glissé, la date n'existe pas. if (d.getFullYear() !== yyyy || d.getMonth() !== mm - 1 || d.getDate() !== dd) { return "Cette date n'existe pas."; } if (yyyy < 1900) return 'Année trop ancienne.'; if (d > new Date()) return 'Date dans le futur.'; return null; } if (field === 'email') { if (!v) return null; if (!/^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(v)) return 'Adresse e-mail invalide.'; return null; } if (field === 'secu') { if (!v) return null; const digits = v.replace(/\s/g, ''); if (!/^\d{15}$/.test(digits)) return '15 chiffres attendus.'; if (!/^[12]/.test(digits)) return 'Doit commencer par 1 ou 2.'; return null; } if (field === 'phone') { if (!v) return null; const digits = v.replace(/[\s.\-]/g, ''); if (!/^(0\d{9}|\+33\d{9})$/.test(digits)) return 'Numéro à 10 chiffres attendu.'; return null; } return null; } function StepBar({ step }) { return (
{[0, 1, 2].map(i => (
))}
); } function Field({ label, required, children, full }) { return (
{children}
); } function Inp({ value, onChange, placeholder, type = 'text', onBlur, error }) { return ( <> onChange(e.target.value)} onBlur={onBlur} placeholder={placeholder} aria-invalid={error ? 'true' : undefined} style={{ width: '100%', height: 42, background: '#fff', border: `1px solid ${error ? WZ_DANGER : P_HAIR2}`, borderRadius: 16, outline: 'none', padding: '0 12px', fontFamily: 'inherit', fontSize: 14, color: P_INK, transition: 'border-color .15s ease', }}/> {error && (
{error}
)} ); } function Sel({ value, onChange, options, placeholder }) { return ( ); } function TA({ value, onChange, placeholder, rows = 3 }) { return (