import React, { useState } from "react"; import { Sparkles, Loader2, X, Check } from "lucide-react"; import { __ } from "../../lib/i18n"; import { aiApi } from "../../api/ai-api"; import { isAiReady } from "../../lib/ai-availability"; import { Modal } from "../ui/modal"; import { Button } from "../ui/button"; /** * AI generator for one email-template's subject + body. * * The caller (EmailTemplateForm) opens this with the template's * catalog metadata pre-filled — template_key, name, description, * recipient_type, merge_tags. The operator picks a tone, optionally * adds extra context, hits Generate. The backend agent emits one * `set_email(subject, body)` tool call so the response shape is * guaranteed structured (no regex parsing). * * On accept, the modal calls `onAccept({subject, body})` and the * form replaces its current values with the AI output. On cancel, * the existing subject/body stay untouched — the operator can try * again without losing what they had. */ interface MergeTag { key: string; label?: string; } interface Props { open: boolean; onClose: () => void; templateKey: string; templateName?: string; templateDescription?: string; recipientType?: "customer" | "admin"; mergeTags: string[]; currentSubject: string; currentBody: string; onAccept: (result: { subject: string; body: string }) => void; } const TONE_PRESETS = [ { id: "warm-professional", label: "Warm + professional" }, { id: "friendly-casual", label: "Friendly + casual" }, { id: "concise-formal", label: "Concise + formal" }, { id: "excited-promotional", label: "Excited + promotional" }, ]; export const AiEmailTemplateModal: React.FC = ({ open, onClose, templateKey, templateName, templateDescription, recipientType = "customer", mergeTags, currentSubject, currentBody, onAccept, }) => { const [tone, setTone] = useState(TONE_PRESETS[0].id); const [extraContext, setExtraContext] = useState(""); const [isGenerating, setIsGenerating] = useState(false); const [error, setError] = useState(null); const [draft, setDraft] = useState<{ subject: string; body: string } | null>( null, ); // Reset when the modal opens/closes — operators should see a fresh // form each time they reopen the sparkle, not stale state. React.useEffect(() => { if (open) { setError(null); setDraft(null); setIsGenerating(false); } }, [open]); if (!open) return null; if (!isAiReady()) { return (

{__("AI Assistant not configured", "yatra")}

{__( "Add an OpenAI or Anthropic key under Yatra → AI Assistant first.", "yatra", )}

); } const generate = async () => { setIsGenerating(true); setError(null); try { const result = await aiApi.generateEmailTemplate({ template_key: templateKey, template_name: templateName, template_description: templateDescription, recipient_type: recipientType, merge_tags: mergeTags, current_subject: currentSubject, current_body: currentBody, tone: TONE_PRESETS.find((t) => t.id === tone)?.label ?? tone, extra_context: extraContext, }); setDraft({ subject: result.subject, body: result.body }); } catch (e: any) { const data = e?.response?.data ?? e?.data ?? null; const msg = data && typeof data === "object" && typeof data.message === "string" ? data.message : e?.message || __("AI generation failed.", "yatra"); setError(msg); } finally { setIsGenerating(false); } }; const accept = () => { if (!draft) return; onAccept({ subject: draft.subject, body: draft.body }); onClose(); }; return (
{/* Header */}

{__("Generate email with AI", "yatra")}

{templateName || templateKey}
{/* Body */}
{!draft && ( <>

{templateDescription || __( "AI will write a fresh subject and body using only the merge tags this template supports.", "yatra", )}

{TONE_PRESETS.map((t) => ( ))}