feat: build and wire the Quick View modal
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
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:
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user