feat(02-04): add ApproveButton, CommentForm, CommentList; wire interactive elements into client dashboard

- ApproveButton: 'use client', POSTs to /api/client/approve with token + deliverableId, calls router.refresh(); shows immutable "Approvato il [date]" badge once approved_at is set
- CommentForm: 'use client', POSTs to /api/client/comment, calls router.refresh() on success; clears textarea after submit
- CommentList: presentational Server Component, labels client author as "Tu" and admin as "iamcavalli"
- page.tsx: fetches all comments server-side (scoped to client's task/deliverable ids), passes token + comments to ClientDashboard; revalidate=0 ensures approvals and comments always fresh
- client-dashboard.tsx: passes token + comments down to PhaseTimeline
- phase-timeline.tsx: renders ApproveButton on each deliverable (pending/submitted/approved), CommentList + CommentForm below each deliverable and each task
This commit is contained in:
Simone Cavalli
2026-05-15 21:50:07 +02:00
parent c24bdde603
commit dc512ec758
6 changed files with 236 additions and 18 deletions
+21 -2
View File
@@ -2,8 +2,12 @@ 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 = 60; // ISR: revalidate ogni 60 secondi
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);
@@ -38,5 +42,20 @@ export default async function ClientPage({
notFound();
}
return <ClientDashboard view={view} />;
// Fetch comments for all tasks and deliverables in this client's data
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 = [...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} />;
}