import React, { useEffect, useState } from "react"; import { whatsappTemplatesApi } from "../../../services/api"; import type { WaOverrideSlot } from "../../../services/api/campaigns"; import type { AttendanceWhatsAppNotifySettings, WhatsAppTemplateRecord, WhatsAppVariableBindings, } from "../../../services/api/types"; import { WA_TEMPLATE_MERGE_OPTIONS } from "../messaging/mergePlaceholders"; type SlotMode = "default" | "merge" | "fixed"; type SlotRow = { mode: SlotMode; mergeKey?: string; fixed?: string }; function emptySlotRow(): SlotRow { return { mode: "default" }; } function parseSlotsFromOverrides( n: number, section: "header" | "body" | "url", notify: AttendanceWhatsAppNotifySettings | undefined, defaultBindings: string[] | undefined, ): SlotRow[] { const o = notify?.waOverrides?.[section]; const out: SlotRow[] = []; for (let i = 0; i < n; i += 1) { const slot = String(i + 1); const raw = o?.[slot] as WaOverrideSlot | undefined; if (!raw) { out.push({ mode: "default" }); continue; } if (raw.kind === "fixed") { out.push({ mode: "fixed", fixed: raw.value || "" }); } else { const def = String(defaultBindings?.[i] || "").trim(); if (def && raw.key === def) { out.push({ mode: "default" }); } else { out.push({ mode: "merge", mergeKey: raw.key || "" }); } } } return out; } function buildSectionOverrides( rows: SlotRow[], defaultBindings: string[] | undefined, ): Record | undefined { const out: Record = {}; for (let i = 0; i < rows.length; i += 1) { const row = rows[i]; const def = String(defaultBindings?.[i] || "").trim(); if (row.mode === "default") continue; if (row.mode === "fixed") { out[String(i + 1)] = { kind: "fixed", value: String(row.fixed || "") }; } else if (row.mode === "merge") { const k = String(row.mergeKey || "").trim(); if (k && k !== def) out[String(i + 1)] = { kind: "merge", key: k }; } } return Object.keys(out).length ? out : undefined; } function hasImageHeader(components: Record[]): boolean { return components.some( (c) => String(c.type || "").toUpperCase() === "HEADER" && String(c.format || "").toUpperCase() === "IMAGE", ); } const IS: React.CSSProperties = { width: "100%", padding: "8px 12px", border: "1px solid var(--border)", borderRadius: "var(--radius-button)", background: "var(--input-background)", fontSize: "var(--text-sm)", color: "var(--card-foreground)", boxSizing: "border-box", }; function SlotTable({ title, rows, setRows, defaultBindings, }: { title: string; rows: SlotRow[]; setRows: (next: SlotRow[]) => void; defaultBindings: string[]; }) { if (!rows.length) return null; return (
{title}
{rows.map((row, idx) => (
{`{{${idx + 1}}}`} {row.mode === "merge" ? ( ) : row.mode === "fixed" ? ( { const next = [...rows]; next[idx] = { mode: "fixed", fixed: e.target.value }; setRows(next); }} /> ) : ( {defaultBindings[idx] ? `Uses ${defaultBindings[idx]}` : "Sample / binding from template"} )}
))}
); } export function AttendanceWhatsAppNotifyFields({ value, onChange, }: { value: AttendanceWhatsAppNotifySettings; onChange: (next: AttendanceWhatsAppNotifySettings) => void; }) { const [templates, setTemplates] = useState([]); const [detail, setDetail] = useState(null); const [headerRows, setHeaderRows] = useState([]); const [bodyRows, setBodyRows] = useState([]); const [urlRows, setUrlRows] = useState([]); const [showImage, setShowImage] = useState(false); useEffect(() => { void whatsappTemplatesApi.list({ status: "APPROVED" }).then((r) => { if (!r.error) setTemplates(r.data?.templates || []); }); }, []); useEffect(() => { const id = value.whatsappTemplateId; if (!id) { setDetail(null); setHeaderRows([]); setBodyRows([]); setUrlRows([]); setShowImage(false); return; } void whatsappTemplatesApi.get(String(id)).then((r) => { if (!r.data) return; setDetail(r.data); const vb: WhatsAppVariableBindings = r.data.variableBindings || { header: [], body: [], url: [] }; setShowImage(hasImageHeader((r.data.components || []) as Record[])); setHeaderRows(parseSlotsFromOverrides((vb.header || []).length, "header", value, vb.header)); setBodyRows(parseSlotsFromOverrides((vb.body || []).length, "body", value, vb.body)); setUrlRows(parseSlotsFromOverrides((vb.url || []).length, "url", value, vb.url)); }); // Only re-load detail when template id changes; slot edits push via emitOverrides // eslint-disable-next-line react-hooks/exhaustive-deps }, [value.whatsappTemplateId]); const emitOverrides = ( nextHeader: SlotRow[], nextBody: SlotRow[], nextUrl: SlotRow[], partial?: Partial, ) => { const vb = detail?.variableBindings || { header: [], body: [], url: [] }; const waOverrides: NonNullable = {}; const h = buildSectionOverrides(nextHeader, vb.header); const b = buildSectionOverrides(nextBody, vb.body); const u = buildSectionOverrides(nextUrl, vb.url); if (h) waOverrides.header = h; if (b) waOverrides.body = b; if (u) waOverrides.url = u; onChange({ ...value, ...partial, waOverrides: Object.keys(waOverrides).length ? waOverrides : null, }); }; return (

Sent after the first successful check-in. Duplicate check-ins do not resend.

{value.enabled ? ( <> {showImage ? ( ) : null} {detail ? (
{ setHeaderRows(next); emitOverrides(next, bodyRows, urlRows); }} /> { setBodyRows(next); emitOverrides(headerRows, next, urlRows); }} /> { setUrlRows(next); emitOverrides(headerRows, bodyRows, next); }} />
) : null} ) : null}
); }