import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ProjectEditorFacade } from '../facade/project-editor.facade'; import { TranslatePipe } from '../../../i18n/translate.pipe'; import { InputComponent } from '../../../shared/ui/input/input.component'; import { FormFieldComponent } from '../../../shared/ui/form-field/form-field.component'; import { ButtonComponent } from '../../../shared/ui/button/button.component'; import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component'; import { KeyValueEditorComponent } from '../../../shared/ui/key-value-editor/key-value-editor.component'; import { MediaPickerComponent } from '../../../shared/media/media-picker/media-picker.component'; import { MediaAsset } from '../../../core/media/models/media-asset.model'; import { FooterPaymentIconConfig, FooterSocialLinkConfig } from '../../../shared/models/config'; import { isValidHttpUrl } from '../schema/validators/primitives'; type FooterMediaTarget = 'logo' | { type: 'paymentIcon'; index: number }; @Component({ selector: 'app-project-editor-footer-section', standalone: true, imports: [ FormsModule, TranslatePipe, InputComponent, FormFieldComponent, ButtonComponent, SectionCardComponent, KeyValueEditorComponent, MediaPickerComponent ], templateUrl: './footer-section.component.html', styleUrls: ['./section.shared.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) export class ProjectEditorFooterSectionComponent { private readonly facade = inject(ProjectEditorFacade); readonly bootstrap = this.facade.bootstrap; readonly staticPagesValue = computed(() => (this.bootstrap()?.footer?.staticPageKeys ?? this.bootstrap()?.footer?.legalPageKeys ?? []).join(', ')); readonly copyrightValue = computed(() => { const value = this.bootstrap()?.footer?.copyrightText; return typeof value === 'string' ? value : ''; }); readonly paymentIconRows = computed(() => this.bootstrap()?.footer?.paymentIcons ?? []); readonly socialLinkRows = computed(() => this.bootstrap()?.footer?.socialLinks ?? []); protected mediaPickerOpen = false; private mediaPickerTarget: FooterMediaTarget | null = null; readonly createPaymentIconRow = (): FooterPaymentIconConfig => ({ src: '', alt: '' }); readonly createSocialLinkRow = (): FooterSocialLinkConfig => { const index = (this.bootstrap()?.footer?.socialLinks?.length ?? 0) + 1; return { id: `social-${index}`, label: '', url: '' }; }; updateCompanyName(value: string): void { this.facade.updateBootstrap(current => ({ ...current, company: { ...current.company, companyName: value } })); } updateAddress(value: string): void { this.facade.updateBootstrap(current => ({ ...current, company: { ...current.company, address: { ...current.company.address, street: value } } })); } updatePhone(value: string): void { this.facade.updateBootstrap(current => ({ ...current, company: { ...current.company, contacts: { ...current.company.contacts, phone: value } }, branding: { ...current.branding, supportPhone: value } })); } updateEmail(value: string): void { this.facade.updateBootstrap(current => ({ ...current, company: { ...current.company, contacts: { ...current.company.contacts, email: value } }, branding: { ...current.branding, supportEmail: value } })); } updateCopyright(value: string): void { this.facade.updateBootstrap(current => ({ ...current, footer: { ...current.footer, copyrightText: value } })); } updateStaticPages(value: string): void { const staticPageKeys = value.split(',').map(item => item.trim()).filter(Boolean); this.facade.updateBootstrap(current => ({ ...current, footer: { ...current.footer, staticPageKeys, legalPageKeys: staticPageKeys } })); } updateLogo(value: string): void { this.facade.updateBootstrap(current => ({ ...current, footer: { ...current.footer, logoUrl: value } })); } openLogoPicker(): void { this.mediaPickerTarget = 'logo'; this.mediaPickerOpen = true; } openPaymentIconPicker(index: number): void { this.mediaPickerTarget = { type: 'paymentIcon', index }; this.mediaPickerOpen = true; } onImagePicked(asset: MediaAsset): void { const target = this.mediaPickerTarget; if (target === 'logo') { this.updateLogo(asset.url); } else if (target?.type === 'paymentIcon') { this.updatePaymentIconSrc(target.index, asset.url); } this.mediaPickerOpen = false; } onPaymentIconsChange(rows: FooterPaymentIconConfig[]): void { this.facade.updateBootstrap(current => ({ ...current, footer: { ...current.footer, paymentIcons: rows } })); } updatePaymentIconSrc(index: number, src: string): void { const rows = this.paymentIconRows().map((row, i) => (i === index ? { ...row, src } : row)); this.onPaymentIconsChange(rows); } updatePaymentIconAlt(index: number, alt: string): void { const rows = this.paymentIconRows().map((row, i) => (i === index ? { ...row, alt } : row)); this.onPaymentIconsChange(rows); } onSocialLinksChange(rows: FooterSocialLinkConfig[]): void { this.facade.updateBootstrap(current => ({ ...current, footer: { ...current.footer, socialLinks: rows } })); } updateSocialLinkLabel(index: number, label: string): void { const rows = this.socialLinkRows().map((row, i) => (i === index ? { ...row, label } : row)); this.onSocialLinksChange(rows); } updateSocialLinkUrl(index: number, url: string): void { const rows = this.socialLinkRows().map((row, i) => (i === index ? { ...row, url } : row)); this.onSocialLinksChange(rows); } isValidUrl(url: string): boolean { return !url || isValidHttpUrl(url); } }