import { useState, useEffect, useCallback } from 'react'; import { useParams, Link } from 'react-router-dom'; import { supabase, supabaseConfig } from '@/config'; import { auditedSupabase } from '@/hooks/useAuditedSupabase'; import { useRealtimeSubscription } from '@/hooks/useRealtimeSubscription'; import { RealtimeStatusIndicator } from '@/components/RealtimeStatusIndicator'; import { useAuth, PERMISSION_KEYS } from '@/contexts/AuthContext'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { PaginationControls } from '@/components/ui/pagination'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Crown, Users, MessageCircle, ChevronRight, ThumbsUp, ThumbsDown, Minus, Gift, Image, Video as VideoIcon, Target, User, Eye, EyeOff, CheckCircle } from 'lucide-react'; import { format } from 'date-fns'; type UsageReason = 'improve_communication' | 'spice_up_intimacy' | 'deeper_connection' | 'have_fun' | 'strengthen_relationship'; type Gender = 'male' | 'female' | 'non-binary' | 'prefer-not-to-say'; // ... (interfaces and constants are the same) interface Profile { id: string; name: string | null; email: string | null; avatar_url: string | null; is_premium: boolean | null; couple_id: string | null; created_at: string | null; gender: Gender | null; usage_reason: UsageReason | null; show_explicit_content: boolean | null; onboarding_completed: boolean | null; } const usageReasonLabels: Record = { improve_communication: { label: 'Improve communication', icon: 'chatbubbles' }, spice_up_intimacy: { label: 'Spice up intimacy', icon: 'flame' }, deeper_connection: { label: 'Build deeper connection', icon: 'heart' }, have_fun: { label: 'Have fun together', icon: 'happy' }, strengthen_relationship: { label: 'Strengthen relationship', icon: 'shield-checkmark' }, }; const genderLabels: Record = { male: 'Male', female: 'Female', 'non-binary': 'Non-binary', 'prefer-not-to-say': 'Prefer not to say', }; interface Partner { id: string; name: string | null; } interface Response { id: string; answer: 'yes' | 'no' | 'maybe'; created_at: string | null; question: { id: string; text: string; pack: { name: string; }; }; } interface Match { id: string; match_type: 'yes_yes' | 'yes_maybe' | 'maybe_maybe'; is_new: boolean; created_at: string | null; question: { id: string; text: string; }; message_count?: number; } const answerIcons = { yes: , no: , maybe: , }; const matchTypeLabels = { yes_yes: 'Both Yes!', yes_maybe: 'Yes + Maybe', maybe_maybe: 'Both Maybe', }; function AdminDecryptedImage({ messageId, alt }: { messageId: string; alt: string }) { const { session, isSuperAdmin } = useAuth(); const [url, setUrl] = useState(null); const [error, setError] = useState(null); useEffect(() => { if (!messageId) return; if (!isSuperAdmin) { setError("Super admin required"); return; } if (!session?.access_token) { setError("Not authenticated"); return; } let cancelled = false; let objectUrl: string | null = null; const load = async () => { try { setError(null); const res = await fetch(`${supabaseConfig.url}/functions/v1/admin-decrypt-media`, { method: 'POST', headers: { 'Content-Type': 'application/json', apikey: supabaseConfig.anonKey, Authorization: `Bearer ${session.access_token}` }, body: JSON.stringify({ messageId }), }); if (!res.ok) { const message = (await res.json().catch(() => ({})))?.error || `Failed to load media (${res.status})`; throw new Error(message); } const blob = await res.blob(); objectUrl = URL.createObjectURL(blob); if (!cancelled) setUrl(objectUrl); } catch (err) { if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load'); } }; load(); return () => { cancelled = true; if (objectUrl) URL.revokeObjectURL(objectUrl); }; }, [isSuperAdmin, messageId, session?.access_token]); if (error) { return
{error}
; } if (!url) { return
; } return {alt} window.open(url, '_blank')} />; } function AdminDecryptedVideo({ messageId }: { messageId: string }) { const { session, isSuperAdmin } = useAuth(); const [url, setUrl] = useState(null); const [error, setError] = useState(null); useEffect(() => { if (!messageId) return; if (!isSuperAdmin) { setError("Super admin required"); return; } if (!session?.access_token) { setError("Not authenticated"); return; } let cancelled = false; let objectUrl: string | null = null; const load = async () => { try { setError(null); const res = await fetch(`${supabaseConfig.url}/functions/v1/admin-decrypt-media`, { method: 'POST', headers: { 'Content-Type': 'application/json', apikey: supabaseConfig.anonKey, Authorization: `Bearer ${session.access_token}` }, body: JSON.stringify({ messageId }), }); if (!res.ok) { const message = (await res.json().catch(() => ({})))?.error || `Failed to load media (${res.status})`; throw new Error(message); } const blob = await res.blob(); objectUrl = URL.createObjectURL(blob); if (!cancelled) setUrl(objectUrl); } catch (err) { if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load'); } }; load(); return () => { cancelled = true; if (objectUrl) URL.revokeObjectURL(objectUrl); }; }, [isSuperAdmin, messageId, session?.access_token]); if (error) { return
{error}
; } if (!url) { return
; } return