Files
HIPCTF2/.kilo/plans/886.md
T
2026-07-22 13:41:06 +00:00

7.3 KiB

Implementation Plan: Admin Area General Settings and Categories 1.04

1. Architectural Reconnaissance

  • Codebase style & conventions: Node.js monorepo with a NestJS 10 REST backend and Angular 17 standalone frontend. Frontend components use inject(), Angular Signals, reactive forms, ChangeDetectionStrategy.OnPush, and async service methods backed by HttpClient/firstValueFrom. Global bootstrap data and CSS theme application are owned by the root-scoped BootstrapService; admin form persistence is owned by AdminGeneralComponent and AdminService.
  • Current flow and root cause: Application startup calls BootstrapService.load(), stores the /api/v1/bootstrap payload, and writes its theme tokens to document.documentElement.style. Saving /admin/general successfully persists themeKey through PUT /api/v1/admin/general/settings, and the backend later resolves the chosen theme in bootstrap. However, AdminGeneralComponent.onSubmit() only patches the returned settings form; it never asks BootstrapService to refresh or apply the newly selected theme. The backend's general SSE event contains only themeKey, while the frontend event-status stream treats every hub payload as event-state data and has no theme propagation path. Thus the save response changes persistence but not the current document tokens.
  • Data Layer: TypeORM with SQLite (better-sqlite3). The existing setting table stores themeKey; no schema or migration change is required. Persistent runtime data remains under /data/hipctf through existing setup conventions; the planned focused frontend tests require no persistent data.
  • Test Framework & Structure: Root Jest 29 multi-project configuration with ts-jest; frontend tests run in jsdom from tests/frontend/**/*.spec.ts, backend tests run in Node from tests/backend/**/*.spec.ts, and all tests are runnable with npm test. The fix should follow a minimal red-green-refactor path and test logic rather than visual appearance or browser automation.
  • Required Tools & Dependencies: No new system tools, global CLIs, npm packages, or /data assets are required. Existing Node/npm, Angular, Jest, jsdom, TypeScript, and setup.sh are sufficient; setup.sh and lockfiles should remain unchanged.

2. Impacted Files

  • To Modify:
    • frontend/src/app/core/services/bootstrap.service.ts — expose a typed, reusable live theme application/refresh path that updates both bootstrap client state and document CSS custom properties without being blocked by the startup request cache.
    • frontend/src/app/features/admin/general.component.ts — inject BootstrapService and invoke the live theme refresh only after updateGeneralSettings() succeeds, before reporting Save success.
  • To Create:
    • tests/frontend/bootstrap-theme.spec.ts — focused jsdom tests for applying all theme token fields and refreshing to a saved non-default theme.

3. Proposed Changes

  1. Database / Schema Migration:
    • No migration or backend persistence change. AdminGeneralService.updateSettings() already saves SETTINGS_KEYS.THEME_KEY, and SystemService.bootstrap() already reads that key and returns the corresponding complete theme object.
  2. Backend Logic & APIs:
    • Keep the existing PUT /api/v1/admin/general/settings and GET /api/v1/bootstrap contracts unchanged. The save response intentionally contains settings rather than theme tokens; reuse bootstrap as the canonical theme-token source instead of duplicating the backend theme model in the admin response.
    • Do not add backend endpoints, dependencies, SSE handling, or theme-token duplication. The reported same-tab Save issue can be resolved deterministically from the successful save path, while existing persistence guarantees future reloads use the selected theme.
  3. Frontend UI Integration:
    • Replace the frontend theme: any shape with local typed Theme/ThemeTokens interfaces matching the bootstrap contract, including colors, font family, radii, and spacing scale. Type the theme application method accordingly and preserve the defensive no-token guard for malformed/absent payloads.
    • Make theme application a reusable service operation rather than startup-only private behavior. It must continue setting all existing CSS variables (--color-primary, --color-secondary, --color-accent, --color-surface, --color-text, --color-success, --color-warning, --color-danger, --font-family, and all three radius properties), including valid zero-valued radius strings such as Monochrome's 0.
    • Add a force-refresh method in BootstrapService that performs a fresh /api/v1/bootstrap request instead of reusing loadPromise, validates HTTP success before consuming the payload, updates payload and initialized, and applies the returned theme to the live document. Keep startup load()/ready() request coalescing intact so guard/bootstrap race behavior does not regress.
    • Inject BootstrapService into AdminGeneralComponent. After AdminService.updateGeneralSettings() resolves and the form is patched, await the force-refresh method before setting saveOk. This orders persistence before re-fetch, applies the canonical saved theme immediately, and also synchronizes other global bootstrap-backed settings in the current tab.
    • Treat a failed refresh as a Save error rather than showing a false success: retain the persisted form response, surface the existing inline error path, and leave submitting cleanup in finally. Do not mutate theme tokens before the PUT succeeds, so failed saves leave the current live theme unchanged.
    • No template or categories changes are needed; the Global Theme select already includes the server-provided canonical choices, and category behavior is unrelated to this regression.

4. Test Strategy

  • Target Unit Test File: tests/frontend/bootstrap-theme.spec.ts, executed by the existing root npm test command (or selectively through the frontend Jest project during development). Do not add visual, browser, or persistence-volume tests.
  • Mocking Strategy: Use jsdom's real document.documentElement.style and mock only global fetch. Reset CSS properties and the fetch mock between tests to avoid shared state. Provide small typed bootstrap payload fixtures rather than Angular TestBed or a backend/database instance.
  • Core success path: Start with Classic CSS values, have the mocked refresh return a Monochrome bootstrap payload, call the refresh method, and assert the payload signal plus representative computed CSS variables change immediately to --color-primary: #000000, --color-text: #0a0a0a, and --radius-md: 0; also verify all token mappings in one compact table-driven assertion so every canonical theme token category is covered.
  • Key error condition: Mock a non-OK bootstrap refresh response and assert the method rejects without replacing the existing payload or applying partial/new CSS tokens. This supports the component's existing save-error handling while keeping the suite minimal.
  • Implementation verification: Run the focused frontend test first to prove the regression, then npm test, npm run build, and the repository's available TypeScript builds. No lint script is defined in the root or workspace package manifests, so no additional lint dependency or setup change should be introduced.