feat(ux): implement sprint 11 user experience module

This commit is contained in:
sdarbinyan
2026-07-09 01:13:54 +04:00
parent 1a8f916942
commit 92e1bdaff8
59 changed files with 2062 additions and 25 deletions

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Observable, map, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { CategoriesWidgetComponent, FooterNavigationWidgetComponent, HeroWidgetComponent, ProductCarouselWidgetComponent } from '../ui';
import { CategoriesWidgetComponent, FooterNavigationWidgetComponent, HeroWidgetComponent, ProductCarouselWidgetComponent, RecentlyViewedWidgetComponent } from '../ui';
import { RegisteredWidget } from '../contracts/widget-component.contract';
import { WidgetRegistryService } from './widget-registry.service';
import { WidgetManifestService } from './widget-manifest.service';
@@ -12,7 +12,8 @@ const APPROVED_WIDGET_COMPONENTS: Record<string, RegisteredWidget['component']>
'footer': FooterNavigationWidgetComponent,
'footer-navigation': FooterNavigationWidgetComponent,
'product-carousel': ProductCarouselWidgetComponent,
'product-collection': ProductCarouselWidgetComponent
'product-collection': ProductCarouselWidgetComponent,
'recently-viewed': RecentlyViewedWidgetComponent
};
@Injectable({ providedIn: 'root' })

View File

@@ -42,6 +42,8 @@ export class DataSourceResolverService {
case 'footer':
case 'footer-navigation':
return of(this.toFooterData(section, settings));
case 'recently-viewed':
return of({ section, settings });
default:
return of({ section, settings });
}

View File

@@ -18,7 +18,7 @@ import { CategoriesWidgetData } from '../contracts/widget-data.contract';
@if (widgetData.categories.length) {
<app-catalog-category-grid
[categories]="widgetData.categories"
(categorySelected)="onCategorySelected($event)"
(categorySelected)="handleCategorySelected($event)"
/>
} @else if (widgetData.emptyMessage) {
<p class="categories-widget__empty">{{ widgetData.emptyMessage }}</p>
@@ -37,9 +37,11 @@ import { CategoriesWidgetData } from '../contracts/widget-data.contract';
export class CategoriesWidgetComponent {
@Input() section: SectionConfig | null = null;
@Input() data: CategoriesWidgetData | null = null;
@Input() categorySelectedCallback: ((category: unknown) => void) | null = null;
@Output() categorySelected = new EventEmitter<unknown>();
onCategorySelected(category: unknown): void {
handleCategorySelected(category: unknown): void {
this.categorySelectedCallback?.(category);
this.categorySelected.emit(category);
}
}

View File

@@ -2,4 +2,5 @@ export * from './categories-widget.component';
export * from './footer-navigation-widget.component';
export * from './hero-widget.component';
export * from './product-carousel-widget.component';
export * from './recently-viewed-widget.component';
export * from './unknown-widget.component';

View File

@@ -0,0 +1,36 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { SectionConfig } from '../../shared/models/config';
import { RecentlyViewedStripComponent } from '../../features/website/user-experience/components/recently-viewed-strip/recently-viewed-strip.component';
@Component({
selector: 'app-recently-viewed-widget',
standalone: true,
imports: [RecentlyViewedStripComponent],
template: `
<app-recently-viewed-strip
[title]="title"
[maxItems]="maxItems"
[showClear]="showClear"
/>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RecentlyViewedWidgetComponent {
@Input() section: SectionConfig | null = null;
@Input() data: { settings?: Record<string, unknown> } | null = null;
get title(): string {
const raw = this.data?.settings?.['title'];
return typeof raw === 'string' && raw.trim().length > 0 ? raw : 'Recently Viewed';
}
get maxItems(): number {
const raw = this.data?.settings?.['maxItems'];
const value = typeof raw === 'number' ? raw : Number(raw);
return Number.isFinite(value) ? Math.max(1, value) : 8;
}
get showClear(): boolean {
return this.data?.settings?.['showClear'] === true;
}
}