# {{capProjectName}} {{capAppName}} — CMS editor

The editor UI for the **api-cms** backend. It signs editors in, gates the app
behind that session, and generates a content form per type by INTROSPECTING the
api — no content-type shape is hardcoded in the browser. Bilingual (en/de).

```bash
voltro init my-cms --api=api-cms --web=frontend-cms
```

## How it works

- **`/login`** — a static sign-in page that POSTs to the api's `/auth/*` routes
  (CSRF token first), which set the HttpOnly session cookie.
- **`(app)/layout.tsx`** — the SSR auth gate. Its loader asks the api's
  `session.me` server-side and RedirectErrors an anonymous visitor to `/login`.
- **`(app)/page.tsx`** — the editor. It fetches the registered content types
  (`content.types`) and renders `<ContentForm>` (from `@voltro/cms/web`) for the
  selected type — the widget per field is driven entirely by the field's
  `editorHint`. It lists a type's drafts live (`content.list`), saves them
  (`content.saveDraft` — validation comes back as `{ ok: false, violations }` and
  is shown inline), and publishes / unpublishes.

Adding a content type on the api needs **zero** changes here — it appears as a
new tab automatically.

## Files

```
app.config.ts                     web app; apis.cms → @{{projectName}}/api
src/pages/
  layout.tsx                      root layout (stylesheet)
  login/page.tsx(.test)           sign-in / sign-up form (POST /auth/*)
  (app)/layout.tsx(.test)         editor shell + SSR auth gate (session.me)
  (app)/page.tsx(.test)           the introspecting editor (<ContentForm>)
  (app)/error.tsx · not-found.tsx scoped boundaries
src/lib/api.ts                    wire shapes + toContentFormType()
src/locales/{en,de}.ts            bilingual catalogs (parity-enforced)
```

## Customising

- **Rich text** — the default `richText` widget is a textarea placeholder. Swap a
  real editor (e.g. TipTap) via `<ContentForm widgets={{ richText: MyEditor }} />`
  without forking the renderer.
- **Media** — a `Schema.Media()` field renders a placeholder; wire an upload with
  `@voltro/plugin-storage` + a custom `media` widget.
