feat(builder): add project editor
This commit is contained in:
106
src/app/features/project-editor/facade/project-editor.facade.ts
Normal file
106
src/app/features/project-editor/facade/project-editor.facade.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Injectable, computed, inject, signal } from '@angular/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { BootstrapConfig, DEFAULT_CATALOG_CONFIG, DEFAULT_HEADER_CONFIG, DEFAULT_PRODUCT_PAGE_CONFIG, DEFAULT_USER_EXPERIENCE_CONFIG } from '../../../shared/models/config';
|
||||
import { ConfigService } from '../../../core/config/config.service';
|
||||
import { ProjectEditorIoService } from '../services/project-editor-io.service';
|
||||
import { ProjectEditorPreviewService } from '../services/project-editor-preview.service';
|
||||
import { ProjectEditorState } from '../models/project-editor.model';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ProjectEditorFacade {
|
||||
private readonly configService = inject(ConfigService);
|
||||
private readonly ioService = inject(ProjectEditorIoService);
|
||||
private readonly previewService = inject(ProjectEditorPreviewService);
|
||||
|
||||
private readonly state = signal<ProjectEditorState>({
|
||||
bootstrap: null,
|
||||
importError: null,
|
||||
activeSection: 'general',
|
||||
});
|
||||
|
||||
readonly bootstrap = computed(() => this.state().bootstrap);
|
||||
readonly importError = computed(() => this.state().importError);
|
||||
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 }))) ?? []);
|
||||
|
||||
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 })),
|
||||
error: () => this.state.update(current => ({ ...current, bootstrap: null, importError: 'builder.importError' })),
|
||||
});
|
||||
}
|
||||
|
||||
updateBootstrap(updater: (current: BootstrapConfig) => BootstrapConfig): void {
|
||||
const current = this.state().bootstrap;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.update(state => ({
|
||||
...state,
|
||||
bootstrap: this.normalize(updater(JSON.parse(JSON.stringify(current)) as BootstrapConfig)),
|
||||
}));
|
||||
}
|
||||
|
||||
exportBootstrap(): string {
|
||||
const current = this.state().bootstrap;
|
||||
return current ? this.ioService.exportBootstrap(current) : '';
|
||||
}
|
||||
|
||||
importBootstrap(raw: string): void {
|
||||
try {
|
||||
const imported = this.normalize(this.ioService.importBootstrap(raw));
|
||||
this.state.update(current => ({ ...current, bootstrap: imported, importError: null }));
|
||||
} catch {
|
||||
this.state.update(current => ({ ...current, importError: 'builder.importError' }));
|
||||
}
|
||||
}
|
||||
|
||||
preview(): void {
|
||||
const current = this.state().bootstrap;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previewService.preview(current);
|
||||
}
|
||||
|
||||
setActiveSection(activeSection: ProjectEditorState['activeSection']): void {
|
||||
this.state.update(current => ({ ...current, activeSection }));
|
||||
}
|
||||
|
||||
private normalize(config: BootstrapConfig): BootstrapConfig {
|
||||
return {
|
||||
...config,
|
||||
header: {
|
||||
...DEFAULT_HEADER_CONFIG,
|
||||
...(config.header ?? {})
|
||||
},
|
||||
catalog: {
|
||||
...DEFAULT_CATALOG_CONFIG,
|
||||
...(config.catalog ?? {})
|
||||
},
|
||||
productPage: {
|
||||
...DEFAULT_PRODUCT_PAGE_CONFIG,
|
||||
...(config.productPage ?? {})
|
||||
},
|
||||
userExperience: {
|
||||
...DEFAULT_USER_EXPERIENCE_CONFIG,
|
||||
...(config.userExperience ?? {})
|
||||
},
|
||||
localization: {
|
||||
...config.localization,
|
||||
supportedLocales: config.localization?.supportedLocales?.length ? config.localization.supportedLocales : config.tenant.supportedLocales,
|
||||
defaultLocale: config.localization?.defaultLocale || config.tenant.defaultLocale,
|
||||
currencyByLocale: config.localization?.currencyByLocale ?? {},
|
||||
dictionaries: config.localization?.dictionaries ?? []
|
||||
},
|
||||
pages: [...(config.pages ?? [])].sort((left, right) => {
|
||||
const leftOrder = Math.min(...left.sections.map(section => section.order));
|
||||
const rightOrder = Math.min(...right.sections.map(section => section.order));
|
||||
return leftOrder - rightOrder;
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user