import { useState } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Loader2, Tags, Check, Plus, X } from 'lucide-react'; import { extractTopicsFromPack, ExtractedTopic } from '@/lib/openai'; import { adminData } from '@/lib/adminApi'; import { toast } from 'sonner'; interface ExtractTopicsDialogProps { open: boolean; onOpenChange: (open: boolean) => void; pack: { id: string; name: string; description: string | null; }; onUpdated: () => void; } export function ExtractTopicsDialog({ open, onOpenChange, pack, onUpdated }: ExtractTopicsDialogProps) { const [analyzing, setAnalyzing] = useState(false); const [topics, setTopics] = useState([]); const [reasoning, setReasoning] = useState(''); const [selectedTopics, setSelectedTopics] = useState>(new Set()); const [step, setStep] = useState<'initial' | 'review'>('initial'); const [applying, setApplying] = useState(false); const handleAnalyze = async () => { setAnalyzing(true); try { // Fetch questions for this pack const { data: questions, error: questionsError } = await adminData .from('questions') .select('text') .eq('pack_id', pack.id); if (questionsError) throw questionsError; if (!questions || questions.length === 0) { toast.error('This pack has no questions to analyze'); return; } // Fetch existing topics const { data: existingTopics, error: topicsError } = await adminData .from('topics') .select('id, name') .order('name'); if (topicsError) throw topicsError; const result = await extractTopicsFromPack( pack.name, pack.description, questions.map(q => q.text), existingTopics || [] ); setTopics(result.topics); setReasoning(result.reasoning); // Default select all setSelectedTopics(new Set(result.topics.map(t => t.name))); setStep('review'); } catch (error) { console.error(error); toast.error('Failed to analyze pack'); } finally { setAnalyzing(false); } }; const handleApply = async () => { setApplying(true); try { const selectedList = topics.filter(t => selectedTopics.has(t.name)); if (selectedList.length === 0) { onOpenChange(false); return; } // First, create any new topics const newTopics = selectedList.filter(t => t.isNew); const existingTopicIds: string[] = selectedList .filter(t => !t.isNew && t.existingTopicId) .map(t => t.existingTopicId!); if (newTopics.length > 0) { const { data: createdTopics, error: createError } = await adminData .from('topics') .insert(newTopics.map(t => ({ name: t.name }))) .select('id'); if (createError) throw createError; if (createdTopics) { existingTopicIds.push(...createdTopics.map(t => t.id)); } } // Remove existing pack_topics for this pack await adminData .from('pack_topics') .delete() .eq('pack_id', pack.id); // Insert new pack_topics relationships if (existingTopicIds.length > 0) { const { error: linkError } = await adminData .from('pack_topics') .insert(existingTopicIds.map(topicId => ({ pack_id: pack.id, topic_id: topicId, }))); if (linkError) throw linkError; } toast.success(`Added ${selectedList.length} topic${selectedList.length !== 1 ? 's' : ''} to pack`); onUpdated(); handleClose(); } catch (error) { console.error(error); toast.error('Failed to apply topics'); } finally { setApplying(false); } }; const handleClose = () => { onOpenChange(false); // Reset state after transition setTimeout(() => { setStep('initial'); setTopics([]); setReasoning(''); setSelectedTopics(new Set()); }, 300); }; const toggleSelection = (name: string) => { const next = new Set(selectedTopics); if (next.has(name)) next.delete(name); else next.add(name); setSelectedTopics(next); }; return ( Extract Topics Use AI to identify topics and kinks for "{pack.name}"
{step === 'initial' ? (

Analyze pack for topics

The AI will analyze the questions in this pack and suggest relevant topics like bondage, exhibitionism, romance, etc. It will prefer matching existing topics when possible.

) : (
{topics.length === 0 ? (
No topics could be identified for this pack.
) : ( <>

Suggested Topics

{topics.map((topic) => ( toggleSelection(topic.name)} > {selectedTopics.has(topic.name) ? ( ) : null} {topic.name} {topic.isNew && ( )} ))}
Existing topic
New topic
{reasoning && (

AI Reasoning

{reasoning}

)} )}
)}
{step === 'review' && ( <> )}
); }