# PROJECT_SPEC.md

# AI-NVR Frontend Platform

## Mission

Build a production-grade frontend for an AI-powered Network Video Recorder (NVR) platform.

The frontend should feel like a modern commercial security product rather than an internal dashboard.

Design inspiration:

* Verkada
* Rhombus
* Linear
* Vercel Dashboard
* OpenAI Dashboard

The final product must be:

* Fully functional
* Production-ready
* Responsive
* Accessible
* Dark-mode first
* Type-safe
* Docker deployable
* Vercel deployable

The coding agent should build the entire application in one execution without waiting for approval between milestones.

---

# IMPORTANT INSTRUCTIONS FOR THE CODING AGENT

This repository should be treated as a complete implementation task.

Do not stop after:

* project scaffolding
* authentication
* routing
* first page implementation

Continue until the application is fully implemented.

The expected output is:

* complete frontend
* complete API integration
* complete UI
* tests
* Docker support
* production-ready architecture

If assumptions are required:

* make reasonable assumptions
* document them
* continue implementation

Do not pause for confirmation.

---

# Technology Stack

Use exactly:

* Next.js 15 (App Router)
* React 19
* TypeScript
* TailwindCSS
* shadcn/ui
* Radix UI
* TanStack Query
* Zustand
* React Hook Form
* Zod
* Sonner
* Framer Motion
* Lucide React
* TanStack Virtual
* date-fns

Testing:

* Vitest
* React Testing Library

Quality:

* ESLint
* Prettier

---

# Environment Variables

Create:

.env.local

Required:

```env
NEXT_PUBLIC_API_URL=http://localhost:8000
```

---

# OpenAPI

The backend exposes a complete OpenAPI specification.

Generate API types automatically.

Install:

```bash
npm install openapi-typescript
```

Generate:

```bash
npx openapi-typescript openapi.json -o src/types/api.ts
```

Generated types are the source of truth.

Never manually duplicate API schemas.

---

# Backend Overview

Authentication:

```http
POST /api/auth/login
POST /api/auth/refresh
GET /api/auth/me
```

Cameras:

```http
GET /api/cameras
GET /api/cameras/{id}
GET /api/live-streams
```

Events:

```http
GET /api/events
```

Clips:

```http
GET /api/clips
GET /api/clips/{id}
GET /api/clips/{id}/playback
GET /api/clips/{id}/thumbnail
```

---

# Authentication System

Implement:

* Login
* Logout
* Session persistence
* Token refresh
* Route protection

Storage strategy:

Access token:

* memory
* Zustand

Refresh token:

* localStorage

Implement:

```text
src/lib/auth.ts
src/lib/token-manager.ts
src/store/auth-store.ts
```

Requirements:

* automatic refresh
* refresh on 401
* retry request once
* logout if refresh fails

Protected routes:

```text
/dashboard
/live
/events
/clips
/cameras
/settings
/profile
```

Unauthenticated users must be redirected to:

```text
/login
```

---

# API Client

Create:

```text
src/lib/api/client.ts
```

Requirements:

* typed requests
* typed responses
* auth header injection
* refresh support
* normalized errors

Expose:

```ts
api.get()
api.post()
api.put()
api.patch()
api.delete()
```

---

# Application Layout

Desktop:

```text
Sidebar
Topbar
Content Area
```

Mobile:

```text
Drawer Navigation
```

Sidebar:

* Dashboard
* Live View
* Events
* Clips
* Cameras
* Settings
* Profile

Features:

* active route highlighting
* collapsible sidebar
* responsive behavior

---

# Theme System

Support:

* Dark
* Light
* System

Default:

Dark

Persist preference.

---

# Routes

Implement:

```text
/login

/dashboard

/live

/events

/clips

/cameras

/cameras/[id]

/settings

/profile
```

---

# Login Page

Route:

```text
/login
```

Requirements:

* username
* password
* validation
* loading states
* error handling

Use:

* React Hook Form
* Zod

Successful login:

```text
/dashboard
```

---

# Dashboard

Route:

```text
/dashboard
```

Purpose:

Operational overview.

Widgets:

### Cameras

* total cameras

### Streams

* cameras with streams

### Events

* events today

### Detections

* person detections
* vehicle detections

### Recent Activity

* recent clips
* recent events

Metrics should be computed client-side.

Use:

* cards
* charts if useful
* animations

---

# Cameras

Route:

```text
/cameras
```

Endpoint:

```http
GET /api/cameras
```

Display:

* camera id
* name

Views:

* grid
* table

Features:

* search
* sort
* responsive layout

---

# Camera Detail

Route:

```text
/cameras/[id]
```

Endpoints:

```http
GET /api/cameras/{id}
GET /api/events?camera_id=id
GET /api/clips?camera_id=id
```

Display:

* camera metadata
* stream preview
* recent clips
* recent events

---

# Live Monitoring

Route:

```text
/live
```

Endpoint:

```http
GET /api/live-streams
```

Response example:

```json
[
  {
    "camera_id": 0,
    "name": "Camera 0",
    "webrtc_url": "/stream/cam0"
  }
]
```

---

# MediaMTX Integration

Create:

```text
src/lib/live/player.ts
components/live/live-player.tsx
```

Player API:

```tsx
<LivePlayer url={stream.webrtc_url} />
```

Requirements:

* fullscreen
* mute
* reconnect
* loading state
* error state

Do not use iframes.

Create an abstraction layer.

Future support:

* MediaMTX WebRTC
* WHEP
* HLS fallback

Construct URL relative to application origin.

Example:

```ts
const streamUrl =
  window.location.origin + stream.webrtc_url;
```

---

# Live Grid

Layouts:

* 1x1
* 2x2
* 3x3
* 4x4

Features:

* fullscreen tile
* responsive resizing
* camera selection

Components:

```text
LiveGrid
LiveToolbar
LiveTile
```

---

# Events

Route:

```text
/events
```

Endpoint:

```http
GET /api/events
```

Response:

```ts
{
  items: Event[]
  page: number
  limit: number
  total: number
}
```

Use:

```ts
useInfiniteQuery()
```

instead of classic pagination.

---

# Event Filters

Support:

* camera
* object type
* date range

Sync filters to URL.

Example:

```text
/events?camera=0&object=person
```

---

# Event Timeline

Display:

```text
Thumbnail

Object Detected

Camera

Timestamp
```

Visual style:

Modern timeline.

---

# Event Detail Drawer

When opening an event:

Fetch:

```http
GET /api/clips/{clip_id}
```

Do not preload.

Display:

* metadata
* thumbnail
* playback action

---

# Clips

Route:

```text
/clips
```

Endpoint:

```http
GET /api/clips
```

Use:

```ts
useInfiniteQuery()
```

Display:

Responsive gallery.

Card:

* thumbnail
* camera
* detected object
* timestamp
* duration

---

# Clip Details

Endpoint:

```http
GET /api/clips/{id}
```

Display:

* metadata
* playback action

---

# Playback

Endpoint:

```http
GET /api/clips/{id}/playback
```

Response:

```ts
{
  url: string
  expires_in: number
}
```

Workflow:

1. click clip
2. request playback URL
3. open modal
4. play video

Requirements:

* seek
* fullscreen
* playback speed
* duration display

---

# Thumbnails

Endpoint:

```http
GET /api/clips/{id}/thumbnail
```

Load lazily.

Requirements:

* skeletons
* fallback image
* retry support

---

# Search

Implement reusable search.

Search:

* cameras
* clips
* events

Create:

```text
components/common/SearchInput.tsx
```

---

# Filters

Create reusable filter system.

Directory:

```text
components/filters/
```

Components:

* camera filter
* object filter
* date range filter

Reusable across:

* events
* clips

---

# State Management

TanStack Query:

Server state only.

Zustand:

* auth
* theme
* layout preferences

Never duplicate API data in Zustand.

---

# Notifications

Use:

```text
Sonner
```

Notify:

* login failures
* refresh failures
* playback failures
* API failures

---

# Loading States

Create:

```text
LoadingCard
LoadingGrid
LoadingTimeline
LoadingTable
```

Use skeletons.

---

# Empty States

Create:

```text
EmptyState
```

Use throughout application.

---

# Error Handling

Create:

```text
ErrorBoundary
```

Global API error handling.

Friendly user-facing messages.

---

# Accessibility

Requirements:

* keyboard navigation
* visible focus states
* ARIA labels
* screen reader compatibility

---

# Performance

Implement:

* route code splitting
* lazy loading
* image optimization
* memoization
* virtualization

Use:

```text
@tanstack/react-virtual
```

for:

* clips
* events

---

# Security

Requirements:

* no tokens in URLs
* secure token handling
* logout on invalid refresh
* sanitize displayed content

---

# Reusable Components

Create:

```text
PageHeader
SearchInput
FilterBar
DateRangePicker
ThemeSwitcher
UserMenu

CameraCard
ClipCard
EventCard

LiveTile
LiveToolbar

PlaybackModal

LoadingState
ErrorState
EmptyState

ConfirmDialog
```

---

# Command Palette

Implement:

```text
⌘K
```

Search:

* cameras
* clips
* events

Inspired by Linear.

---

# Keyboard Shortcuts

Implement:

```text
G + D → Dashboard

G + L → Live

G + E → Events

G + C → Clips

G + A → Cameras
```

---

# Future Extension Points

Architect support for:

* PTZ controls
* RBAC
* Alert Center
* Live AI bounding boxes
* WebSockets
* Push notifications

Do not implement.

Only provide clean extension points.

---

# Testing

Install:

```bash
npm install -D vitest @testing-library/react
```

Test:

* auth flow
* route protection
* API client
* filters
* playback modal

Target:

70%+ coverage

---

# CI/CD

Create:

```text
.github/workflows/ci.yml
```

Pipeline:

1. install
2. lint
3. typecheck
4. test
5. build

---

# Docker

Create:

```text
Dockerfile
docker-compose.yml
```

Production build:

```bash
next build
next start
```

Expose:

```text
3000
```

---

# Deployment

Support:

* Docker
* Vercel

No platform-specific assumptions.

---

# UI Quality Requirements

The UI should feel like:

* Linear
* Vercel
* Verkada

Not:

* Bootstrap admin panel
* Internal enterprise dashboard

Use:

* generous spacing
* subtle animations
* modern cards
* clean typography
* excellent dark mode

---

# Final Acceptance Criteria

The project is complete only when:

✓ Authentication works

✓ Protected routes work

✓ Dashboard works

✓ Cameras page works

✓ Camera detail page works

✓ Live monitoring works

✓ Events page works

✓ Clip gallery works

✓ Playback modal works

✓ Dark mode works

✓ Mobile responsive

✓ Desktop responsive

✓ TypeScript clean

✓ ESLint clean

✓ Tests passing

✓ Docker build passes

✓ Production build passes

The coding agent should continue implementing until all acceptance criteria are satisfied.
