AI Implementation feature(884): Admin Area General Settings and Categories 1.02 (#24)

This commit was merged in pull request #24.
This commit is contained in:
2026-07-22 12:45:45 +00:00
parent 0a2b2664b4
commit b7e0f3c608
7 changed files with 252 additions and 52 deletions
@@ -9,7 +9,7 @@ import {
deriveEventState,
endAfterStartValidator,
normalizePageTitle,
pageTitleError,
pageTitleMessage,
toDatetimeLocal,
toIsoUtc,
} from './general.pure';
@@ -173,6 +173,10 @@ export class AdminGeneralComponent implements OnInit {
readonly uploadingLogo = signal(false);
readonly logoUploadError = signal<string | null>(null);
private readonly pageTitleValue = signal<string>('');
private readonly pageTitleInvalid = signal<boolean>(false);
private readonly pageTitleTouchedOrDirty = signal<boolean>(false);
readonly form = this.fb.nonNullable.group(
{
pageTitle: this.fb.nonNullable.control('', [Validators.required, Validators.maxLength(120), this.pageTitleNotBlankValidator()]),
@@ -196,30 +200,35 @@ export class AdminGeneralComponent implements OnInit {
return state.toUpperCase();
});
readonly showPageTitleError = computed(() => {
const c = this.form.controls.pageTitle;
return (c.touched || c.dirty) && c.invalid;
});
readonly showPageTitleError = computed(
() => this.pageTitleTouchedOrDirty() && this.pageTitleInvalid(),
);
readonly pageTitleMessage = computed(() => {
const err = pageTitleError(this.form.controls.pageTitle.value);
if (err === 'required') {
return 'Page title is required and cannot contain only whitespace.';
}
if (err === 'maxlength') {
return 'Page title must be 120 characters or fewer.';
}
const controlErrors = this.form.controls.pageTitle.errors;
if (controlErrors?.['required']) return 'Page title is required and cannot contain only whitespace.';
if (controlErrors?.['maxlength']) return 'Page title must be 120 characters or fewer.';
if (controlErrors?.['whitespace']) return 'Page title is required and cannot contain only whitespace.';
return null;
});
readonly pageTitleMessage = computed(() =>
pageTitleMessage(this.pageTitleValue(), this.form.controls.pageTitle.errors),
);
constructor() {
this.form.controls.welcomeMarkdown.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((v) => this.previewHtml.set(this.markdown.render(v)));
const pt = this.form.controls.pageTitle;
this.pageTitleValue.set(pt.value);
this.pageTitleInvalid.set(pt.invalid);
this.pageTitleTouchedOrDirty.set(pt.touched || pt.dirty);
pt.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((v) => {
this.pageTitleValue.set(v);
this.pageTitleTouchedOrDirty.set(pt.touched || pt.dirty);
});
pt.statusChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.pageTitleInvalid.set(pt.invalid);
this.pageTitleTouchedOrDirty.set(pt.touched || pt.dirty);
});
}
async ngOnInit(): Promise<void> {
@@ -258,6 +267,9 @@ export class AdminGeneralComponent implements OnInit {
defaultChallengeIp: s.defaultChallengeIp,
registrationsEnabled: s.registrationsEnabled,
});
this.form.controls.pageTitle.markAsUntouched();
this.form.controls.pageTitle.markAsPristine();
this.pageTitleTouchedOrDirty.set(false);
}
async onSubmit(): Promise<void> {