diff --git a/.gitignore b/.gitignore index 3ba7d15..6da28d3 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,4 @@ next-env.d.ts # uploaded assets (runtime-generated, not needed in repo) /public/uploads/ -/assets/review-images/ \ No newline at end of file +/assets/review-images/ diff --git a/ROADMAP.md b/ROADMAP.md index 74b88c6..3616eea 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,7 +3,7 @@ > 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-14 (Phase 2 complete)* +*Last updated: 2026-03-16* --- @@ -11,7 +11,7 @@ 1. [What's Built](#whats-built) 2. [Remaining Work — Priority Order](#remaining-work--priority-order) - - [A. Image Comparison & Visual Review](#a-image-comparison--visual-review) + - [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) @@ -114,70 +114,107 @@ --- -### A. Image Comparison & Visual Review +### A. Visual Review Tool -The highest-impact remaining feature area. CG production review is fundamentally visual — everything else supports this core workflow. +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 — Reference Image Comparison (Lightweight, in Stage Detail) +#### A1 — Review Viewer & Image Upload `[x]` -**What:** A-B comparison tools embedded in the existing stage detail sheet. Producers and artists compare the reference/approved image against the current WIP render without leaving the deliverable page. +The foundation. A dedicated review page with a high-fidelity image viewer and the upload infrastructure to get images into the system. -**Why first:** Lower complexity than the full review viewer (A2). Directly improves the daily review loop for existing users and unblocks meaningful visual feedback on revision rounds. - -**Implementation:** -- New "Compare" tab in the stage detail sheet (alongside Revisions and Comments tabs) -- Upload zones for reference image + current render per revision round -- Three comparison modes: - - **A-B Slider** — draggable vertical divider (CSS clip-path + pointer events, no dependency) - - **Toggle** — click/Space to crossfade between images - - **Side-by-side** — synced zoom/pan on wide screens -- Scroll-to-zoom + click-drag pan, synced across both images in all modes -- Image gallery: thumbnails of all uploaded images across all revision rounds with round labels -- **PNG alpha compositing on upload** — flatten transparent PNGs onto white background server-side using `sharp`, so CG renders with transparent drop shadows compare correctly -- Extend `Revision.attachments` JSON schema: `{ referenceImage?: {...}, currentImage?: {...}, annotations?: [...] }` -- Lightweight markup overlay (optional): arrow, rectangle, freehand, text pin drawn on top of current image +**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, reference or current image -- `DELETE /api/stages/[stageId]/revisions/[revisionId]/upload` — remove image +- `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/components/revisions/image-compare-slider.tsx` — A-B slider with drag handle -- `src/components/revisions/image-toggle.tsx` — Toggle/crossfade -- `src/components/revisions/image-side-by-side.tsx` — Synced side-by-side with zoom/pan -- `src/components/revisions/image-upload-zone.tsx` — Drag-and-drop upload -- `src/components/revisions/revision-image-gallery.tsx` — Browse images across revision rounds -- `src/components/revisions/compare-tab.tsx` — Orchestrator: mode selector + comparison view -- `src/components/revisions/markup-overlay.tsx` — Lightweight annotation layer - -**New dependency:** `sharp` (server-side PNG alpha compositing on upload) +- `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 — Full-Screen Image Viewer +#### A2 — Version Comparison `[x]` -**What:** Dedicated full-screen viewer with high-fidelity zoom (up to 200%+) for pixel-level inspection of CG renders. +Compare two revisions of the same deliverable stage. This is the daily workhorse — producers and artists check what changed between rounds. -**Implementation:** -- Canvas-based viewer with WebGL acceleration for large images -- Zoom: scroll wheel, pinch, keyboard (+/-), toolbar buttons (fit, 50%, 100%, 150%, 200%, free) -- Pan via click-drag when zoomed; minimap overlay showing viewport position -- Pixel coordinate + color value readout (RGB/Hex) in status bar -- High-DPI display support (retina) +**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/image-viewer.tsx` -- `src/components/review/zoom-controls.tsx` -- `src/components/review/minimap.tsx` -- `src/hooks/use-image-viewer.ts` +- `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 — Pixel-Accurate Annotations +#### A3 — Annotations `[x]` -**What:** Draw annotations directly on images — circles, rectangles, arrows, freehand, text labels, pins. Anchored to image coordinates so they stay accurate at any zoom level. +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 @@ -188,63 +225,80 @@ model Annotation { revisionId String revision Revision @relation(fields: [revisionId], references: [id], onDelete: Cascade) type AnnotationType - data Json // { x, y, width, height, points[], text, color, strokeWidth } - imageX Float + 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 } +enum AnnotationType { RECTANGLE ELLIPSE ARROW FREEHAND TEXT PIN SCREENSHOT } ``` **Key files:** -- `src/components/review/annotation-layer.tsx` -- `src/components/review/annotation-tools.tsx` -- `src/components/review/annotation-renderer.tsx` -- `src/lib/services/annotation-service.ts` -- `src/lib/validators/annotation.ts` -- `src/hooks/use-annotations.ts` +- `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 A2 (Image Viewer) +**Dependencies:** Requires A1 --- -#### A4 — Side-by-Side Version Comparison (Full Viewer) +#### A4 — Revision History Timeline `[x]` -**What:** Compare two revisions in the full-screen viewer using side-by-side, overlay (opacity slider), wipe (draggable divider), or onion skin modes. +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/comparison-viewer.tsx` -- `src/components/review/wipe-divider.tsx` -- `src/components/review/overlay-controls.tsx` -- `src/app/(app)/projects/[projectId]/deliverables/[deliverableId]/review/page.tsx` +- `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 A2 +**Dependencies:** Requires A1. Enhanced by A3 (annotation counts and filtering). --- -#### A5 — Revision History Timeline +#### A5 — Feedback Checklist (Artist Action Items) `[x]` -**What:** Collapsible panel in the review viewer showing all revision rounds as a vertical timeline. Each node shows thumbnail, status badge, submitter, annotation count, comment summary, and decision record. +Every annotation and actionable comment becomes a structured to-do item on a checklist for the assigned artist. Closes the feedback-to-fix loop. -**No new data model** — aggregation over existing `Revision`, `Comment`, and `Annotation` records. +**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 -**Key files:** -- `src/components/review/revision-timeline.tsx` -- `src/components/review/revision-node.tsx` -- `src/components/review/revision-comments.tsx` -- `src/components/review/revision-annotations-summary.tsx` -- `src/hooks/use-revision-history.ts` +**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. -**Dependencies:** Requires A2 + A3 - ---- - -#### A6 — Feedback Action Items (Artist Checklist) - -**What:** Every annotation and actionable comment auto-creates a FeedbackItem on a structured checklist. Artists work through items (Critical / Major / Minor / Suggestion), check them off with resolution notes. Unresolved critical items block resubmission. Checklist appears in three places: review viewer panel, My Work page, and stage card badge. +**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 @@ -259,7 +313,7 @@ model FeedbackItem { commentId String? comment Comment? @relation(...) summary String - severity FeedbackSeverity @default(MAJOR) + isActionItem Boolean @default(true) status FeedbackStatus @default(OPEN) sortOrder Int @default(0) assignedToId String? @@ -275,26 +329,32 @@ model FeedbackItem { @@map("feedback_items") } -enum FeedbackSeverity { CRITICAL MAJOR MINOR SUGGESTION } -enum FeedbackStatus { OPEN IN_PROGRESS RESOLVED VERIFIED REOPENED } +enum FeedbackStatus { OPEN IN_PROGRESS RESOLVED VERIFIED REOPENED } ``` **Key files:** -- `src/components/review/feedback-checklist.tsx` -- `src/components/review/feedback-item.tsx` -- `src/components/review/feedback-progress-bar.tsx` -- `src/components/my-work/feedback-summary.tsx` -- `src/components/stages/feedback-indicator.tsx` -- `src/lib/services/feedback-service.ts` +- `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 + A5 +**Dependencies:** Requires A3 (annotations to generate items from) + A4 (timeline for round context) --- -#### A7 — Review Sessions & Playlists +#### A6 — Review Sessions & Playlists `[x]` -**What:** Curate an ordered set of deliverables into a review session. Walk through them in presenter mode with per-item approve/request-changes/reject decisions. Shareable via link. +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 @@ -327,15 +387,277 @@ enum ReviewSessionStatus { DRAFT IN_PROGRESS COMPLETED } enum ReviewDecision { APPROVED CHANGES_REQUESTED REJECTED } ``` -**Key files:** -- `src/app/(app)/reviews/page.tsx` -- `src/app/(app)/reviews/[sessionId]/page.tsx` -- `src/components/review/session-builder.tsx` -- `src/components/review/session-presenter.tsx` -- `src/components/review/session-summary.tsx` -- `src/lib/services/review-session-service.ts` +**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) -**Dependencies:** Requires A2 + A3 +**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: 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 server-side frame extraction + optional transcoding) + +The video feature is broken into sub-stages, each independently useful: + +--- + +##### A7.1 — FFmpeg in Docker + Video Upload Infrastructure + +Add FFmpeg to the Docker environment and extend the upload system to accept video files. + +**What gets built:** +- **Dockerfile update** — install `ffmpeg` in both builder and runner stages (`apk add --no-cache ffmpeg` on Alpine) +- **docker-compose.yml** — no changes needed (ffmpeg is in the app container) +- **Upload service extension** — accept MP4 files up to 500MB alongside existing image types + - New `type` value: `"video"` (in addition to "reference", "current", "screenshot") + - Validate MIME type (`video/mp4`) and file size + - Store in `/public/uploads/revisions/[revisionId]/video_[timestamp].mp4` +- **Thumbnail extraction** — on upload, run `ffmpeg` to extract a poster frame (first frame or frame at 1s) as JPEG +- **Video metadata extraction** — use `ffmpeg -i` (or `ffprobe`) to read duration, resolution, codec, frame rate +- **Extend `Revision.attachments` JSON:** + ```json + { + "referenceImage": { ... }, + "currentImage": { ... }, + "video": { + "url": "/uploads/revisions/.../video_xxx.mp4", + "filename": "video_xxx.mp4", + "size": 52428800, + "width": 1920, + "height": 1080, + "duration": 12.5, + "fps": 24, + "codec": "h264", + "thumbnailUrl": "/uploads/revisions/.../video_xxx_thumb.jpg", + "uploadedAt": "2026-03-16T..." + }, + "referenceVideo": { ... } + } + ``` + +**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 + +**Key files (new/modified):** +- `Dockerfile` — add `ffmpeg` installation +- `src/lib/services/upload-service.ts` — extend with video handling, ffprobe metadata, thumbnail extraction +- `src/lib/services/video-service.ts` — FFmpeg wrapper (thumbnail extraction, metadata parsing, frame extraction) +- `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 + +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 `