import React, { useEffect, useState } from 'react'; import type { PromptResponse } from '../../service/visibility/visibility.interface'; import { regeneratePrompt, updatePrompt, } from '../../service/visibility/visibility.service'; import Modal from './Modal'; /** One-click rewrite presets; the backend grounds the actual product wording. */ const REGENERATE_PRESETS: { label: string; instruction: string }[] = [ { label: 'More specific', instruction: 'Make it more specific and long-tail, naming a concrete product and use-case.', }, { label: 'Where to buy', instruction: 'Phrase it as a where-to-buy / purchase-intent query.', }, { label: 'Comparison', instruction: 'Turn it into a comparison of two real options within the same product type.', }, { label: 'Shorter', instruction: 'Make it shorter and more natural, the way a person quickly types it.', }, ]; /** Props for {@link EditPromptModal}. */ interface EditPromptModalProps { /** Modal open flag. */ open: boolean; /** The prompt being edited. ``null`` hides the modal body. */ prompt: PromptResponse | null; /** Close handler. Fires on cancel and after a successful save. */ onClose: () => void; /** Called with the refreshed prompt row after the backend confirms. */ onSaved: (updated: PromptResponse) => void; /** Recomaze client id. */ clientId: string; /** Recomaze JWT. */ token: string; /** Brand the prompt belongs to. */ brandId: string; } /** * Free-text edit dialog for one tracked prompt. The change takes effect * on the next weekly scan, so we surface that expectation explicitly. * * @param {EditPromptModalProps} props - Dialog props. * @returns {JSX.Element} The modal. */ const EditPromptModal = ({ open, prompt, onClose, onSaved, clientId, token, brandId, }: EditPromptModalProps): JSX.Element => { const [text, setText] = useState(''); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [instruction, setInstruction] = useState(''); const [regenerating, setRegenerating] = useState(false); useEffect(() => { if (open && prompt) { setText(prompt.prompt_text); setError(null); setInstruction(''); } }, [open, prompt]); const handleRegenerate = async ( presetInstruction?: string ): Promise => { if (!prompt) return; const steer = (presetInstruction ?? instruction).trim(); setRegenerating(true); setError(null); try { const updated = await regeneratePrompt( clientId, token, brandId, prompt.prompt_id, { instruction: steer || undefined } ); setText(updated.prompt_text); onSaved(updated); } catch (err) { setError( err instanceof Error ? err.message : 'Failed to regenerate prompt.' ); } finally { setRegenerating(false); } }; const trimmed = text.trim(); const unchanged = prompt !== null && trimmed === prompt.prompt_text.trim(); const tooShort = trimmed.length < 3; const handleSave = async (): Promise => { if (!prompt || tooShort || unchanged) return; setSubmitting(true); setError(null); try { const updated = await updatePrompt( clientId, token, brandId, prompt.prompt_id, { text: trimmed } ); onSaved(updated); onClose(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to save prompt.'); } finally { setSubmitting(false); } }; return ( } >
{error && (
{error}
)}