Add progress checkboxes to A7 sub-stages. D1 automation engine (all 3 phases) and A7.1 (video upload + HLS) + A7.2 (video player) are done. Next: A7.3 (timestamped video annotations). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1322 lines
59 KiB
Markdown
1322 lines
59 KiB
Markdown
# HP CG Production Tracker — Roadmap
|
||
|
||
> Single source of truth for project status and remaining work.
|
||
> Previous planning documents (IMPLEMENTATION_PLAN.md, UPGRADE_PLAN.md) are archived in `docs/archive/`.
|
||
|
||
*Last updated: 2026-03-16*
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [What's Built](#whats-built)
|
||
2. [Remaining Work — Priority Order](#remaining-work--priority-order)
|
||
- [A. Visual Review Tool](#a-visual-review-tool)
|
||
- [B. Collaboration Enhancements](#b-collaboration-enhancements)
|
||
- [C. Reporting Completions](#c-reporting-completions)
|
||
- [D. Automation Completions](#d-automation-completions)
|
||
- [E. Asset Intelligence](#e-asset-intelligence)
|
||
- [F. Quality of Life](#f-quality-of-life)
|
||
- [G. Docker Deployment](#g-docker-deployment)
|
||
3. [Data Model Status](#data-model-status)
|
||
4. [Architecture Reference](#architecture-reference)
|
||
|
||
---
|
||
|
||
## What's Built
|
||
|
||
### Core Foundation (Phases 1–4)
|
||
|
||
| Feature | Status |
|
||
|---|---|
|
||
| Project scaffold (Next.js, TS, Tailwind, Prisma, Auth.js) | ✅ Complete |
|
||
| Design system (Oliver Agency palette, Montserrat + Inter, light/dark) | ✅ Complete |
|
||
| Auth — SSO (Google + Microsoft Entra ID) + dev bypass | ✅ Complete |
|
||
| Project CRUD + deliverable CRUD with auto-pipeline | ✅ Complete |
|
||
| User management + role assignment | ✅ Complete |
|
||
| Dependency engine + stage state machine | ✅ Complete |
|
||
| Artist assignments + My Work page | ✅ Complete |
|
||
| Table view (TanStack Table, filters, sort, column visibility) | ✅ Complete |
|
||
| Board view (Kanban drag-and-drop) | ✅ Complete |
|
||
| Timeline view (Gantt with dependencies) | ✅ Complete |
|
||
| Dashboard (KPIs, charts, overdue alerts) | ✅ Complete |
|
||
| Revision tracking (rounds, feedback notes, history) | ✅ Complete |
|
||
| Threaded comment system | ✅ Complete |
|
||
| Notification system (in-app bell + full page) | ✅ Complete |
|
||
| Excel import (bulk upload from Master CG Tracker) | ✅ Complete |
|
||
| Excel export | ✅ Complete |
|
||
| Deadline tracking (approaching/overdue + notifications) | ✅ Complete |
|
||
| Command palette (Cmd+K) | ✅ Complete |
|
||
| Bulk operations (multi-select → batch status/assignment/priority) | ✅ Complete |
|
||
| Loading skeletons, error boundaries, empty states | ✅ Complete |
|
||
| Responsive design + mobile sidebar | ✅ Complete |
|
||
| Accessibility (skip-to-content, ARIA, focus-visible) | ✅ Complete |
|
||
| Stage date override + scheduling features | ✅ Complete |
|
||
|
||
### Beyond Phase 4 (implemented opportunistically)
|
||
|
||
| Feature | Notes |
|
||
|---|---|
|
||
| **Workload capacity grid** | Artist × Week grid with status bars, overallocation warnings, 4/8/12-week projections |
|
||
| **Workload utilization heatmap** | Color-coded heatmap on workload page (green → yellow → red by load). Added in `c8f88c6`. |
|
||
| **Calendar view** | Monthly calendar with event pills, filters, day detail. *Not in original plan.* |
|
||
| **Calendar heatmap** | Heatmap overlay on calendar grid with toggle and bar visualization. Added in `bac6d4c`. *Not in original plan.* |
|
||
| **Automation rule engine** | Full trigger/action engine with event bus, rule evaluator, action executor, execution log |
|
||
| **Semantic search (pgvector + Ollama)** | Embedding service, hybrid search, pgvector columns on Project + Deliverable |
|
||
| **AI Chat panel** | SSE streaming chat with tool calls (search_entities), entity cards, project/deliverable context. *Not in original plan.* |
|
||
| **Weekly executive report** | Full report page at `/reports/weekly/[date]` with KPI strip, completed, deadlines, at-risk, upcoming sections |
|
||
| **Skill data model** | `Skill`, `UserSkill`, `StageSkillRequirement` in schema with seed data — **no assignment UI yet** |
|
||
|
||
### Phase 2: Multi-Brand Ready (Dynamic Pipelines)
|
||
|
||
| Feature | Status |
|
||
|---|---|
|
||
| **WP1 — RBAC Foundation** | ✅ Complete |
|
||
| `Permission` enum (20 perms) + `OrgRolePermission` model | ✅ |
|
||
| `requireAuth(permission?)` replaces `getAuthSession()` across all 28 API routes | ✅ |
|
||
| Default permissions per role (ADMIN/PRODUCER/ARTIST) with org-level overrides | ✅ |
|
||
| Permission matrix settings page (`/settings/permissions`) | ✅ |
|
||
| **WP2 — Org Hardening** | ✅ Complete |
|
||
| Denormalized `organizationId` on Deliverable + DeliverableStage | ✅ |
|
||
| `assertOrgAccess()` on all project/deliverable/stage API routes | ✅ |
|
||
| Backfill script (`scripts/backfill-org-ids.ts`) | ✅ |
|
||
| **WP3 — Dynamic Pipeline Schema** | ✅ Complete |
|
||
| `PipelineTemplate`, `PipelineStageDefinition`, `PipelineStageDependencyV2` models | ✅ |
|
||
| `pipelineTemplateId` on Project, `stageDefinitionId` on DeliverableStage | ✅ |
|
||
| Stage resolver normalizes old/new definitions | ✅ |
|
||
| `deliverable-service.ts` creates stages from project's dynamic pipeline | ✅ |
|
||
| Migration script (`scripts/migrate-to-dynamic-pipelines.ts`) | ✅ |
|
||
| **WP4 — Pipeline Template CRUD** | ✅ Complete |
|
||
| Full service: list, get, create, update, archive, duplicate | ✅ |
|
||
| Stage CRUD: add, update, remove, reorder | ✅ |
|
||
| Dependency management with cycle detection | ✅ |
|
||
| Pipeline validation (cycles, orphans, root stages) | ✅ |
|
||
| 8 API routes under `/api/pipelines/` | ✅ |
|
||
| **WP5 — Pipeline Builder UI** | ✅ Complete |
|
||
| `@xyflow/react` dependency graph visualization | ✅ |
|
||
| Pipeline list page (`/settings/pipelines`) with create, duplicate, archive | ✅ |
|
||
| Pipeline editor page with two-panel layout (stage list + dependency graph) | ✅ |
|
||
| Stage edit sheet, validation banner, custom edge with delete | ✅ |
|
||
| **WP6 — Invite System & Org Onboarding** | ✅ Complete |
|
||
| `Invitation` model with token-based accept flow | ✅ |
|
||
| Invitation service: create, list, revoke, accept | ✅ |
|
||
| Team settings page (`/settings/team`) with member list + invite form | ✅ |
|
||
| **WP7 — Custom Fields & Notification Rules** | ✅ Complete |
|
||
| `CustomFieldDefinition` model + `customFields` JSON on Project/Deliverable | ✅ |
|
||
| `NotificationRule` model (org-scoped event + conditions + channels) | ✅ |
|
||
| CRUD services, validators, hooks, API routes for both | ✅ |
|
||
| Custom fields settings page (`/settings/fields`) | ✅ |
|
||
| Notification rules settings page (`/settings/notifications`) | ✅ |
|
||
|
||
---
|
||
|
||
## Remaining Work — Priority Order
|
||
|
||
---
|
||
|
||
### A. Visual Review Tool
|
||
|
||
The highest-impact remaining feature. CG production review is fundamentally visual — this is a single, unified review tool built in stages, each independently useful. No throwaway prototypes.
|
||
|
||
The review tool lives at its own dedicated page (`/projects/[projectId]/deliverables/[deliverableId]/review`) and is also accessible from the stage detail sheet via a "Review" button. It's the primary interface for inspecting renders, comparing revisions, annotating feedback, and making approve/reject decisions.
|
||
|
||
**Infrastructure built so far:**
|
||
- Review page at `/projects/[projectId]/deliverables/[deliverableId]/review` with image viewer, upload, gallery
|
||
- Canvas-based image viewer with zoom/pan/minimap, retina support
|
||
- Image upload API with PNG alpha compositing + TIFF conversion (sharp)
|
||
- `Revision.attachments` JSON stores `{ referenceImage, currentImage }` with metadata
|
||
- Comparison viewer with 4 modes: side-by-side, wipe, overlay, toggle
|
||
- SVG annotation layer with 7 tools (rect, ellipse, arrow, freehand, text, pin, screenshot paste)
|
||
- Annotation model in Prisma schema (requires `db push` to sync)
|
||
- Annotation API: GET/POST `/api/revisions/[id]/annotations`, PATCH/DELETE `/api/revisions/[id]/annotations/[id]`
|
||
- Annotations linked to comments (transactional create), undo/redo stack
|
||
- Screenshot paste: Cmd+V pastes clipboard image as draggable/resizable callout
|
||
- "Review" button on stage cards in deliverable detail page
|
||
- Review sessions: session builder, presenter mode, summary grid, decision recording
|
||
- `ReviewSession` + `ReviewSessionItem` models in schema
|
||
|
||
**New dependency for all stages:** `sharp` (server-side PNG alpha compositing + image processing)
|
||
|
||
---
|
||
|
||
#### A1 — Review Viewer & Image Upload `[x]`
|
||
|
||
The foundation. A dedicated review page with a high-fidelity image viewer and the upload infrastructure to get images into the system.
|
||
|
||
**What gets built:**
|
||
- **Review page** at `/projects/[projectId]/deliverables/[deliverableId]/review` — full-width layout with image viewer as the centerpiece, toolbar at top, panels on the sides
|
||
- **Image viewer** — canvas-based with WebGL acceleration for large CG renders (4000×4000px+)
|
||
- Zoom: scroll wheel, pinch gesture, keyboard (+/-), toolbar presets (Fit, 50%, 100%, 150%, 200%)
|
||
- Pan: click-drag when zoomed, minimap overlay showing viewport position
|
||
- Pixel info: coordinate display (x, y) + color readout (RGB/Hex) in status bar
|
||
- High-DPI (retina) support with proper pixel ratio handling
|
||
- **Image upload** — drag-and-drop zone + click-to-browse for reference and current render per revision round
|
||
- Supports PNG, TIFF, JPEG, WebP up to 50MB
|
||
- **PNG alpha compositing on upload** — flatten transparent PNGs onto white (#FFF) background server-side using `sharp`, so CG renders with semi-transparent drop shadows display correctly in all comparison and overlay modes
|
||
- Store flattened version for viewing + optionally keep original transparent PNG for download
|
||
- Extend `Revision.attachments` JSON: `{ referenceImage?: { url, filename, size, uploadedAt, originalUrl? }, currentImage?: { ... } }`
|
||
- **Image gallery** — thumbnail strip of all uploaded images across revision rounds, with round number labels. Click any to load it in the viewer.
|
||
- **"Review" button** on stage cards in the deliverable detail page — opens the review page for that stage
|
||
|
||
**New API endpoints:**
|
||
- `POST /api/stages/[stageId]/revisions/[revisionId]/upload` — multipart form upload (type: "reference" | "current")
|
||
- `DELETE /api/stages/[stageId]/revisions/[revisionId]/upload` — remove an uploaded image
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx` — Review page
|
||
- `src/components/review/image-viewer.tsx` — Core canvas viewer
|
||
- `src/components/review/zoom-controls.tsx` — Zoom toolbar + keyboard handler
|
||
- `src/components/review/minimap.tsx` — Navigation minimap overlay
|
||
- `src/components/review/image-upload-zone.tsx` — Drag-and-drop upload
|
||
- `src/components/review/image-gallery.tsx` — Thumbnail strip across rounds
|
||
- `src/hooks/use-image-viewer.ts` — Pan/zoom state management
|
||
- `src/lib/services/upload-service.ts` — File storage + PNG alpha compositing
|
||
|
||
---
|
||
|
||
#### A2 — Version Comparison `[x]`
|
||
|
||
Compare two revisions of the same deliverable stage. This is the daily workhorse — producers and artists check what changed between rounds.
|
||
|
||
**What gets built:**
|
||
- **Comparison modes** (toolbar toggle in the review page):
|
||
- **Side-by-side** — dual panes with synced zoom/pan, version labels
|
||
- **A-B Slider (wipe)** — draggable vertical/horizontal divider revealing one image on each side
|
||
- **Overlay** — second image overlaid with adjustable opacity (0–100%)
|
||
- **Toggle** — click or press Space to crossfade between images (default on narrow screens)
|
||
- **Revision selectors** — dropdowns for left/right revision (default: previous round vs. current)
|
||
- **Synced navigation** — zoom/pan one pane, both move together across all modes
|
||
- **Keyboard shortcuts** — 1/2/3/4 to switch modes, left/right arrows to cycle revisions
|
||
|
||
**Key files:**
|
||
- `src/components/review/comparison-viewer.tsx` — Dual-pane orchestrator
|
||
- `src/components/review/wipe-divider.tsx` — Draggable split control
|
||
- `src/components/review/overlay-controls.tsx` — Opacity slider + mode toggles
|
||
|
||
**Dependencies:** Requires A1
|
||
|
||
---
|
||
|
||
#### A3 — Annotations `[x]`
|
||
|
||
Draw pixel-accurate annotations directly on images. Each annotation is anchored to image coordinates so it stays accurate at any zoom level, and is linked to a comment for context.
|
||
|
||
**What gets built:**
|
||
- **SVG overlay layer** on top of the canvas viewer (annotations in SVG for crisp scaling, image in canvas for performance)
|
||
- **Annotation tools** — toolbar with: rectangle, ellipse, arrow, freehand path, text label, pin (point marker)
|
||
- **Screenshot paste callouts** — Cmd+V / Ctrl+V pastes a clipboard image directly onto the review image as a floating, repositionable callout. Use case: paste a screenshot of a Maya attribute editor panel, a Photoshop layer setting, or a cropped reference to show the artist exactly what you mean. Callouts can be:
|
||
- **Moved** — drag to reposition anywhere on the image
|
||
- **Resized** — corner handles to scale up/down
|
||
- **Bordered** — automatic contrast border so the callout stands out against the render
|
||
- **Linked to a comment** — same as other annotations, the pasted screenshot becomes part of the feedback thread
|
||
- Stored as a small image (uploaded to server, URL in annotation data) anchored to image coordinates like any other annotation
|
||
- **Color picker** for annotation stroke (default: accent red for visibility against CG renders)
|
||
- **Image-coordinate anchoring** — annotations stored in image space, rendered correctly at any zoom
|
||
- **Comment linking** — each annotation creates or attaches to a Comment record; clicking an annotation highlights its comment in the sidebar, and vice versa
|
||
- **Visibility controls** — show/hide all, show/hide per revision round, show resolved vs. open
|
||
- **Undo/redo** for drawing sessions
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model Annotation {
|
||
id String @id @default(cuid())
|
||
commentId String
|
||
comment Comment @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
||
revisionId String
|
||
revision Revision @relation(fields: [revisionId], references: [id], onDelete: Cascade)
|
||
type AnnotationType
|
||
data Json // { x, y, width, height, points[], text, color, strokeWidth, imageUrl? }
|
||
imageX Float // anchor point in image coordinates
|
||
imageY Float
|
||
createdById String
|
||
createdBy User @relation(fields: [createdById], references: [id])
|
||
createdAt DateTime @default(now())
|
||
}
|
||
|
||
enum AnnotationType { RECTANGLE ELLIPSE ARROW FREEHAND TEXT PIN SCREENSHOT }
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/components/review/annotation-layer.tsx` — SVG overlay with tool switching
|
||
- `src/components/review/annotation-tools.tsx` — Toolbar with tool selection
|
||
- `src/components/review/annotation-renderer.tsx` — Renders individual shapes
|
||
- `src/components/review/screenshot-callout.tsx` — Pasted screenshot with drag/resize handles
|
||
- `src/lib/services/annotation-service.ts` — CRUD
|
||
- `src/lib/validators/annotation.ts` — Zod schemas
|
||
- `src/hooks/use-annotations.ts` — TanStack Query hook
|
||
|
||
**Dependencies:** Requires A1
|
||
|
||
---
|
||
|
||
#### A4 — Revision History Timeline `[x]`
|
||
|
||
A collapsible sidebar panel in the review page showing the full version history for a deliverable stage. The connective tissue between annotations, comparison, and feedback — provides the longitudinal view across all rounds.
|
||
|
||
**What gets built:**
|
||
- **Vertical timeline** in a collapsible right-side panel, each revision round as a node:
|
||
- Thumbnail preview of that round's submitted image
|
||
- Round number + status badge (Submitted, Changes Requested, Approved)
|
||
- Submitted by (name + avatar) + timestamp
|
||
- Annotation count (clickable to filter annotation layer to that round)
|
||
- Comment thread summary — first line + total count
|
||
- Decision record — who approved/rejected, when, with what note
|
||
- **Click any round** to load that version in the viewer with its annotations
|
||
- **Keyboard navigation** — up/down arrows to move through rounds
|
||
- **Comparison integration** — select two rounds from the timeline to open them in comparison mode (A2)
|
||
- **Filtering** — show all rounds, show only rounds with feedback, show only decision points
|
||
|
||
**No new data model** — read-only aggregation over existing `Revision`, `Comment`, and `Annotation` records.
|
||
|
||
**Key files:**
|
||
- `src/components/review/revision-timeline.tsx` — Main timeline panel
|
||
- `src/components/review/revision-node.tsx` — Individual round node
|
||
- `src/components/review/revision-comments.tsx` — Per-round comment display
|
||
- `src/components/review/revision-annotations-summary.tsx` — Annotation count + filters
|
||
- `src/hooks/use-revision-history.ts` — TanStack Query hook aggregating revisions + comments + annotations
|
||
|
||
**Dependencies:** Requires A1. Enhanced by A3 (annotation counts and filtering).
|
||
|
||
---
|
||
|
||
#### A5 — Feedback Checklist (Artist Action Items) `[x]`
|
||
|
||
Every annotation and actionable comment becomes a structured to-do item on a checklist for the assigned artist. Closes the feedback-to-fix loop.
|
||
|
||
**The feedback loop:**
|
||
1. Reviewer draws annotation or posts actionable comment
|
||
2. System auto-creates a FeedbackItem linked to the annotation/comment
|
||
3. Artist sees checklist — action items with direct links to annotations on the image
|
||
4. Artist works through items — checks each off with optional resolution note
|
||
5. Artist submits new revision — unchecked action items carry forward with a warning
|
||
6. Reviewer verifies — can confirm resolution or reopen
|
||
|
||
**Two item types (simplified from 4-level severity):**
|
||
- **Action Item** (default) — something the artist needs to fix. Has checkbox, can be resolved/verified.
|
||
- **Info Callout** — context or reference that doesn't require action (e.g., "FYI the client prefers warmer tones"). No checkbox. Can be toggled from action item and vice versa.
|
||
|
||
**Where the checklist appears (3 locations):**
|
||
1. **Review page — Feedback Panel** (primary): full checklist with action items first, then info callouts. Progress bar counts only action items. Filter by type and status.
|
||
2. **My Work page** — feedback badge per assignment ("5 open items")
|
||
3. **Stage card on deliverable page** — compact badge ("4/7 resolved") for action items
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model FeedbackItem {
|
||
id String @id @default(cuid())
|
||
deliverableStageId String
|
||
deliverableStage DeliverableStage @relation(...)
|
||
revisionId String
|
||
revision Revision @relation(...)
|
||
annotationId String?
|
||
annotation Annotation? @relation(...)
|
||
commentId String?
|
||
comment Comment? @relation(...)
|
||
summary String
|
||
isActionItem Boolean @default(true)
|
||
status FeedbackStatus @default(OPEN)
|
||
sortOrder Int @default(0)
|
||
assignedToId String?
|
||
createdById String
|
||
resolvedById String?
|
||
resolvedAt DateTime?
|
||
resolutionNote String?
|
||
verifiedById String?
|
||
verifiedAt DateTime?
|
||
carriedFromId String? // carried forward from prior round
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
@@map("feedback_items")
|
||
}
|
||
|
||
enum FeedbackStatus { OPEN IN_PROGRESS RESOLVED VERIFIED REOPENED }
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/components/review/feedback-checklist.tsx` — Main checklist panel
|
||
- `src/components/review/feedback-item.tsx` — Individual item with resolve action
|
||
- `src/components/review/feedback-progress-bar.tsx` — Progress bar
|
||
- `src/components/my-work/feedback-summary.tsx` — Inline checklist on My Work
|
||
- `src/components/stages/feedback-indicator.tsx` — Compact badge for stage cards
|
||
- `src/lib/services/feedback-service.ts` — CRUD, auto-creation, carry-forward logic
|
||
- `src/hooks/use-feedback-items.ts`
|
||
|
||
**Dependencies:** Requires A3 (annotations to generate items from) + A4 (timeline for round context)
|
||
|
||
---
|
||
|
||
#### A6 — Review Sessions & Playlists `[x]`
|
||
|
||
Curate a batch of deliverables into a structured review session. Walk through them sequentially in presenter mode with per-item decisions. The formal review workflow for HP stakeholder reviews.
|
||
|
||
**What gets built:**
|
||
- **Session builder** — pick deliverables/stages to include, drag to reorder, auto-generate from filters ("all Catalog Images in Review for Project X")
|
||
- **Presenter mode** — full-screen, navigate with arrow keys, image viewer with annotations, comment sidebar, prominent approve/request-changes/reject buttons per item
|
||
- **Summary view** — thumbnail grid with decision status badges, overall session progress
|
||
- **Session states** — DRAFT → IN_PROGRESS → COMPLETED
|
||
- **Shareable link** with optional expiry and access control
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model ReviewSession {
|
||
id String @id @default(cuid())
|
||
name String
|
||
status ReviewSessionStatus @default(DRAFT)
|
||
createdById String
|
||
organizationId String
|
||
shareToken String? @unique
|
||
expiresAt DateTime?
|
||
items ReviewSessionItem[]
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model ReviewSessionItem {
|
||
id String @id @default(cuid())
|
||
sessionId String
|
||
deliverableStageId String
|
||
revisionId String?
|
||
sortOrder Int
|
||
decision ReviewDecision?
|
||
decisionNote String?
|
||
decidedById String?
|
||
decidedAt DateTime?
|
||
}
|
||
|
||
enum ReviewSessionStatus { DRAFT IN_PROGRESS COMPLETED }
|
||
enum ReviewDecision { APPROVED CHANGES_REQUESTED REJECTED }
|
||
```
|
||
|
||
**Infrastructure built so far:**
|
||
- Review sessions list page at `/reviews` with create/delete, status filtering
|
||
- Session detail page at `/reviews/[sessionId]` with builder (list) and summary (grid) views
|
||
- Session builder with auto-fill from project (filter by stage status), reorder, remove items
|
||
- Presenter mode with full-screen walkthrough, image viewer, annotation overlay (read-only), keyboard shortcuts (A=approve, C=changes, arrows=navigate, Esc=exit), fullscreen toggle
|
||
- Session summary with thumbnail grid, decision badges, stats strip
|
||
- `ReviewSession` + `ReviewSessionItem` models in Prisma schema (decision stored as string, no enum coupling)
|
||
- Full service layer, API routes (CRUD + add-items/remove/reorder/decide/generate), TanStack Query hooks
|
||
- "Reviews" added to sidebar navigation
|
||
- `AnnotationLayer` extended with `readOnly` prop for presenter mode
|
||
- No shareable links (deferred to B3)
|
||
- No pipeline advancement on approve (manual "client approved" comes later)
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/reviews/page.tsx` — Session list
|
||
- `src/app/(app)/reviews/[sessionId]/page.tsx` — Session detail (builder/summary/presenter)
|
||
- `src/components/review/create-session-dialog.tsx` — Create session dialog
|
||
- `src/components/review/session-builder.tsx` — Item list with auto-fill
|
||
- `src/components/review/session-presenter.tsx` — Full-screen walkthrough
|
||
- `src/components/review/session-summary.tsx` — Thumbnail grid with decisions
|
||
- `src/lib/services/review-session-service.ts` — All business logic
|
||
- `src/lib/validators/review-session.ts` — Zod schemas
|
||
- `src/hooks/use-review-sessions.ts` — TanStack Query hooks
|
||
- `src/app/api/reviews/route.ts` — List/create API
|
||
- `src/app/api/reviews/[sessionId]/route.ts` — CRUD + actions API
|
||
|
||
**Dependencies:** Requires A1 + A3
|
||
|
||
---
|
||
|
||
#### A7 — Video Review with Timestamped Annotations
|
||
|
||
Extend the review tool to support video. Artists submit MP4 renders; reviewers scrub to a frame, pause, and draw spatial annotations (reusing the existing annotation tools) tied to that timestamp. Optional reference video for side-by-side comparison. Self-hosted transcoding via FFmpeg — no external services.
|
||
|
||
**Reference implementation:** [lawn-video-reviewer](https://github.com/) — React video player with HLS streaming, timestamped comments, and timeline markers. Key patterns to adapt: HLS-first streaming for instant playback and smooth seeking, custom video player with frame-accurate seeking, comment markers on the scrub timeline, and keyboard-driven playback controls.
|
||
|
||
**New dependency:** `ffmpeg` (added to Docker image for HLS transcoding, frame extraction, and metadata parsing)
|
||
|
||
The video feature is broken into sub-stages, each independently useful:
|
||
|
||
---
|
||
|
||
##### A7.1 — FFmpeg in Docker + Video Upload with HLS Streaming `[x]`
|
||
|
||
Add FFmpeg to the Docker environment, extend the upload system to accept video files, and transcode to HLS for fast, seekable streaming from day 1. Inspired by the lawn-video-reviewer reference: prioritize playback speed and responsiveness so artists and producers actually want to use the platform.
|
||
|
||
**Architecture decision:** Videos are stored outside `/public` in a mounted volume (`/data/uploads`) and served via a custom API route with proper `Range`, `Cache-Control`, and MIME headers. This avoids baking large files into the container image and enables proper streaming. HLS transcoding (MP4 → `.m3u8` + `.ts` segments) runs async after upload so the user isn't blocked.
|
||
|
||
**What gets built:**
|
||
|
||
- **Dockerfile update** — install `ffmpeg` in both builder and runner stages (`apk add --no-cache ffmpeg` on Alpine)
|
||
- **docker-compose.yml** — add `/data/uploads` volume mount for persistent media storage
|
||
- **Upload service extension** — accept MP4 files up to 500MB alongside existing image types
|
||
- New `type` values: `"video"` and `"referenceVideo"` (in addition to "reference", "current", "screenshot")
|
||
- Validate MIME type (`video/mp4`) and file size (500MB for video, 50MB for images)
|
||
- Streaming write to disk (no full file buffering in memory)
|
||
- Store raw MP4 in `/data/uploads/revisions/[revisionId]/video_[timestamp].mp4`
|
||
- **HLS transcoding (async)** — after raw MP4 is stored:
|
||
1. Extract metadata via `ffprobe` (duration, resolution, fps, codec) — fast, reads headers only
|
||
2. Extract thumbnail JPEG (poster frame at 1s)
|
||
3. Transcode to HLS: `ffmpeg` → `/data/uploads/revisions/[id]/hls/index.m3u8` + `segment_NNN.ts`
|
||
4. HLS settings: 6s segments, single quality tier matching source resolution, fast preset
|
||
5. Update `revision.attachments` with `status: "ready"` and `hlsUrl` when complete
|
||
6. On failure: set `status: "failed"`, keep raw MP4 as fallback
|
||
- **Streaming API route** — `GET /api/uploads/[...path]`
|
||
- Serves files from `/data/uploads/` (not `/public`)
|
||
- Proper MIME types: `.m3u8` → `application/vnd.apple.mpegurl`, `.ts` → `video/mp2t`, `.mp4` → `video/mp4`
|
||
- `Range` header support for MP4 fallback seeking
|
||
- `Cache-Control` headers for segment caching
|
||
- **Thumbnail extraction** — `ffmpeg` extracts poster frame (at 1s or first keyframe) as JPEG
|
||
- **Video metadata extraction** — `ffprobe` reads duration, resolution, codec, frame rate
|
||
- **Extend `Revision.attachments` JSON:**
|
||
```json
|
||
{
|
||
"referenceImage": { ... },
|
||
"currentImage": { ... },
|
||
"video": {
|
||
"url": "/api/uploads/revisions/.../video_xxx.mp4",
|
||
"hlsUrl": "/api/uploads/revisions/.../hls/index.m3u8",
|
||
"status": "processing | ready | failed",
|
||
"thumbnailUrl": "/api/uploads/revisions/.../video_xxx_thumb.jpg",
|
||
"filename": "video_xxx.mp4",
|
||
"size": 52428800,
|
||
"width": 1920,
|
||
"height": 1080,
|
||
"duration": 12.5,
|
||
"fps": 24,
|
||
"codec": "h264",
|
||
"uploadedAt": "2026-03-16T..."
|
||
},
|
||
"referenceVideo": { ... }
|
||
}
|
||
```
|
||
|
||
**Upload flow:**
|
||
```
|
||
Artist uploads MP4
|
||
→ Stream-write raw file to /data/uploads/revisions/[id]/video_[ts].mp4
|
||
→ Extract metadata via ffprobe (instant — reads headers only)
|
||
→ Extract thumbnail JPEG (poster frame at 1s)
|
||
→ Save to revision.attachments with status: "processing"
|
||
→ Return 201 immediately (artist sees thumbnail + "Processing..." indicator)
|
||
→ Async: FFmpeg transcodes to HLS segments
|
||
→ On complete: update revision.attachments with hlsUrl + status: "ready"
|
||
```
|
||
|
||
**Why HLS over raw MP4 serving (lawn learning):**
|
||
- Instant playback — browser starts streaming the first segment immediately, no full download
|
||
- Smooth seeking — jumps to nearest segment boundary without buffering the whole file
|
||
- Works in all browsers — hls.js for Chrome/Firefox, native HLS for Safari
|
||
- Future-proof — adaptive bitrate (multiple quality tiers) can be added later without changing the player
|
||
|
||
**New API endpoints:**
|
||
- `POST /api/stages/[stageId]/revisions/[revisionId]/upload` — extend existing endpoint to handle `type: "video" | "referenceVideo"`
|
||
- `DELETE /api/stages/[stageId]/revisions/[revisionId]/upload?type=video` — remove uploaded video + HLS segments
|
||
- `GET /api/uploads/[...path]` — streaming file server for all uploaded media
|
||
|
||
**Key files (new/modified):**
|
||
- `Dockerfile` — add `ffmpeg` installation
|
||
- `docker-compose.yml` — add `/data/uploads` volume mount
|
||
- `src/lib/services/upload-service.ts` — extend with video handling, streaming writes
|
||
- `src/lib/services/video-service.ts` — FFmpeg/ffprobe wrapper (HLS transcoding, thumbnail extraction, metadata parsing)
|
||
- `src/app/api/uploads/[...path]/route.ts` — streaming file server with Range support
|
||
- `src/components/review/video-upload-zone.tsx` — drag-and-drop for video files (reuse `image-upload-zone.tsx` pattern)
|
||
|
||
**Dependencies:** Requires A1 (upload infrastructure)
|
||
|
||
---
|
||
|
||
##### A7.2 — Video Player Component `[x]`
|
||
|
||
A custom video player built for frame-accurate review, embedded in the existing review page alongside the image viewer.
|
||
|
||
**What gets built:**
|
||
- **Video player component** — HTML5 `<video>` element with custom controls (no native browser chrome)
|
||
- Play/pause (Space or K)
|
||
- Seek bar / scrub timeline with hover preview
|
||
- Frame-by-frame stepping (← / → arrow keys, step by 1/fps seconds)
|
||
- Playback speed control (0.25x, 0.5x, 1x, 1.5x, 2x)
|
||
- Volume control + mute (M)
|
||
- Fullscreen toggle (F)
|
||
- Current time / duration display (timecode format: `HH:MM:SS:FF`)
|
||
- Loop toggle
|
||
- **Review page integration** — detect whether the revision has a video or image attachment and render the appropriate viewer
|
||
- Tab or toggle to switch between image viewer and video player when both exist
|
||
- Gallery strip shows video thumbnails alongside image thumbnails
|
||
- **Keyboard shortcuts** — consistent with lawn's patterns:
|
||
- Space/K = play/pause
|
||
- J/L = skip back/forward 5s
|
||
- ← / → = frame step (when paused)
|
||
- , / . = frame step alternative (matching common NLEs)
|
||
- F = fullscreen
|
||
- M = mute
|
||
- [ / ] = playback speed down/up
|
||
|
||
**Key files:**
|
||
- `src/components/review/video-player.tsx` — Core player component with custom controls
|
||
- `src/components/review/video-controls.tsx` — Play, seek, speed, volume, fullscreen controls
|
||
- `src/components/review/video-timeline.tsx` — Scrub bar with hover time preview
|
||
- `src/components/review/video-frame-display.tsx` — Timecode display (HH:MM:SS:FF)
|
||
- `src/hooks/use-video-player.ts` — Player state management (current time, playing, speed, volume)
|
||
|
||
**Dependencies:** Requires A7.1 (video upload)
|
||
|
||
---
|
||
|
||
##### A7.3 — Timestamped Video Annotations `[ ]`
|
||
|
||
The core differentiator: pause a video at any frame, draw spatial annotations on that frame using the existing annotation tools, and have those annotations tied to both a timestamp and image coordinates.
|
||
|
||
**What gets built:**
|
||
- **Frame capture** — when the user activates an annotation tool while the video is playing:
|
||
1. Video auto-pauses
|
||
2. Current frame is captured to a canvas (via `drawImage` from `<video>`)
|
||
3. SVG annotation layer overlays the paused frame (reuse existing `annotation-layer.tsx`)
|
||
4. User draws annotation using existing tools (rectangle, ellipse, arrow, freehand, text, pin)
|
||
5. On submit, annotation is saved with `timestampSeconds` + spatial coordinates
|
||
- **Server-side frame extraction fallback** — API endpoint to extract a specific frame via FFmpeg for cases where client-side capture fails (e.g., CORS, codec issues):
|
||
- `GET /api/stages/[stageId]/revisions/[revisionId]/frame?t=5.25` → returns JPEG of frame at 5.25s
|
||
- **Timeline annotation markers** — colored dots/ticks on the video scrub bar showing where annotations exist
|
||
- Orange = unresolved, green = resolved (matches lawn's pattern)
|
||
- Click a marker to seek to that timestamp and show the annotation
|
||
- Cluster nearby markers when zoomed out
|
||
- **Annotation visibility by time** — annotations fade in/out based on current playback position
|
||
- When playing: briefly flash annotations as playhead crosses their timestamp
|
||
- When paused: show annotations for the current frame (±0.5s window configurable)
|
||
- Toggle: "show all annotations" mode shows all regardless of timestamp
|
||
- **Comment linking** — each video annotation creates a Comment (same as image annotations) with the timestamp embedded
|
||
- Comment sidebar shows timestamp badge (clickable to seek)
|
||
- Comments sorted by timestamp (not creation time)
|
||
|
||
**Data model extension:**
|
||
```prisma
|
||
// Extend existing Annotation model — add optional timestamp field
|
||
model Annotation {
|
||
// ... existing fields ...
|
||
timestampSeconds Float? // null for image annotations, set for video annotations
|
||
frameThumbnailUrl String? // cached frame thumbnail for the annotation's moment
|
||
}
|
||
```
|
||
|
||
**New API endpoints:**
|
||
- `GET /api/stages/[stageId]/revisions/[revisionId]/frame?t=5.25` — extract frame at timestamp via FFmpeg
|
||
- Existing annotation CRUD endpoints gain `timestampSeconds` field
|
||
|
||
**Key files (new/modified):**
|
||
- `src/components/review/video-annotation-layer.tsx` — Orchestrates frame capture + annotation overlay on paused video
|
||
- `src/components/review/video-timeline-markers.tsx` — Annotation markers on scrub bar
|
||
- `src/components/review/annotation-layer.tsx` — extend to accept optional timestamp context
|
||
- `src/components/review/comment-list.tsx` — extend to show timestamp badges, sort by time
|
||
- `src/lib/services/video-service.ts` — add frame extraction endpoint
|
||
- `src/lib/services/annotation-service.ts` — extend to handle `timestampSeconds`
|
||
- `prisma/schema.prisma` — add `timestampSeconds` and `frameThumbnailUrl` to Annotation
|
||
|
||
**Dependencies:** Requires A7.2 (video player) + A3 (annotation system)
|
||
|
||
---
|
||
|
||
##### A7.4 — Video Comparison (Reference vs. Current) `[ ]`
|
||
|
||
Side-by-side video comparison for reviewing changes between a reference render and the current submission.
|
||
|
||
**What gets built:**
|
||
- **Dual video player** — two synced `<video>` elements playing in lockstep
|
||
- Synced playback: play/pause/seek one, both follow
|
||
- Synced frame stepping
|
||
- Independent volume controls
|
||
- Labels: "Reference" / "Current"
|
||
- **Comparison modes** (reuse concepts from image comparison):
|
||
- **Side-by-side** — two players next to each other (default)
|
||
- **Toggle** — press Space to swap which video is visible (single player area)
|
||
- **Wipe mode (stretch goal)** — CSS clip-path or canvas compositing to do A/B wipe on video
|
||
- More complex due to dual video decoding — defer if performance is an issue
|
||
- **Revision selectors** — dropdowns to pick which revision's video goes on each side
|
||
- **Annotations shared across comparison** — annotations from either video visible, with source label
|
||
|
||
**Key files:**
|
||
- `src/components/review/video-comparison.tsx` — Dual synced player orchestrator
|
||
- `src/components/review/synced-video-player.tsx` — Player that accepts external time/play state
|
||
- `src/components/review/video-comparison-toolbar.tsx` — Mode switcher
|
||
- `src/hooks/use-synced-video.ts` — Shared playback state management
|
||
|
||
**Dependencies:** Requires A7.2 (video player). Reference video upload from A7.1.
|
||
|
||
---
|
||
|
||
##### A7.5 — Video in Review Sessions `[ ]`
|
||
|
||
Extend the existing review session presenter (A6) to handle video deliverables alongside images.
|
||
|
||
**What gets built:**
|
||
- **Presenter mode video support** — when a ReviewSessionItem's revision has a video attachment, render the video player instead of the image viewer
|
||
- All video controls available (play, pause, seek, frame-step, speed)
|
||
- Annotations visible on the timeline and as overlays when paused
|
||
- Decision buttons (Approve / Changes Requested) work the same as for images
|
||
- **Mixed sessions** — sessions can contain both image and video items; presenter mode switches viewer type per item
|
||
- **Session summary** — video items show poster frame thumbnail (extracted in A7.1) in the summary grid
|
||
|
||
**Key files (modified):**
|
||
- `src/components/review/session-presenter.tsx` — add video player branch
|
||
- `src/components/review/session-summary.tsx` — video thumbnail support
|
||
- `src/components/review/session-builder.tsx` — show video icon for video items
|
||
|
||
**Dependencies:** Requires A6 (review sessions) + A7.2 (video player)
|
||
|
||
---
|
||
|
||
##### A7.6 — Feedback Checklist for Video Annotations `[ ]`
|
||
|
||
Extend the feedback checklist (A5) to work with timestamped video annotations.
|
||
|
||
**What gets built:**
|
||
- **Timestamp in feedback items** — each feedback item created from a video annotation shows the timestamp
|
||
- **Click-to-seek** — clicking a video feedback item in the checklist seeks the player to that timestamp and highlights the annotation
|
||
- **Frame thumbnail in checklist** — small thumbnail of the annotated frame next to each feedback item for quick visual context
|
||
- **Carry-forward for video** — when a new revision is submitted, unresolved video feedback items carry forward with their timestamps (timestamps may shift if the video changes length — flag these for manual review)
|
||
|
||
**Key files (modified):**
|
||
- `src/components/review/feedback-checklist.tsx` — add timestamp display + seek action
|
||
- `src/components/review/feedback-item-card.tsx` — frame thumbnail + timestamp badge
|
||
- `src/lib/services/feedback-service.ts` — carry-forward logic for video annotations
|
||
|
||
**Dependencies:** Requires A5 (feedback checklist) + A7.3 (timestamped annotations)
|
||
|
||
---
|
||
|
||
##### Implementation Order & Dependency Graph
|
||
|
||
```
|
||
A7.1 FFmpeg + Video Upload
|
||
│
|
||
▼
|
||
A7.2 Video Player Component
|
||
│
|
||
├──────────────┐
|
||
▼ ▼
|
||
A7.3 Timestamped A7.4 Video
|
||
Annotations Comparison
|
||
│ │
|
||
▼ ▼
|
||
A7.6 Feedback A7.5 Review
|
||
Checklist Sessions
|
||
```
|
||
|
||
**Estimated build order:**
|
||
1. **A7.1** — Foundation. Small scope, mostly backend. Do this first.
|
||
2. **A7.2** — Core player. Can start immediately after A7.1.
|
||
3. **A7.3** — The main feature. Requires the player + existing annotation system.
|
||
4. **A7.4** — Comparison. Can be built in parallel with A7.3 if desired.
|
||
5. **A7.5 + A7.6** — Integration with existing features. Do last.
|
||
|
||
---
|
||
|
||
### B. Collaboration Enhancements
|
||
|
||
---
|
||
|
||
#### B1 — Rich @Mentions with Deep Linking
|
||
|
||
**What:** `@` trigger in comment input opens user autocomplete. Mentioned users get a notification with a direct link to the comment.
|
||
|
||
**Key files:**
|
||
- `src/components/comments/mention-input.tsx`
|
||
- `src/components/comments/mention-renderer.tsx`
|
||
- Update `src/lib/services/comment-service.ts` — extract mentions + create notifications
|
||
|
||
---
|
||
|
||
#### B2 — Project Activity Feed
|
||
|
||
**What:** Unified chronological stream of all activity on a project — status changes, comments, uploads, assignments, approvals.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model ActivityEntry {
|
||
id String @id @default(cuid())
|
||
projectId String
|
||
deliverableId String?
|
||
stageId String?
|
||
userId String
|
||
type ActivityType
|
||
summary String
|
||
metadata Json?
|
||
createdAt DateTime @default(now())
|
||
}
|
||
|
||
enum ActivityType {
|
||
STATUS_CHANGE COMMENT_ADDED REVISION_SUBMITTED ASSIGNMENT_CHANGED
|
||
APPROVAL_DECISION FILE_UPLOADED DEADLINE_CHANGED PRIORITY_CHANGED
|
||
PROJECT_CREATED DELIVERABLE_CREATED
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/components/activity/activity-feed.tsx`
|
||
- `src/components/activity/activity-entry.tsx`
|
||
- `src/lib/services/activity-service.ts`
|
||
- `src/app/api/projects/[projectId]/activity/route.ts`
|
||
|
||
---
|
||
|
||
#### B3 — External Review Links
|
||
|
||
**What:** Token-based URLs for external HP stakeholders to view specific deliverables without an account. Optional watermark (reviewer name, date, CONFIDENTIAL), configurable expiry, access logging, revoke capability.
|
||
|
||
**Key files:**
|
||
- `src/app/(external)/review/[token]/page.tsx`
|
||
- `src/components/review/watermark-overlay.tsx`
|
||
- `src/lib/services/external-link-service.ts`
|
||
|
||
---
|
||
|
||
### C. Reporting Completions
|
||
|
||
---
|
||
|
||
#### C1 — Velocity & Throughput Metrics
|
||
|
||
**What:** Deliverables completed per week, average time per stage, bottleneck detection. Trend charts over 4/8/12/26-week windows.
|
||
|
||
**Key files:**
|
||
- `src/components/dashboard/velocity-chart.tsx`
|
||
- `src/components/dashboard/cycle-time-chart.tsx`
|
||
- `src/components/dashboard/bottleneck-chart.tsx`
|
||
- `src/lib/services/analytics-service.ts`
|
||
|
||
---
|
||
|
||
#### C2 — Burndown Charts
|
||
|
||
**What:** Per-project burndown with ideal line, actual line, and velocity-based projection with confidence interval. Warning when projection exceeds deadline.
|
||
|
||
**Key files:**
|
||
- `src/components/dashboard/burndown-chart.tsx`
|
||
- `src/components/dashboard/projection-engine.ts`
|
||
|
||
---
|
||
|
||
#### C3 — Client-Facing Dashboard (Read-Only Portal)
|
||
|
||
**What:** Simplified read-only view for HP stakeholders via secure share link. No account required. Configurable visibility, optional password + expiry.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model PortalLink {
|
||
id String @id @default(cuid())
|
||
token String @unique
|
||
organizationId String
|
||
projectIds String[]
|
||
createdById String
|
||
password String?
|
||
expiresAt DateTime?
|
||
lastAccessedAt DateTime?
|
||
accessCount Int @default(0)
|
||
isActive Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/app/(portal)/[token]/page.tsx`
|
||
- `src/app/(portal)/[token]/projects/[projectId]/page.tsx`
|
||
- `src/components/portal/portal-header.tsx`
|
||
- `src/lib/services/portal-service.ts`
|
||
|
||
**New dependency:** `bcryptjs` (password hashing for portal links)
|
||
|
||
---
|
||
|
||
#### C4 — SLA Tracking
|
||
|
||
**What:** Target turnaround times per stage type. Real-time on-track / at-risk / breached status. Business-hours-aware calculation. Compliance dashboard widget.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model SLATarget {
|
||
id String @id @default(cuid())
|
||
organizationId String
|
||
stageType String
|
||
targetHours Int
|
||
warningThreshold Float @default(0.75)
|
||
isActive Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/settings/sla/page.tsx`
|
||
- `src/components/dashboard/sla-compliance-chart.tsx`
|
||
- `src/components/stages/sla-indicator.tsx`
|
||
- `src/lib/services/sla-service.ts`
|
||
|
||
---
|
||
|
||
### D. Automation Completions
|
||
|
||
The automation rule engine (Phase 7.1) is fully built. These features extend it.
|
||
|
||
---
|
||
|
||
#### D1 — Automation Engine Activation
|
||
|
||
The automation engine backend is fully built (event bus, rule engine, action executor, execution logging, REST API). What's missing: event emission isn't wired into the service layer, deadline events have no scheduler, and there's no admin UI. This feature activates the engine end-to-end.
|
||
|
||
**Three sub-stages, each independently useful:**
|
||
|
||
---
|
||
|
||
##### D1.1 — Wire Event Emission into Service Layer `[x]`
|
||
|
||
Connect the existing emit helpers to actual service mutations so automation rules fire in production.
|
||
|
||
**What gets wired:**
|
||
- **`stage-service.ts`** — `emitStageStatusChanged()` after `updateStageStatus` and `bulkUpdateStageStatuses` transactions commit. Extend Prisma queries to include `deliverable.project` for event payload context.
|
||
- **`revision-service.ts`** — `emitRevisionSubmitted()` after `createRevision`. Add stage context query (deliverable + project + org) since the function only receives `stageId`.
|
||
- **`assignment-service.ts`** — `emitAssignmentCreated()` after `assignUserToStage` and `bulkAssignArtists`. Extend stage query to include deliverable/project context.
|
||
- **Handler registration** — ensure `automation-service.ts` (which self-registers on import) is imported before events fire.
|
||
|
||
**Critical constraint:** Events must fire AFTER transaction commit, not inside it. If emitted inside `$transaction` and it rolls back, the event would have already fired.
|
||
|
||
**Key files (modified):**
|
||
- `src/lib/services/stage-service.ts`
|
||
- `src/lib/services/revision-service.ts`
|
||
- `src/lib/services/assignment-service.ts`
|
||
- `src/lib/automation/event-bus.ts` (no changes, but used by all above)
|
||
|
||
**No new dependencies. No new data models.**
|
||
|
||
---
|
||
|
||
##### D1.2 — Deadline Scheduler `[x]`
|
||
|
||
Implement the trigger mechanism for `deadline.approaching` and `deadline.passed` events. The existing `deadline-service.ts` already has `checkDeadlines()` — this adds event emission and a scheduler.
|
||
|
||
**What gets built:**
|
||
- **`emitDeadlineEvents()`** in `deadline-service.ts` — calls `checkDeadlines()`, emits automation events per approaching/overdue item
|
||
- **24-hour deduplication** — before executing a matched deadline rule, check `AutomationExecution` for recent successful execution of the same rule (prevents repeated firing on every scheduler run)
|
||
- **Cron API route** at `/api/cron/deadlines` — protected by `CRON_SECRET` env var, callable by external scheduler or manual testing
|
||
- **Next.js instrumentation file** — `setInterval` hourly in the Node.js server process as primary trigger mechanism
|
||
|
||
**Key files (new):**
|
||
- `src/app/api/cron/deadlines/route.ts`
|
||
- `instrumentation.ts`
|
||
|
||
**Key files (modified):**
|
||
- `src/lib/services/deadline-service.ts`
|
||
- `src/lib/services/automation-service.ts` (dedup logic)
|
||
|
||
**Dependencies:** Requires D1.1
|
||
|
||
---
|
||
|
||
##### D1.3 — Automation Rules Admin UI `[x]`
|
||
|
||
Settings page at `/settings/automations` for producers and admins to create, edit, and monitor automation rules.
|
||
|
||
**What gets built:**
|
||
- **Rule list** — card per rule with name, enabled `<Switch>`, trigger event badge, action type badges, condition count, execution count, edit/delete actions
|
||
- **Rule builder dialog** — form with:
|
||
- Trigger event picker (5 event types)
|
||
- Condition builder (dynamic rows, contextual field dropdowns per event type, operator select, value input; AND logic)
|
||
- Action configurator (dynamic rows, type selector with contextual params per action type)
|
||
- `send_webhook` includes Teams Adaptive Card auto-formatting when URL contains `webhook.office.com`
|
||
- **Execution log** — dense table of recent executions across all rules with status badges (SUCCESS/PARTIAL_FAILURE/FAILURE), expandable detail rows
|
||
- **TanStack Query hooks** for all CRUD + execution history
|
||
- **New API endpoint** `GET /api/automations/executions` for org-wide execution log
|
||
|
||
**Key files (new):**
|
||
- `src/app/(app)/settings/automations/page.tsx`
|
||
- `src/hooks/use-automations.ts`
|
||
- `src/lib/automation/field-options.ts` (event field definitions + status enums for condition builder)
|
||
- `src/app/api/automations/executions/route.ts`
|
||
|
||
**Key files (modified):**
|
||
- `src/app/(app)/settings/page.tsx` (add entry to settings index)
|
||
- `src/lib/automation/action-executor.ts` (Teams Adaptive Card formatting in webhook action)
|
||
- `src/lib/services/automation-service.ts` (add `listAllExecutions()`)
|
||
|
||
**Dependencies:** Requires D1.1. Enhanced by D1.2 (deadline rules only useful once scheduler exists).
|
||
|
||
---
|
||
|
||
#### D2 — Multi-Level Approval Chains
|
||
|
||
**What:** Approval workflows requiring sign-off from multiple stakeholders in sequence. Each step: approver role/user, required/optional, auto-advance on approve. Visual progress indicator.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model ApprovalChain {
|
||
id String @id @default(cuid())
|
||
name String
|
||
organizationId String
|
||
stageType String?
|
||
steps ApprovalStep[]
|
||
createdAt DateTime @default(now())
|
||
}
|
||
|
||
model ApprovalStep {
|
||
id String @id @default(cuid())
|
||
chainId String
|
||
stepOrder Int
|
||
approverRole Role?
|
||
approverUserId String?
|
||
isRequired Boolean @default(true)
|
||
autoAdvance Boolean @default(true)
|
||
approvalRecords ApprovalRecord[]
|
||
}
|
||
|
||
model ApprovalRecord {
|
||
id String @id @default(cuid())
|
||
deliverableStageId String
|
||
stepId String
|
||
decision ReviewDecision
|
||
note String?
|
||
decidedById String
|
||
decidedAt DateTime @default(now())
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/settings/approvals/page.tsx`
|
||
- `src/components/approvals/chain-builder.tsx`
|
||
- `src/components/approvals/approval-progress.tsx`
|
||
- `src/lib/services/approval-service.ts`
|
||
|
||
---
|
||
|
||
#### D3 — Project Templates
|
||
|
||
**What:** Save a full project configuration (deliverables, stage settings, default assignments) as a reusable template. One-click project creation for repeat HP SKU types.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model ProjectTemplate {
|
||
id String @id @default(cuid())
|
||
name String
|
||
description String?
|
||
organizationId String
|
||
createdById String
|
||
isShared Boolean @default(true)
|
||
configuration Json
|
||
deliverables ProjectTemplateDeliverable[]
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model ProjectTemplateDeliverable {
|
||
id String @id @default(cuid())
|
||
templateId String
|
||
name String
|
||
type String?
|
||
priority Priority @default(MEDIUM)
|
||
defaultAssignments Json?
|
||
sortOrder Int
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/templates/page.tsx`
|
||
- `src/components/templates/template-card.tsx`
|
||
- `src/components/templates/template-builder.tsx`
|
||
- `src/lib/services/template-service.ts`
|
||
|
||
---
|
||
|
||
### E. Asset Intelligence
|
||
|
||
---
|
||
|
||
#### E1 — File Validation on Upload
|
||
|
||
**What:** Automatically validate uploaded files against spec requirements (resolution, color space, format, file size, aspect ratio). Reject non-conforming files with clear error messages. Override option for producers.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model AssetSpec {
|
||
id String @id @default(cuid())
|
||
organizationId String
|
||
stageType String
|
||
name String
|
||
rules Json // { minWidth, minHeight, maxFileSize, allowedFormats[], colorSpace, dpi }
|
||
isActive Boolean @default(true)
|
||
validationResults AssetValidationResult[]
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model AssetValidationResult {
|
||
id String @id @default(cuid())
|
||
revisionId String
|
||
specId String
|
||
passed Boolean
|
||
results Json // [{ check, expected, actual, passed }]
|
||
overrideById String?
|
||
overrideReason String?
|
||
validatedAt DateTime @default(now())
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/settings/asset-specs/page.tsx`
|
||
- `src/components/upload/file-validator.tsx`
|
||
- `src/lib/services/asset-validation-service.ts`
|
||
|
||
**New dependency:** `sharp` (already needed for A1 PNG compositing)
|
||
|
||
---
|
||
|
||
#### E2 — Thumbnail & Preview Generation
|
||
|
||
**What:** Auto-generate web-optimized thumbnails (200px), previews (1200px), and full copies from uploaded high-res assets. Background processing queue.
|
||
|
||
**Key files:**
|
||
- `src/lib/services/preview-service.ts`
|
||
- `src/lib/jobs/generate-previews.ts`
|
||
- `src/components/common/optimized-image.tsx`
|
||
|
||
---
|
||
|
||
#### E3 — AI Review Engine Integration Point
|
||
|
||
**What:** Webhook endpoint for an external AI Review Engine to post structured quality analysis results. AI-flagged regions rendered as dashed-border annotations in the viewer. Configuration per stage type.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model AIReviewResult {
|
||
id String @id @default(cuid())
|
||
revisionId String
|
||
overallScore Float
|
||
passed Boolean
|
||
checks Json
|
||
flaggedRegions Json
|
||
engineVersion String
|
||
processingTimeMs Int
|
||
createdAt DateTime @default(now())
|
||
}
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/app/api/webhooks/ai-review/route.ts`
|
||
- `src/components/review/ai-review-overlay.tsx`
|
||
- `src/components/review/ai-review-summary.tsx`
|
||
- `src/lib/services/ai-review-service.ts`
|
||
|
||
---
|
||
|
||
### F. Quality of Life
|
||
|
||
---
|
||
|
||
#### F1 — Saved Filters & Custom Views
|
||
|
||
**What:** Save current filter/sort/column config as a named view. Personal and shared views. URL-encodable for link sharing.
|
||
|
||
**New data model:**
|
||
```prisma
|
||
model SavedView {
|
||
id String @id @default(cuid())
|
||
name String
|
||
userId String
|
||
organizationId String
|
||
projectId String?
|
||
viewType ViewType
|
||
configuration Json
|
||
isShared Boolean @default(false)
|
||
isPinned Boolean @default(false)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
enum ViewType { TABLE BOARD TIMELINE }
|
||
```
|
||
|
||
**Key files:**
|
||
- `src/components/views/saved-view-picker.tsx`
|
||
- `src/components/views/save-view-dialog.tsx`
|
||
- `src/lib/services/saved-view-service.ts`
|
||
|
||
---
|
||
|
||
#### F2 — Skill-Based Assignment UI
|
||
|
||
**What:** The Skill / UserSkill / StageSkillRequirement data models already exist. This adds the UI: skill tags on user profiles, skill requirements on stage templates, and best-fit artist suggestions in the assignment dialog (sorted by skill match + lowest current load).
|
||
|
||
**Key files:**
|
||
- `src/app/(app)/settings/skills/page.tsx`
|
||
- `src/components/assignments/skill-match-suggestions.tsx`
|
||
- `src/lib/services/skill-service.ts` — matching algorithm
|
||
|
||
---
|
||
|
||
#### F3 — Batch Upload with Auto-Matching
|
||
|
||
**What:** Drop multiple files onto a project; auto-match to deliverables by file naming convention (configurable regex). Preview table shows match confidence, manual override available.
|
||
|
||
**Key files:**
|
||
- `src/components/upload/batch-upload-zone.tsx`
|
||
- `src/components/upload/file-matcher.tsx`
|
||
- `src/lib/services/file-matching-service.ts`
|
||
|
||
---
|
||
|
||
#### F4 — Extended Command Palette Actions
|
||
|
||
**What:** Add quick-action commands to the existing Cmd+K palette: "approve and advance," "assign to me," "mark blocked," "start review session." Context-aware based on current page.
|
||
|
||
**Key files:**
|
||
- Update `src/components/layout/command-palette.tsx`
|
||
- `src/lib/commands/action-registry.ts`
|
||
|
||
---
|
||
|
||
### G. Docker Deployment
|
||
|
||
---
|
||
|
||
#### G1 — Docker Compose Stack
|
||
|
||
**What:** `docker-compose.yml` with three services — Next.js app, PostgreSQL + pgvector, Ollama with pre-configured models. One `docker compose up` starts everything.
|
||
|
||
Note: `Dockerfile` and `docker-compose.yml` already exist in the repo root — review and complete as needed. The Dockerfile must include `ffmpeg` (required by A7 — Video Review).
|
||
|
||
**Services:**
|
||
|
||
| Service | Image | Purpose |
|
||
|---|---|---|
|
||
| `app` | Custom Dockerfile | Next.js production build |
|
||
| `db` | `pgvector/pgvector:pg17` | PostgreSQL with pgvector |
|
||
| `ollama` | `ollama/ollama:latest` | Local AI (nomic-embed-text + qwen3.5:9b) |
|
||
|
||
**Key files:**
|
||
- `Dockerfile` — multi-stage Next.js build (review existing)
|
||
- `docker-compose.yml` — full stack (review existing)
|
||
- `docker/ollama-entrypoint.sh` — model bootstrap script
|
||
- `docker/db-init.sql` — `CREATE EXTENSION IF NOT EXISTS vector;`
|
||
- `.env.example` — environment template
|
||
|
||
---
|
||
|
||
#### G2 — Health Checks & Startup Orchestration
|
||
|
||
**What:** `pg_isready` health check on `db`, Ollama API health check, `prisma migrate deploy` runs on app startup before `next start`. `depends_on: condition: service_healthy` ensures correct start order.
|
||
|
||
---
|
||
|
||
#### G3 — Dev Environment Override
|
||
|
||
**What:** `docker-compose.dev.yml` that mounts source code and runs `next dev` for hot reloading, while keeping database and Ollama in containers.
|
||
|
||
**Key files:**
|
||
- `docker-compose.dev.yml`
|
||
|
||
---
|
||
|
||
## Data Model Status
|
||
|
||
### Models Currently in Schema
|
||
|
||
| Model | Status |
|
||
|---|---|
|
||
| Organization, User, Account, Session | ✅ |
|
||
| PipelineStageTemplate, PipelineStageDependency | ✅ (legacy global, kept for backward compat) |
|
||
| PipelineTemplate, PipelineStageDefinition, PipelineStageDependencyV2 | ✅ (Phase 2 — org-scoped dynamic pipelines) |
|
||
| OrgRolePermission | ✅ (Phase 2 — RBAC) |
|
||
| Invitation | ✅ (Phase 2 — invite system) |
|
||
| CustomFieldDefinition | ✅ (Phase 2 — custom fields) |
|
||
| NotificationRule | ✅ (Phase 2 — notification rules) |
|
||
| Project, Deliverable, DeliverableStage | ✅ (with pgvector, customFields JSON, org denormalization) |
|
||
| StageAssignment, Revision, Comment | ✅ |
|
||
| Notification | ✅ |
|
||
| AutomationRule, AutomationExecution | ✅ |
|
||
| Skill, UserSkill, StageSkillRequirement | ✅ |
|
||
| SearchLog | ✅ |
|
||
| ChatMessage | ✅ |
|
||
|
||
### Models Needed (not yet in schema)
|
||
|
||
| Model | Feature |
|
||
|---|---|
|
||
| Annotation (+ `timestampSeconds`, `frameThumbnailUrl` fields) | A3, A7.3 |
|
||
| FeedbackItem | A5 |
|
||
| ~~ReviewSession, ReviewSessionItem~~ | ~~A6~~ ✅ |
|
||
| ApprovalChain, ApprovalStep, ApprovalRecord | D2 |
|
||
| ProjectTemplate, ProjectTemplateDeliverable | D3 |
|
||
| AssetSpec, AssetValidationResult | E1 |
|
||
| AIReviewResult | E3 |
|
||
| PortalLink | C3 |
|
||
| SLATarget | C4 |
|
||
| ActivityEntry | B2 |
|
||
| SavedView | F1 |
|
||
| Revision.attachments extended with `video` + `referenceVideo` | A7.1 |
|
||
|
||
---
|
||
|
||
## Architecture Reference
|
||
|
||
```
|
||
src/
|
||
├── app/
|
||
│ ├── (auth)/login/ # SSO login
|
||
│ ├── (app)/ # Authenticated routes
|
||
│ │ ├── dashboard/
|
||
│ │ ├── projects/[projectId]/
|
||
│ │ │ ├── table/ board/ timeline/
|
||
│ │ │ └── deliverables/[deliverableId]/
|
||
│ │ ├── my-work/
|
||
│ │ ├── workload/ # ✅ Capacity grid + heatmap
|
||
│ │ ├── calendar/ # ✅ Calendar view
|
||
│ │ ├── reports/weekly/ # ✅ Weekly executive report
|
||
│ │ ├── reviews/ # A7 Review sessions
|
||
│ │ ├── templates/ # D3 Project templates
|
||
│ │ └── settings/
|
||
│ │ ├── permissions/ # ✅ Phase 2 RBAC matrix
|
||
│ │ ├── pipelines/ # ✅ Phase 2 Pipeline builder
|
||
│ │ ├── team/ # ✅ Phase 2 Team + invitations
|
||
│ │ ├── fields/ # ✅ Phase 2 Custom fields
|
||
│ │ ├── notifications/ # ✅ Phase 2 Notification rules
|
||
│ │ └── skills/ # ✅ Skills management
|
||
│ ├── (portal)/[token]/ # C3 Client portal (external)
|
||
│ ├── (external)/review/ # B3 External review links
|
||
│ └── api/
|
||
├── components/
|
||
│ ├── ui/ # shadcn/ui primitives
|
||
│ ├── layout/ # Sidebar, topbar, command palette
|
||
│ ├── views/ # Table, Board, Timeline, Dashboard
|
||
│ ├── workload/ # ✅ Capacity grid + heatmap components
|
||
│ ├── calendar/ # ✅ Calendar components
|
||
│ ├── reports/weekly/ # ✅ Weekly report components
|
||
│ ├── pipeline-builder/ # ✅ Phase 2 Pipeline graph + stage list
|
||
│ ├── revisions/ # A1 Image comparison (to build)
|
||
│ ├── review/ # A2-A7 Full review viewer (image + video)
|
||
│ ├── automations/ # D1 Automation UI (to build)
|
||
│ └── search/ # ✅ Smart search panel
|
||
├── lib/
|
||
│ ├── pipeline/ # ✅ Dependency engine + stage machine + stage resolver
|
||
│ ├── rbac/ # ✅ Phase 2 permissions + org scope + requireAuth
|
||
│ ├── automation/ # ✅ Event bus + rule engine + executor
|
||
│ ├── services/ # Business logic per entity
|
||
│ └── validators/ # Zod schemas
|
||
├── hooks/ # TanStack Query hooks
|
||
└── stores/ # Zustand (UI state, filters)
|
||
```
|
||
|
||
### Key Patterns
|
||
- **Thin API routes** → delegate to `lib/services/` for all business logic
|
||
- **Service layer** → Prisma queries + business rules (testable, reusable)
|
||
- **URL-synced filters** via `nuqs` — views are shareable/bookmarkable
|
||
- **Server Components** for initial fetch, **TanStack Query** for mutations/cache
|
||
- **`npx prisma db push`** for schema changes (no migrations dir — use db push)
|
||
|
||
### Local Dev
|
||
```bash
|
||
# Start DB
|
||
brew services start postgresql@17
|
||
|
||
# Start app (must source nvm)
|
||
export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && npm run dev
|
||
```
|
||
|
||
### Third-Party Libraries Needed for Remaining Work
|
||
|
||
| Library | Purpose | Feature |
|
||
|---|---|---|
|
||
| `@xyflow/react` | Pipeline dependency graph visualization | ✅ Phase 2 WP5 (installed) |
|
||
| `sharp` | PNG alpha compositing + image validation + thumbnail generation | A1, E1, E2 |
|
||
| `bcryptjs` | Password hashing for portal links | C3 |
|
||
| `@react-pdf/renderer` | PDF export for weekly report download button | C — weekly report enhancement |
|
||
| `ffmpeg` (system) | Video frame extraction, metadata parsing, thumbnail generation | A7 |
|
||
|
||
---
|
||
|
||
*Document version: 1.1 — Updated 2026-03-14 (Phase 2: Dynamic Pipelines complete)*
|
||
*To be updated as features are built and priorities shift.*
|