2026-07-03 01:36:27 +04:00
|
|
|
import { CommonModule } from '@angular/common';
|
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel.
Homepage section (sections list -> visual blocks):
- Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html
- Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3')
- Added duplicate and remove per block, alongside the existing drag-to-reorder
- Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end
Widgets section (hero widget):
- 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind
- 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values
- New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before
- HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control
Carousel arrows root cause and fix:
- widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place
- Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise
- Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug
New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
|
|
|
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, ViewChild, signal } from '@angular/core';
|
2026-07-05 02:08:15 +04:00
|
|
|
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';
|
2026-07-03 01:36:27 +04:00
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'app-product-carousel-widget',
|
|
|
|
|
standalone: true,
|
2026-07-05 02:08:15 +04:00
|
|
|
imports: [CommonModule, CatalogProductGridComponent],
|
2026-07-03 01:36:27 +04:00
|
|
|
template: `
|
|
|
|
|
<section class="product-carousel-widget">
|
2026-07-05 02:08:15 +04:00
|
|
|
@if (data?.title) {
|
2026-07-16 23:32:31 +04:00
|
|
|
<h2 class="product-carousel-widget__title">{{ data?.title }}</h2>
|
2026-07-03 01:36:27 +04:00
|
|
|
}
|
|
|
|
|
|
2026-07-05 02:08:15 +04:00
|
|
|
@if (data?.products?.length) {
|
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel.
Homepage section (sections list -> visual blocks):
- Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html
- Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3')
- Added duplicate and remove per block, alongside the existing drag-to-reorder
- Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end
Widgets section (hero widget):
- 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind
- 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values
- New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before
- HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control
Carousel arrows root cause and fix:
- widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place
- Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise
- Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug
New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
|
|
|
@if (isCarousel) {
|
|
|
|
|
<div class="product-carousel-widget__track">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class="product-carousel-widget__arrow product-carousel-widget__arrow--prev"
|
|
|
|
|
aria-label="Previous products"
|
|
|
|
|
[disabled]="atStart()"
|
|
|
|
|
(click)="scrollBy(-1)"
|
|
|
|
|
>←</button>
|
|
|
|
|
|
|
|
|
|
<div class="product-carousel-widget__scroller" #scroller (scroll)="onScroll()">
|
|
|
|
|
<app-catalog-product-grid [products]="data?.products ?? []" layout="compact" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
class="product-carousel-widget__arrow product-carousel-widget__arrow--next"
|
|
|
|
|
aria-label="Next products"
|
|
|
|
|
[disabled]="atEnd()"
|
|
|
|
|
(click)="scrollBy(1)"
|
|
|
|
|
>→</button>
|
|
|
|
|
</div>
|
|
|
|
|
} @else {
|
|
|
|
|
<app-catalog-product-grid [products]="data?.products ?? []" />
|
|
|
|
|
}
|
2026-07-05 02:08:15 +04:00
|
|
|
} @else if (data?.emptyMessage) {
|
|
|
|
|
<p class="product-carousel-widget__empty">{{ data?.emptyMessage }}</p>
|
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel.
Homepage section (sections list -> visual blocks):
- Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html
- Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3')
- Added duplicate and remove per block, alongside the existing drag-to-reorder
- Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end
Widgets section (hero widget):
- 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind
- 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values
- New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before
- HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control
Carousel arrows root cause and fix:
- widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place
- Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise
- Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug
New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
|
|
|
}
|
2026-07-03 01:36:27 +04:00
|
|
|
</section>
|
|
|
|
|
`,
|
|
|
|
|
styles: [
|
|
|
|
|
`
|
2026-07-16 23:32:31 +04:00
|
|
|
.product-carousel-widget { display: grid; gap: var(--space-lg, 24px); }
|
|
|
|
|
|
|
|
|
|
.product-carousel-widget__title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 1.75rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--text-primary, #1e3c38);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-05 02:08:15 +04:00
|
|
|
.product-carousel-widget__empty { margin: 0; color: var(--text-secondary, #667a77); }
|
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel.
Homepage section (sections list -> visual blocks):
- Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html
- Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3')
- Added duplicate and remove per block, alongside the existing drag-to-reorder
- Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end
Widgets section (hero widget):
- 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind
- 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values
- New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before
- HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control
Carousel arrows root cause and fix:
- widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place
- Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise
- Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug
New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
|
|
|
|
|
|
|
|
.product-carousel-widget__track {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-carousel-widget__scroller {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
scroll-behavior: smooth;
|
|
|
|
|
scrollbar-width: none;
|
|
|
|
|
|
|
|
|
|
&::-webkit-scrollbar { display: none; }
|
|
|
|
|
|
|
|
|
|
::ng-deep .catalog-product-grid {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
gap: var(--space-md, 16px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::ng-deep .catalog-product-shell {
|
|
|
|
|
flex: 0 0 auto;
|
|
|
|
|
width: 220px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-carousel-widget__arrow {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
border: 1px solid var(--border-color, #d3dad9);
|
|
|
|
|
background: #fff;
|
|
|
|
|
color: var(--text-primary, #1e3c38);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
|
|
|
|
&:hover:not(:disabled) { background: var(--primary-color, #497671); color: #fff; }
|
|
|
|
|
&:disabled { opacity: 0.35; cursor: default; }
|
|
|
|
|
&:focus-visible { outline: 2px solid var(--primary-color, #497671); outline-offset: 2px; }
|
|
|
|
|
}
|
2026-07-03 01:36:27 +04:00
|
|
|
`
|
|
|
|
|
],
|
|
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush
|
|
|
|
|
})
|
|
|
|
|
export class ProductCarouselWidgetComponent {
|
2026-07-05 02:08:15 +04:00
|
|
|
@Input() section: SectionConfig | null = null;
|
|
|
|
|
@Input() data: ProductCollectionWidgetData | null = null;
|
2026-07-03 01:36:27 +04:00
|
|
|
|
2026-07-05 02:08:15 +04:00
|
|
|
@Output() productSelected = new EventEmitter<unknown>();
|
feat(builder): visual homepage blocks, merchant-language widget settings, real carousel arrows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
P0 user feedback: homepage builder showed raw section.id like 'section-hero'/'section-categories'; hero widget exposed 'full-bleed'/'boxed' and raw px/vh as free text with no explanation; Overlay/Autoplay toggles made no sense without a slides concept; product carousel widgets rendered arrows nowhere near a working carousel.
Homepage section (sections list -> visual blocks):
- Replaced raw section.id display with a merchant-facing block catalog (icon + name + one-line explanation) for hero/categories/featured-products/product-carousel/recently-viewed/banner/partners/custom-html
- Added block catalog picker to append new blocks (was fixed at whatever the seed data had - task asked 'what if we add manually? not fixed 3')
- Added duplicate and remove per block, alongside the existing drag-to-reorder
- Verified in browser: labels render correctly, add-block and duplicate both confirmed working end-to-end
Widgets section (hero widget):
- 'Layout' free-text replaced with a select (Full width / Boxed) instead of typing 'full-bleed'/'boxed' blind
- 'Height' free-text replaced with a select (Compact/Medium/Tall/Full screen) mapped to real vh values
- New Slides editor: title/subtitle pairs an admin can add/remove: this is the actual multi-slide data the Overlay/Autoplay toggles were referring to with nothing to point at before
- HeroWidgetData contract gains slides[]/autoplay; HeroWidgetComponent now renders a real rotator (dots, click-to-jump, autoplay interval) when more than one slide exists - previously autoplay/overlay props existed but there was no slideshow behavior anywhere to control
Carousel arrows root cause and fix:
- widget-manifest.json offers 'carousel' as a layout option for product-collection/product-carousel widgets, and the admin UI let you select it, but ProductCarouselWidgetComponent always rendered a static CSS grid regardless - there was no carousel implementation to have arrows in the first place
- Now renders a real horizontally-scrollable strip with working prev/next buttons (native scrollBy, disabled at each end) when section.layout.strategy === 'carousel'; falls back to the existing grid otherwise
- Confirmed src/app/components/items-carousel (a PrimeNG p-carousel) is dead code, not wired into any route or widget - not the source of the reported bug
New builder.* i18n keys (en/ru/hy), zero duplicate-key collisions verified via scan
2026-07-19 14:14:29 +04:00
|
|
|
|
|
|
|
|
@ViewChild('scroller') private scroller?: ElementRef<HTMLDivElement>;
|
|
|
|
|
|
|
|
|
|
readonly atStart = signal(true);
|
|
|
|
|
readonly atEnd = signal(false);
|
|
|
|
|
|
|
|
|
|
get isCarousel(): boolean {
|
|
|
|
|
return this.section?.layout?.strategy === 'carousel';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scrollBy(direction: -1 | 1): void {
|
|
|
|
|
const el = this.scroller?.nativeElement;
|
|
|
|
|
if (!el) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
el.scrollBy({ left: direction * el.clientWidth * 0.8, behavior: 'smooth' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onScroll(): void {
|
|
|
|
|
const el = this.scroller?.nativeElement;
|
|
|
|
|
if (!el) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.atStart.set(el.scrollLeft <= 4);
|
|
|
|
|
this.atEnd.set(el.scrollLeft + el.clientWidth >= el.scrollWidth - 4);
|
|
|
|
|
}
|
2026-07-03 01:36:27 +04:00
|
|
|
}
|