feat(sprint18): editor autosave/reset, admin auth, QR reuse, Ed25519 prep
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
- Project editor: persist draft to localStorage, restore on reload, last-saved/draft-restored status indicators, section/whole-draft reset with confirmation. - Extract shared QR/polling/expiry engine from TelegramLoginComponent (shared/qr-login) and reuse it for a new admin login flow. - Admin authentication kept fully separate from customer session: own cookie/localStorage keys, signals, guard, and header interceptor (core/admin-auth). - ?login=true / ?adminLogin=true open the respective login dialog for manual testing. - Ed25519 challenge/verify interfaces (fail-closed no-op binding) ready for backend delivery. - Document autosave/reset/admin-auth/QR-reuse/Ed25519 model and the remaining full-field-coverage gap in docs/Project-Editor.md.
This commit is contained in:
@@ -13,6 +13,11 @@
|
||||
</section>
|
||||
} @else {
|
||||
<section class="project-editor-stack">
|
||||
@if (canResetActiveSection()) {
|
||||
<div class="project-editor-section-actions">
|
||||
<button type="button" (click)="resetActiveSection()">{{ 'builder.resetSection' | translate }}</button>
|
||||
</div>
|
||||
}
|
||||
@switch (activeSection()) {
|
||||
@case ('general') { <app-project-editor-general-section /> }
|
||||
@case ('branding') { <app-project-editor-branding-section /> }
|
||||
|
||||
@@ -25,6 +25,20 @@
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.project-editor-section-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.project-editor-section-actions button {
|
||||
color: #b91c1c;
|
||||
background: transparent;
|
||||
border: 1px solid #d3dad9;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.project-editor-empty {
|
||||
min-height: 240px;
|
||||
display: grid;
|
||||
|
||||
@@ -16,9 +16,10 @@ import { ProjectEditorLanguagesSectionComponent } from '../sections/languages-se
|
||||
import { ProjectEditorNavigationSectionComponent } from '../sections/navigation-section.component';
|
||||
import { ProjectEditorPreviewSectionComponent } from '../sections/preview-section.component';
|
||||
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
||||
import { TranslateService } from '../../../i18n/translate.service';
|
||||
import { StaticPagesEditorComponent } from '../../content-management/components/static-pages-editor.component';
|
||||
import { ProjectEditorSaveBarComponent } from '../components/save-bar/project-editor-save-bar.component';
|
||||
import { ProjectEditorSectionId } from '../models/project-editor.model';
|
||||
import { EDITOR_SECTION_BOOTSTRAP_KEYS, ProjectEditorSectionId } from '../models/project-editor.model';
|
||||
|
||||
const KNOWN_SECTIONS: ProjectEditorSectionId[] = [
|
||||
'general', 'branding', 'theme', 'header', 'footer', 'homepage', 'widgets', 'static-pages', 'features', 'languages', 'navigation', 'preview'
|
||||
@@ -51,8 +52,10 @@ const KNOWN_SECTIONS: ProjectEditorSectionId[] = [
|
||||
export class ProjectEditorPageComponent {
|
||||
readonly facade = inject(ProjectEditorFacade);
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly translate = inject(TranslateService);
|
||||
readonly bootstrap = this.facade.bootstrap;
|
||||
readonly activeSection = this.facade.activeSection;
|
||||
readonly canResetActiveSection = () => !!EDITOR_SECTION_BOOTSTRAP_KEYS[this.activeSection()];
|
||||
|
||||
private readonly routeSection = toSignal(
|
||||
this.route.paramMap.pipe(map(params => params.get('section') as ProjectEditorSectionId | null)),
|
||||
@@ -69,6 +72,12 @@ export class ProjectEditorPageComponent {
|
||||
});
|
||||
}
|
||||
|
||||
resetActiveSection(): void {
|
||||
if (confirm(this.translate.t('builder.confirmResetSection'))) {
|
||||
this.facade.resetSection(this.activeSection());
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:beforeunload', ['$event'])
|
||||
warnBeforeUnload(event: BeforeUnloadEvent): void {
|
||||
if (this.facade.dirty()) {
|
||||
|
||||
Reference in New Issue
Block a user