Commit Graph

56 Commits

Author SHA1 Message Date
Simone Cavalli a4942d7684 docs(03): plan Phase 3 — Service Catalog & Quote Builder (4 plans, 2 waves)
Wave 1: schema push (service_id nullable + custom_label).
Wave 2 (parallel): catalog CRUD page + quote builder tab.
Wave 3: E2E human verification checkpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:23:15 +02:00
Simone Cavalli 31845b471b chore: rimuovi progetti annidati (ContactMe, SparklingOrbit) da ClientHub
Spostati come repository indipendenti a livello ~/ContactMe e ~/SparklingOrbit.
Rimossi i gitlink orfani (mode 160000 senza .gitmodules).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:47:07 +02:00
Simone Cavalli e4d4dfa5be fix: remove brief from public client dashboard (internal data only)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:38:56 +02:00
Simone Cavalli 0f48570cd7 feat: client edit/delete/archive + time tracker + analytics time section
Schema:
- clients.archived boolean (default false)
- time_entries table (client_id, started_at, ended_at, duration_seconds)

Client management:
- /admin/clients/[id]/edit — form pre-compilato con nome, brand, brief
- ClientActions: Modifica / Archivia / Elimina con doppia conferma
- setClientArchived: toggle archiviazione senza perdere dati
- deleteClient: elimina con cascade, redirect a /admin
- Admin list: toggle "Mostra archiviati" via ?archived=1, righe archiviate opache

Time tracker:
- startTimer: crea sessione, ferma automaticamente quella precedente
- stopTimer: chiude sessione, calcola duration_seconds
- TimerCell: ▶/⏹ per ogni cliente, contatore live in secondi, totale cumulativo
- Una sola sessione attiva alla volta su tutta la lista

Analytics:
- Sezione "Fatturato" (invariata) + sezione "Tempo tracciato" separata
- Ore totali per anno + barre orizzontali per cliente
- getTotalTrackedHours, getTimeByClient queries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:28:01 +02:00
Simone Cavalli d322162c0a feat: chat globale revisioni + pagina statistiche admin
- Chat revisioni: rimuovi commenti inline da timeline/kanban, aggiungi
  ChatSection con feed cronologico + selector task opzionale (Invio per inviare)
  Bolle stile chat: Tu (destra, giallo) / iamcavalli (sinistra, verde)
  Tag task su ogni messaggio quando il messaggio non è generale
- API /api/client/comment: supporto entity_type "general" (entity_id = clientId)
- Pagina /admin/analytics: year selector ←→, 4 metric card (contrattualizzato,
  incassato, da incassare, clienti acquisiti), bar chart mensile incassato via CSS
- NavBar: link "Statistiche"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:52:25 +02:00
Simone Cavalli 457656a2a9 feat: add analytics query helpers for admin financial reporting
Provides getAnalyticsByYear, getMonthlyCollected, and getAvailableYears
to power the admin dashboard analytics view (not yet wired to a page).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:49:21 +02:00
Simone Cavalli 549cf0b592 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>
2026-05-16 12:49:14 +02:00
Simone Cavalli c467ef300b docs(01): UI design contract + research Phase 1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:38:06 +02:00
Simone Cavalli 3582e26970 feat: document edit inline + client dashboard sidebar layout
- actions.ts: add updateDocument server action (label + url, Zod validated)
- DocumentRow: Client Component with hover-reveal edit/remove buttons,
  inline edit form with pre-filled fields and cancel/save
- DocumentsTab: use DocumentRow, remove variant dependency
- client-dashboard: two-column layout (sidebar left on lg+):
  sidebar = payments + documents + notes (sticky top)
  main = brief + phases toggle (timeline / kanban)
  mobile: main first, sidebar below (order-1/order-2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:24:49 +02:00
Simone Cavalli 7af917fe80 feat: brand color system + Kanban view (admin + client)
- Fix button contrast: add all missing shadcn tokens (primary-foreground,
  ring, input, muted, destructive) aligned to iamcavalli brand
- NavBar: #1A463C green bar with white text
- Login page: clean brand layout with iamcavalli wordmark
- Admin pages: brand colors on headings, borders, links
- Admin ClientRow: semantic payment badges (green/yellow/red)
- Admin phases tab: Lista ↔ Kanban toggle with @dnd-kit drag & drop
  between Da fare / In corso / Fatto columns (optimistic updates)
- Client dashboard: Timeline ↔ Kanban toggle, expandable task cards
  with approve button + comment form inline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 23:14:29 +02:00
Simone Cavalli 80d93993a9 docs(phase-02): evolve PROJECT.md after phase completion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 21:55:53 +02:00
Simone Cavalli 88f4e0fb08 docs(phase-02): complete phase execution — admin area + interactive features
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 21:55:16 +02:00
Simone Cavalli 447a00cadd docs(phase-02): update tracking after wave 4 completion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 21:53:07 +02:00
Simone Cavalli ed23cd71ac chore: merge 02-04 executor worktree 2026-05-15 21:52:38 +02:00
Simone Cavalli cf1f67229a docs(02-04): complete client interactions plan summary
- 2 tasks completed: approve + comment API routes, and ApproveButton/CommentForm/CommentList client components
- 3 auto-fixed blocking issues documented (missing dep, missing .env.local, prop signature updates)
- all STRIDE threats T-02-15 through T-02-20 mitigated
- build passes cleanly
2026-05-15 21:52:12 +02:00
Simone Cavalli dc512ec758 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
2026-05-15 21:50:07 +02:00
Simone Cavalli c24bdde603 feat(02-04): add POST /api/client/approve and POST /api/client/comment API routes
- approve: validates token, checks deliverable ownership via phase→client join, sets status=approved + approved_at=now() only if approved_at is currently null (CLAUDE.md immutability rule enforced)
- comment: validates token, checks entity ownership (task or deliverable) via phase→client chain, inserts comment with author='client'
- both routes return 404 on invalid token or unknown entity
- neither route references quote_items (CLAUDE.md constraint enforced)
- Zod validation on comment body: min 1 char, max 2000 chars (T-02-20 DoS mitigation)
2026-05-15 21:39:32 +02:00
Simone Cavalli bd3c26d6f1 chore: merge 02-03 executor worktree 2026-05-15 21:31:05 +02:00
Simone Cavalli 20c25cbb70 docs(02-03): complete admin client workspace plan summary
- SUMMARY.md for Plan 02-03: Radix Tabs workspace with all four tab components
- Documents deviations: Next.js 16 params await fix and FormData type annotations
- Records all threat mitigations applied (T-02-10 through T-02-14)
2026-05-15 21:30:34 +02:00
Simone Cavalli 59a46d37fa feat(02-03): build /admin/clients/[id] workspace with tabbed layout and all tab components
- Create /admin/clients/[id]/page.tsx — Server Component using Radix Tabs (Fasi & Task, Pagamenti, Documenti, Commenti)
- Create PhasesTab: phases list with add-phase form, task lists with add-task form, status selects for phases and tasks
- Create PaymentsTab: accepted_total editor (splits to 50% on each payment), payment status selects with paid_at on saldato
- Create DocumentsTab: add document (label + URL) form, document list with delete action
- Create CommentsTab: chronological comment display (admin vs cliente style), admin reply form with entity selector
- All mutations via inline Server Action closures bound to action= props; revalidatePath ensures fresh data
2026-05-15 21:16:10 +02:00
Simone Cavalli 7733566f5b feat(02-03): install @radix-ui/react-tabs, add getClientFullDetail, create Server Actions
- Add shadcn tabs component (src/components/ui/tabs.tsx) backed by @radix-ui/react-tabs
- Extend admin-queries.ts with getClientFullDetail() — fetches client + phases + tasks + deliverables + payments + documents + notes + comments in one call
- Create src/app/admin/clients/[id]/actions.ts with all mutations: addPhase, updatePhaseStatus, addTask, updateTaskStatus, addDeliverable, addDocument, deleteDocument, updatePaymentStatus, updateAcceptedTotal, postAdminComment
- All actions include server-side allowlist validation and revalidatePath
- approved_at immutability enforced by omission in addDeliverable
2026-05-15 20:10:10 +02:00
Simone Cavalli feede57c05 docs(02-02): complete admin client list and create-client plan summary
- Covers Task 1 (admin-queries, NavBar, layout) and Task 2 (admin page, ClientRow, new client form + Server Action)
2026-05-15 19:55:19 +02:00
Simone Cavalli f77051a3fc feat(02-02): add admin client list page and create-client flow
- /admin page: Server Component fetching all clients with payment badges
- ClientRow component with Acconto/Saldo status badges and secret link
- /admin/clients/new: form wired to createClient Server Action
- createClient action: Zod validation, inserts client + 2 payment stubs (Acconto 50%, Saldo 50%)
- Token auto-generated server-side via nanoid $defaultFn
- Redirects to /admin/clients/[id] after creation; revalidates /admin
2026-05-15 18:18:22 +02:00
Simone Cavalli dbcd00ffd6 chore: merge partial 02-02 worktree (task 1 complete) 2026-05-15 16:16:25 +02:00
Simone Cavalli 7029583475 feat(02-02): add admin-queries, NavBar, and admin layout
- src/lib/admin-queries.ts: getAllClientsWithPayments() and getClientById() for admin DB reads
- src/components/admin/NavBar.tsx: minimal nav with Clienti link and Esci (logout) button
- src/app/admin/layout.tsx: wraps all /admin/* pages with NavBar + centered main content area
2026-05-15 10:45:59 +02:00
Simone Cavalli e7279ee957 docs(02-01): complete Auth.js admin session plan summary
- SUMMARY.md for plan 02-01: next-auth@4 CredentialsProvider + proxy guard
- Documents two auto-fixed deviations (Next.js 16 proxy export name, Suspense boundary)
- Self-check PASSED: all files and commits verified
2026-05-15 10:43:30 +02:00
Simone Cavalli 69f8a7eae3 feat(02-01): extend proxy.ts with admin session guard, add login page
- Extend src/proxy.ts to guard /admin/* routes with getToken() JWT check
- /admin/login and /api/auth/* exempted from session guard (pass-through)
- Unauthenticated /admin/* requests redirect to /admin/login?callbackUrl=...
- /c/:path* client token validation logic preserved unchanged
- matcher updated: ["/admin/:path*", "/c/:path*"]
- Create src/app/admin/login/page.tsx: email+password form, signIn('credentials'), error on failure, redirect on success
- Fix: Next.js 16 requires export named 'proxy' not 'middleware'
- Fix: useSearchParams wrapped in Suspense boundary (Next.js App Router requirement)
2026-05-15 10:42:21 +02:00
Simone Cavalli 5d363a633d feat(02-01): install next-auth@4, configure CredentialsProvider auth
- Add next-auth@4 dependency (stable v4, not beta v5)
- Create src/lib/auth.ts with CredentialsProvider validating ADMIN_EMAIL/ADMIN_PASSWORD env vars
- Create src/app/api/auth/[...nextauth]/route.ts catch-all handler (GET + POST)
- JWT session strategy — stateless, no DB users table
- Custom sign-in page set to /admin/login
- Add NEXTAUTH_URL, NEXTAUTH_SECRET, ADMIN_EMAIL, ADMIN_PASSWORD to .env.local
2026-05-15 10:40:30 +02:00
Simone Cavalli 56dd18b0c2 docs(02-admin-area-interactive-features): complete phase 2 planning with 4-plan structure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 10:30:27 +02:00
Simone Cavalli 904849178d docs(02): add Phase 2 context — admin auth, mutation pattern, client interactions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:39:31 +02:00
Simone Cavalli c22457b9a7 docs: mark Phase 1 complete, advance state to Phase 2 planning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:35:46 +02:00
Simone Cavalli 9708149f60 docs(01-05): plan completion summary with DNS pending steps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:33:50 +02:00
Simone Cavalli 073eec7193 feat(seed): add seed script + fix Tailwind scanning adjacent projects
- scripts/seed.ts: inserts one complete test client with 3 phases,
  6 tasks, 4 deliverables, 2 payments, 2 documents, 2 notes; prints
  shareable URL to console
- globals.css: add @source not directives to exclude .01_projects/
  and .claude/ — Tailwind v4 was scanning the SparklingOrbit .venv
  Python files and generating invalid CSS class [-:|] from a regex
  pattern in a markdown-it table parser, causing dev server 500s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:33:03 +02:00
Simone Cavalli 1010041e45 docs(01-04): complete client dashboard UI plan — SUMMARY.md
5 task completati: design token Tailwind v4, ClientDashboard wrapper,
PhaseTimeline laterale, PaymentStatus (zero importi singoli), DocumentsSection
e NotesSection. Build Next.js 16 senza errori TypeScript.
2026-05-14 22:15:19 +02:00
Simone Cavalli 8602bfa92f feat(01-04): DocumentsSection + NotesSection — link esterni e log decisioni read-only
- DocumentsSection: link clickabili con target="_blank" rel="noopener noreferrer"
  icone SVG inline per documento ed external link, hover state con colore accent
- NotesSection: note read-only con timestamp in locale it-IT (D-12: cliente legge, admin scrive)
  empty state informativo per entrambi i componenti
- SVG inline al posto di lucide-react per compatibilita' massima
2026-05-14 22:13:33 +02:00
Simone Cavalli a4e2de0611 feat(01-04): PaymentStatus — totale accettato + righe stato pagamento (senza importi)
- Mostra accepted_total formattato in EUR (unico importo permesso — LOCKED)
- Righe pagamento: solo label + badge stato (da_saldare/inviata/saldato)
- Nessun importo singolo visibile al cliente (T-04-001 mitigato)
- Dot colorato + badge per ogni riga: blu=da_saldare, giallo=inviata, verde=saldato
2026-05-14 22:13:27 +02:00
Simone Cavalli 5d5c8eaa7d feat(01-04): PhaseTimeline — timeline laterale con indicatori, progress bar per fase, task list
- Layout a due colonne: indicatore cerchio (sinistra) + card fase (destra)
- Icone SVG inline per stato fase (done/active/upcoming) e task (done/in_progress/todo)
- Badge stato fase con colori semantici (verde/blu/grigio)
- Progress bar per fase con contatore task done/totale
- Task list con line-through per done, testo grigio chiaro
- Deliverable annidati sotto ogni task con badge "Approvato" se approved
- Linea verticale tra fasi (non sull'ultima)
2026-05-14 22:13:20 +02:00
Simone Cavalli debd3916db feat(01-04): ClientDashboard wrapper con header, progress bar globale, layout sezioni
- Header sticky: logo iamcavalli (xs, angolo sinistro) + brand_name cliente (prominente, centrato)
- Progress bar globale con percentuale visibile
- Brief cliente con accent bar sinistra
- Sezioni: PhaseTimeline, PaymentStatus (sempre), Documents/Notes (condizionali)
- Footer con avviso link privato
- Server Component puro, nessun 'use client' necessario
2026-05-14 22:13:11 +02:00
Simone Cavalli 4e703d7068 feat(01-04): design tokens Tailwind v4 + wire ClientDashboard in page.tsx
- globals.css: aggiunto @theme con palette light & clean (primary, secondary,
  tertiary, bg-subtle, border-light, accent, success, warning, info)
- app/c/[token]/page.tsx: import ClientDashboard, generateMetadata dinamico,
  React.cache() per deduplicare DB call tra metadata e render
2026-05-14 21:43:41 +02:00
Simone Cavalli 4707ab5d6f docs(01-03): complete token middleware + client portal data layer plan
- 01-03-SUMMARY.md: 3/3 tasks complete, npm run build passes
- Edge proxy pattern (proxy.ts), validate-token API route, ClientView type system
- /c/[token] Server Component operational, ready for Plan 04 dashboard UI
2026-05-14 21:25:56 +02:00
Simone Cavalli 8b5e723f81 feat(01-03): add /c/[token] Server Component route + layout
- src/app/c/[token]/page.tsx: Server Component calls getClientView(), notFound() on null
- src/app/c/[token]/layout.tsx: layout with metadata for client portal
- [Rule 1 - Bug] Renamed middleware.ts → proxy.ts and export middleware → proxy
  (Next.js 16 deprecated 'middleware' file convention; requires 'proxy' export name)
- params typed as Promise<{ token: string }> per Next.js 15+ breaking change
- npm run build: SUCCESS (no TypeScript errors)
2026-05-14 21:11:32 +02:00
Simone Cavalli 14787bab10 feat(01-03): add ClientView type system and getClientView() query function
- ClientView interface enforces admin data exclusion: no quote_items, no service prices
- getClientView() queries clients.token, phases, tasks, deliverables, payments, documents, notes
- inArray() scoping prevents full table scan on tasks and deliverables
- accepted_total: client.accepted_total ?? '0' null coalescing
- Progress percentages calculated server-side (per-phase + global)
- Payment amount intentionally excluded — only label and status returned to client
2026-05-14 20:58:30 +02:00
Simone Cavalli ef3481744c feat(01-03): add Edge middleware + internal validate-token API route
- src/middleware.ts: Edge-compatible token validation via fetch() (no Drizzle import)
- src/app/api/internal/validate-token/route.ts: Node.js route queries clients.token via Drizzle
- Invalid tokens rewrite to /not-found (404); matcher scoped to /c/:path*
2026-05-14 20:15:11 +02:00
Simone Cavalli 1faca1f522 docs(01-02): complete Drizzle schema plan — 10 tables live on Postgres
- SUMMARY documenta schema, deviazioni (uuid->text+nanoid, drizzle-kit env),
  threat surface e self-check PASSED
- Piano 03 (Middleware + /c/[token]) può partire
2026-05-13 22:49:47 +02:00
Simone Cavalli abcbb5224e feat(01-02): [BLOCKING] drizzle-kit push — schema live on Postgres
- Connected to postgresql://178.104.27.55:5432/clienthub
- 10 tables created: clients, phases, tasks, deliverables, comments,
  payments, documents, notes, service_catalog, quote_items
- UNIQUE constraint on clients.token active at DB level
- All FK constraints and CASCADE rules applied
- Verified via information_schema.tables query
2026-05-13 22:47:51 +02:00
Simone Cavalli a6ec599188 chore(01-02): generate Drizzle migration from schema (0000_pretty_typhoid_mary.sql)
- 10 tables: clients, phases, tasks, deliverables, comments, payments,
  documents, notes, service_catalog, quote_items
- UNIQUE constraint on clients.token enforced at DB level
- All FK cascades correct (deliverables->tasks->phases->clients)
- approved_at: timestamp with time zone (nullable, immutable)
- drizzle.config.ts already correct from Plan 01 (no changes needed)
2026-05-13 22:46:54 +02:00
Simone Cavalli 1bdbe7ab5d feat(01-02): create complete Drizzle schema with all 10 tables
- clients: token as separate text field (notNull, unique, nanoid) — never PK
- accepted_total denormalized on clients — client API never touches quote_items
- deliverables.approved_at immutable timestamp (TIMESTAMPTZ) — audit trail
- payments: label (Acconto 50% / Saldo 50%), status (da_saldare/inviata/saldato)
- comments: polymorphic entity_type+entity_id pattern
- service_catalog + quote_items: admin-only, never exposed to client API
- Full relations defined for all FK chains
- TypeScript types exported: Client, Phase, Task, Deliverable, etc.
- ID strategy: text + nanoid() via $defaultFn (cryptographically secure, URL-safe)
2026-05-13 22:46:30 +02:00
Simone Cavalli 2a24067005 docs(01-01): complete walking skeleton plan — Next.js 16 + Drizzle + shadcn/ui
Summary: 3/3 tasks completed, 4 auto-fixed deviations (all blocking/bug),
build passes, TypeScript strict, DATABASE_URL placeholder in .env.local
2026-05-13 15:33:31 +02:00
Simone Cavalli f8420072f4 feat(01-01): install shadcn/ui components and lucide-react icons
- components.json configured (Radix preset, CSS variables, @/ aliases)
- src/lib/utils.ts: cn() helper with clsx + tailwind-merge
- 10 shadcn/ui components: button, card, badge, progress, input, label,
  select, separator, table, textarea
- lucide-react@0.511 installed for iconography
- clsx, tailwind-merge, class-variance-authority added as dependencies
2026-05-13 15:32:19 +02:00
Simone Cavalli 6b5609b0cb feat(01-01): install Drizzle ORM, postgres-js, and configure DB client
- drizzle-orm@0.45.2 + postgres@3.4.9 installed (postgres-js driver)
- drizzle-kit@0.31.10 installed as dev dependency
- nanoid@5, zod@4, react-hook-form, @hookform/resolvers installed
- src/db/index.ts: Drizzle client initialized with postgres-js driver
- drizzle.config.ts: configured for postgresql dialect + src/db/schema.ts
- .env.example: added as public template (no secrets)
- .gitignore: allow .env.example while blocking all other .env* files
2026-05-13 15:30:53 +02:00