diff --git a/src/app/admin/clients/[id]/actions.ts b/src/app/admin/clients/[id]/actions.ts index 413abe3..3506fd2 100644 --- a/src/app/admin/clients/[id]/actions.ts +++ b/src/app/admin/clients/[id]/actions.ts @@ -18,7 +18,6 @@ import { payments, clients, projects, - comments, client_emails, otp_codes, } from "@/db/schema"; @@ -368,21 +367,11 @@ export async function updateAcceptedTotal(id: string, formData: FormData) { } } -// ── COMMENTS (admin reply) ──────────────────────────────────────────────────── - -export async function postAdminComment(id: string, formData: FormData) { - await requireAdmin(); - const entity = formData.get("entity") as string; - const body = (formData.get("body") as string)?.trim(); - if (!body || !entity) throw new Error("Dati mancanti"); - const [entity_type, entity_id] = entity.split(":"); - if (!entity_type || !entity_id) throw new Error("Formato entity non valido"); - const allowedTypes = ["task", "deliverable", "phase", "general"]; - if (!allowedTypes.includes(entity_type)) throw new Error("entity_type non valido"); - await db.insert(comments).values({ entity_type, entity_id, author: "admin", body }); - const { path } = await resolveEntity(id); - revalidatePath(path); -} +// ── COMMENTS ────────────────────────────────────────────────────────────────── +// La risposta dell'admin vive in `replyToConversation` +// (src/app/admin/conversazioni/actions.ts): unica inbox, un solo punto di +// scrittura. Qui c'era `postAdminComment`, che rispondeva sulla singola entità +// dal tab Commenti del progetto — tab rimosso, action con lui. // ── ACCESSI PORTALE (whitelist OTP) ────────────────────────────────────────── // La whitelist è l'unico modo per entrare nel portale: nessuna auto-registrazione. diff --git a/src/app/admin/conversazioni/actions.ts b/src/app/admin/conversazioni/actions.ts index 1d0f1bc..a5984fe 100644 --- a/src/app/admin/conversazioni/actions.ts +++ b/src/app/admin/conversazioni/actions.ts @@ -15,7 +15,11 @@ async function requireAdmin() { /** * Admin reply from the Conversazioni inbox. Per project decision, replies are * saved as a "general" comment on the client (entity_id = clientId), so they - * surface in the client's general chat and in the client detail CommentsTab. + * surface in the client's general chat. + * + * Questa è l'UNICA via di risposta dell'admin da quando il tab Commenti del + * progetto è stato rimosso: i messaggi su fase/task/deliverable si leggono qui + * con la loro etichetta, ma la risposta torna sempre sul thread generale. */ export async function replyToConversation(clientId: string, formData: FormData) { await requireAdmin(); diff --git a/src/app/admin/projects/[id]/page.tsx b/src/app/admin/projects/[id]/page.tsx index 9303190..83ca08e 100644 --- a/src/app/admin/projects/[id]/page.tsx +++ b/src/app/admin/projects/[id]/page.tsx @@ -5,7 +5,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { PhasesTab } from "@/components/admin/tabs/PhasesTab"; import { PaymentsTab } from "@/components/admin/tabs/PaymentsTab"; import { DocumentsTab } from "@/components/admin/tabs/DocumentsTab"; -import { CommentsTab } from "@/components/admin/tabs/CommentsTab"; import { TimerTab } from "@/components/admin/tabs/TimerTab"; import { OffersTab } from "@/components/admin/tabs/OffersTab"; import { PhasesViewToggle } from "@/components/admin/kanban/PhasesViewToggle"; @@ -34,7 +33,6 @@ export default async function ProjectDetailPage({ payments, documents, notes, - comments, activeTimerEntryId, activeTimerStartedAt, totalTrackedSeconds, @@ -72,7 +70,6 @@ export default async function ProjectDetailPage({ Pagamenti Documenti Note - Commenti Timer Offerte @@ -115,10 +112,6 @@ export default async function ProjectDetailPage({ - - - - Valore Totale Acconto Saldo - Timer Redditività (€/H) diff --git a/src/components/admin/ProjectRow.tsx b/src/components/admin/ProjectRow.tsx index af448ae..076cc6e 100644 --- a/src/components/admin/ProjectRow.tsx +++ b/src/components/admin/ProjectRow.tsx @@ -1,5 +1,4 @@ import Link from "next/link"; -import { TimerCell } from "@/components/admin/TimerCell"; import type { ProjectWithPayments } from "@/lib/admin-queries"; const statusConfig: Record = { @@ -80,18 +79,6 @@ export function ProjectRow({ project }: { project: ProjectWithPayments }) { )} - -
- -
- - {eurPerHour === null ? ( diff --git a/src/components/admin/tabs/CommentsTab.tsx b/src/components/admin/tabs/CommentsTab.tsx deleted file mode 100644 index 41666f5..0000000 --- a/src/components/admin/tabs/CommentsTab.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { postAdminComment } from "@/app/admin/clients/[id]/actions"; -import { Button } from "@/components/ui/button"; -import { Textarea } from "@/components/ui/textarea"; -import type { Comment } from "@/db/schema"; -import type { ClientFullDetail } from "@/lib/admin-queries"; - -type Props = { - comments: Comment[]; - phases: ClientFullDetail["phases"]; - clientId: string; -}; - -export async function CommentsTab({ comments, phases, clientId }: Props) { - // Build entity label map for display (phases, tasks, deliverables, and general) - const entityLabels: Record = { - [clientId]: "Messaggio generale", - }; - for (const phase of phases) { - entityLabels[phase.id] = `Fase: ${phase.title}`; - for (const task of phase.tasks) { - entityLabels[task.id] = `Task: ${task.title}`; - for (const d of task.deliverables) { - entityLabels[d.id] = `Deliverable: ${d.title}`; - } - } - } - - // Build list of entities the admin can reply on - const entities: Array<{ id: string; type: string; label: string }> = [ - { id: clientId, type: "general", label: "Messaggio generale" }, - ]; - for (const phase of phases) { - entities.push({ id: phase.id, type: "phase", label: `Fase: ${phase.title}` }); - for (const task of phase.tasks) { - entities.push({ id: task.id, type: "task", label: `Task: ${task.title}` }); - for (const d of task.deliverables) { - entities.push({ - id: d.id, - type: "deliverable", - label: `Deliverable: ${d.title}`, - }); - } - } - } - - return ( -
- {/* Comment list */} - {comments.length === 0 && ( -

Nessun commento ancora.

- )} -
- {comments.map((c) => ( -
-
-

- {c.author === "admin" ? "iamcavalli" : "Cliente"} —{" "} - {entityLabels[c.entity_id] ?? c.entity_id} -

-

{c.body}

-
-
- ))} -
- - {/* Admin reply form */} -
{ - "use server"; - await postAdminComment(clientId, fd); - }} - className="bg-white border border-gray-200 rounded-lg p-4 space-y-3" - > -

- Rispondi come admin -

- -