infra(04-00): route /c/ → /client/, Dockerfile, Gitea deploy
- Rename src/app/c/[token] → src/app/client/[token] - Update proxy.ts, ClientRow, admin client detail with /client/ path - Add output: "standalone" to next.config.ts for Docker build - Add Dockerfile (multi-stage, node:20-alpine) and .dockerignore - Push schema to Coolify Postgres via SSH tunnel (drizzle-kit push ✓) - Update CLAUDE.md constraint 4 to reflect /client/ route - Add Phase 4 planning artifacts (04-00, 04-RESEARCH, 04-PATTERNS) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { cache } from 'react';
|
||||
import { getClientView } from '@/lib/client-view';
|
||||
import { ClientDashboard } from '@/components/client-dashboard';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { db } from '@/db';
|
||||
import { comments } from '@/db/schema';
|
||||
import { inArray } from 'drizzle-orm';
|
||||
import type { Comment } from '@/db/schema';
|
||||
|
||||
export const revalidate = 0; // Always revalidate — comments and approvals must be fresh
|
||||
|
||||
// React cache deduplicates DB calls within the same render
|
||||
const getCachedClientView = cache(getClientView);
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ token: string }>;
|
||||
}) {
|
||||
const { token } = await params;
|
||||
const view = await getCachedClientView(token);
|
||||
|
||||
if (!view) {
|
||||
return { title: 'Not Found' };
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${view.client.brand_name} — Stato Progetto | iamcavalli`,
|
||||
description: view.client.brief || 'Dashboard stato progetto',
|
||||
};
|
||||
}
|
||||
|
||||
export default async function ClientPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ token: string }>;
|
||||
}) {
|
||||
const { token } = await params;
|
||||
const view = await getCachedClientView(token);
|
||||
|
||||
if (!view) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Fetch comments: tasks, deliverables, and general (entity_id = clientId)
|
||||
const allTaskIds = view.phases.flatMap((p) => p.tasks.map((t) => t.id));
|
||||
const allDeliverableIds = view.phases.flatMap((p) =>
|
||||
p.tasks.flatMap((t) => t.deliverables.map((d) => d.id))
|
||||
);
|
||||
const allEntityIds = [view.client.id, ...allTaskIds, ...allDeliverableIds];
|
||||
|
||||
const allComments: Comment[] =
|
||||
allEntityIds.length > 0
|
||||
? await db
|
||||
.select()
|
||||
.from(comments)
|
||||
.where(inArray(comments.entity_id, allEntityIds))
|
||||
: [];
|
||||
|
||||
return <ClientDashboard view={view} token={token} comments={allComments} />;
|
||||
}
|
||||
Reference in New Issue
Block a user