feat: unify client messaging into ChatSection + add general comment type
Removed per-task/deliverable CommentList/CommentForm from PhaseTimeline and ClientKanban. Replaced with a single ChatSection at the bottom of the dashboard that handles general, task, and deliverable messages in a unified chat UI. Added "general" entity_type to the comment API (entity_id = client UUID). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import { clients, comments, tasks, phases, deliverables } from "@/db/schema";
|
||||
|
||||
const commentSchema = z.object({
|
||||
token: z.string().min(1),
|
||||
entity_type: z.enum(["task", "deliverable"]),
|
||||
entity_type: z.enum(["task", "deliverable", "general"]),
|
||||
entity_id: z.string().min(1),
|
||||
body: z.string().min(1, "Il commento non può essere vuoto").max(2000),
|
||||
});
|
||||
@@ -38,59 +38,50 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const clientId = clientRows[0].id;
|
||||
|
||||
// Verify entity belongs to this client (prevent cross-client comment injection)
|
||||
if (entity_type === "task") {
|
||||
if (entity_type === "general") {
|
||||
// General messages: entity_id must be the client's own id
|
||||
if (entity_id !== clientId) {
|
||||
return NextResponse.json({ error: "Entity non valida" }, { status: 403 });
|
||||
}
|
||||
} else if (entity_type === "task") {
|
||||
const phasesForClient = await db
|
||||
.select({ id: phases.id })
|
||||
.from(phases)
|
||||
.where(eq(phases.client_id, clientId));
|
||||
const phaseIds = phasesForClient.map((p) => p.id);
|
||||
|
||||
if (phaseIds.length === 0) {
|
||||
return NextResponse.json({ error: "Nessuna fase trovata" }, { status: 404 });
|
||||
}
|
||||
|
||||
const taskRows = await db
|
||||
.select({ id: tasks.id })
|
||||
.from(tasks)
|
||||
.where(inArray(tasks.phase_id, phaseIds));
|
||||
|
||||
const taskCheck = taskRows.find((r) => r.id === entity_id);
|
||||
|
||||
if (!taskCheck) {
|
||||
if (!taskRows.find((r) => r.id === entity_id)) {
|
||||
return NextResponse.json({ error: "Task non trovato" }, { status: 404 });
|
||||
}
|
||||
} else {
|
||||
// deliverable — verify via task → phase → client chain
|
||||
// deliverable
|
||||
const phasesForClient = await db
|
||||
.select({ id: phases.id })
|
||||
.from(phases)
|
||||
.where(eq(phases.client_id, clientId));
|
||||
const phaseIds = phasesForClient.map((p) => p.id);
|
||||
|
||||
if (phaseIds.length === 0) {
|
||||
return NextResponse.json({ error: "Nessuna fase trovata" }, { status: 404 });
|
||||
}
|
||||
|
||||
const taskRows = await db
|
||||
.select({ id: tasks.id })
|
||||
.from(tasks)
|
||||
.where(inArray(tasks.phase_id, phaseIds));
|
||||
|
||||
const taskIds = taskRows.map((r) => r.id);
|
||||
|
||||
if (taskIds.length === 0) {
|
||||
return NextResponse.json({ error: "Nessun task trovato" }, { status: 404 });
|
||||
}
|
||||
|
||||
const delivRows = await db
|
||||
.select({ id: deliverables.id })
|
||||
.from(deliverables)
|
||||
.where(inArray(deliverables.task_id, taskIds));
|
||||
|
||||
const delivCheck = delivRows.find((r) => r.id === entity_id);
|
||||
|
||||
if (!delivCheck) {
|
||||
if (!delivRows.find((r) => r.id === entity_id)) {
|
||||
return NextResponse.json({ error: "Deliverable non trovato" }, { status: 404 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@ export default async function ClientPage({
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Fetch comments for all tasks and deliverables in this client's data
|
||||
// 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 = [...allTaskIds, ...allDeliverableIds];
|
||||
const allEntityIds = [view.client.id, ...allTaskIds, ...allDeliverableIds];
|
||||
|
||||
const allComments: Comment[] =
|
||||
allEntityIds.length > 0
|
||||
|
||||
Reference in New Issue
Block a user