import React, { useCallback, useEffect, useState } from 'react';
import { useAppStateContext } from '../../context/user.data.context';
import {
listPromptTemplates,
createPromptTemplate,
updatePromptTemplate,
deletePromptTemplate,
improvePrompt,
} from '../../service/content-generator/content-generator.service';
import type { PromptTemplate } from '../../service/content-generator/content-generator.interface';
import {
EMPTY_PROMPT_TEMPLATE_DRAFT,
toPromptTemplateDraft,
toTemplatePayload,
type PromptTemplateDraft,
} from './PromptTemplateDraft';
const TONES = [
{ value: 'professional', label: 'Professional' },
{ value: 'friendly', label: 'Friendly' },
{ value: 'luxury', label: 'Luxury' },
{ value: 'technical', label: 'Technical' },
{ value: 'custom', label: 'Custom…' },
];
const LANGUAGES = [
{ value: 'en', label: 'English' },
{ value: 'ro', label: 'Romanian' },
{ value: 'de', label: 'German' },
{ value: 'fr', label: 'French' },
{ value: 'es', label: 'Spanish' },
{ value: 'it', label: 'Italian' },
{ value: 'nl', label: 'Dutch' },
{ value: 'pt', label: 'Portuguese' },
{ value: 'pl', label: 'Polish' },
{ value: 'el', label: 'Greek' },
{ value: 'tr', label: 'Turkish' },
{ value: 'hu', label: 'Hungarian' },
{ value: 'cs', label: 'Czech' },
{ value: 'sv', label: 'Swedish' },
{ value: 'da', label: 'Danish' },
{ value: 'fi', label: 'Finnish' },
{ value: 'no', label: 'Norwegian' },
{ value: 'bg', label: 'Bulgarian' },
{ value: 'hr', label: 'Croatian' },
{ value: 'ar', label: 'Arabic' },
];
const PROMPT_FIELDS: {
key: keyof Pick<
PromptTemplateDraft,
'titles_prompt' | 'descriptions_prompt' | 'tags_prompt' | 'faq_prompt'
>;
label: string;
placeholder: string;
fieldName: string;
maxLength: number;
}[] = [
{
key: 'titles_prompt',
label: 'Titles',
placeholder: 'e.g., Always start with the brand name, max 70 characters…',
fieldName: 'titles',
maxLength: 1500,
},
{
key: 'descriptions_prompt',
label: 'Descriptions',
placeholder:
'e.g., Include a benefits section, use bullet points, max 200 words…',
fieldName: 'descriptions',
maxLength: 4500,
},
{
key: 'tags_prompt',
label: 'Tags',
placeholder: 'e.g., Include seasonal tags, max 8 tags, comma-separated…',
fieldName: 'tags',
maxLength: 1500,
},
{
key: 'faq_prompt',
label: 'FAQ',
placeholder:
'e.g., Focus on shipping and returns questions, 3-5 Q&A pairs…',
fieldName: 'faq',
maxLength: 1500,
},
];
interface ConfirmModalProps {
open: boolean;
message: string;
onConfirm: () => void;
onCancel: () => void;
busy?: boolean;
}
const ConfirmModal = ({
open,
message,
onConfirm,
onCancel,
busy,
}: ConfirmModalProps): JSX.Element | null => {
if (!open) return null;
return (
{message}
);
};
const PAGE_SIZE = 10;
const PromptTemplates = (): JSX.Element => {
const { clientId } = useAppStateContext();
const [view, setView] = useState<'list' | 'form'>('list');
const [templates, setTemplates] = useState([]);
const [loading, setLoading] = useState(false);
const [listError, setListError] = useState(null);
const [nextCursor, setNextCursor] = useState(null);
const [prevCursors, setPrevCursors] = useState([]);
const [draft, setDraft] = useState(
EMPTY_PROMPT_TEMPLATE_DRAFT
);
const [saving, setSaving] = useState(false);
const [formError, setFormError] = useState(null);
const [deleteId, setDeleteId] = useState(null);
const [deleteTemplateName, setDeleteTemplateName] = useState('');
const [deleteBusy, setDeleteBusy] = useState(false);
const [improvingField, setImprovingField] = useState(null);
const fetchPage = useCallback(
async (cursor?: string | null) => {
if (!clientId) return;
setLoading(true);
setListError(null);
try {
const res = await listPromptTemplates(clientId, cursor);
if (res?.data) {
setTemplates(res.data.templates);
setNextCursor(res.data.next_cursor);
} else {
setListError(res?.error || 'Failed to load templates.');
}
} catch {
setListError('Failed to load templates.');
} finally {
setLoading(false);
}
},
[clientId]
);
useEffect(() => {
fetchPage(null);
}, [fetchPage]);
const handleNext = () => {
if (!nextCursor) return;
setPrevCursors(prev => [...prev, /* current offset */ '']);
fetchPage(nextCursor);
};
const handlePrev = () => {
const cursors = [...prevCursors];
cursors.pop();
setPrevCursors(cursors);
fetchPage(cursors[cursors.length - 1] ?? null);
};
const openCreate = () => {
setDraft(EMPTY_PROMPT_TEMPLATE_DRAFT);
setFormError(null);
setView('form');
};
const openEdit = (template: PromptTemplate) => {
setDraft(toPromptTemplateDraft(template));
setFormError(null);
setView('form');
};
const handleSave = async () => {
if (!clientId) return;
if (!draft.name.trim()) {
setFormError('Template name is required.');
return;
}
setSaving(true);
setFormError(null);
try {
const payload = toTemplatePayload(draft);
const res = draft.template_id
? await updatePromptTemplate(draft.template_id, payload, clientId)
: await createPromptTemplate(payload, clientId);
if (res?.error || res?.errors) {
setFormError(
(res.error ||
String(res.errors) ||
'Failed to save template.') as string
);
return;
}
setView('list');
fetchPage(null);
setPrevCursors([]);
} catch {
setFormError('Failed to save template.');
} finally {
setSaving(false);
}
};
const handleDelete = async () => {
if (!clientId || !deleteId) return;
setDeleteBusy(true);
try {
await deletePromptTemplate(deleteId, clientId);
setDeleteId(null);
fetchPage(null);
setPrevCursors([]);
} catch {
/* ignore */
} finally {
setDeleteBusy(false);
}
};
const handleImproveField = async (
fieldKey: keyof Pick<
PromptTemplateDraft,
'titles_prompt' | 'descriptions_prompt' | 'tags_prompt' | 'faq_prompt'
>,
fieldName: string
) => {
if (!clientId) return;
const current = draft[fieldKey];
if (!current.trim()) return;
setImprovingField(fieldKey);
try {
const res = await improvePrompt(
current,
fieldName,
draft.language,
clientId
);
const improved = res?.data?.improved_prompt;
if (improved) {
setDraft(prev => ({ ...prev, [fieldKey]: improved }));
}
} catch {
/* ignore */
} finally {
setImprovingField(null);
}
};
if (view === 'form') {
return (
{draft.template_id ? 'Edit Template' : 'New Template'}
setDraft(prev => ({ ...prev, name: e.target.value }))
}
placeholder="e.g., Electronics – English"
className="block w-full rounded-md border-gray-300 px-3 py-2 text-sm focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900"
/>
setDraft(prev => ({ ...prev, description: e.target.value }))
}
placeholder="Short description for your reference"
className="block w-full rounded-md border-gray-300 px-3 py-2 text-sm focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900"
/>
{draft.tone === 'custom' && (
)}
Field Instructions
{PROMPT_FIELDS.map(
({ key, label, placeholder, fieldName, maxLength }) => (
)
)}
{formError && (
{formError}
)}
);
}
return (
setDeleteId(null)}
busy={deleteBusy}
/>
Prompt Templates
Reusable instruction sets you can apply when generating content.
{listError && (
{listError}
)}
{loading ? (
{[1, 2, 3].map(n => (
))}
) : templates.length === 0 ? (
No templates yet
Create a template to save your generation settings for reuse.
) : (
{templates.map(template => {
const promptPreviews = [
{ label: 'Titles', value: template.titles_prompt },
{ label: 'Descriptions', value: template.descriptions_prompt },
{ label: 'Tags', value: template.tags_prompt },
{ label: 'FAQ', value: template.faq_prompt },
].filter(p => p.value);
return (
{template.name}
{template.language}
{template.tone === 'custom'
? 'Custom tone'
: template.tone}
temp {template.temperature}
{template.description && (
{template.description}
)}
{promptPreviews.length > 0 && (
{promptPreviews.map(p => (
{p.label}:
{p.value}
))}
)}
);
})}
)}
{(prevCursors.length > 0 || nextCursor) && (
{templates.length} of {PAGE_SIZE} per page
)}
);
};
export default PromptTemplates;