Sprint 8: add widget manifest and data source engine
This commit is contained in:
@@ -1,53 +1,37 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { ProductCardConfig } from '../../shared/models/ui';
|
||||
import { SectionConfig } from '../../shared/models/config';
|
||||
import { CatalogProductGridComponent } from '../../features/website/catalog/components/product-grid/product-grid.component';
|
||||
import { ProductCollectionWidgetData } from '../contracts/widget-data.contract';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-carousel-widget',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule, CatalogProductGridComponent],
|
||||
template: `
|
||||
<section class="product-carousel-widget">
|
||||
@if (title) {
|
||||
<h2>{{ title }}</h2>
|
||||
@if (data?.title) {
|
||||
<h2>{{ data?.title }}</h2>
|
||||
}
|
||||
|
||||
<div class="product-carousel-widget__grid">
|
||||
@for (item of items; track item.id) {
|
||||
<article class="product-card">
|
||||
<img [src]="item.imageUrl" [alt]="item.title" loading="lazy" />
|
||||
<h3>{{ item.title }}</h3>
|
||||
@if (item.subtitle) {
|
||||
<p>{{ item.subtitle }}</p>
|
||||
}
|
||||
<strong>{{ item.price.amount }} {{ item.price.currency }}</strong>
|
||||
<button type="button" (click)="onProductClick(item)">{{ actionLabel }}</button>
|
||||
</article>
|
||||
@if (data?.products?.length) {
|
||||
<app-catalog-product-grid [products]="data?.products ?? []" />
|
||||
} @else if (data?.emptyMessage) {
|
||||
<p class="product-carousel-widget__empty">{{ data?.emptyMessage }}</p>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.product-carousel-widget { padding: 1rem; }
|
||||
.product-carousel-widget__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; }
|
||||
.product-card { padding: 0.75rem; border: 1px solid var(--border-color, #d3dad9); border-radius: var(--radius-md, 12px); }
|
||||
img { width: 100%; height: 140px; object-fit: cover; border-radius: 8px; margin-bottom: 0.5rem; }
|
||||
h3 { margin: 0 0 0.25rem; font-size: 1rem; }
|
||||
p { margin: 0 0 0.5rem; color: var(--text-secondary, #667a77); }
|
||||
button { margin-top: 0.5rem; border: none; border-radius: 8px; padding: 0.5rem 0.75rem; cursor: pointer; background: var(--primary-color, #497671); color: #fff; }
|
||||
.product-carousel-widget__empty { margin: 0; color: var(--text-secondary, #667a77); }
|
||||
`
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProductCarouselWidgetComponent {
|
||||
@Input() title: string = '';
|
||||
@Input() actionLabel: string = 'Select';
|
||||
@Input() items: ProductCardConfig[] = [];
|
||||
@Input() section: SectionConfig | null = null;
|
||||
@Input() data: ProductCollectionWidgetData | null = null;
|
||||
|
||||
@Output() productSelected = new EventEmitter<ProductCardConfig>();
|
||||
|
||||
onProductClick(item: ProductCardConfig): void {
|
||||
this.productSelected.emit(item);
|
||||
}
|
||||
@Output() productSelected = new EventEmitter<unknown>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user