AI Implementation feature(886): Admin Area General Settings and Categories 1.04 (#26)

This commit was merged in pull request #26.
This commit is contained in:
2026-07-22 13:41:09 +00:00
parent b2f0a4736d
commit 98fee8f7ee
10 changed files with 332 additions and 96 deletions
@@ -1,21 +1,21 @@
import { Injectable, signal, computed } from '@angular/core';
import {
applyThemeToCss,
BootstrapPayload,
PasswordPolicyInfo,
Theme,
} from './bootstrap.types';
export interface PasswordPolicyInfo {
minLength: number;
requireMixed: boolean;
description: string;
}
export interface BootstrapPayload {
initialized: boolean;
pageTitle: string;
logo: string;
welcomeMarkdown: string;
theme: any;
defaultChallengeIp: string;
registrationsEnabled: boolean;
passwordPolicy: PasswordPolicyInfo;
}
export {
applyThemeToCss,
clearThemeCss,
THEME_CSS_PROPERTIES,
Theme,
ThemeTokens,
ThemeCssTarget,
BootstrapPayload,
PasswordPolicyInfo,
} from './bootstrap.types';
const DEFAULT_POLICY: PasswordPolicyInfo = {
minLength: 12,
@@ -42,11 +42,7 @@ export class BootstrapService {
private async _load(): Promise<BootstrapPayload | null> {
try {
const res = await fetch('/api/v1/bootstrap', { credentials: 'include' });
const data = (await res.json()) as BootstrapPayload;
this.payload.set(data);
this.initialized.set(data.initialized);
this.applyTheme(data.theme);
const data = await this.fetchAndApply('/api/v1/bootstrap');
return data;
} catch (e) {
console.error('Bootstrap load failed', e);
@@ -61,25 +57,28 @@ export class BootstrapService {
return this.loadPromise;
}
async refresh(): Promise<BootstrapPayload> {
const data = await this.fetchAndApply('/api/v1/bootstrap');
return data;
}
markInitialized(): void {
this.initialized.set(true);
}
private applyTheme(theme: any): void {
if (!theme?.tokens) return;
const root = document.documentElement.style;
const t = theme.tokens;
root.setProperty('--color-primary', t.primary);
root.setProperty('--color-secondary', t.secondary);
root.setProperty('--color-accent', t.accent);
root.setProperty('--color-surface', t.surface);
root.setProperty('--color-text', t.text);
root.setProperty('--color-success', t.success);
root.setProperty('--color-warning', t.warning);
root.setProperty('--color-danger', t.danger);
root.setProperty('--font-family', t.fontFamily);
root.setProperty('--radius-sm', t.radii.sm);
root.setProperty('--radius-md', t.radii.md);
root.setProperty('--radius-lg', t.radii.lg);
applyTheme(theme: Theme | null | undefined): void {
applyThemeToCss(theme);
}
private async fetchAndApply(url: string): Promise<BootstrapPayload> {
const res = await fetch(url, { credentials: 'include' });
if (!res.ok) {
throw new Error(`Bootstrap request failed: ${res.status}`);
}
const data = (await res.json()) as BootstrapPayload;
this.payload.set(data);
this.initialized.set(data.initialized);
this.applyTheme(data.theme);
return data;
}
}