import { useEffect, useMemo, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Skeleton } from "@/components/ui/skeleton"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Textarea } from "@/components/ui/textarea"; import { useAIPrompt, useAIPromptKeys, useAIPromptVersions, useActivateAIPrompt, useCreateAIPrompt, useRenderAIPrompt, } from "@/hooks/queries/useAIPrompts"; import type { AIPromptSummary } from "@/types/ai-prompt"; import { CheckCircle2, FileText, History, Play, PlusCircle } from "lucide-react"; import { toast } from "sonner"; function SelectedKeyPanel({ selectedKey, onSelectVersion, activePromptId, }: { selectedKey: string; onSelectVersion: (id: number) => void; activePromptId: number | null; }) { const { data, isLoading } = useAIPromptVersions(selectedKey); const activate = useActivateAIPrompt(); if (isLoading) { return ; } const versions = data ?? []; if (!versions.length) { return (

No versions yet for this prompt.

); } return ( Version Title Author Status Actions {versions.map((v) => ( v{v.version} {v.title} {v.author_name || "—"} {v.is_active ? ( Active ) : ( Archived )} {!v.is_active ? ( ) : null} ))}
); } function PromptDetail({ promptId }: { promptId: number }) { const { data, isLoading } = useAIPrompt(promptId); const render = useRenderAIPrompt(); // Variables detected from the prompt body — keep local state keyed by name // so the editor can show one input per placeholder. const [sampleValues, setSampleValues] = useState>({}); const [rendered, setRendered] = useState(""); useEffect(() => { setRendered(""); setSampleValues({}); }, [promptId]); if (isLoading || !data) { return ; } const variables = data.variables ?? []; return (

{data.title}{" "} ({data.key} v{data.version})

{data.description ? (

{data.description}

) : null}
            {data.content}
          
{variables.length > 0 ? (
{variables.map((v) => (
setSampleValues((prev) => ({ ...prev, [v]: e.target.value, })) } placeholder={`Sample value for {${v}}`} />
))}
{rendered ? (
                {rendered}
              
) : null}
) : null}
); } function NewVersionDialog({ open, onOpenChange, defaultKey, defaultContent, }: { open: boolean; onOpenChange: (v: boolean) => void; defaultKey: string; defaultContent: string; }) { const create = useCreateAIPrompt(); const [key, setKey] = useState(defaultKey); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [content, setContent] = useState(defaultContent); const [activate, setActivate] = useState(true); useEffect(() => { if (open) { setKey(defaultKey); setContent(defaultContent); setTitle(""); setDescription(""); setActivate(true); } }, [open, defaultKey, defaultContent]); return ( New prompt version
setKey(e.target.value.trim())} placeholder="exam.mcq.generate" />

Lowercase dotted identifier. Reuse an existing key to bump its version.

setTitle(e.target.value)} placeholder="MCQ generation — CEFR B2 — v6" />