docs: update documentation to OKF v0.1 format
This commit is contained in:
@@ -2,8 +2,8 @@
|
|||||||
type: guide
|
type: guide
|
||||||
title: Admin — General Settings
|
title: Admin — General Settings
|
||||||
description: How an admin edits required platform-wide settings, including the validated event window, from the /admin/general page.
|
description: How an admin edits required platform-wide settings, including the validated event window, from the /admin/general page.
|
||||||
tags: [guide, admin, settings, general, tester, datetime, validation]
|
tags: [guide, admin, settings, general, tester, datetime, validation, utc, timezone]
|
||||||
timestamp: 2026-07-22T14:50:25Z
|
timestamp: 2026-07-22T15:09:39Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# When this view is available
|
# When this view is available
|
||||||
@@ -56,7 +56,8 @@ The page is a single reactive form with these controls (every
|
|||||||
* **Initial load:** `loading() === true` renders `general-loading`. After
|
* **Initial load:** `loading() === true` renders `general-loading`. After
|
||||||
both requests resolve, the form is patched with the values from the
|
both requests resolve, the form is patched with the values from the
|
||||||
backend (UTC timestamps are converted to `datetime-local` strings via
|
backend (UTC timestamps are converted to `datetime-local` strings via
|
||||||
`toDatetimeLocal` so the native picker shows them).
|
`toDatetimeLocal` so the native picker shows them — see
|
||||||
|
[Datetime UTC round-trip](#datetime-utc-round-trip) below).
|
||||||
* **Logo upload:** selecting a file fires `POST /api/v1/uploads/logo`,
|
* **Logo upload:** selecting a file fires `POST /api/v1/uploads/logo`,
|
||||||
then writes the returned `publicUrl` into the hidden `logo` control.
|
then writes the returned `publicUrl` into the hidden `logo` control.
|
||||||
If the upload fails, `general-logo-error` shows the message; the
|
If the upload fails, `general-logo-error` shows the message; the
|
||||||
@@ -179,6 +180,62 @@ is rendered inside the per-field error region
|
|||||||
(`data-testid="general-eventEnd-error"`) by the
|
(`data-testid="general-eventEnd-error"`) by the
|
||||||
`eventEndFieldMessage` helper.
|
`eventEndFieldMessage` helper.
|
||||||
|
|
||||||
|
# Datetime UTC round-trip
|
||||||
|
|
||||||
|
Both Event-window fields (`general-eventStart`, `general-eventEnd`) are
|
||||||
|
backed by HTML `<input type="datetime-local">` controls, which the
|
||||||
|
browser renders as a wall-clock picker **without** any timezone
|
||||||
|
indicator. The two pure helpers exported from
|
||||||
|
`frontend/src/app/features/admin/general.pure.ts` make the wall-clock
|
||||||
|
values behave as if they were already UTC, so the admin sees the same
|
||||||
|
instant no matter where the browser is running:
|
||||||
|
|
||||||
|
| Helper | Direction | Purpose |
|
||||||
|
|------------------------------------------------|-------------------------------------|---------|
|
||||||
|
| `toDatetimeLocal(iso: string): string` | ISO-8601 UTC → `YYYY-MM-DDTHH:mm` | Renders a backend UTC instant in the native picker using `getUTC*` getters. |
|
||||||
|
| `toIsoUtc(local: string): string` | `YYYY-MM-DDTHH:mm[:ss[.fff]]` → ISO-8601 UTC | Parses the wall-clock components as **UTC** (not local time) via `Date.UTC(...)` and returns `toISOString()`. |
|
||||||
|
|
||||||
|
The `toIsoUtc` helper explicitly does **not** call `new Date(local)`,
|
||||||
|
which would interpret the components in the browser's local timezone
|
||||||
|
and shift the round-tripped instant by the browser's UTC offset. Instead
|
||||||
|
it regex-parses the year / month / day / hour / minute components (and
|
||||||
|
optional seconds / milliseconds), constructs the instant with
|
||||||
|
`new Date(Date.UTC(...))`, and returns `toISOString()`. As a result,
|
||||||
|
`toIsoUtc(toDatetimeLocal(iso)) === iso` for any minute-precision
|
||||||
|
`datetime-local` value the picker can produce, regardless of the
|
||||||
|
browser's `Intl.DateTimeFormat().resolvedOptions().timeZone` value.
|
||||||
|
|
||||||
|
The helper preserves its existing fallback contract:
|
||||||
|
|
||||||
|
| Input | Output |
|
||||||
|
|------------------------------------|---------------------------------|
|
||||||
|
| `''` (empty) | `''` |
|
||||||
|
| Non-matching `YYYY-MM-DDTHH:mm…` | Returns the input verbatim so the reactive-form `invalidDatetime` validator still fires. |
|
||||||
|
| Matching `YYYY-MM-DDTHH:mm` | `<value>:00.000Z` |
|
||||||
|
| Matching `YYYY-MM-DDTHH:mm:ss` | `<value>.000Z` |
|
||||||
|
| Matching `YYYY-MM-DDTHH:mm:ss.fff` | `<value>Z` (fraction padded to ms) |
|
||||||
|
|
||||||
|
`AdminGeneralComponent` calls `toIsoUtc` on both `eventStartUtc` and
|
||||||
|
`eventEndUtc` before issuing `PUT /api/v1/admin/general/settings`, so
|
||||||
|
correcting the shared helper fixes start and end submissions without
|
||||||
|
any further component, form, or backend rewiring. The backend zod
|
||||||
|
schema (`GeneralSettingsSchema` in
|
||||||
|
`backend/src/modules/admin/dto/general.dto.ts`) accepts the resulting
|
||||||
|
timezone-aware ISO-8601 strings unchanged and enforces
|
||||||
|
`eventEndUtc > eventStartUtc` via `superRefine`.
|
||||||
|
|
||||||
|
The regression for this fix lives in
|
||||||
|
`tests/frontend/admin-general-pure.spec.ts` under the `datetime helpers`
|
||||||
|
suite, with cases that lock in:
|
||||||
|
|
||||||
|
* `toIsoUtc('2027-03-15T08:30')` → `'2027-03-15T08:30:00.000Z'`
|
||||||
|
(would previously have returned `'2027-03-15T15:30:00.000Z'` from a
|
||||||
|
PDT browser).
|
||||||
|
* `toIsoUtc('2027-03-15T08:30:15')` → `'2027-03-15T08:30:15.000Z'`.
|
||||||
|
* `toIsoUtc('2027-03-15T08:30:15.123')` → `'2027-03-15T08:30:15.123Z'`.
|
||||||
|
* `toIsoUtc(toDatetimeLocal('2027-03-15T08:30:00.000Z'))` round-trips
|
||||||
|
back to the original ISO string.
|
||||||
|
|
||||||
# Visual elements
|
# Visual elements
|
||||||
|
|
||||||
| Element | Selector |
|
| Element | Selector |
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin
|
|||||||
controls.
|
controls.
|
||||||
|
|
||||||
The docs below are organized by purpose so agents can pull just the slice
|
The docs below are organized by purpose so agents can pull just the slice
|
||||||
they need. Last regenerated 2026-07-22T14:50:25Z.
|
they need. Last regenerated 2026-07-22T15:09:39Z.
|
||||||
|
|
||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user