11 KiB
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, orFINALare 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 (0–1) 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)
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"