diff --git a/docs/guides/admin-general-settings.md b/docs/guides/admin-general-settings.md index b9feecf..d2a6ccb 100644 --- a/docs/guides/admin-general-settings.md +++ b/docs/guides/admin-general-settings.md @@ -3,7 +3,7 @@ type: guide title: Admin — General Settings description: How an admin edits global platform settings (page title, logo, theme, event window, default challenge IP, registrations, welcome Markdown) from the /admin/general page. tags: [guide, admin, settings, general, tester] -timestamp: 2026-07-22T12:00:00Z +timestamp: 2026-07-22T12:44:45Z --- # When this view is available @@ -17,7 +17,7 @@ by navigating directly to the URL. |------------------|----------------------------------------------------------------------------|------------------------------------------------| | Client route | `frontend/src/app/app.routes.ts` | `/admin/general` child of `adminGuard`. | | Client component | `frontend/src/app/features/admin/general.component.ts` | `AdminGeneralComponent.ngOnInit` fetches settings + themes. | -| Client predicate | `frontend/src/app/features/admin/general.pure.ts` (`deriveEventState`) | Computes the read-only event-state label. | +| Client predicate | `frontend/src/app/features/admin/general.pure.ts` (`deriveEventState`, `pageTitleError`, `pageTitleMessage`) | Pure helpers for event-state derivation and Page-title error/message mapping. | | Server route | `backend/src/modules/admin/admin-general.controller.ts` | `GET/PUT /api/v1/admin/general/settings` + `GET /api/v1/admin/general/themes`. | # How to access (tester steps) @@ -81,13 +81,42 @@ The page is a single reactive form with these controls (every never issued. The server trims surrounding whitespace and re-validates against the same 1–120 character rule, so invalid payloads return `400 VALIDATION_FAILED` and the stored Page title is unchanged. - On success the form is patched with the response, - `general-save-ok` renders briefly, and the backend emits an SSE - `general` event via `SseHubService` so other tabs refresh their theme. + On success the form is patched with the response, the Page-title + control is marked untouched/pristine (so a freshly-loaded valid + form does not flash an error), `general-save-ok` renders briefly, and + the backend emits an SSE `general` event via `SseHubService` so + other tabs refresh their theme. * **Error states:** load failures render `general-error`; save failures render `general-save-error` with the `error.message` (or `error.error.message`) from the standard envelope. +# Page-title inline error message + +The inline error message under the Page-title field +(`general-pageTitle-error`) is driven by the pure helper +`pageTitleMessage(value, controlErrors, maxLength = 120)` exported from +`frontend/src/app/features/admin/general.pure.ts:10-22`. The component +keeps three local `signal`s — `pageTitleValue`, `pageTitleInvalid`, +`pageTitleTouchedOrDirty` — and subscribes to the reactive form +control's `valueChanges` and `statusChanges` (via +`takeUntilDestroyed(this.destroyRef)`) so the `computed`s for +`showPageTitleError` and `pageTitleMessage` re-evaluate when the user +types or blurs the input. The helper returns: + +| Condition | Message | +|--------------------------------------------------------------|-----------------------------------------------------------| +| Empty or whitespace-only value | `Page title is required and cannot contain only whitespace.` | +| Value length > 120 | `Page title must be 120 characters or fewer.` | +| Control has `required`, `maxlength`, or `whitespace` error | Matching human-readable message (whitespace falls back to "required" wording). | +| Valid value | `null` (no message rendered, Save becomes enabled). | + +As a result, the `general-pageTitle-error` element renders as soon as +the Page-title control becomes both `touched || dirty` AND `invalid`, +without requiring a full component re-render. After a successful +Save, the control is reset to untouched/pristine and the +`pageTitleTouchedOrDirty` signal is cleared, so reloading valid +settings does not flash a stale error. + # Visual elements | Element | Selector | @@ -98,6 +127,7 @@ The page is a single reactive form with these controls (every | Form | `[data-testid="general-form"]` | | Welcome preview | `[data-testid="general-welcome-preview"]` | | End-before-start message | `[data-testid="general-endBeforeStart"]` | +| Page-title inline error | `[data-testid="general-pageTitle-error"]` | # Architecture map diff --git a/docs/index.md b/docs/index.md index 79602aa..e56426b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin controls. The docs below are organized by purpose so agents can pull just the slice -they need. Last regenerated 2026-07-22T10:32:00Z. +they need. Last regenerated 2026-07-22T12:44:45Z. # Architecture