import { ProjectsClient, type ProjectClientOption, type ProjectListItem, } from "@/app/(dashboard)/projects/projects-client"; import { createClient } from "@/lib/supabase/server"; import { createServiceRoleClient } from "@/lib/supabase/admin"; type ProjectRow = { id: string; user_id: string; client_id: string | null; name: string; type: "client_project" | "side_project"; description: string | null; status: "planning" | "active" | "paused" | "completed" | "cancelled"; start_date: string | null; due_date: string | null; budget_amount: number | string | null; currency: string; progress: number; cover_image_path: string | null; cover_image_alt: string | null; clients: { name: string } | { name: string }[] | null; }; type TaskRow = { project_id: string | null; status: string | null; }; export default async function ProjectsPage() { const supabase = await createClient(); const { data: { user }, } = await supabase.auth.getUser(); if (!user) { return null; } const [{ data: projectRows }, { data: clientRows }, { data: taskRows }] = await Promise.all([ supabase .from("projects") .select( "id, user_id, client_id, name, type, description, status, start_date, due_date, budget_amount, currency, progress, cover_image_path, cover_image_alt, clients(name)", ) .eq("user_id", user.id) .order("created_at", { ascending: false }), supabase .from("clients") .select("id, name") .eq("user_id", user.id) .neq("status", "archived") .order("name", { ascending: true }), supabase.from("tasks").select("project_id, status").eq("user_id", user.id), ]); const taskStats = countTasksByProject((taskRows || []) as TaskRow[]); const clients = (clientRows || []) as ProjectClientOption[]; const signedUrls = await createProjectImageUrls( ((projectRows || []) as unknown as ProjectRow[]) .map((project) => project.cover_image_path) .filter(Boolean) as string[], ); const projects: ProjectListItem[] = ((projectRows || []) as unknown as ProjectRow[]).map((project) => { const stats = taskStats.get(project.id) || { total: 0, done: 0 }; return { id: project.id, client_id: project.client_id, clientName: getClientName(project.clients), name: project.name, type: project.type, description: project.description, status: project.status, start_date: project.start_date, due_date: project.due_date, budget_amount: project.budget_amount === null ? null : Number(project.budget_amount), currency: project.currency, progress: project.progress, cover_image_path: project.cover_image_path, cover_image_alt: project.cover_image_alt, coverImageUrl: project.cover_image_path ? signedUrls.get(project.cover_image_path) || null : null, taskCount: stats.total, doneTaskCount: stats.done, }; }); return ; } async function createProjectImageUrls( paths: string[], ) { const admin = createServiceRoleClient(); const urls = new Map(); const uniquePaths = Array.from(new Set(paths)); await Promise.all( uniquePaths.map(async (path) => { const { data } = await admin.storage .from("project-assets") .createSignedUrl(path, 60 * 15); if (data?.signedUrl) { urls.set(path, data.signedUrl); } }), ); return urls; } function getClientName(client: ProjectRow["clients"]) { if (!client) return null; return Array.isArray(client) ? client[0]?.name || null : client.name; } function countTasksByProject(tasks: TaskRow[]) { const statsByProject = new Map(); for (const task of tasks) { if (!task.project_id) continue; const current = statsByProject.get(task.project_id) || { total: 0, done: 0 }; current.total += 1; if (task.status === "done") { current.done += 1; } statsByProject.set(task.project_id, current); } return statsByProject; }