import { NextResponse } from 'next/server'; import { and, eq, isNull, gte, sql } from 'drizzle-orm'; import { withAuth, withPermission } from '@/lib/api/helpers'; import { db } from '@/lib/db'; import { interests } from '@/lib/db/schema/interests'; import { berths } from '@/lib/db/schema/berths'; import { clients } from '@/lib/db/schema/clients'; import { websiteSubmissions } from '@/lib/db/schema/website-submissions'; import { errorResponse } from '@/lib/errors'; import { PIPELINE_STAGES } from '@/lib/constants'; export const GET = withAuth( withPermission('reports', 'view_dashboard', async (_req, ctx) => { try { const portId = ctx.portId; const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const [pipelineRows, berthStatusRows, totals, recent] = await Promise.all([ db .select({ stage: interests.pipelineStage, count: sql`count(*)::int`, }) .from(interests) .where(and(eq(interests.portId, portId), isNull(interests.archivedAt))) .groupBy(interests.pipelineStage), db .select({ status: berths.status, count: sql`count(*)::int`, }) .from(berths) .where(eq(berths.portId, portId)) .groupBy(berths.status), Promise.all([ db .select({ count: sql`count(*)::int` }) .from(clients) .where(and(eq(clients.portId, portId), isNull(clients.archivedAt))), db .select({ count: sql`count(*)::int` }) .from(interests) .where(and(eq(interests.portId, portId), isNull(interests.archivedAt))), db .select({ count: sql`count(*)::int` }) .from(berths) .where(eq(berths.portId, portId)), ]), Promise.all([ db .select({ count: sql`count(*)::int` }) .from(websiteSubmissions) .where( and( eq(websiteSubmissions.portId, portId), gte(websiteSubmissions.receivedAt, sevenDaysAgo), ), ), db .select({ count: sql`count(*)::int` }) .from(interests) .where( and( eq(interests.portId, portId), eq(interests.pipelineStage, 'completed'), gte(interests.updatedAt, thirtyDaysAgo), ), ), ]), ]); const pipeline = Object.fromEntries(PIPELINE_STAGES.map((s) => [s, 0])) as Record< string, number >; for (const row of pipelineRows) pipeline[row.stage] = row.count; const berthStatus: Record = { available: 0, under_offer: 0, sold: 0, }; for (const row of berthStatusRows) berthStatus[row.status] = row.count; const totalClients = totals[0][0]?.count ?? 0; const totalInterests = totals[1][0]?.count ?? 0; const totalBerths = totals[2][0]?.count ?? 0; const newInquiries7d = recent[0][0]?.count ?? 0; const completed30d = recent[1][0]?.count ?? 0; const closedTotal = pipeline['completed'] ?? 0; const openTotal = totalInterests - closedTotal; const conversionPct = totalInterests > 0 ? Math.round((closedTotal / totalInterests) * 100) : 0; return NextResponse.json({ data: { totals: { totalClients, totalInterests, totalBerths }, recent: { newInquiries7d, completed30d }, pipeline, berthStatus, conversion: { closedTotal, openTotal, conversionPct }, }, }); } catch (error) { return errorResponse(error); } }), );