diff --git a/docs/context/adrs/ADR-0002-media-manager-contract.md b/docs/context/adrs/ADR-0002-media-manager-contract.md new file mode 100644 index 0000000..5bd7d0b --- /dev/null +++ b/docs/context/adrs/ADR-0002-media-manager-contract.md @@ -0,0 +1,53 @@ +--- +id: ADR-0002 +title: Media Manager backend contract and mock storage adapter +status: active +date: 2026-07-15 +tags: ["architecture", "media", "backend-gap", "repository-pattern"] +--- + +## Context + +Sprint 4 (Media Manager) needs a media library: upload, browse, delete, and pick +images/files for use across Product Editor, Static Pages (CMS), and Branding. +No media backend exists yet — `/media` currently routes to a "coming soon" +placeholder (`BackofficeComingSoonPageComponent`), and `docs/BACKEND.md` does +not document any upload/storage endpoint. This mirrors the already-documented +draft-publish-flow gap in the Project Editor (see `PE-20260713T010000Z-0003`): +build the real contract, then implement a client-side mock adapter behind the +same interface so the UI never needs to change when the backend ships. + +## Decision + +- **Domain model** `MediaAsset`: `{ id, url, thumbnailUrl?, filename, mimeType, + size, width?, height?, altText?: Record, tags?: string[], + createdAt }`. `altText` follows the platform's `translations.{lang}` rule + (ADR-0001) — never a flat string. +- **Repository contract** (future backend, to be implemented server-side): + - `GET /media?page=&pageSize=&search=` → paginated `MediaAsset[]` + - `POST /media/upload` (multipart) → `MediaAsset` + - `DELETE /media/:id` → 204 + - `PATCH /media/:id` (altText/tags only) → `MediaAsset` +- **Frontend abstraction**: a `MediaRepository` interface (Repository pattern, + per `docs/context/features/*` conventions) with two implementations selected + via DI token: + - `MockMediaRepository` — stores assets in IndexedDB (not localStorage: binary + blobs need it) as an interim store until the backend exists. Data URLs are + generated for rendering; the shape returned matches `MediaAsset` exactly. + - `HttpMediaRepository` — thin wrapper over the endpoints above, added when + the backend ships. Swapping providers is the only change required. +- **Media never enters the Bootstrap model.** Like products/orders/users, media + assets are runtime admin data, not tenant configuration — consistent with + ADR-0001's rule that Bootstrap contains only what's needed before the app + starts. +- **Media Picker** is a standalone, reusable dialog (built on the existing + `app-dialog` Design System primitive) so Product Editor and CMS editors + consume the same selection UI instead of each building their own. + +## Consequences + +- Any feature needing to reference an image (product gallery, static page + hero, branding logo) does so via `MediaAsset.url`/`id`, obtained through the + shared Media Picker — never a raw file input duplicated per feature. +- When the backend ships, only `MediaRepository`'s DI provider changes; no + component or facade code should need to change. diff --git a/docs/context/features/media-manager/FACTS.jsonl b/docs/context/features/media-manager/FACTS.jsonl new file mode 100644 index 0000000..783eb63 --- /dev/null +++ b/docs/context/features/media-manager/FACTS.jsonl @@ -0,0 +1,2 @@ +{"id":"MM-20260715T000000Z-0001","subject":"media-backend","predicate":"is","object":"not implemented yet; /media routes to BackofficeComingSoonPageComponent; GET /media, POST /media/upload, DELETE /media/:id, PATCH /media/:id are the documented backend gap","src":["docs/context/adrs/ADR-0002-media-manager-contract.md","src/app/app.routes.ts"],"status":"active","kind":"constraint","confidence":"high","updated_at":"2026-07-15T00:00:00Z","tags":["media-manager","backend-gap"]} +{"id":"MM-20260715T000000Z-0002","subject":"media-storage","predicate":"is-implemented-by","object":"MediaRepository interface with MockMediaRepository (IndexedDB-backed, interim) and HttpMediaRepository (future) selected via DI token; media assets never enter the Bootstrap model","src":["docs/context/adrs/ADR-0002-media-manager-contract.md"],"status":"active","kind":"decision","confidence":"high","updated_at":"2026-07-15T00:00:00Z","tags":["media-manager","repository-pattern"]}