import Link from 'next/link'; import { notFound, redirect } from 'next/navigation'; import { ArrowLeft, Plus, Settings, Share2, Play, Globe, Lock, UserPlus, Users, Building2, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { VideoCard } from '@/components/video-card'; import { GuestGate } from '@/components/guest-gate'; import { auth } from '@/lib/auth'; import { db } from '@/lib/db'; function VisibilityIcon({ visibility }: { visibility: string }) { switch (visibility) { case 'PUBLIC': return ; case 'INVITE': return ; default: return ; } } function formatDuration(seconds: number | null): string { if (!seconds) return '0:00'; const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; } function formatRelativeTime(date: Date): string { const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return 'just now'; if (diffMins < 60) return `${diffMins}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays < 7) return `${diffDays}d ago`; return date.toLocaleDateString(); } interface ProjectPageProps { params: Promise<{ projectId: string }>; } export default async function ProjectPage({ params }: ProjectPageProps) { const session = await auth(); const { projectId } = await params; // Fetch project with videos const project = await db.project.findUnique({ where: { id: projectId }, include: { workspace: { select: { id: true, name: true } }, owner: { select: { id: true, name: true } }, members: { where: { userId: session?.user?.id || '' }, select: { role: true }, }, videos: { orderBy: { position: 'asc' }, include: { versions: { where: { isActive: true }, take: 1, include: { _count: { select: { comments: true } }, }, }, _count: { select: { versions: true } }, }, }, }, }); if (!project) { notFound(); } // Check access const isOwner = session?.user?.id === project.ownerId; const isMember = project.members.length > 0; const isPublic = project.visibility === 'PUBLIC'; // Check workspace membership let isWorkspaceMember = false; let workspaceRole: string | null = null; if (session?.user?.id) { const wsMember = await db.workspaceMember.findUnique({ where: { workspaceId_userId: { workspaceId: project.workspaceId, userId: session.user.id, }, }, }); const ws = await db.workspace.findUnique({ where: { id: project.workspaceId }, select: { ownerId: true }, }); if (ws?.ownerId === session.user.id || wsMember) { isWorkspaceMember = true; workspaceRole = ws?.ownerId === session.user.id ? 'OWNER' : wsMember?.role || null; } } if (!isOwner && !isMember && !isPublic && !isWorkspaceMember) { redirect('/dashboard'); } // Transform videos for VideoCard component const videos = project.videos.map((video) => { const activeVersion = video.versions[0]; return { id: video.id, title: video.title, thumbnailUrl: activeVersion?.thumbnailUrl || 'https://via.placeholder.com/320x180?text=No+Thumbnail', currentVersion: video._count.versions, commentCount: activeVersion?._count.comments || 0, duration: formatDuration(activeVersion?.duration), lastUpdated: formatRelativeTime(video.updatedAt), }; }); const canEdit = isOwner || project.members[0]?.role === 'ADMIN' || workspaceRole === 'OWNER' || workspaceRole === 'ADMIN'; const isAuthenticated = !!session?.user?.id; // Guest name gate for unauthenticated users on public projects if (!isAuthenticated && isPublic) { return ( ); } return ( ); } function ProjectContent({ project, projectId, videos, canEdit, isOwner, isPublic, workspaceRole, }: { project: { name: string; description: string | null; visibility: string; workspace: { id: string; name: string } | null; members: { role: string }[] }; projectId: string; videos: { id: string; title: string; thumbnailUrl: string; currentVersion: number; commentCount: number; duration: string; lastUpdated: string }[]; canEdit: boolean; isOwner: boolean; isPublic: boolean; workspaceRole: string | null; }) { return (
{/* Back link */}
Back to Projects
{/* Project Header */}

{project.name}

{project.visibility.toLowerCase()}
{project.workspace && ( {project.workspace.name} )} {project.description && ( {project.description} )}
{(isOwner || project.members[0]?.role === 'ADMIN') && ( <> )} {canEdit && ( )}
{/* Videos Grid */} {videos.length > 0 ? (
{videos.map((video) => ( ))}
) : (

No videos yet

Add your first video to start collecting feedback

{canEdit && ( )}
)}
); }