7.3 KiB
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 byHttpClient/firstValueFrom. Global bootstrap data and CSS theme application are owned by the root-scopedBootstrapService; admin form persistence is owned byAdminGeneralComponentandAdminService. - Current flow and root cause: Application startup calls
BootstrapService.load(), stores the/api/v1/bootstrappayload, and writes its theme tokens todocument.documentElement.style. Saving/admin/generalsuccessfully persiststhemeKeythroughPUT /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 asksBootstrapServiceto refresh or apply the newly selected theme. The backend'sgeneralSSE event contains onlythemeKey, 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 existingsettingtable storesthemeKey; no schema or migration change is required. Persistent runtime data remains under/data/hipctfthrough 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 fromtests/frontend/**/*.spec.ts, backend tests run in Node fromtests/backend/**/*.spec.ts, and all tests are runnable withnpm 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
/dataassets are required. Existing Node/npm, Angular, Jest, jsdom, TypeScript, andsetup.share sufficient;setup.shand 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— injectBootstrapServiceand invoke the live theme refresh only afterupdateGeneralSettings()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
- Database / Schema Migration:
- No migration or backend persistence change.
AdminGeneralService.updateSettings()already savesSETTINGS_KEYS.THEME_KEY, andSystemService.bootstrap()already reads that key and returns the corresponding complete theme object.
- No migration or backend persistence change.
- Backend Logic & APIs:
- Keep the existing
PUT /api/v1/admin/general/settingsandGET /api/v1/bootstrapcontracts 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.
- Keep the existing
- Frontend UI Integration:
- Replace the frontend
theme: anyshape with local typedTheme/ThemeTokensinterfaces 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's0. - Add a force-refresh method in
BootstrapServicethat performs a fresh/api/v1/bootstraprequest instead of reusingloadPromise, validates HTTP success before consuming the payload, updatespayloadandinitialized, and applies the returned theme to the live document. Keep startupload()/ready()request coalescing intact so guard/bootstrap race behavior does not regress. - Inject
BootstrapServiceintoAdminGeneralComponent. AfterAdminService.updateGeneralSettings()resolves and the form is patched, await the force-refresh method before settingsaveOk. 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
submittingcleanup infinally. 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.
- Replace the frontend
4. Test Strategy
- Target Unit Test File:
tests/frontend/bootstrap-theme.spec.ts, executed by the existing rootnpm testcommand (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.styleand mock only globalfetch. 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.