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 { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Loader2, ShieldCheck, ArrowRight, Check } from 'lucide-react'; import { analyzeQuestionTargets, TargetAnalysis } from '@/lib/openai'; import { auditedAdminData } from '@/hooks/useAuditedAdminData'; import { toast } from 'sonner'; // Define local compatible interface interface Question { id: string; text: string; partner_text: string | null; intensity: number; allowed_couple_genders: string[] | null; target_user_genders: string[] | null; } interface FixTargetsDialogProps { open: boolean; onOpenChange: (open: boolean) => void; questions: Question[]; onUpdated: () => void; } export function FixTargetsDialog({ open, onOpenChange, questions, onUpdated }: FixTargetsDialogProps) { const [analyzing, setAnalyzing] = useState(false); const [suggestions, setSuggestions] = useState([]); const [selectedIds, setSelectedIds] = useState>(new Set()); const [step, setStep] = useState<'initial' | 'review'>('initial'); const [applying, setApplying] = useState(false); // Check if couple targets represent "All" (null, empty, or all three values) const isAllCoupleTargets = (targets: string[] | null): boolean => { if (!targets || targets.length === 0) return true; if (targets.length === 3) { const sorted = [...targets].sort(); return sorted[0] === 'female+female' && sorted[1] === 'female+male' && sorted[2] === 'male+male'; } return false; }; // Check if initiator targets represent "All" (null or empty) const isAllInitiator = (targets: string[] | null): boolean => { return !targets || targets.length === 0; }; // Check if two arrays are equal const arraysEqual = (a: string[] | null, b: string[] | null): boolean => { if (!a && !b) return true; if (!a || !b) return false; if (a.length !== b.length) return false; const sortedA = [...a].sort(); const sortedB = [...b].sort(); return sortedA.every((val, i) => val === sortedB[i]); }; const handleAnalyze = async () => { setAnalyzing(true); try { const results = await analyzeQuestionTargets(questions); // Filter out redundant suggestions where nothing meaningful changes const meaningfulResults = results.filter(suggestion => { const question = questions.find(q => q.id === suggestion.id); if (!question) return false; // Check if couple targets change meaningfully const currentCoupleIsAll = isAllCoupleTargets(question.allowed_couple_genders); const suggestedCoupleIsAll = isAllCoupleTargets(suggestion.suggested_targets); const coupleChanged = !(currentCoupleIsAll && suggestedCoupleIsAll) && !arraysEqual(question.allowed_couple_genders, suggestion.suggested_targets); // Check if initiator changes meaningfully const currentInitiatorIsAll = isAllInitiator(question.target_user_genders); const suggestedInitiatorIsAll = isAllInitiator(suggestion.suggested_initiator); const initiatorChanged = !(currentInitiatorIsAll && suggestedInitiatorIsAll) && !arraysEqual(question.target_user_genders, suggestion.suggested_initiator); // Include if either changed return coupleChanged || initiatorChanged; }); setSuggestions(meaningfulResults); // Default select all setSelectedIds(new Set(meaningfulResults.map(r => r.id))); setStep('review'); } catch (error) { console.error(error); toast.error('Failed to analyze questions'); } finally { setAnalyzing(false); } }; const handleApply = async () => { setApplying(true); try { const updates = suggestions.filter(s => selectedIds.has(s.id)); if (updates.length === 0) { onOpenChange(false); return; } // Apply updates in parallel with audit logging await Promise.all(updates.map(update => auditedAdminData.update('questions', update.id, { allowed_couple_genders: update.suggested_targets && update.suggested_targets.length > 0 ? update.suggested_targets : null, target_user_genders: update.suggested_initiator && update.suggested_initiator.length > 0 ? update.suggested_initiator : null, }) )); toast.success(`Updated ${updates.length} questions`); onUpdated(); handleClose(); } catch (error) { console.error(error); toast.error('Failed to apply updates'); } finally { setApplying(false); } }; const handleClose = () => { onOpenChange(false); // Reset state after transition setTimeout(() => { setStep('initial'); setSuggestions([]); }, 300); }; const toggleSelection = (id: string) => { const next = new Set(selectedIds); if (next.has(id)) next.delete(id); else next.add(id); setSelectedIds(next); }; const formatTargets = (targets: string[] | null) => { if (!targets || targets.length === 0) return 'All'; return targets.map(t => t.replace('male+male', 'M+M').replace('female+male', 'M+F').replace('female+female', 'F+F')).join(', '); }; const formatInitiator = (targets: string[] | null) => { if (!targets || targets.length === 0) return 'Any'; return targets.map(t => t.charAt(0).toUpperCase() + t.slice(1)).join(', '); }; return ( Fix Question Targets Use AI to detect anatomical or configuration constraints in your questions.
{step === 'initial' ? (

Ready to analyze {questions.length} questions

The AI will look for anatomical requirements (e.g., acts requiring specific genitalia) and suggest target restrictions.

) : (
{suggestions.length === 0 ? (
No issues found! All questions seem to have appropriate targets.
) : (
0} onChange={(e) => { if (e.target.checked) setSelectedIds(new Set(suggestions.map(s => s.id))); else setSelectedIds(new Set()); }} /> Question Couples Initiator Reason {suggestions.map((suggestion) => { const question = questions.find(q => q.id === suggestion.id); if (!question) return null; return ( toggleSelection(suggestion.id)} />
{question.text}
{question.partner_text && (
Partner: {question.partner_text}
)}
{formatTargets(question.allowed_couple_genders)} {formatTargets(suggestion.suggested_targets)}
{formatInitiator(question.target_user_genders)} {formatInitiator(suggestion.suggested_initiator)}
{suggestion.reason}
); })}
)}
)}
{step === 'review' && ( <> )}
); }