feat: build and wire the Quick View modal
Some checks failed
Architecture Governance / architecture (push) Has been cancelled

The Quick View button (search results page only) emitted
quickViewPlaceholder with zero listeners anywhere up the chain - the
button did nothing. Built a minimal QuickViewDialogComponent (image,
name, price incl. discount, short description, Add to Cart, link to
full product page) and wired the event through product-grid ->
search-results -> catalog-container, which fetches the product via
ProductFacade and opens the dialog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-13 09:45:03 +04:00
parent b04e3a67f5
commit 6cc5d43a10
13 changed files with 189 additions and 1 deletions

View File

@@ -0,0 +1,34 @@
<app-dialog
[open]="!!product || loading"
[titleText]="'catalog.quickView' | translate"
size="md"
(closed)="closed.emit()"
>
@if (loading) {
<div class="quick-view__loading" role="status" aria-live="polite">{{ 'common.loading' | translate }}</div>
} @else if (product) {
<div class="quick-view">
<img class="quick-view__image" [src]="mainImage" [alt]="product.name" />
<div class="quick-view__body">
<h3 class="quick-view__title">{{ product.name }}</h3>
<p class="quick-view__price">
@if (hasDiscount) {
<span class="quick-view__price-original">{{ product.price | number:'1.2-2' }} {{ product.currency }}</span>
<span class="quick-view__price-final">{{ discountedPrice | number:'1.2-2' }} {{ product.currency }}</span>
} @else {
<span class="quick-view__price-final">{{ product.price | number:'1.2-2' }} {{ product.currency }}</span>
}
</p>
@if (product.simpleDescription) {
<p class="quick-view__description">{{ product.simpleDescription }}</p>
}
<div class="quick-view__actions">
<app-button variant="primary" (click)="addToCart.emit(product)">{{ 'carousel.addToCart' | translate }}</app-button>
<a class="quick-view__link" [routerLink]="('/product/' + product.itemID) | langRoute" (click)="closed.emit()">
{{ 'catalog.quickViewDetails' | translate }}
</a>
</div>
</div>
</div>
}
</app-dialog>

View File

@@ -0,0 +1,73 @@
.quick-view {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
@media (min-width: 560px) {
grid-template-columns: 200px 1fr;
}
}
.quick-view__image {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
border-radius: var(--radius-md, 8px);
background: var(--bg-secondary, #f3f3f3);
}
.quick-view__body {
display: flex;
flex-direction: column;
gap: 8px;
}
.quick-view__title {
margin: 0;
font-size: var(--font-size-lg, 1.125rem);
color: var(--text-primary);
}
.quick-view__price {
margin: 0;
display: flex;
align-items: baseline;
gap: 8px;
}
.quick-view__price-original {
text-decoration: line-through;
color: var(--text-secondary);
font-size: var(--font-size-sm, 0.875rem);
}
.quick-view__price-final {
font-weight: var(--font-weight-bold, 700);
font-size: var(--font-size-lg, 1.125rem);
color: var(--text-primary);
}
.quick-view__description {
margin: 0;
color: var(--text-secondary);
font-size: var(--font-size-sm, 0.875rem);
}
.quick-view__actions {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
}
.quick-view__link {
text-align: center;
color: var(--primary-color);
font-size: var(--font-size-sm, 0.875rem);
}
.quick-view__loading {
padding: 40px 0;
text-align: center;
color: var(--text-secondary);
}

View File

@@ -0,0 +1,37 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { RouterLink } from '@angular/router';
import { Product } from '../../../../../core/products/models/product-domain.model';
import { DialogComponent } from '../../../../../shared/ui/dialog/dialog.component';
import { ButtonComponent } from '../../../../../shared/ui/button/button.component';
import { TranslatePipe } from '../../../../../i18n/translate.pipe';
import { LangRoutePipe } from '../../../../../pipes/lang-route.pipe';
import { getDiscountedPrice, getMainImage } from '../../../../../utils/item.utils';
@Component({
selector: 'app-quick-view-dialog',
standalone: true,
imports: [DialogComponent, ButtonComponent, TranslatePipe, LangRoutePipe, DecimalPipe, RouterLink],
templateUrl: './quick-view-dialog.component.html',
styleUrl: './quick-view-dialog.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class QuickViewDialogComponent {
@Input() product: Product | null = null;
@Input() loading = false;
@Output() closed = new EventEmitter<void>();
@Output() addToCart = new EventEmitter<Product>();
get mainImage(): string {
return this.product ? getMainImage(this.product) : '';
}
get discountedPrice(): number {
return this.product ? getDiscountedPrice(this.product) : 0;
}
get hasDiscount(): boolean {
return !!this.product?.discount && this.product.discount > 0;
}
}