Commit graph

19 commits

Author SHA1 Message Date
DJP
18ae429924 Dow-ify project form + seed placeholder roster + Resource Manager + docs
You flagged three concrete gaps after the deploy went live — all
addressed in this commit, plus the API + how-to docs you asked for.

A) Create Project dialog was still HP-centric
   Placeholders like "HP Envy x360 Renders" / "HP-2026-001" / "NPI /
   Refresh" / "Form Factor" etc. bore no relation to Dow's actual XLSX
   columns and the form had no ClientTeam selector — so any
   admin-created project was orphaned from the visibility layer.
   - src/lib/validators/project.ts: added clientTeamId + omgJobNumber;
     status enum now includes PIPELINE and CANCELED
   - src/components/projects/project-form-dialog.tsx: rewritten around
     the Dow XLSX schema. Three tabs (Details / Dates / References)
     instead of four. Placeholders reference real Dow values
     (Celena / Yzabella etc. for Owner, 2337959 for OMG #, Brand / Events
     etc. for Team, Copywriting/Display/... for Category). ClientTeam
     selector populated from /api/client-teams with a "no teams — add
     one in Settings" fallback. Category is a typed enum dropdown with
     the 8 XLSX values. Risk/Priority wording mirrors the XLSX labels
     (Priority = URGENT). Dropped HP-only fields from the UI
     (formFactor, codeName, npiOrRefresh, businessUnit placeholder,
     agency, Financial tab, Workfront ID placeholder). Legacy fields
     are still in the Zod schema for back-compat but not rendered.

B) Users invisible because only the admin was seeded
   The plan flagged "real Dow/Oliver roster — open question" and we
   never got the list, so the seed only created admin@dowjones.com.
   prisma/seed-dow.ts now also creates the 9 placeholder resources
   from the Resources.html prototype (Alice Chen, Ben Marsh, Cara Wu,
   Dan Koch, Eva Stone, Frank Osei, Grace Lee, Hiro Tanaka, Isla Reeve),
   distributed round-robin across the three placeholder pods. Each has
   role + department + maxCapacity set but no passwordHash, so they
   show up in the UI immediately but can't log in until an admin
   invites them via Settings → Team (which issues a reset link).
   Swap for the real roster whenever Zia delivers it — the emails are
   @example.com so they're safe to delete.

C) Resource Manager page (matching Resources.html)
   New capacity planner UI — daily hours-per-job grid.
   - Schema: new ResourceBooking model { userId, date, jobNumber,
     hours, note, organizationId, createdById }. Migration at
     prisma/migrations/20260421000000_resource_bookings.
   - Validator (src/lib/validators/booking.ts): create + list schemas
     with date-only coercion.
   - Service (src/lib/services/booking-service.ts): week window
     helpers, create/list/delete + known-job-numbers lookup for the
     popover autocomplete.
   - API: GET/POST /api/resources/bookings, DELETE
     /api/resources/bookings/[id], GET /api/resources/job-numbers.
     Writes gated to ADMIN + PRODUCER; reads open to any signed-in
     member of the org (capacity view is a shared studio-level thing,
     not per-team visibility).
   - Hook (src/hooks/use-bookings.ts) with TanStack Query wiring +
     week-scoped cache keys.
   - Page (src/app/(app)/resources/page.tsx) ports the Resources.html
     design to the app's Tailwind + shadcn primitives: Resource × Day
     grid grouped by department, week navigator, click-to-assign
     popover with job-number autocomplete + hour chips (1/2/3/4/6/8 +
     custom), capacity bar per cell, week total column with over-cap
     warning, collapsible role bands. Matches the prototype's
     color-hashed job chips so the same job number gets a consistent
     color across the grid.
   - Sidebar nav: added "Resources" entry next to Workload.

D) Docs — full README + API reference + how-to
   - API.md: complete REST + webhook reference. Three auth modes
     documented (session cookie / X-API-Key / OMG HMAC). XLSX upload
     header map with the Dow XLSX column correspondences. OMG webhook
     has the speculative payload shape + a working bash example that
     signs + sends a request. Common flows at the bottom: bootstrap
     from zero, OMG publishes a status change, update a job from an
     external script.
   - HOWTO.md: end-to-end runbook. Mental model, local dev, prod
     deploy pointers, first-login ritual, add-users flow (UI + API),
     client teams + pods config, XLSX ingest (UI + curl + idempotency
     notes), OMG webhook wiring (secret gen through verification),
     producer daily workflow, client-viewer experience, resource
     planning walk-through, RBAC matrix, common-problems table, and
     "change the model" pointer map for future edits.
   - README.md: top intro now points at API.md / HOWTO.md / DEPLOY.md.

Verified: npx tsc --noEmit ✓ zero errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:31:19 -04:00
DJP
4361d4cd2a Phase 6e: ClientTeam + Pod CRUD — settings pages and APIs
Until now the only way to set up Dow's six client teams + production pods
was via the seed script. Now admins manage them from the Settings UI.

Validators (Zod):
- src/lib/validators/client-team.ts: create/update/membership schemas;
  slug regex enforced (lowercase + dashes only — keeps it stable for the
  XLSX/webhook ingest path which resolves teams by slug).
- src/lib/validators/pod.ts: create/update + setHomePod schemas.

Services:
- src/lib/services/client-team-service.ts: list/create/update/delete +
  addMember/removeMember. delete blocks if the team still has projects
  (forces an explicit move first). Auto-derives slug from name when not
  provided.
- src/lib/services/pod-service.ts: list/create/update/delete + setUserHomePod.
  delete is non-cascading on members — sets User.homePodId=null instead
  of deleting people. Lead-user assignment is org-scope-validated.

API routes (gated by new permissions CLIENT_TEAM_MANAGE / POD_MANAGE
seeded for ADMIN in Phase 3):
- GET/POST  /api/client-teams
- PATCH/DELETE  /api/client-teams/[teamId]
- POST/DELETE  /api/client-teams/[teamId]/members
- GET/POST  /api/pods
- PATCH/DELETE  /api/pods/[podId]
- POST/DELETE  /api/pods/[podId]/members

GET endpoints are open to any signed-in user — they need the lists for
filter dropdowns and to know their own team. Project-row visibility is
still enforced via Phase 2's visibility helpers, untouched.

Hooks:
- src/hooks/use-client-teams.ts and src/hooks/use-pods.ts — TanStack
  Query wrappers with cache invalidation on mutations.

Settings pages:
- src/app/(app)/settings/client-teams/page.tsx — create teams, manage
  memberships, see project counts. Hides external (CLIENT_VIEWER) users
  with a "client" badge so admins know who's who.
- src/app/(app)/settings/pods/page.tsx — create pods, set lead, add/remove
  members. Filters out external users from the pod-eligible list.
- src/app/(app)/settings/page.tsx — added Client Teams + Pods cards to
  the index, reordered to surface user-management first.

Verified: tsc --noEmit ✓ zero errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 19:25:29 -04:00
DJP
69f293682a Fix deploy port clash + wire admin invite → add-user flow
Deploy fixes (critical — Phase 0 string-rebrand didn't touch numeric ports):
- deploy.sh APP_PORT 3001 → 3002 (health check was hitting HP's app!)
- apache/dow-prod-tracker.conf — all proxy/websocket rules 3001 → 3002
  (traffic to /dow-prod-tracker would have been served by HP's container)
- deploy.sh: added COMPOSE_PROJECT=dow-prod-tracker and `-p $COMPOSE_PROJECT`
  on every `docker compose` invocation (down, up, exec, logs, ps). This is
  the CLAUDE.md belt-and-braces rule — without it, a future move of the
  deploy dir to `deploy/` would collapse the compose project name to
  `deploy` and collide with any other app in a sibling `deploy/` dir on
  the shared server. The `name:` field in compose covers us today, -p
  covers us tomorrow.
- apache conf header comment rewritten to explain the port convention and
  where to keep it in sync.

Admin add-user flow (answers the open question):
- createInvitation now creates/upserts the placeholder User row
  (email + role + organizationId + isExternal + mustChangePassword=true)
  in addition to the Invitation bookkeeping row. It stores a 24-byte
  password-reset token on BOTH the User (passwordResetToken) and the
  Invitation (token) — same token, so the existing /reset-password/[token]
  page accepts the invite URL without a separate accept endpoint.
- Role enum now includes CLIENT_VIEWER. isExternal auto-derives from role
  but can be overridden. When admin invites a CLIENT_VIEWER, the placeholder
  user lands correctly pre-flagged for external handling.
- POST /api/org/invitations now returns {acceptUrl} — the full
  /reset-password/<token> link admin can hand over out-of-band while SMTP
  is unwired.
- revokeInvitation also clears the reset token on the placeholder user so
  a leaked URL can't be used to claim the account after revocation.
- Deleted /api/invitations/accept (SSO-era — the accept IS the password
  reset now) and removed acceptInvitationSchema from the validator.

Team settings UI (src/app/(app)/settings/team/page.tsx):
- Role dropdown now has "Client (read-only)" alongside Admin/Producer/Artist.
- After a successful invite, a banner shows the accept URL with a Copy
  button so admin can paste it into Teams/email. Dismissible.
- Current-members list renders CLIENT_VIEWER with an amber badge.

Plumbing verified: tsc --noEmit ✓ zero errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 19:06:47 -04:00
DJP
7598f4285e Phase 4+5: Dow XLSX ingest API + OMG webhook receiver
Shared canonical path: both ingest channels transform inputs into a single
DowRow shape (src/lib/validators/dow-import.ts) and write via the single
upsertProjectFromDow() function (src/lib/services/dow-excel-service.ts),
so the XLSX importer and the webhook cannot drift. Upserts on
Project.omgJobNumber (unique) — idempotent under replay.

XLSX ingest (Phase 4):
- New src/lib/validators/dow-import.ts — Zod schema with STATUS_MAP,
  RISK_MAP, header normalizer, team-slug normalizer, preview + commit
  result types.
- New src/lib/services/dow-excel-service.ts:
  - parseDowTracker(buffer): locates "Job Tracker " (or "Job Tracker"),
    scans first 5 rows to find the header row with 4+ matched columns,
    skips the example/instructions row at header+1, substring-matches
    headers (handles "Creative Team Member Deliverable is Assigned to"
    → assignee), collects row-level errors without aborting the batch.
  - upsertProjectFromDow(row, organizationId): auto-creates
    ClientTeam if missing (seed covers the 6 canonical teams, but stay
    forgiving); on create, generates N deliverables from outputCount +
    pipeline stages from the default Dow pipeline template with
    BLOCKED/NOT_STARTED status derived from stage dependencies; on
    update, only overwrites fields that are set so producer-edited data
    isn't clobbered by blanks.
  - previewDowImport() and commitDowImport() wrap the flow for the API.
- Rewrote src/app/api/projects/bulk-import/route.ts for the Dow schema.
  POST ?commit=true|false, multipart file=<xlsx>. commit=false returns
  {preview, totalRows, validRows, errors[], rows[]} (first 25 samples);
  commit=true returns {imported, created, updated, deliverablesCreated,
  errors[]}. Batch never aborts on a single bad row.

OMG webhook (Phase 5):
- New src/app/api/webhooks/omg/route.ts — POST-only. HMAC-SHA256
  signature verification via X-OMG-Signature: sha256=<hex> against
  OMG_WEBHOOK_SECRET, timing-safe compare. OMG_WEBHOOK_ALLOW_INSECURE
  escape hatch for stub testing. Looks up the Dow org by canonical
  domain dowjones.com. Transforms the (speculative, documented)
  OMG payload into DowRow then calls upsertProjectFromDow. Unknown
  fields from payload.raw land on Project.customFields JSON so OMG
  can add fields without us losing data. Logs every event (never
  the raw payload — PII).
- middleware.ts: /api/webhooks/ added to the unauthenticated-allowed
  path list (alongside /api/auth and /api/health) — HMAC auth happens
  inside the handler.

Verified: tsc --noEmit ✓ zero errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:57:06 -04:00
DJP
cadffa4bd6 Phase 1: Dow-customized Prisma schema + strip HP-only features
Schema changes:
- Add ClientTeam, ClientTeamMembership (visibility grouping — Brand/Events/etc)
- Add Pod, Pod.leadUser/members (capacity grouping orthogonal to ClientTeam)
- Add local-auth fields on User (passwordHash, reset tokens, mustChangePassword,
  lastLoginAt, isExternal) — coexists with Entra SSO, flipped on post-MVP
- Add CLIENT_VIEWER role (read-only external Dow Jones users)
- Add ProjectStatus.PIPELINE (unaccepted brief) and ProjectStatus.CANCELED
- Add Permission.CLIENT_TEAM_MANAGE and POD_MANAGE
- Project: add clientTeamId (visibility FK) and omgJobNumber (canonical ingest key)

Removals (HP-specific approval workflow, not needed for Dow):
- Model ColorProbe (HP-CMF eyedropper)
- Model ReviewSession + ReviewSessionItem (batch approval)
- Model FeedbackItem + enum FeedbackStatus (formal OPEN→RESOLVED→VERIFIED chain)
- All cross-relations on User, Revision, Comment, Annotation, DeliverableStage

Migration: squashed HP baseline into clean 20260420000000_init with
CREATE EXTENSION IF NOT EXISTS vector; at top for non-docker deployments.

Code plumbing:
- DEFAULT_PERMISSIONS: added CLIENT_VIEWER entry (read + COMMENT_CREATE only)
- org-scope.ts: added clientTeam + pod cases, removed colorProbe/feedbackItem/reviewSession
- Deleted 29 files: review pages, review API routes, feedback/color-probe
  components + services + validators + hooks
- Stripped eyedropper tool from annotation-tools.tsx, use-annotation-state.ts,
  video-annotation-layer.tsx
- Removed "Reviews" nav entry from sidebar
- Deleted src/lib/utils/color.ts (CMF-only, unused after ColorProbe removal)

Verified: prisma validate ✓, npx tsc --noEmit ✓ (zero errors)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:35:14 -04:00
Leivur Djurhuus
95dbaef318 Add timestamped video annotations with timeline markers (A7.3)
- Add timestampSeconds and frameThumbnailUrl fields to Annotation model
- New VideoAnnotationLayer component: auto-pause on draw tool activation,
  SVG annotation overlay on paused video, time-filtered visibility,
  All/Timed toggle, timecode display in toolbar
- New VideoTimelineMarkers: orange=unresolved, green=resolved, clustered
  markers on scrub bar with click-to-seek and hover scale
- Thread timestampSeconds through validator, service, and API layers
- Feedback item cards show timestamp badges for video annotations
- VideoPlayer gains renderOverlay, timelineMarkers, pause/seek in state
- Fix "Processing" overlay shown when MP4 is available (FFmpeg fallback)
- Add revision polling when video status is "processing"
- Configure proxyClientMaxBodySize: 500mb for large video uploads
- Fix pre-existing Prisma JSON type error in upload-service.ts
- Update ROADMAP with lawn reference learnings and A7.3 progress

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:00:23 -05:00
Leivur R. Djurhuus
bd69208a84 Eyedropper comparison added for visual review tool. Needs to be tested and finessed on workstation 2026-03-17 22:20:52 -05:00
Leivur R. Djurhuus
6f5cbc2f1f feat: add review session components and hooks
- Implemented SessionPresenter and SessionSummary components for managing review sessions.
- Created AlertDialog component for modal dialogs.
- Developed hooks for managing review sessions, including fetching, creating, updating, and deleting sessions.
- Added service functions for review session operations in the backend.
- Introduced validation schemas for review session inputs using Zod.
2026-03-17 22:20:01 -05:00
Leivur R. Djurhuus
db82eb4fed refactor: simplify feedback from 4-level severity to action item / info callout
Replace FeedbackSeverity enum (Critical/Major/Minor/Suggestion) with a
simple isActionItem boolean. Annotations default to action items (things
the artist must fix). Any item can be toggled to an info callout (context
that doesn't need action). Progress bar and carry-forward only count
action items. Screenshot paste limited to 5MB with user notification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:19:31 -05:00
Leivur R. Djurhuus
05061baf26 feat: add revision history timeline (A4) and feedback checklist (A5)
A4 — Revision History Timeline:
- Collapsible right panel with vertical timeline of all revision rounds
- Each node shows thumbnail, status badge, timestamp, annotation count,
  comment summary, and decision record
- Keyboard navigation (up/down arrows), auto-scroll to active round
- Filter by rounds with feedback, "Compare from here" action
- Enriched revision data hook aggregating annotations + comments

A5 — Feedback Checklist:
- FeedbackItem model with severity (Critical/Major/Minor/Suggestion),
  status flow (Open → In Progress → Resolved → Verified), and
  carry-forward between revision rounds
- Auto-creation from annotations (non-blocking, post-transaction)
- Checklist panel in review page with progress bar, severity grouping,
  resolve-with-note flow, verify/reopen actions
- FeedbackIndicator badge on stage cards in deliverable detail page
- CRUD API routes + TanStack Query hooks
- Prisma schema additions (requires db push)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:19:11 -05:00
Leivur R. Djurhuus
eba5e30c98 feat: add version comparison (A2) and annotation system (A3)
A2 — Version Comparison:
- 4 comparison modes: side-by-side, A-B wipe slider, overlay with
  opacity, toggle with crossfade
- Synced zoom/pan across all modes
- Revision selectors for left/right image
- Keyboard shortcuts: 1-4 switch modes, Escape exits

A3 — Annotations:
- SVG overlay with 7 annotation types: rectangle, ellipse, arrow,
  freehand, text, pin, screenshot paste (Cmd+V)
- All annotations anchored to image coordinates (accurate at any zoom)
- Annotation model added to Prisma schema (requires db push)
- CRUD API routes at /api/revisions/[id]/annotations
- Annotations linked to comments (transactional create)
- Screenshot callouts: draggable, resizable with corner handles
- Undo/redo stack, color picker, visibility toggle
- Floating toolbar with backdrop blur

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:17:44 -05:00
Leivur Djurhuus
0ca56a5201 Dynamic Pipeline Builder pretty functional now. 2026-03-17 22:09:42 -05:00
Leivur R. Djurhuus
40028b7ced feat: add pipeline stage resolver and organization access control
- Implemented `stage-resolver.ts` to unify old and new pipeline stage definitions.
- Created `org-scope.ts` for organization access verification and scoping queries.
- Added role-based permissions management in `permissions.ts` and `rbac-service.ts`.
- Introduced invitation management in `invitation-service.ts` with validation schemas.
- Developed custom field and notification rule services with respective validators.
- Established pipeline template CRUD operations in `pipeline-template-service.ts`.
- Added Zustand store for managing pipeline builder state in `pipeline-builder-store.ts`.
2026-03-14 22:43:43 -05:00
Leivur Djurhuus
5b8c09de9e feat: implement stage date override and scheduling features
- Add PATCH endpoint to handle date overrides and clear manual overrides in the stage API.
- Introduce hooks for overriding stage dates and clearing overrides.
- Enhance the stage dependency engine to allow reopening from terminal states.
- Update stage status transitions to support reopening stages.
- Implement scheduling logic to auto-schedule stages based on due dates, considering manual overrides.
- Create a new component for managing stage dates with a popover interface.
- Add database migration for new fields related to manual scheduling and schedule conflicts.
- Document the executive overview and producer guide for the HP CG Production Tracker.
2026-03-12 23:13:29 -05:00
Leivur Djurhuus
a47c6791d9 feat: add validators for PowerPoint and Word documents, and Excel recalculation script
- Implement PPTXSchemaValidator for validating PowerPoint presentation XML files against XSD schemas.
- Create RedliningValidator to check tracked changes in Word documents, ensuring proper author tracking.
- Introduce recalc.py script to recalculate Excel formulas using LibreOffice, including error handling for Excel-specific errors.
- Add UI components for collapsible sections and tabs using Radix UI.
- Implement stage validation schema using Zod for managing project stages.
2026-03-02 12:23:09 -06:00
Leivur R. Djurhuus
2128e79c1a Add revision tracking and threaded comments for pipeline stages
Services, API routes, hooks, and UI components for:
- Revision rounds with submit/review/approve/request-changes flow
- Threaded comments with replies on each stage
- Stage detail sheet accessible from deliverable detail page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 21:40:12 -06:00
Leivur R. Djurhuus
740957d443 Add artist assignment system and My Work page
- Assignment service: assign/unassign users to stages, get user's work
- API routes: POST/DELETE /api/stages/:id/assignments, GET /api/my-work
- My Work page with assignments grouped by project
- StageStatusBadge component with semantic status colors
- Zod validator for assignment input

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 21:27:21 -06:00
Leivur R. Djurhuus
9f97cfe218 Add Deliverable CRUD with pipeline dependency engine
- Deliverable service auto-creates 10 pipeline stages on creation
- Stages start as BLOCKED/NOT_STARTED based on dependency rules
- Dependency engine: canStageStart() validates all prerequisites
- Stage machine: enforces valid status transitions
- Critical gate approval auto-unblocks downstream stages
- API routes for deliverables (nested under projects) and stages
- Zod validators for deliverable create/update

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 21:14:32 -06:00
Leivur R. Djurhuus
2859a50761 Add Project CRUD: API routes, service layer, form, and list page
- Zod validation schemas for create/update project
- Service layer with listProjects, getProject, createProject,
  updateProject, deleteProject
- API routes: GET/POST /api/projects, GET/PATCH/DELETE /api/projects/:id
- TanStack Query hooks for all project operations
- Project list page with card grid, status/priority badges
- Project create dialog with form validation
- QueryProvider + API utility helpers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 21:11:46 -06:00