Files
vfxreview/OVERVIEW.md
T
twotalesanimation 0fbe856dce Initial commit
2026-05-19 22:20:29 +02:00

301 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FeedBack — App Overview & Status
> Last updated: May 2026
---
## What It Is
FeedBack is a self-hosted VFX review and approval platform for boutique studios. Internal teams (admins, producers, supervisors, artists) manage projects and shots, upload versioned video files, draw frame-accurate annotations, and leave timestamped comments. External clients receive token-gated review links and can approve, reject, or request changes — no login required.
---
## Current Status
All core features are built and functional:
| Area | Status |
|------|--------|
| Auth (login, roles, sessions) | ✅ Complete |
| Projects & shots (CRUD) | ✅ Complete |
| Version upload (local storage) | ✅ Complete |
| Review player + frame timeline | ✅ Complete |
| Annotation drawing (canvas) | ✅ Complete — persists across frame navigation |
| Frame-anchored comments + replies | ✅ Complete |
| Approval workflow | ✅ Complete |
| Client management (internal) | ✅ Complete |
| Client portal (external, token-gated) | ✅ Complete |
| Tokenized review links | ✅ Complete |
| Slack notifications | ✅ Complete (optional webhook) |
| In-app notifications | ✅ Complete |
| Email (nodemailer) | ⚙️ Wired, requires SMTP env vars |
| S3 / R2 / MinIO / B2 storage | ⚙️ Wired, requires env vars |
| Docker Compose deployment | ✅ Complete |
---
## Route Map
### Public / Unauthenticated
| Route | Description |
|-------|-------------|
| `/login` | Credentials sign-in page |
| `/client/[token]` | External client project overview — lists shots visible to client |
| `/client/[token]/review/[versionId]` | External client review page — video player, comments, approve/reject |
### Dashboard (requires login)
| Route | Description |
|-------|-------------|
| `/dashboard` | Home — stats cards, shot queue, recent activity |
| `/projects` | Project list |
| `/projects/[id]` | Project detail — shot grid, stats |
| `/projects/[id]/shots/[shotId]` | Shot detail — version list, approval history |
| `/review/[versionId]` | Full-screen review player (internal) |
| `/clients` | Client list — ADMIN/PRODUCER only |
| `/clients/[clientId]` | Client detail — linked projects, active review sessions |
| `/settings` | User profile settings |
---
## API Routes
### Internal (authenticated)
| Method | Route | Purpose |
|--------|-------|---------|
| GET/POST | `/api/projects` | List / create projects |
| GET/POST/DELETE | `/api/shots` | Shot CRUD |
| GET | `/api/versions/[versionId]/comments` | Fetch comments for a version |
| GET | `/api/versions/[versionId]/annotations` | Fetch annotations for a version |
| GET/POST | `/api/clients` | List / create clients |
| GET/POST/DELETE | `/api/review-sessions` | Manage tokenized review links |
| GET/POST | `/api/notifications` | In-app notification CRUD |
| GET | `/api/files/[...key]` | Serve locally-stored files (no auth) |
| POST | `/api/upload/local` | Local file upload handler |
### Client portal (no auth — token-gated)
| Method | Route | Purpose |
|--------|-------|---------|
| GET | `/api/client/[token]/project` | Project + shots (CLIENT_REVIEW / REVISIONS / APPROVED / FINAL only) |
| GET | `/api/client/[token]/versions/[versionId]` | Version detail + comments |
| POST | `/api/client/[token]/approve` | Submit approval decision |
| POST | `/api/client/[token]/comment` | Post a comment |
---
## Server Actions (`/actions`)
| File | Exports |
|------|---------|
| `projects.ts` | `createProject`, `updateProject` |
| `shots.ts` | `createShot`, `updateShotStatus`, `getShotById` |
| `versions.ts` | `createVersion`, `setLatestVersion` |
| `comments.ts` | `addComment`, `addReply`, `resolveComment` |
| `annotations.ts` | `saveAnnotation`, `getAnnotationsForVersion`, `getAnnotationsForFrame` |
| `approvals.ts` | `submitApproval` |
---
## Database Schema
### Enums
| Enum | Values |
|------|--------|
| `Role` | `ADMIN` · `PRODUCER` · `SUPERVISOR` · `ARTIST` · `CLIENT` |
| `ProjectStatus` | `ACTIVE` · `ON_HOLD` · `COMPLETED` · `ARCHIVED` |
| `ShotStatus` | `WAITING` · `IN_PROGRESS` · `INTERNAL_REVIEW` · `CLIENT_REVIEW` · `REVISIONS` · `APPROVED` · `FINAL` |
| `ShotPriority` | `LOW` · `NORMAL` · `HIGH` · `URGENT` |
| `ApprovalStatus` | `PENDING_REVIEW` · `APPROVED` · `REJECTED` · `NEEDS_CHANGES` |
| `NotificationType` | `VERSION_UPLOADED` · `FEEDBACK_ADDED` · `SHOT_APPROVED` · `SHOT_REJECTED` · `COMMENT_REPLY` · `MENTION` · `REVISION_REQUESTED` |
### Models
#### `User`
Core identity. Has a `role` (see enum). `passwordHash` used for credentials login. NextAuth `Account` and `Session` models hang off this.
| Key field | Type | Notes |
|-----------|------|-------|
| `id` | cuid | PK |
| `email` | String | unique |
| `role` | Role | default `ARTIST` |
| `passwordHash` | String? | bcrypt hash |
| `isActive` | Boolean | soft-disable |
#### `Client`
Represents an external studio or client company.
| Key field | Type | Notes |
|-----------|------|-------|
| `company` | String | |
| `contactPerson` | String | |
| `email` | String | unique |
| `phone`, `notes`, `logoUrl` | optional | |
#### `ClientAccess`
Junction between a `User` (with `CLIENT` role) and a `Client` record. Allows a portal user account to be linked to a specific client company.
#### `Project`
Top-level container. Linked to an optional `Client`, `producer` (User), and `supervisor` (User).
| Key field | Type | Notes |
|-----------|------|-------|
| `code` | String | unique, used as short label |
| `status` | ProjectStatus | default `ACTIVE` |
| `clientId` | String? | nullable |
| `slackWebhook` | String? | per-project Slack alerts |
#### `Shot`
Belongs to a `Project`. Tracks pipeline status and priority.
| Key field | Type | Notes |
|-----------|------|-------|
| `shotCode` | String | unique per project |
| `sequence` | String? | grouping label |
| `status` | ShotStatus | default `WAITING` |
| `priority` | ShotPriority | default `NORMAL` |
| `fps` | Float | default 24 |
| `frameStart`, `frameEnd` | Int? | optional frame range |
> **Client visibility rule**: only shots with status `CLIENT_REVIEW`, `REVISIONS`, `APPROVED`, or `FINAL` are returned by the client portal API.
#### `Version`
A specific upload for a shot. Multiple versions per shot; only one has `isLatest = true`.
| Key field | Type | Notes |
|-----------|------|-------|
| `versionNumber` | Int | unique per shot |
| `fileUrl` | String | path/URL to video |
| `fileSize` | BigInt? | serialized to string in API responses |
| `fps` | Float | |
| `approvalStatus` | ApprovalStatus | default `PENDING_REVIEW` |
| `isLatest` | Boolean | |
#### `Comment`
Frame-anchored comment on a version. Supports replies via `CommentReply`.
| Key field | Type | Notes |
|-----------|------|-------|
| `frameNumber` | Int | exact frame |
| `timestamp` | Float | seconds |
| `text` | Text | |
| `isResolved` | Boolean | |
#### `CommentReply`
Flat child of `Comment`. No threading beyond one level.
#### `Annotation`
Canvas drawing saved against a version + frame. `drawingData` is JSON containing an array of `AnnotationShape` objects with normalized (01) coordinates.
| Key field | Type | Notes |
|-----------|------|-------|
| `frameNumber` | Int | |
| `drawingData` | Json | `{ shapes, canvasWidth, canvasHeight, version }` |
| `color` | String | hex, default `#ef4444` |
| `isVisible` | Boolean | soft-hide |
| `commentId` | String? | optional companion comment link |
#### `Approval`
Immutable approval record created each time a user submits a decision. The version's `approvalStatus` is updated separately.
| Key field | Type | Notes |
|-----------|------|-------|
| `status` | ApprovalStatus | |
| `notes` | Text? | |
#### `Notification`
In-app notification for a user.
| Key field | Type | Notes |
|-----------|------|-------|
| `type` | NotificationType | |
| `data` | Json? | extra context (versionId, shotCode, etc.) |
| `isRead` | Boolean | |
#### `ReviewSession`
A tokenized, time-limited review link for external clients. Tied to a `Project`.
| Key field | Type | Notes |
|-----------|------|-------|
| `token` | String | unique cuid, used in client portal URLs |
| `label` | String? | friendly name shown to client |
| `email` | String? | recipient email |
| `expiresAt` | DateTime | |
| `isActive` | Boolean | can be deactivated manually |
| `accessCount` | Int | incremented on each portal visit |
---
## Key Component Map
```
components/
player/
ReviewPlayer.tsx — Forwardref player, keyboard shortcuts, fullscreen
FrameTimeline.tsx — Canvas ruler with comment/annotation markers
PlaybackControls.tsx — Transport bar, speed selector, annotation toggle
annotations/
AnnotationCanvas.tsx — Canvas overlay; per-frame shape map persists navigation
AnnotationTools.tsx — Tool/colour/stroke picker (shown when annotating)
comments/
CommentPanel.tsx — Comment list, filter, reply, resolve; seekToFrame on click
versions/
VersionUpload.tsx — Drag-and-drop uploader
VersionList.tsx — Version history with approval badges
shots/
ShotCard.tsx — Shot grid card with status dropdown
NewShotDialog.tsx — Create shot form
projects/
ProjectCard.tsx — Project list card
NewProjectDialog.tsx — Create project form
clients/
NewClientDialog.tsx — Create client form
ShareReviewDialog.tsx — Generate tokenized review link
ReviewSessionList.tsx — Active sessions with copy/deactivate actions
dashboard/
StatsCards.tsx — Top-level counts
ShotQueue.tsx — Shots needing attention
RecentActivity.tsx — Latest version/comment events
layout/
Sidebar.tsx — Nav + role-based link visibility
Header.tsx — Breadcrumb + notification bell
NotificationBell.tsx — Dropdown of unread notifications
```
---
## Auth & Roles
Authentication uses **NextAuth v5** with a Credentials provider (email + bcrypt password). Sessions are JWT-based.
| Role | Access |
|------|--------|
| `ADMIN` | Full access including user management, client management |
| `PRODUCER` | Full project/shot/version access, client management |
| `SUPERVISOR` | Full project/shot/version access, no client management |
| `ARTIST` | Own shots and versions; can comment |
| `CLIENT` | Dashboard login only (legacy); primary access via portal token links |
The **client portal** routes (`/client/[token]/*` and `/api/client/[token]/*`) bypass auth entirely — access is controlled by token validity, `isActive`, and `expiresAt`.
---
## Storage
Controlled by `STORAGE_PROVIDER` env var. Currently defaults to `local` (files saved in `/public/uploads/`, served via `/api/files/[...key]`). Switching to S3/R2/B2/MinIO/UploadThing requires only env var changes.
---
## Environment Variables (minimum for local dev)
```env
DATABASE_URL="postgresql://user:pass@localhost:5432/feedback"
AUTH_SECRET="<32-char random string>"
NEXTAUTH_URL="http://localhost:3000"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
STORAGE_PROVIDER="local"
```