import { useState, useEffect, useCallback, useRef } from 'react' import { getWhatsAppTemplates, createWhatsAppTemplate, deleteWhatsAppTemplate } from '../api/api' import './TemplateEditor.css' // ── Param catalogue ─────────────────────────────────────────────────────────── const PARAM_OPTIONS = [ { key: 'contact_name', label: 'שם האורח', sample: 'דביר' }, { key: 'groom_name', label: 'שם החתן', sample: 'דביר' }, { key: 'bride_name', label: 'שם הכלה', sample: 'ורד' }, { key: 'venue', label: 'שם האולם', sample: 'אולם הגן' }, { key: 'event_date', label: 'תאריך האירוע', sample: '15/06' }, { key: 'event_time', label: 'שעת ההתחלה', sample: '18:30' }, { key: 'guest_link', label: 'קישור RSVP', sample: 'https://invy.dvirlabs.com/guest' }, ] const SAMPLE_MAP = Object.fromEntries(PARAM_OPTIONS.map(o => [o.key, o.sample])) const he = { pageTitle: 'ניהול תבניות WhatsApp', back: '← חזרה', newTemplateTitle: 'יצירת תבנית חדשה', editTemplateTitle: 'עריכת תבנית', savedTemplatesTitle: 'התבניות שלי', builtInTitle: 'תבניות מובנות', noCustom: 'אין תבניות מותאמות עדיין.', friendlyName: 'שם תצוגה', metaName: 'שם ב-Meta (מדויק)', templateKey: 'מזהה (key)', language: 'שפה', description: 'תיאור', headerSection: 'כותרת (Header) — אופציונלי', bodySection: 'גוף ההודעה (Body)', headerText: 'טקסט הכותרת', bodyText: 'טקסט ההודעה', paramMapping: 'מיפוי פרמטרים', preview: 'תצוגה מקדימה', save: 'שמור תבנית', update: 'עדכן תבנית', saving: 'שומר...', cancelEdit: 'ביטול עריכה', reset: 'נקה טופס', builtIn: 'מובנת', chars: 'תווים', headerHint: 'השתמש ב-{{1}} לשם האורח — השאר ריק אם אין כותרת', bodyHint: 'השתמש ב-{{1}}, {{2}}, ... לפרמטרים — בדיוק כמו ב-Meta', keyHint: 'אותיות קטנות, מספרים ו-_ בלבד', metaHint: 'שם מדויק כפי שאושר ב-Meta Business Manager', saved: '✓ התבנית נשמרה בהצלחה!', confirmDelete: key => `האם למחוק את התבנית "${key}"?\nפעולה זו אינה הפיכה.`, headerParam: 'כותרת', bodyParam: 'גוף', params: 'פרמטרים', loadingTpls: 'טוען תבניות...', } function parsePlaceholders(text) { const found = new Set() const re = /\{\{(\d+)\}\}/g let m while ((m = re.exec(text)) !== null) found.add(parseInt(m[1], 10)) return Array.from(found).sort((a, b) => a - b) } function renderPreview(text, paramKeys) { if (!text) return '' return text.replace(/\{\{(\d+)\}\}/g, (_m, n) => { const key = paramKeys[parseInt(n, 10) - 1] if (!key) return `{{${n}}}` // Known built-in key → use sample value; custom key → show the key name itself return SAMPLE_MAP[key] || key }) } const EMPTY_FORM = { key: '', friendlyName: '', metaName: '', language: 'he', description: '', headerText: '', bodyText: '', } export default function TemplateEditor({ onBack }) { const [form, setForm] = useState(EMPTY_FORM) const [headerParamKeys, setHPK] = useState([]) const [bodyParamKeys, setBPK] = useState([]) const [guestNameKey, setGuestNameKey] = useState('') const [editMode, setEditMode] = useState(false) const [editingKey, setEditingKey] = useState('') const [saving, setSaving] = useState(false) const [error, setError] = useState('') const [successMsg, setSuccessMsg] = useState('') const [templates, setTemplates] = useState([]) const [loadingTpls, setLoadingTpls] = useState(true) const isLoadingHeader = useRef(false) const isLoadingBody = useRef(false) const loadTemplates = useCallback(() => { setLoadingTpls(true) getWhatsAppTemplates() .then(d => setTemplates(d.templates || [])) .catch(console.error) .finally(() => setLoadingTpls(false)) }, []) useEffect(loadTemplates, [loadTemplates]) useEffect(() => { if (isLoadingHeader.current) { isLoadingHeader.current = false; return } const nums = parsePlaceholders(form.headerText) setHPK(prev => nums.map((_, i) => prev[i] || '')) }, [form.headerText]) useEffect(() => { if (isLoadingBody.current) { isLoadingBody.current = false; return } const nums = parsePlaceholders(form.bodyText) setBPK(prev => nums.map((_, i) => prev[i] || '')) }, [form.bodyText]) const handleInput = useCallback(e => { const { name, value } = e.target if (name === 'metaName') { const slug = value.toLowerCase().replace(/[^a-z0-9_]/g, '_') setForm(f => ({ ...f, metaName: value, key: f.key || slug })) } else { setForm(f => ({ ...f, [name]: value })) } }, []) const handleFriendlyBlur = () => { if (!form.metaName) { const slug = form.friendlyName .toLowerCase() .replace(/[\s\u0590-\u05FF]+/g, '_') .replace(/[^a-z0-9_]/g, '') .replace(/__+/g, '_') .replace(/^_|_$/g, '') setForm(f => ({ ...f, metaName: f.metaName || slug, key: f.key || slug })) } } const validate = () => { if (!form.key.trim()) return 'יש להזין מזהה תבנית' if (!/^[a-z0-9_]+$/.test(form.key)) return 'מזהה יכול להכיל רק אותיות קטנות, מספרים ו-_' if (!form.metaName.trim()) return 'יש להזין שם תבנית ב-Meta' if (!form.friendlyName.trim()) return 'יש להזין שם תצוגה' if (!form.bodyText.trim()) return 'יש להזין את גוף ההודעה' const bNums = parsePlaceholders(form.bodyText) if (bNums.length && bodyParamKeys.some(k => !k)) return 'יש למפות את כל פרמטרי גוף ההודעה' const hNums = parsePlaceholders(form.headerText) if (hNums.length && headerParamKeys.some(k => !k)) return 'יש למפות את כל פרמטרי הכותרת' return null } const loadTemplateForEdit = (tpl) => { isLoadingHeader.current = true isLoadingBody.current = true setHPK(tpl.header_params || []) setBPK(tpl.body_params || []) setGuestNameKey(tpl.guest_name_key || '') setForm({ key: tpl.key, friendlyName: tpl.friendly_name, metaName: tpl.meta_name, language: tpl.language_code || 'he', description: tpl.description || '', headerText: tpl.header_text || '', bodyText: tpl.body_text || '', }) setEditMode(true) setEditingKey(tpl.key) setError('') setSuccessMsg('') window.scrollTo({ top: 0, behavior: 'smooth' }) } const cancelEdit = () => { setEditMode(false) setEditingKey('') setForm(EMPTY_FORM) setHPK([]); setBPK([]); setGuestNameKey('') setError(''); setSuccessMsg('') } const handleSave = async () => { const err = validate() if (err) { setError(err); return } setSaving(true); setError(''); setSuccessMsg('') try { await createWhatsAppTemplate({ key: form.key.trim(), friendly_name: form.friendlyName.trim(), meta_name: form.metaName.trim(), language_code: form.language, description: form.description.trim(), header_text: form.headerText.trim(), body_text: form.bodyText.trim(), header_param_keys: headerParamKeys, body_param_keys: bodyParamKeys, fallbacks: Object.fromEntries(PARAM_OPTIONS.map(o => [o.key, o.sample])), guest_name_key: guestNameKey, }) setSuccessMsg(editMode ? '✓ התבנית עודכנה בהצלחה!' : he.saved) if (!editMode) { setForm(EMPTY_FORM) setHPK([]); setBPK([]); setGuestNameKey('') } else { setEditMode(false); setEditingKey('') } loadTemplates() } catch (e) { setError(e?.response?.data?.detail || 'שגיאה בשמירת התבנית') } finally { setSaving(false) } } const handleDelete = async (key) => { if (!window.confirm(he.confirmDelete(key))) return try { await deleteWhatsAppTemplate(key) loadTemplates() } catch (e) { alert(e?.response?.data?.detail || 'שגיאה במחיקת התבנית') } } const hNums = parsePlaceholders(form.headerText) const bNums = parsePlaceholders(form.bodyText) const previewHeader = renderPreview(form.headerText, headerParamKeys) const previewBody = renderPreview(form.bodyText, bodyParamKeys) const customTemplates = templates.filter(t => t.is_custom) const builtInTemplates = templates.filter(t => !t.is_custom) return (
{he.loadingTpls}
) : customTemplates.length === 0 ? ({he.noCustom}
) : (