Files
marketplaces/src/app/components/product-card/product-card.component.ts
sdarbinyan 4510eb769a
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
feat: client-side currency conversion with admin-configurable rates
- CurrencyRatesService: RUB-based rates, persisted via localStorage
- CurrencyConvertPipe: impure pipe converting item price to selected currency
- Admin settings page: editable currency rates form
- Applied conversion to product-card, product-information, quick-view-dialog,
  delivery-information, compare-table, cart totals + payment payload

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 01:45:12 +04:00

112 lines
4.0 KiB
TypeScript

import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core';
import { DecimalPipe } from '@angular/common';
import { RouterLink } from '@angular/router';
import { Product } from '../../core/products/models/product-domain.model';
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
import { TranslatePipe } from '../../i18n/translate.pipe';
import { CurrencyConvertPipe } from '../../pipes/currency-convert.pipe';
import { LanguageService } from '../../services/language.service';
import { cleanDescription, getBadgeClass, getDiscountedPrice, getMainImage, onImageError } from '../../utils/item.utils';
const STOCK_LABEL_KEYS: Record<string, string> = {
high: 'catalog.stockHigh',
medium: 'catalog.stockMedium',
low: 'catalog.stockLow',
out: 'catalog.stockOut'
};
export type ProductCardAppearance = 'standard' | 'compact';
@Component({
selector: 'app-product-card',
standalone: true,
imports: [DecimalPipe, RouterLink, LangRoutePipe, TranslatePipe, CurrencyConvertPipe],
templateUrl: './product-card.component.html',
styleUrls: ['./product-card.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductCardComponent {
private readonly languageService = inject(LanguageService);
readonly displayCurrency = this.languageService.currentCurrency;
@Input({ required: true }) item!: Product;
@Input() title = '';
@Input() description = '';
@Input() addToCartLabel = '';
@Input() appearance: ProductCardAppearance = 'standard';
@Input() showDescription = false;
@Input() showStock = true;
@Input() showRating = true;
@Input() showDiscountBadge = true;
@Input() showFavoritePlaceholder = false;
@Input() showComparePlaceholder = false;
@Input() showQuickViewPlaceholder = false;
@Input() showFavoriteAction = false;
@Input() showCompareAction = false;
@Input() showShareAction = false;
@Input() isFavorite = false;
@Input() isCompared = false;
@Output() addToCart = new EventEmitter<{ itemID: number; event: Event }>();
@Output() preview = new EventEmitter<number>();
@Output() selected = new EventEmitter<{ itemID: number; event: Event }>();
@Output() favoritePlaceholder = new EventEmitter<number>();
@Output() comparePlaceholder = new EventEmitter<number>();
@Output() quickViewPlaceholder = new EventEmitter<number>();
@Output() favoriteToggled = new EventEmitter<number>();
@Output() compareToggled = new EventEmitter<number>();
@Output() shareRequested = new EventEmitter<number>();
readonly getMainImage = getMainImage;
readonly getDiscountedPrice = getDiscountedPrice;
readonly getBadgeClass = getBadgeClass;
readonly cleanDescription = cleanDescription;
readonly onImageError = onImageError;
onAddToCart(event: Event): void {
this.addToCart.emit({ itemID: this.item.itemID, event });
}
onSelected(event: Event): void {
this.selected.emit({ itemID: this.item.itemID, event });
}
onFavoritePlaceholder(event: Event): void {
event.preventDefault();
event.stopPropagation();
this.favoritePlaceholder.emit(this.item.itemID);
this.favoriteToggled.emit(this.item.itemID);
}
onComparePlaceholder(event: Event): void {
event.preventDefault();
event.stopPropagation();
this.comparePlaceholder.emit(this.item.itemID);
this.compareToggled.emit(this.item.itemID);
}
onQuickViewPlaceholder(event: Event): void {
event.preventDefault();
event.stopPropagation();
this.quickViewPlaceholder.emit(this.item.itemID);
}
onShare(event: Event): void {
event.preventDefault();
event.stopPropagation();
this.shareRequested.emit(this.item.itemID);
}
stockLabelKey(remainings: string): string {
return STOCK_LABEL_KEYS[remainings.toLowerCase()] ?? STOCK_LABEL_KEYS['high'];
}
showFavoriteControl(): boolean {
return this.showFavoriteAction || this.showFavoritePlaceholder;
}
showCompareControl(): boolean {
return this.showCompareAction || this.showComparePlaceholder;
}
}