import { useEffect, useState, type ReactElement } from "react"; import { useNavigate } from "react-router-dom"; import { Layout } from "@/components/layout/Layout"; import { FormField, FormSelect, FormTextArea, PrimaryButton, SecondaryButton, FormSlider, FormTagInput, } from "@/components/shared"; import { Plus, Trash2 } from "lucide-react"; import { aiService } from "@/services/ai-service"; import { showToast } from "@/utils/toast"; import { useForm, useFieldArray, Controller } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { cn } from "@/lib/utils"; import type { AIProviderInfo } from "@/types/ai"; const variableSchema = z.object({ name: z.string().min(1, "Variable name is required").max(100), type: z.enum(["string", "number", "boolean", "array"]), required: z.boolean(), default: z.string().optional(), }); const promptSchema = z.object({ name: z.string().min(1, "Name is required").max(255), description: z.string().optional(), use_case: z.string().min(1, "Use case is required").max(100), system_message: z.string().optional(), user_template: z.string().min(1, "User template is required").max(50000), provider: z.string().optional(), model: z.string().optional(), temperature: z.number().min(0).max(2), max_tokens: z.number().int().min(1).max(128000), tags: z.array(z.string()), is_default: z.boolean(), variables: z.array(variableSchema), }); type PromptFormData = z.infer; const PromptCreate = (): ReactElement => { const navigate = useNavigate(); const [isSubmitting, setIsSubmitting] = useState(false); const [apiProviders, setApiProviders] = useState([]); const { control, handleSubmit, setValue, watch, formState: { errors }, } = useForm({ resolver: zodResolver(promptSchema), defaultValues: { name: "", description: "", use_case: "", system_message: "", user_template: "", provider: "", model: "", temperature: 0.7, max_tokens: 2048, tags: [], is_default: true, variables: [ { name: "", type: "string", required: false, default: "", }, ], }, }); const { fields, append, remove } = useFieldArray({ control, name: "variables", }); const selectedProvider = watch("provider"); const providersOptions = apiProviders.map((p) => ({ value: p.name, label: p.displayName || p.name, })); const providerDetail = apiProviders.find((p) => p.name === selectedProvider); const modelsOptions = (providerDetail?.models || []).map((m) => ({ value: m, label: m, })); useEffect(() => { const loadMeta = async (): Promise => { try { const providerData = await aiService.getProviders(); setApiProviders(providerData); } catch (err: unknown) { showToast.error( (err as { response?: { data?: { error?: { message?: string } } } }) ?.response?.data?.error?.message || "Failed to load provider metadata", ); } }; void loadMeta(); }, []); const onFormSubmit = async (data: PromptFormData): Promise => { const parsedTags = data.tags || []; const sanitizedVariables = data.variables ?.filter((v) => v.name.trim()) .map((v) => ({ name: v.name.trim(), type: v.type, required: v.required, ...(v.default?.trim() ? { default: v.default.trim() } : {}), })); setIsSubmitting(true); try { await aiService.createPrompt({ name: data.name.trim(), description: data.description?.trim() || undefined, use_case: data.use_case.trim(), system_message: data.system_message?.trim() || undefined, user_template: data.user_template, model: data.model || undefined, provider: data.provider || undefined, temperature: data.temperature, max_tokens: data.max_tokens, variables: sanitizedVariables, tags: parsedTags, is_default: data.is_default, }); showToast.success("Prompt created successfully"); navigate("/tenant/ai/prompts"); } catch (err: unknown) { showToast.error( (err as { response?: { data?: { error?: { message?: string } } } }) ?.response?.data?.error?.message || "Failed to create prompt", ); } finally { setIsSubmitting(false); } }; const handleCreatePrompt = () => { handleSubmit(onFormSubmit)(); }; return ( Create Prompt Draft ), description: "Manage and reuse prompts for different use cases", action: (
navigate("/tenant/ai/prompts")}> Cancel {isSubmitting ? "Creating..." : "Create Prompt"}
), }} >
{/* LEFT SIDE */}
{/* General Information */}

General Information

( )} /> ( )} />
{/* Prompt Content */}

Prompt Content

( )} /> ( )} /> {/* Variables Section */}

Variables Configuration

append({ name: "", type: "string", required: false, default: "", }) } size="small" className="h-8 py-0" > Add Variable
{fields.length > 0 && (
Variable Name Type Default Value Required
)} {fields.length === 0 && (

No variables configured yet.

)}
{fields.map((field, index) => (
(
{errors.variables?.[index]?.name && (

{errors.variables?.[index]?.name?.message}

)}
)} /> (
)} /> (
{errors.variables?.[index]?.default && (

{errors.variables?.[index]?.default?.message}

)}
)} />
(
field.onChange(!field.value)} >
)} />
))}
{/* RIGHT SIDE (Sidebar) */}
{/* Settings */}

Settings

( )} />
(
field.onChange(!field.value)} >
)} /> Make default for this use case
{/* Model Config */}

Model Configuration

( { field.onChange(val); const pDetail = apiProviders.find((p) => p.name === val); if (pDetail && pDetail.defaultModel) { setValue("model", pDetail.defaultModel); } else if (pDetail && pDetail.models && pDetail.models.length > 0) { setValue("model", pDetail.models[0]); } else { setValue("model", ""); } }} options={providersOptions} error={errors.provider?.message} /> )} /> ( )} /> ( )} /> ( )} />
{/* Organization */}

Organization

( )} />
); }; export default PromptCreate;