// components/project/ProjectDashboard.tsx 'use client'; import React, { useState, useEffect } from 'react'; import { Crown, Users, Settings, FolderOpen, Shield, UserPlus, Trash2, BarChart3, Eye, Download, HardDrive, UserCog, Loader2, Edit2, Check, ChevronsUpDown } from 'lucide-react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { useToast } from '@/hooks/use-toast'; import { useSession } from 'next-auth/react'; import { getUsersForFilter } from '@/lib/gtc-contract/service'; import { cn } from '@/lib/utils'; interface ProjectDashboardProps { projectId: string; } interface ProjectStats { files: { totalFiles: number; totalSize: number; publicFiles: number; restrictedFiles: number; confidentialFiles: number; }; members: { totalMembers: number; admins: number; editors: number; viewers: number; }; activity: { views: number; downloads: number; uploads: number; uniqueUsers: number; }; } interface User { id: number; name: string; email: string; domain?: string; // 'partners' | 'internal' etc } export function ProjectDashboard({ projectId }: ProjectDashboardProps) { const { data: session } = useSession(); const [isOwner, setIsOwner] = useState(false); const [projectRole, setProjectRole] = useState('viewer'); const [stats, setStats] = useState(null); const [members, setMembers] = useState([]); const [loading, setLoading] = useState(true); // Dialog states const [addMemberOpen, setAddMemberOpen] = useState(false); const [transferOwnershipOpen, setTransferOwnershipOpen] = useState(false); const [newOwnerId, setNewOwnerId] = useState(''); // User selection related states const [availableUsers, setAvailableUsers] = useState([]); const [selectedUser, setSelectedUser] = useState(null); const [userSearchTerm, setUserSearchTerm] = useState(''); const [userPopoverOpen, setUserPopoverOpen] = useState(false); const [loadingUsers, setLoadingUsers] = useState(false); const [isExternalUser, setIsExternalUser] = useState(false); const [newMemberRole, setNewMemberRole] = useState('viewer'); const { toast } = useToast(); // Fetch project info and permissions useEffect(() => { const fetchProjectData = async () => { try { // Check permissions const accessRes = await fetch(`/api/projects/${projectId}/access`); const accessData = await accessRes.json(); setIsOwner(accessData.isOwner); setProjectRole(accessData.role); // Get stats if owner if (accessData.isOwner) { const statsRes = await fetch(`/api/projects/${projectId}/stats`); const statsData = await statsRes.json(); setStats(statsData); } // Get member list const membersRes = await fetch(`/api/projects/${projectId}/members`); const membersData = await membersRes.json(); setMembers(membersData.member); } catch (error) { console.error('Failed to load project data:', error); } finally { setLoading(false); } }; fetchProjectData(); }, [projectId]); // Fetch user list when dialog opens useEffect(() => { if (addMemberOpen) { fetchAvailableUsers(); } else { // Reset when dialog closes setSelectedUser(null); setUserSearchTerm(''); setNewMemberRole('viewer'); setIsExternalUser(false); } }, [addMemberOpen]); const fetchAvailableUsers = async () => { try { setLoadingUsers(true); const users = await getUsersForFilter(); // Exclude members already in project const memberUserIds = members.map(m => m.userId); const filteredUsers = users.filter(u => !memberUserIds.includes(u.id)); setAvailableUsers(filteredUsers); } catch (error) { console.error('Failed to load user list:', error); toast({ title: 'Error', description: 'Unable to load user list.', variant: 'destructive', }); } finally { setLoadingUsers(false); } }; // Add member const handleAddMember = async () => { if (!selectedUser) { toast({ title: 'Error', description: 'Please select a user.', variant: 'destructive', }); return; } try { const response = await fetch(`/api/projects/${projectId}/members`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: selectedUser.id, role: newMemberRole, }), }); if (!response.ok) { throw new Error('Failed to add member'); } toast({ title: 'Success', description: 'New member has been added.', }); setAddMemberOpen(false); // Refresh member list const membersRes = await fetch(`/api/projects/${projectId}/members`); const membersData = await membersRes.json(); setMembers(membersData.member); } catch (error) { toast({ title: 'Error', description: 'Failed to add member.', variant: 'destructive', }); } }; // Transfer ownership const handleTransferOwnership = async () => { try { const response = await fetch(`/api/projects/${projectId}/transfer-ownership`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ newOwnerId: newOwnerId, }), }); if (!response.ok) { throw new Error('Failed to transfer ownership'); } toast({ title: 'Success', description: 'Project ownership has been transferred.', }); setTransferOwnershipOpen(false); setIsOwner(false); } catch (error) { toast({ title: 'Error', description: 'Failed to transfer ownership.', variant: 'destructive', }); } }; const formatBytes = (bytes: number) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; const roleConfig = { owner: { label: 'Owner', icon: Crown, color: 'text-yellow-500' }, admin: { label: 'Admin', icon: Shield, color: 'text-blue-500' }, editor: { label: 'Editor', icon: Edit2, color: 'text-green-500' }, viewer: { label: 'Viewer', icon: Eye, color: 'text-gray-500' }, }; // User search filtering const filteredUsers = availableUsers.filter(user => user.name.toLowerCase().includes(userSearchTerm.toLowerCase()) || user.email.toLowerCase().includes(userSearchTerm.toLowerCase()) ); if (loading) { return (

Loading project information...

); } return (
{/* Header */}

Project Dashboard

{roleConfig[projectRole as keyof typeof roleConfig].icon && React.createElement(roleConfig[projectRole as keyof typeof roleConfig].icon, { className: `h-3 w-3 ${roleConfig[projectRole as keyof typeof roleConfig].color}` }) } {roleConfig[projectRole as keyof typeof roleConfig].label}
{isOwner && (
)}
{/* Owner-only statistics */} {isOwner && stats && (
Total Files
{stats.storage.fileCount}

{formatBytes(stats.storage.used)}

Members
{stats.users.total}
Admins {stats.users.byRole.admins} Editors {stats.users.byRole.editors}
Views (30 days)
{stats.activity.views}

{stats.users.active} active users

Downloads (30 days)
{stats.activity.downloads}

{stats.activity.uploads} uploads

)} {/* Tab content */} Members {isOwner && ( <> Permission Management Danger Zone )} Project Members List of users who can access this project
{members.map((member) => (
{member.user.name?.charAt(0).toUpperCase()}

{member.user.name}

{member.user.email}

{roleConfig[member.role as keyof typeof roleConfig].icon && React.createElement(roleConfig[member.role as keyof typeof roleConfig].icon, { className: `h-3 w-3 mr-1 ${roleConfig[member.role as keyof typeof roleConfig].color}` }) } {roleConfig[member.role as keyof typeof roleConfig].label}
))}
{isOwner && ( Danger Zone These actions cannot be undone. Please proceed with caution.

Transfer Ownership

Transfer project ownership to another member

Delete Project

Permanently delete project and all files

)}
{/* Add Member Dialog */} Add Member Add a member to the project Internal Users External Users Viewer Only
{loadingUsers ? (
Loading user list...
) : ( <> No user found. {filteredUsers .filter(u => u.domain !== 'partners') .map((user) => ( { setSelectedUser(user); setUserPopoverOpen(false); setIsExternalUser(false); setNewMemberRole('viewer'); }} value={`${user.name} ${user.email}`} className="truncate" >
{user.name}
{user.email}
))}

Internal users can be assigned any role.

)}

Security Policy Notice
External users (partners) can only be granted Viewer permissions due to security policy.

{loadingUsers ? (
Loading user list...
) : ( No external users found. {filteredUsers .filter(u => u.domain === 'partners') .map((user) => ( { setSelectedUser(user); setUserPopoverOpen(false); setIsExternalUser(true); setNewMemberRole('viewer'); }} value={user.name} className="truncate" > {user.name} External ))} )}
{/* Transfer Ownership Dialog */} Transfer Ownership Warning: This action is irreversible. All permissions will be transferred to the new owner.
); }