Initial commit

This commit is contained in:
twotalesanimation
2026-05-19 22:20:29 +02:00
commit 0fbe856dce
173 changed files with 38316 additions and 0 deletions
+300
View File
@@ -0,0 +1,300 @@
# 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"
```