AI Implementation feature(884): Admin Area General Settings and Categories 1.02 #24
@@ -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
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user