feat(project-editor): add draft/publish status, dirty tracking, save/publish to facade
Wires ProjectValidator into ProjectEditorFacade and extends ProjectEditorState
with status ('draft'|'published') and lastSavedBootstrap. Adds dirty computed
(diffed against lastSavedBootstrap), validationIssues computed, and save()/publish()
methods. publish() calls PlatformRuntimeService.reloadFromBootstrap() and refuses
when validation issues exist. loadBootstrap() seeds lastSavedBootstrap so a
freshly-loaded bootstrap is not dirty.
This commit is contained in:
@@ -7,6 +7,8 @@ import { ProjectEditorIoService } from '../services/project-editor-io.service';
|
||||
import { ProjectEditorPreviewService } from '../services/project-editor-preview.service';
|
||||
import { LocaleSyncService } from '../services/locale-sync.service';
|
||||
import { ProjectEditorState } from '../models/project-editor.model';
|
||||
import { PlatformRuntimeService } from '../../../core/runtime/platform-runtime.service';
|
||||
import { ProjectValidator } from '../services/project-validator.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ProjectEditorFacade {
|
||||
@@ -14,11 +16,15 @@ export class ProjectEditorFacade {
|
||||
private readonly ioService = inject(ProjectEditorIoService);
|
||||
private readonly previewService = inject(ProjectEditorPreviewService);
|
||||
private readonly localeSync = inject(LocaleSyncService);
|
||||
private readonly runtime = inject(PlatformRuntimeService);
|
||||
private readonly validator = inject(ProjectValidator);
|
||||
|
||||
private readonly state = signal<ProjectEditorState>({
|
||||
bootstrap: null,
|
||||
importError: null,
|
||||
activeSection: 'general',
|
||||
status: 'draft',
|
||||
lastSavedBootstrap: null,
|
||||
});
|
||||
|
||||
readonly bootstrap = computed(() => this.state().bootstrap);
|
||||
@@ -26,10 +32,25 @@ export class ProjectEditorFacade {
|
||||
readonly activeSection = computed(() => this.state().activeSection);
|
||||
readonly homepagePage = computed(() => this.bootstrap()?.pages.find(page => page.key === 'home' || page.route.path === '/') ?? null);
|
||||
readonly homepageWidgets = computed(() => this.homepagePage()?.sections.flatMap(section => section.widgets.map(widget => ({ sectionId: section.id, sectionType: section.type, widget }))) ?? []);
|
||||
readonly status = computed(() => this.state().status);
|
||||
readonly validationIssues = computed(() => {
|
||||
const current = this.bootstrap();
|
||||
return current ? this.validator.validate(current) : [];
|
||||
});
|
||||
readonly dirty = computed(() => {
|
||||
const current = this.bootstrap();
|
||||
if (!current) {
|
||||
return false;
|
||||
}
|
||||
return JSON.stringify(current) !== JSON.stringify(this.state().lastSavedBootstrap);
|
||||
});
|
||||
|
||||
loadBootstrap(): void {
|
||||
this.configService.loadBootstrap(true).pipe(take(1)).subscribe({
|
||||
next: config => this.state.update(current => ({ ...current, bootstrap: this.normalize(JSON.parse(JSON.stringify(config)) as BootstrapConfig), importError: null })),
|
||||
next: config => {
|
||||
const normalized = this.normalize(JSON.parse(JSON.stringify(config)) as BootstrapConfig);
|
||||
this.state.update(current => ({ ...current, bootstrap: normalized, importError: null, lastSavedBootstrap: normalized, status: 'draft' }));
|
||||
},
|
||||
error: () => this.state.update(current => ({ ...current, bootstrap: null, importError: 'builder.importError' })),
|
||||
});
|
||||
}
|
||||
@@ -89,6 +110,28 @@ export class ProjectEditorFacade {
|
||||
}));
|
||||
}
|
||||
|
||||
save(): void {
|
||||
const current = this.state().bootstrap;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
this.state.update(state => ({ ...state, lastSavedBootstrap: JSON.parse(JSON.stringify(current)) }));
|
||||
}
|
||||
|
||||
publish(): boolean {
|
||||
const current = this.state().bootstrap;
|
||||
if (!current || this.validationIssues().length > 0) {
|
||||
return false;
|
||||
}
|
||||
this.runtime.reloadFromBootstrap(current);
|
||||
this.state.update(state => ({
|
||||
...state,
|
||||
status: 'published',
|
||||
lastSavedBootstrap: JSON.parse(JSON.stringify(current)),
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
addNavLink(target: 'header' | 'footer'): void {
|
||||
this.updateBootstrap(current => {
|
||||
const list = current.navigation[target];
|
||||
|
||||
Reference in New Issue
Block a user