'use client'; import React, { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { useSession } from 'next-auth/react'; import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'; import { AppSidebar } from '@/components/app-sidebar'; import { SiteHeader } from '@/components/site-header'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { SegmentedProgressBar, WatchSegment } from '@/components/segmented-progress-bar'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { formatDistanceToNow } from 'date-fns'; import { ArrowLeft, Trash2, Save } from 'lucide-react'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { toast } from 'sonner'; type Video = { id: string; title: string; }; type UserProgress = { id: string; videoId: string; video: Video; watchedSec: number; lastPos: number | null; percent: number; completed: boolean; durationSec: number | null; updatedAt: string; createdAt: string; }; type UserComment = { id: string; content: string; createdAt: string; video: Video; replies: Array<{ id: string }>; }; type UserDetail = { id: string; email: string; name: string | null; image: string | null; role: string; createdAt: string; enrollments: Array<{ course: { id: string; title: string; }; }>; progress: UserProgress[]; comments: UserComment[]; }; interface UserDetailClientProps { userId: string; } function UserDetailClientUI({ userId }: UserDetailClientProps) { const { data: session } = useSession(); const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const [progressSegments, setProgressSegments] = useState>({}); const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [selectedRole, setSelectedRole] = useState(''); const [isRoleChangeAlertOpen, setIsRoleChangeAlertOpen] = useState(false); const [isUpdatingRole, setIsUpdatingRole] = useState(false); const router = useRouter(); const isSuperadmin = (session?.user as any)?.role === 'superadmin'; useEffect(() => { const fetchUserDetail = async () => { setIsLoading(true); try { const res = await fetch(`/api/admin/users/${userId}`); if (res.ok) { const data = await res.json(); setUser(data); setSelectedRole(data.role); // Fetch segments for all progress entries const segments: Record = {}; for (const prog of data.progress) { try { const segRes = await fetch(`/api/admin/users/${userId}/progress/${prog.videoId}/segments`); if (segRes.ok) { const segData = await segRes.json(); segments[prog.videoId] = segData.segments ?? []; } } catch (err) { console.error(`Failed to fetch segments for video ${prog.videoId}:`, err); } } setProgressSegments(segments); } } catch (err) { console.error('Failed to fetch user detail:', err); } finally { setIsLoading(false); } }; fetchUserDetail(); }, [userId]); const handleDeleteUser = async () => { setIsDeleting(true); try { const res = await fetch(`/api/admin/users/${userId}`, { method: 'DELETE', }); if (res.ok) { toast.success('User deleted successfully'); router.push('/admin/users'); } else { const data = await res.json(); toast.error(data.error || 'Failed to delete user'); } } catch (err) { console.error('Failed to delete user:', err); toast.error('Error deleting user'); } finally { setIsDeleting(false); setIsDeleteAlertOpen(false); } }; const handleUpdateRole = async () => { if (!user || selectedRole === user.role) { setIsRoleChangeAlertOpen(false); return; } setIsUpdatingRole(true); try { const res = await fetch('/api/admin/update-user-role', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId, newRole: selectedRole, }), }); if (res.ok) { const data = await res.json(); setUser({ ...user, role: selectedRole }); toast.success(`User role updated to ${selectedRole}`); setIsRoleChangeAlertOpen(false); } else { const data = await res.json(); toast.error(data.error || 'Failed to update user role'); setSelectedRole(user.role); } } catch (err) { console.error('Failed to update user role:', err); toast.error('Error updating user role'); setSelectedRole(user.role); } finally { setIsUpdatingRole(false); } }; if (isLoading) { return (
Loading user details...
); } if (!user) { return (
User not found
); } const getInitials = (name: string | null, email: string) => { if (!name) { return email.substring(0, 2).toUpperCase(); } return name .split(' ') .map((n) => n[0]) .join('') .toUpperCase(); }; const formatSeconds = (seconds: number) => { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; if (hours > 0) { return `${hours}h ${minutes}m ${secs}s`; } return `${minutes}m ${secs}s`; }; return (
{/* User Header */}
{getInitials(user.name, user.email)}

{user.name || user.email}

{user.role}

{user.email}

Joined {formatDistanceToNow(new Date(user.createdAt), { addSuffix: true })}

{/* Role Assignment Card (Superadmin Only) */} {isSuperadmin && ( Assign Role

User: Regular student with access to enrolled courses
Admin: Can manage courses, playlists, videos, and users
Superadmin: Full access + can assign roles to other admins

)} {/* Enrollments */} {user.enrollments.length > 0 && ( Enrollments ({user.enrollments.length})
{user.enrollments.map((enrollment, idx) => ( {enrollment.course.title} ))}
)} {/* Tabs for Watch History and Comments */} Watch History ({user.progress.length}) Comments ({user.comments.length}) {/* Watch History Tab */} {user.progress.length === 0 ? (
No watch history
) : (
Video Watched Duration Progress Status Last Updated {user.progress.map((progress) => ( {progress.video.title} {formatSeconds(progress.watchedSec)} {progress.durationSec ? formatSeconds(progress.durationSec) : '-'} {progress.completed ? ( Completed ) : ( In Progress )} {formatDistanceToNow(new Date(progress.updatedAt), { addSuffix: true, })} ))}
)}
{/* Comments Tab */} {user.comments.length === 0 ? (
No comments
) : (
{user.comments.map((comment) => (

Video: {comment.video.title}

{formatDistanceToNow(new Date(comment.createdAt), { addSuffix: true, })}

{comment.replies.length > 0 && ( {comment.replies.length} replies )}

{comment.content}

))}
)}
{/* Delete User Alert Dialog */} Delete User

Are you sure you want to delete {user?.email}?

⚠️ This will permanently delete:

  • User account and profile
  • All enrollments
  • All progress and watch history
  • All comments and replies
  • All video likes
  • All video unlocks

This action cannot be undone.

Cancel {isDeleting ? 'Deleting...' : 'Delete User'}
{/* Role Change Confirmation Dialog */} Update User Role

Change {user?.email}'s role from{' '} {user?.role} to{' '} {selectedRole}?

{selectedRole === 'superadmin' && (

⚠️ This user will have full access including the ability to assign roles to other users.

)} {selectedRole === 'user' && user?.role !== 'user' && (

ℹ️ This user will lose admin access but will still have access to enrolled courses.

)}
Cancel {isUpdatingRole ? 'Updating...' : 'Update Role'}
); } export default function UserDetailClient({ userId }: UserDetailClientProps) { return (
); }