# MediaRouter Frontend Phase 2: Home and Projects ## Scope Phase 2 replaces the operation-oriented content of the existing `/dashboard` route with a project-oriented Home experience and introduces the frontend project domain. It does not change the Workspace Shell, backend contracts, social behavior, or unfinished product areas. The existing authenticated shell continues to wrap Home, Assets, and the new project route structure: ```text Workspace Shell ├── /dashboard HomeWorkspace ├── /assets MediaAssetsBrowser ├── /projects ProjectsPage └── /projects/[projectId] ProjectProvider + ProjectDetail ``` `/projects` and `/projects/[projectId]` are safe, capability-gated route foundations. The sidebar and command palette do not advertise them until the backend provides an authoritative projects API and the signed-in user has `projects:read`. ## Backend capability audit ### Available now - Server-side authoritative workspaces, users, and memberships exist in the tenancy database and are enforced through RLS. - Canonical media assets are workspace-owned server records. - `GET /v1/social/assets` exposes the workspace-owned media assets registered for publishing, including filename, MIME type, size, metadata, and creation time. - `GET /v1/social/jobs` exposes workspace-scoped publishing jobs. - Generation requests/jobs exist under `/v1/generation/*`. - Template discovery is available through `/v1/templates`. - Operations are discoverable through OpenAPI. ### Missing now There is no backend `projects` resource: no `projects` table/model, no project ownership relation, no project metadata, no project-to-asset/job/template relation, and no project API path in OpenAPI. Workspace membership data is server-side only; there is no browser-facing workspace membership/switching contract. For this reason, the frontend never invents a project, writes browser-local persistence, shows a Create Project action, or emits a project mutation. `ProjectApiUnavailableError` is the explicit façade behavior when no canonical endpoint exists. ## Home architecture `features/home/home-workspace.tsx` composes the existing design-system and workspace primitives into: - greeting based on authenticated user context; - Continue Working, prioritizing a real active job and then the latest registered asset; - Recent Projects, which is an explicit unavailable state until a project API exists; - Active Work from `useLiveJobs()` and the existing social-jobs query only; - Recent Assets from `useSocialMediaAssets()`; - Suggested Actions generated only from resolved navigation/capabilities; and - concise Activity from the same real job records. Home contains no hard-coded counters, progress values, projects, workspace names, or action placeholders. Upload, project creation, import, AI, and similar actions are intentionally absent until their authoritative APIs and UI flows exist. ## Project architecture The project feature is isolated under `frontend/features/projects/`: - `types.ts` defines the typed frontend project domain and maps only explicit backend fields. - `lib/api/projects.ts` discovers a `/vN/projects` collection and `/vN/projects/{id}` detail API from OpenAPI. It fails closed otherwise. - `api/queries.ts` owns project query keys, collection/detail queries, and a shared invalidation boundary for future audited mutations. - `context/project-provider.tsx` supplies route-derived `projectId`, project data, loading/error state, project availability, and read/write permission state. It uses TanStack Query rather than a project Zustand store. - `components/` contains `ProjectCard`, `ProjectGrid`, `ProjectList`, `ProjectHeader`, `ProjectActions`, page, and detail adapters. Project cards can represent thumbnail, title, description, timestamps, status, owner, asset count, job status, and selection using real response fields. Only **Open project** is implemented in the action menu because no backend operation supports rename, duplicate, archive, delete, favorite, or share. ## Data fetching and mutations All network access continues through `lib/api/*` and TanStack Query. Components do not use `fetch()`. | Domain | Query source | Current behavior | | --- | --- | --- | | Projects | OpenAPI-discovered project facade | Disabled/unavailable until both collection and detail reads exist | | Assets | Existing `useSocialMediaAssets()` | Real workspace-scoped publishable media assets | | Active work | Existing `useLiveJobs()` and social jobs query | Real records only; no parallel custom polling | | Templates | Existing `useTemplates()` | Backend registry | | Operations | Existing backend discovery | OpenAPI metadata | No project mutation is exposed today because the backend lacks a schema to validate or authorize one. The feature already has `projectKeys` and `useProjectInvalidation()` so a future audited mutation can invalidate correctly without changing page components. ## Assets and search The existing `/assets` route now renders the real publishing-asset collection via reusable `AssetCard`, `AssetGrid`, and `AssetList` components. It does not claim to be a complete canonical asset repository; it only displays the assets the current backend actually exposes. Global Search retains its shared command/search infrastructure and now maps real project records (when available), social media assets, and publishing jobs. Asset results use `/assets?asset=` to select the loaded authoritative asset. Project results use `/projects/`. Routes are still filtered by resolved navigation, capability, permission, and feature-flag context. ## Commands and permissions `registerProjectCommands()` registers **Open Projects** only when the projects API is discovered. It does not register create, delete, share, or recent-project commands without authoritative support. Project navigation requires both `projects` capability metadata and `projects:read`. The frontend permission vocabulary has reserved `projects:read` and `projects:write` for the future backend contract; neither grants access by itself while the capability is absent. ## Responsive and accessibility behavior - Project and asset grids use one column on narrow screens, two at small/tablet widths, three at large desktop widths, and four on very wide displays. - Project lists use the reusable keyboard-accessible table system with client-side search, sorting, and selection over already loaded records only. - Cards expose descriptive links, selection controls have labels, action menus use Radix focus management, and all interactive controls use design-system focus styles and touch-friendly sizing. - Home does not load editor, timeline, AI Studio, or analytics modules. ## Backend requirements for the next phase To activate actual project persistence, the backend needs at minimum: 1. `GET /v1/projects` — workspace-authorized, paginated project records with stable IDs and `projects:read` authorization. 2. `GET /v1/projects/{project_id}` — workspace-authorized detail lookup returning 404 for missing/non-owned records. 3. `POST /v1/projects` — a strict, documented creation schema and `projects:write` authorization. 4. A persisted project ownership model linked to the authoritative workspace/user membership boundary. 5. Explicit project metadata/capability fields, rather than browser assumptions, for permitted actions. 6. Workspace-authorized relations or summary fields for project assets, jobs, templates/recipes, and future publishing/automation resources. 7. Optional audited `PATCH`/archive/delete/duplicate/share endpoints only when their behavior, authorization, and idempotency contracts are ready. Once these endpoints are available, the project facade will discover the routes from OpenAPI, the sidebar/command entry will resolve, queries will activate, and a schema-driven creation flow can be implemented without an architecture rewrite.