import React, { useState, useEffect, useMemo } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { __ } from "../lib/i18n"; import { useToast } from "../components/ui/toast"; import { Card, CardContent, CardHeader, CardTitle, } from "../components/ui/card"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Badge } from "../components/ui/badge"; import { Mail, ArrowLeft, Save, Eye, Send, Loader2, Code, Copy, Check, Info, Zap, ChevronDown, Sparkles, } from "lucide-react"; import { AiEmailTemplateModal } from "../components/ai/AiEmailTemplateModal"; import { isAiEligible, isAiModuleEnabled } from "../lib/ai-availability"; import { Switch } from "../components/ui/switch"; import { getCatalogEntryByTemplateKey, getCoreTemplateDefinition, isCoreTemplateSlug, } from "../lib/email-templates-catalog"; import { isEmailAutomationModuleEnabled, isModuleActive, isProPluginActive, } from "../lib/plugin-utils"; import { fetchSettings, previewCoreEmailTemplate, saveSettings, } from "../api/settings-api"; import { EmailPreviewModal } from "../components/email/EmailPreviewModal"; import { createEmailTemplate, fetchEmailTemplate, fetchEmailTemplateVariables, previewEmailTemplate, sendEmailTemplateTest, updateEmailTemplate, } from "../api/email-automation-api"; interface PreviewData { subject: string; body: string; } const EmailTemplateForm: React.FC = () => { const { id, isCreateMode, coreTemplateSlug, coreReadOnlyParam } = useMemo(() => { const params = new URLSearchParams(window.location.search); const templateId = params.get("id"); const action = params.get("action"); const core = params.get("core_template") || ""; return { id: templateId, isCreateMode: action === "create", coreTemplateSlug: core, coreReadOnlyParam: params.get("readonly") === "1", }; }, []); const isCoreSettingsEdit = isCoreTemplateSlug(coreTemplateSlug); const coreDef = isCoreSettingsEdit ? getCoreTemplateDefinition(coreTemplateSlug) : undefined; const catalogEntryForCore = useMemo( () => coreTemplateSlug ? getCatalogEntryByTemplateKey(coreTemplateSlug) : undefined, [coreTemplateSlug], ); /** URL ?readonly=1 — no edits at all from this screen */ const isCoreStrictViewOnly = isCoreSettingsEdit && Boolean(coreReadOnlyParam); /** Module required but inactive: subject/body/save locked; on/off can still be changed (site setting). */ const isCoreModuleBodyLocked = useMemo(() => { if (!isCoreSettingsEdit || isCoreStrictViewOnly) { return false; } const req = catalogEntryForCore?.requiresModule; if (!req) { return false; } return !isProPluginActive() || !isModuleActive(req); }, [isCoreSettingsEdit, isCoreStrictViewOnly, catalogEntryForCore]); /** Subject/body (and merge-tag insert) read-only; strict URL also locks status. */ const isCoreBodyReadOnly = isCoreStrictViewOnly || isCoreModuleBodyLocked; const isCoreViewMode = isCoreStrictViewOnly || isCoreModuleBodyLocked; const goBack = () => { window.location.href = `admin.php?page=yatra&subpage=email-automation&tab=templates`; }; const queryClient = useQueryClient(); const { showToast } = useToast(); const [formData, setFormData] = useState({ name: "", description: "", from_name: "", from_email: "", to_email: "", reply_to: "", subject: "", body: "", event_key: "", is_active: true, }); const [showPreview, setShowPreview] = useState(false); const [previewData, setPreviewData] = useState(null); const [testEmail, setTestEmail] = useState(""); const [copiedVar, setCopiedVar] = useState(null); const [showEventDropdown, setShowEventDropdown] = useState(false); const [showAiModal, setShowAiModal] = useState(false); useEffect(() => { if (isCreateMode && !isEmailAutomationModuleEnabled()) { window.location.href = "admin.php?page=yatra&subpage=email-automation&tab=templates"; } }, [isCreateMode]); useEffect(() => { const params = new URLSearchParams(window.location.search); if (params.get("action") !== "edit") return; const ct = params.get("core_template"); if (ct && !isCoreTemplateSlug(ct)) { window.location.href = "admin.php?page=yatra&subpage=email-automation&tab=templates"; } }, []); const { data: settings, isLoading: settingsLoading } = useQuery({ queryKey: ["settings"], queryFn: () => fetchSettings(), enabled: isCoreSettingsEdit, }); const { data: templateData, isLoading } = useQuery({ queryKey: ["email-template", id], queryFn: () => fetchEmailTemplate(id as string), enabled: Boolean(id) && !isCoreSettingsEdit, }); // Event-scoped variables: the sidebar re-fetches whenever the // operator switches the trigger event so the "Available // Variables" panel reflects only tags that will actually // resolve at send-time for the chosen event. Empty event_key // returns the full union (for the create-template starter // state before they've picked an event). const { data: variablesData } = useQuery({ queryKey: ["email-variables", formData.event_key || ""], queryFn: () => fetchEmailTemplateVariables(formData.event_key || ""), enabled: !isCoreSettingsEdit && isEmailAutomationModuleEnabled(), }); // Get events from window.yatraAdmin (passed from PHP) const events = (window as any).yatraAdmin?.emailEvents || []; useEffect(() => { if (!templateData || typeof templateData !== "object") return; const t = templateData as Record; setFormData({ name: String(t.name ?? ""), description: String(t.description ?? ""), from_name: String(t.from_name ?? ""), from_email: String(t.from_email ?? ""), to_email: String(t.to_email ?? ""), reply_to: String(t.reply_to ?? ""), subject: String(t.subject ?? ""), body: String(t.body ?? ""), event_key: String(t.event_key ?? ""), is_active: Boolean(t.is_active ?? true), }); }, [templateData]); useEffect(() => { if (!isCoreSettingsEdit || !settings || !coreDef) return; const flag = coreDef.settingsFlag; const subj = coreDef.settingsSubject; const bodyKey = coreDef.settingsBody; if (!flag || !subj || !bodyKey) return; const s = settings as Record; setFormData({ name: coreDef.name, description: coreDef.description, from_name: "", from_email: "", to_email: coreDef.to_email || "", reply_to: "", subject: String(s[subj] ?? ""), body: String(s[bodyKey] ?? ""), event_key: coreDef.event_key, is_active: Boolean(s[flag]), }); }, [isCoreSettingsEdit, settings, coreDef]); const saveMutation = useMutation({ mutationFn: async (data: typeof formData) => { if (isCreateMode) { const response = await createEmailTemplate(data); const r = response as { success?: boolean; message?: string; data?: { id?: string | number }; }; if (r && r.success === false) { throw new Error(r.message || __("Failed to create template")); } return response; } return await updateEmailTemplate(id as string, data); }, onSuccess: (response) => { queryClient.invalidateQueries({ queryKey: ["email-templates"] }); if (isCreateMode) { showToast(__("Template created successfully"), "success"); // Redirect to edit page after creation - response.data contains the template const newId = (response as { data?: { id?: string | number } })?.data ?.id; if (newId) { window.location.href = `admin.php?page=yatra&subpage=email-automation&tab=template&action=edit&id=${newId}`; } else { // Fallback to list page window.location.href = "admin.php?page=yatra&subpage=email-automation&tab=templates"; } } else { queryClient.invalidateQueries({ queryKey: ["email-template", id] }); showToast(__("Template saved successfully"), "success"); } }, onError: (error: any) => { const message = error?.message || (isCreateMode ? __("Failed to create template") : __("Failed to save template")); showToast(message, "error"); }, }); const saveCoreMutation = useMutation({ mutationFn: async () => { if (isCoreStrictViewOnly) { throw new Error(__("This template is view-only.", "yatra")); } if ( !coreDef?.settingsFlag || !coreDef.settingsSubject || !coreDef.settingsBody ) { throw new Error(__("Invalid template", "yatra")); } if (!settings) { throw new Error(__("Settings not loaded", "yatra")); } const base = { ...(settings as Record) }; if (isCoreModuleBodyLocked) { base[coreDef.settingsFlag] = formData.is_active; await saveSettings(base); return base; } base[coreDef.settingsSubject] = formData.subject; base[coreDef.settingsBody] = formData.body; base[coreDef.settingsFlag] = formData.is_active; await saveSettings(base); return base; }, onSuccess: (saved) => { queryClient.setQueryData(["settings"], saved); showToast(__("Template saved", "yatra"), "success"); }, onError: (error: unknown) => { const message = error instanceof Error ? error.message : __("Failed to save", "yatra"); showToast(message, "error"); }, }); const previewMutation = useMutation({ mutationFn: async () => { return previewEmailTemplate(id as string); }, onSuccess: (data) => { setPreviewData(data); setShowPreview(true); }, onError: () => { showToast(__("Failed to load preview"), "error"); }, }); const corePreviewMutation = useMutation({ mutationFn: async () => { if (!coreTemplateSlug) { throw new Error(__("Invalid template", "yatra")); } return previewCoreEmailTemplate({ template_key: coreTemplateSlug, subject: formData.subject, body: formData.body, }); }, onSuccess: (data) => { setPreviewData(data); setShowPreview(true); }, onError: (error: unknown) => { const message = error instanceof Error ? error.message : __("Preview failed", "yatra"); showToast(message, "error"); }, }); const testMutation = useMutation({ mutationFn: async (email: string) => { return sendEmailTemplateTest(id as string, email); }, onSuccess: () => { showToast(__("Test email sent successfully"), "success"); setTestEmail(""); }, onError: () => { showToast(__("Failed to send test email"), "error"); }, }); const handleSubmit = (e?: React.FormEvent | React.MouseEvent) => { if (e) e.preventDefault(); if (isCoreSettingsEdit) { if (isCoreStrictViewOnly) { return; } saveCoreMutation.mutate(); return; } if (!formData.name.trim()) { showToast(__("Template name is required"), "error"); return; } if (!formData.subject.trim()) { showToast(__("Subject line is required"), "error"); return; } saveMutation.mutate(formData); }; const copyVariable = (variable: string) => { navigator.clipboard.writeText(`{{${variable}}}`); setCopiedVar(variable); setTimeout(() => setCopiedVar(null), 2000); }; const insertVariable = (variable: string) => { if (isCoreBodyReadOnly) { return; } const textarea = document.getElementById( "email-body", ) as HTMLTextAreaElement; if (textarea) { const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = formData.body; const newText = text.substring(0, start) + `{{${variable}}}` + text.substring(end); setFormData({ ...formData, body: newText }); } }; const showEditSkeleton = !isCreateMode && (isCoreSettingsEdit ? settingsLoading : isLoading); if (showEditSkeleton) { return (
{/* Header Skeleton */}
{/* Main Form Skeleton */}
{/* Template Details Card */}
{/* Test Email Card */}
{/* Sidebar Skeleton */}
{/* Variables Card */}
{[1, 2, 3].map((group) => (
{[1, 2, 3].map((item) => (
))}
))}
{/* Template Info Card */}
{[1, 2, 3].map((i) => (
))}
); } return (
{/* Header */}

{isCreateMode ? __("Create Email Template") : isCoreViewMode ? __("View email template", "yatra") : __("Edit Email Template")}

{isCreateMode ? __("Create a new custom email template") : isCoreSettingsEdit ? isCoreViewMode ? __( "This template uses site settings. Enable Yatra Pro and the required module to edit subject and HTML body.", "yatra", ) : __( "Core customer email (saved with site settings). From address is set under Email → Delivery.", "yatra", ) : String(templateData?.name ?? "")}

{!isCreateMode && ( )}
{isCoreBodyReadOnly && (

{isCoreStrictViewOnly ? __( "This link is view-only. Subject, body, and status cannot be changed from this screen.", "yatra", ) : __( "Subject and HTML body are read-only until the required module is active. You can still turn this email on or off below; values reflect your site settings.", "yatra", )}

)}
{/* Main Form */}
{__("Template Details")}
{formData.is_active ? __("Active") : __("Inactive")} setFormData({ ...formData, is_active: checked }) } />
setFormData({ ...formData, name: e.target.value }) } placeholder={__("Enter template name")} readOnly={isCoreSettingsEdit} className={ isCoreSettingsEdit ? "bg-gray-50 dark:bg-gray-900/40" : "" } />
setFormData({ ...formData, description: e.target.value }) } placeholder={__( "Brief description of when this email is sent", )} readOnly={isCoreSettingsEdit} className={ isCoreSettingsEdit ? "bg-gray-50 dark:bg-gray-900/40" : "" } />
{/* Event Selector */}
{Boolean(templateData?.is_system) || isCoreSettingsEdit ? (
{formData.event_key || "-"} {__("System templates cannot change events")}
{formData.event_key && (

{events.find((e: any) => e.key === formData.event_key) ?.description || ""}

)}
) : (
{/* Dropdown Trigger Button */} {/* Dropdown List */} {showEventDropdown && (

{__( "Select an event to trigger this email, or leave empty for sequence use", )}

{/* None option - for templates used in sequences */} {events.map((event: any) => ( ))}
)} {/* Click outside to close */} {showEventDropdown && (
setShowEventDropdown(false)} /> )}
)}

{__( "This email will be triggered when the selected event occurs", )}

{!isCoreSettingsEdit && ( <> {/* From/Reply-To Settings */}
setFormData({ ...formData, from_name: e.target.value, }) } placeholder={__("e.g., Travel Agency Name")} />

{__("Leave empty to use site default")}

setFormData({ ...formData, from_email: e.target.value, }) } placeholder={__("e.g., noreply@yoursite.com")} />

{__("Leave empty to use site default")}

setFormData({ ...formData, to_email: e.target.value }) } placeholder={__("e.g., {{customer_email}}")} />

{__( "Recipient email. Use variables or leave empty for default", )}

setFormData({ ...formData, reply_to: e.target.value }) } placeholder={__("e.g., support@yoursite.com")} />

{__( "Where replies will be sent. Leave empty to use From Email", )}

)} {isCoreSettingsEdit && (

{__( "From / reply addresses are configured under Email → Delivery. Recipient uses the customer on the booking.", "yatra", )}

)}
setFormData({ ...formData, subject: e.target.value }) } placeholder={__("Email subject with {{variables}}")} readOnly={isCoreBodyReadOnly} className={`font-mono text-sm ${isCoreBodyReadOnly ? "bg-gray-50 dark:bg-gray-900/40" : ""}`} />

{__("Use {{variable}} syntax to insert dynamic content")}

{/* Label + AI sparkle on the same row — sparkle sits to the right of the "Email Body (HTML)" label so it's visually associated with the editor it affects, not floating above Subject Line. */}
{!isCoreBodyReadOnly && isAiEligible() && isAiModuleEnabled() && ( )}