Spaces:
Running
Running
| # 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=<id>` to select the loaded authoritative asset. Project results use `/projects/<id>`. 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. | |