import { useState, useEffect, useCallback } from 'react'; import { Link } from 'react-router-dom'; import { useAuth, PERMISSION_KEYS } from '@/contexts/AuthContext'; import { adminData, adminRequest } from '@/lib/adminApi'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { LayoutGrid, Users, ClipboardList, ArrowRight, Activity, Target, TrendingUp, Heart, MessageCircle, Bell } from 'lucide-react'; import { Skeleton } from '@/components/ui/skeleton'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis, Tooltip } from 'recharts'; import { format, subDays, startOfDay, endOfDay, formatDistanceToNow } from 'date-fns'; import { useRealtimeSubscription } from '@/hooks/useRealtimeSubscription'; import { RealtimeStatusIndicator } from '@/components/RealtimeStatusIndicator'; type FeatureInterestChartPoint = { date: string; count: number }; type RecentFeatureInterest = { id: string; user_id: string; feature_name: string; created_at: string; profile?: { id: string; name: string | null; email: string | null; avatar_url: string | null; }; }; type FeatureInterestCountsRow = { feature_name: string; opt_in_count: number; opt_in_count_last_7_days: number; }; function formatFeatureName(featureName: string) { return featureName .split('_') .filter(Boolean) .map(part => part.charAt(0).toUpperCase() + part.slice(1)) .join(' '); } export function DashboardPage() { const { hasPermission } = useAuth(); const canViewUsers = hasPermission(PERMISSION_KEYS.VIEW_USERS); const canViewChats = hasPermission(PERMISSION_KEYS.VIEW_CHATS); const [stats, setStats] = useState({ categories: 0, packs: 0, questions: 0, users: 0, }); const [messageStats, setMessageStats] = useState<{ date: string; count: number }[]>([]); const [featureInterestStats, setFeatureInterestStats] = useState([]); const [featureInterestCounts, setFeatureInterestCounts] = useState([]); const [recentFeatureInterests, setRecentFeatureInterests] = useState([]); const [loading, setLoading] = useState(true); const fetchStats = useCallback(async () => { try { // Fetch counts const [ { count: categoriesCount }, { count: packsCount }, { count: questionsCount }, { count: usersCount }, ] = await Promise.all([ adminData.from('categories').select('*', { count: 'exact', head: true }), adminData.from('question_packs').select('*', { count: 'exact', head: true }), adminData.from('questions').select('*', { count: 'exact', head: true }), canViewUsers ? adminData.from('profiles').select('*', { count: 'exact', head: true }) : Promise.resolve({ count: 0 }), ]); setStats({ categories: categoriesCount || 0, packs: packsCount || 0, questions: questionsCount || 0, users: usersCount || 0, }); const endDate = endOfDay(new Date()); const startDate = subDays(startOfDay(new Date()), 6); // Last 7 days including today const [ { data: messages }, { data: featureInterests }, { data: recentInterests }, ] = await Promise.all([ canViewChats ? adminData.from('messages').select('created_at').gte('created_at', startDate.toISOString()).lte('created_at', endDate.toISOString()) : Promise.resolve({ data: [] }), canViewUsers ? adminData.from('feature_interests').select('created_at, user_id').gte('created_at', startDate.toISOString()).lte('created_at', endDate.toISOString()) : Promise.resolve({ data: [] }), canViewUsers ? adminData.from('feature_interests').select('id, user_id, feature_name, created_at').order('created_at', { ascending: false }).limit(10) : Promise.resolve({ data: [] }), ]); if (canViewUsers) { try { const { counts } = await adminRequest<{ counts: FeatureInterestCountsRow[] }>('/v1/admin/feature-interest-counts'); setFeatureInterestCounts(counts); } catch (countsError) { console.error('Failed to fetch feature interest counts:', countsError); setFeatureInterestCounts([]); } } else { setFeatureInterestCounts([]); } // Process messages for chart const messageDaysMap = new Map(); for (let i = 0; i < 7; i++) { const date = subDays(new Date(), 6 - i); const key = format(date, 'MMM d'); messageDaysMap.set(key, 0); } (messages ?? []).forEach(msg => { const date = new Date(msg.created_at); const key = format(date, 'MMM d'); if (messageDaysMap.has(key)) { messageDaysMap.set(key, (messageDaysMap.get(key) || 0) + 1); } }); setMessageStats( Array.from(messageDaysMap.entries()).map(([date, count]) => ({ date, count, })) ); // Process feature interests for chart (unique users per day) const featureInterestDaysMap = new Map>(); for (let i = 0; i < 7; i++) { const date = subDays(new Date(), 6 - i); const key = format(date, 'MMM d'); featureInterestDaysMap.set(key, new Set()); } (featureInterests ?? []).forEach(interest => { const date = new Date(interest.created_at); const key = format(date, 'MMM d'); const set = featureInterestDaysMap.get(key); if (set) { set.add(interest.user_id); } }); setFeatureInterestStats( Array.from(featureInterestDaysMap.entries()).map(([date, userIds]) => ({ date, count: userIds.size, })) ); let recentActivity: RecentFeatureInterest[] = (recentInterests ?? []).map(item => ({ id: item.id, user_id: item.user_id, feature_name: item.feature_name, created_at: item.created_at, })); if (canViewUsers && recentActivity.length > 0) { const userIds = Array.from(new Set(recentActivity.map(item => item.user_id))); const { data: profiles } = await adminData .from('profiles') .select('id, name, email, avatar_url') .in('id', userIds); if (profiles && profiles.length > 0) { const profileById = new Map(profiles.map(profile => [profile.id, profile] as const)); recentActivity = recentActivity.map(item => ({ ...item, profile: profileById.get(item.user_id), })); } } setRecentFeatureInterests(recentActivity); } catch (error) { console.error('Failed to fetch stats:', error); } finally { setLoading(false); } }, [canViewChats, canViewUsers]); // Real-time subscriptions for dashboard stats (with debouncing) const { status: profilesStatus } = useRealtimeSubscription({ table: 'profiles', enabled: canViewUsers, onInsert: fetchStats, onDelete: fetchStats, debounceMs: 2000, }); const { status: messagesStatus } = useRealtimeSubscription({ table: 'messages', enabled: canViewChats, onInsert: fetchStats, debounceMs: 2000, }); const { status: featureInterestsStatus } = useRealtimeSubscription({ table: 'feature_interests', onInsert: fetchStats, onDelete: fetchStats, debounceMs: 2000, enabled: canViewUsers, }); useRealtimeSubscription({ table: 'categories', onInsert: fetchStats, onDelete: fetchStats, debounceMs: 2000, }); useRealtimeSubscription({ table: 'question_packs', onInsert: fetchStats, onDelete: fetchStats, debounceMs: 2000, }); useRealtimeSubscription({ table: 'questions', onInsert: fetchStats, onDelete: fetchStats, debounceMs: 2000, }); useEffect(() => { fetchStats(); }, [fetchStats]); const quickLinks = [ { title: 'Categories', description: 'Organize your question packs into themed collections', icon: LayoutGrid, href: '/categories', gradient: 'from-rose-500 to-pink-600', permission: PERMISSION_KEYS.MANAGE_CATEGORIES, }, { title: 'Users', description: 'View user profiles, responses, and activity', icon: Users, href: '/users', gradient: 'from-violet-500 to-purple-600', permission: PERMISSION_KEYS.VIEW_USERS, }, { title: 'Usage Insights', description: 'Understand why users join and how they engage', icon: Target, href: '/usage-insights', gradient: 'from-amber-500 to-orange-600', permission: PERMISSION_KEYS.VIEW_USERS, }, { title: 'Audit Logs', description: 'Track all admin actions and system changes', icon: ClipboardList, href: '/audit-logs', gradient: 'from-emerald-500 to-teal-600', permission: PERMISSION_KEYS.VIEW_AUDIT_LOGS, }, ]; const statCards = [ { label: 'Categories', value: stats.categories, icon: LayoutGrid, color: 'text-rose-400' }, { label: 'Packs', value: stats.packs, icon: Heart, color: 'text-violet-400' }, { label: 'Questions', value: stats.questions, icon: MessageCircle, color: 'text-amber-400' }, { label: 'Active Users', value: stats.users, icon: Users, color: 'text-emerald-400', permission: PERMISSION_KEYS.VIEW_USERS }, ]; const isRealtimeFullySubscribed = profilesStatus === 'SUBSCRIBED' && messagesStatus === 'SUBSCRIBED' && (!canViewUsers || featureInterestsStatus === 'SUBSCRIBED'); return (
{/* Header */}

Dashboard

Welcome back. Here's what's happening with Sauci.

{/* Stats Grid */}
{statCards .filter(card => !card.permission || hasPermission(card.permission)) .map((stat) => (

{stat.label}

{loading ? ( ) : ( stat.value.toLocaleString() )}
))}
{/* Main Content Grid */}
{/* Quick Links - 4 columns */}

Quick Access

{quickLinks .filter(link => hasPermission(link.permission)) .map((link) => (
{link.title} {link.description}
Open
))}
{/* Activity Chart - 3 columns */}
Message Activity
Messages sent over the last 7 days
{loading ? ( ) : ( `${value}`} /> )}
{/* Notify Overview - 7 columns */}
Notify Overview
Unique users opting into feature updates over the last 7 days
{!canViewUsers ? (
You don’t have permission to view feature opt-ins.
) : ( <>
{loading ? ( ) : ( `${value}`} /> )}

Opt-ins by feature

All time • last 7d

{loading ? (
{Array.from({ length: 6 }).map((_, idx) => (
))}
) : featureInterestCounts.length === 0 ? (

No opt-ins yet.

) : (
{featureInterestCounts.slice(0, 12).map((row) => (

{formatFeatureName(row.feature_name)}

{row.opt_in_count} {row.opt_in_count_last_7_days} 7d
))}
)}

Recent opt-ins

Last 10

{loading ? (
{Array.from({ length: 5 }).map((_, idx) => (
))}
) : recentFeatureInterests.length === 0 ? (

No opt-ins yet.

) : (
{recentFeatureInterests.map((item) => { const primaryText = item.profile?.name || item.profile?.email || `User ${item.user_id.slice(0, 8)}`; const subtitleParts: string[] = []; if (item.profile?.name && item.profile.email) { subtitleParts.push(item.profile.email); } subtitleParts.push(formatFeatureName(item.feature_name)); const fallbackChar = (item.profile?.name || item.profile?.email || 'U') .charAt(0) .toUpperCase(); return (
{fallbackChar}

{primaryText}

{subtitleParts.join(' • ')}

{formatDistanceToNow(new Date(item.created_at), { addSuffix: true })}

); })}
)}
)}
); }