// components/project/ProjectList.tsx 'use client'; import { useState, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { Plus, Folder, Users, Globe, Lock, Crown, Calendar, Search, Filter, Grid3x3, List } 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 { Input } from '@/components/ui/input'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useToast } from '@/hooks/use-toast'; import { cn } from '@/lib/utils'; import { useRouter, usePathname } from "next/navigation" interface Project { id: string; code: string; name: string; description?: string; isPublic: boolean; createdAt: string; updatedAt: string; role?: string; memberCount?: number; fileCount?: number; } interface ProjectFormData { code: string; name: string; description?: string; isPublic: boolean; } export function ProjectList() { const [projects, setProjects] = useState<{ owned: Project[]; member: Project[]; public: Project[]; }>({ owned: [], member: [], public: [] }); const [searchQuery, setSearchQuery] = useState(''); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const pathname = usePathname() const internal = pathname?.includes('evcp') console.log(projects) const router = useRouter(); const { toast } = useToast(); // React Hook Form setup const { register, handleSubmit, reset, formState: { errors, isValid }, watch, setValue, } = useForm({ mode: 'onChange', defaultValues: { code: '', name: '', description: '', isPublic: false, }, }); const watchIsPublic = watch('isPublic'); useEffect(() => { fetchProjects(); }, []); // components/project/ProjectList.tsx 의 fetchProjects 함수 수정 const fetchProjects = async () => { try { const response = await fetch('/api/projects'); const data = await response.json(); setProjects(data); // 멤버인 프로젝트가 정확히 1개일 때 자동 리다이렉트 const memberProjects = data.member || []; const ownedProjects = data.owned || []; const totalProjects = [...memberProjects, ...ownedProjects]; if (totalProjects.length === 1) { const singleProject = totalProjects[0]; router.push(`/evcp/data-room/${singleProject.id}/files`); } } catch (error) { toast({ title: 'Error', description: 'Unable to load project list.', variant: 'destructive', }); } }; const onSubmit = async (data: ProjectFormData) => { setIsSubmitting(true); try { const response = await fetch('/api/projects', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); if (!response.ok) throw new Error('Failed to create project'); const project = await response.json(); toast({ title: 'Success', description: 'Project has been created.', }); setCreateDialogOpen(false); reset(); fetchProjects(); // Navigate to created project router.push(`/evcp/data-room/${project.id}`); } catch (error) { toast({ title: 'Error', description: 'Failed to create project.', variant: 'destructive', }); } finally { setIsSubmitting(false); } }; const handleDialogClose = (open: boolean) => { setCreateDialogOpen(open); if (!open) { reset(); } }; const filteredProjects = { owned: projects.owned?.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) ) || [], // Return empty array instead of undefined member: projects.member?.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) ) || [], public: projects.public?.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) ) || [], }; const ProjectCard = ({ project, role }: { project: Project; role?: string }) => ( router.push(`/evcp/data-room/${project.id}/files`)} >
{project.code} {project.name}
{role === 'owner' && ( )} {project.isPublic ? ( ) : ( )}
{project.description || 'No description'}
{project.memberCount && ( {project.memberCount} )} {project.fileCount !== undefined && ( {project.fileCount} )}
{new Date(project.updatedAt).toLocaleDateString()}
{role && ( {role} )}
); return ( <> {/* Header */}

Projects

Manage files and collaborate with your team

{internal && }
{/* Search and Filter */}
setSearchQuery(e.target.value)} />
{/* Project List */} {internal && My Projects ({filteredProjects.owned?.length}) } Joined Projects ({filteredProjects.member?.length}) Public Projects ({filteredProjects.public?.length}) {/* My Projects Tab */} {internal && {filteredProjects.owned?.length === 0 ? (

You don't own any projects

) : viewMode === 'grid' ? (
{filteredProjects.owned?.map(project => ( ))}
) : (
{filteredProjects.owned?.map(project => ( router.push(`/evcp/data-room/${project.id}/files`)} >

{project.code} {project.name}

{project.description || 'No description'}

Owner {project.isPublic ? ( ) : ( )}
))}
)}
} {filteredProjects.member?.length === 0 ? (

You are not a member of any projects

) : viewMode === 'grid' ? (
{filteredProjects.member?.map(project => ( ))}
) : (
{filteredProjects.member?.map(project => ( router.push(`/evcp/data-room/${project.id}/files`)} >

{project.code} {project.name}

{project.description || 'No description'}

{project.role} {project.isPublic ? ( ) : ( )}
))}
)}
{filteredProjects.public?.length === 0 ? (

No public projects

) : viewMode === 'grid' ? (
{filteredProjects.public?.map(project => ( ))}
) : (
{filteredProjects.public?.map(project => ( router.push(`/evcp/data-room/${project.id}/files`)} >

{project.code} {project.name}

{project.description || 'No description'}

Public
))}
)}
{/* Create Project Dialog */} Create New Project Create a new project to share files with your team
{errors.code && (

{errors.code.message}

)}
{errors.name && (

{errors.name.message}

)}
{errors.description && (

{errors.description.message}

)}

All users can view this project

setValue('isPublic', checked)} />
); }