Add reusable marketplace product card
This commit is contained in:
@@ -9,53 +9,13 @@
|
||||
@if (!error()) {
|
||||
<div class="items-grid">
|
||||
@for (item of items(); track trackByItemId($index, item)) {
|
||||
<div class="item-card" (mouseenter)="onItemHover(item.itemID)">
|
||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-link">
|
||||
<div class="item-image">
|
||||
<img [src]="getMainImage(item)" [alt]="itemName(item)" loading="lazy" decoding="async" width="300" height="300" />
|
||||
@if (item.discount > 0) {
|
||||
<div class="discount-badge">-{{ item.discount }}%</div>
|
||||
}
|
||||
@if (item.badges && item.badges.length > 0) {
|
||||
<div class="item-badges-overlay">
|
||||
@for (badge of item.badges; track badge) {
|
||||
<span class="item-badge" [class]="getBadgeClass(badge)">{{ badge }}</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="item-details">
|
||||
<h3 class="item-name">{{ itemName(item) }}</h3>
|
||||
|
||||
<div class="item-rating">
|
||||
<span class="rating-stars">⭐ {{ item.rating }}</span>
|
||||
<span class="rating-count">({{ item.callbacks?.length || 0 }})</span>
|
||||
</div>
|
||||
|
||||
<div class="item-price">
|
||||
@if (item.discount > 0) {
|
||||
<span class="original-price">{{ item.price }} {{ item.currency }}</span>
|
||||
<span class="discounted-price">{{ getDiscountedPrice(item) | number:'1.2-2' }} {{ item.currency }}</span>
|
||||
} @else {
|
||||
<span class="current-price">{{ item.price }} {{ item.currency }}</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="item-stock">
|
||||
<div class="stock-bar">
|
||||
<span class="bar-segment" [class.filled]="item.remainings === 'high' || item.remainings === 'medium' || item.remainings === 'low'" [class.high]="item.remainings === 'high'" [class.medium]="item.remainings === 'medium'" [class.low]="item.remainings === 'low'"></span>
|
||||
<span class="bar-segment" [class.filled]="item.remainings === 'high' || item.remainings === 'medium'" [class.high]="item.remainings === 'high'" [class.medium]="item.remainings === 'medium'"></span>
|
||||
<span class="bar-segment" [class.filled]="item.remainings === 'high'" [class.high]="item.remainings === 'high'"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<button class="add-to-cart-btn" (click)="addToCart(item.itemID, $event)" [attr.aria-label]="('category.addToCart' | translate) + ': ' + item.name">
|
||||
{{ 'category.addToCart' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<app-product-card
|
||||
[item]="item"
|
||||
[title]="itemName(item)"
|
||||
[addToCartLabel]="'category.addToCart' | translate"
|
||||
(addToCart)="addToCart($event.itemID, $event.event)"
|
||||
(preview)="onItemHover($event)"
|
||||
/>
|
||||
}
|
||||
|
||||
@if (loading() && items().length > 0) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, OnInit, OnDestroy, signal, HostListener, ChangeDetectionStrategy, inject } from '@angular/core';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { CartService } from '../../services';
|
||||
import { PrefetchService } from '../../services/prefetch.service';
|
||||
@@ -11,10 +10,11 @@ import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
import { SCROLL_THRESHOLD_PX, SCROLL_DEBOUNCE_MS, ITEMS_PER_PAGE } from '../../config/constants';
|
||||
import { ProductFacade } from '../../facades/platform/product.facade';
|
||||
import { ProductCardComponent } from '../../components/product-card/product-card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-category',
|
||||
imports: [DecimalPipe, RouterLink, LangRoutePipe, TranslatePipe],
|
||||
imports: [RouterLink, LangRoutePipe, TranslatePipe, ProductCardComponent],
|
||||
templateUrl: './category.component.html',
|
||||
styleUrls: ['./category.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
@@ -56,59 +56,15 @@
|
||||
@if (items().length > 0) {
|
||||
<div class="items-grid">
|
||||
@for (item of items(); track trackByItemId($index, item)) {
|
||||
<div class="item-card" (mouseenter)="onItemHover(item.itemID)">
|
||||
<a [routerLink]="['/item', item.itemID] | langRoute" class="item-link">
|
||||
<div class="item-image">
|
||||
<img [src]="getMainImage(item)" [alt]="itemName(item)" loading="lazy" decoding="async" width="300" height="300" />
|
||||
@if (item.discount > 0) {
|
||||
<div class="discount-badge">-{{ item.discount }}%</div>
|
||||
}
|
||||
@if (item.badges && item.badges.length > 0) {
|
||||
<div class="item-badges-overlay">
|
||||
@for (badge of item.badges; track badge) {
|
||||
<span class="item-badge" [class]="getBadgeClass(badge)">{{ badge }}</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="item-details">
|
||||
<h3 class="item-name">{{ itemName(item) }}</h3>
|
||||
|
||||
@if (itemDesc(item)) {
|
||||
<p class="item-simple-desc">{{ itemDesc(item) }}</p>
|
||||
}
|
||||
|
||||
<div class="item-rating">
|
||||
<span class="rating-stars">⭐ {{ item.rating }}</span>
|
||||
<span class="rating-count">({{ item.callbacks?.length || 0 }})</span>
|
||||
</div>
|
||||
|
||||
<div class="item-price">
|
||||
@if (item.discount > 0) {
|
||||
<span class="original-price">{{ item.price }} {{ item.currency }}</span>
|
||||
<span class="discounted-price">{{ getDiscountedPrice(item) | number:'1.2-2' }} {{ item.currency }}</span>
|
||||
} @else {
|
||||
<span class="current-price">{{ item.price }} {{ item.currency }}</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (item.remainings) {
|
||||
<div class="item-stock">
|
||||
<div class="stock-bar">
|
||||
<span class="bar-segment" [class.filled]="item.remainings === 'high' || item.remainings === 'medium' || item.remainings === 'low'" [class.high]="item.remainings === 'high'" [class.medium]="item.remainings === 'medium'" [class.low]="item.remainings === 'low'"></span>
|
||||
<span class="bar-segment" [class.filled]="item.remainings === 'high' || item.remainings === 'medium'" [class.high]="item.remainings === 'high'" [class.medium]="item.remainings === 'medium'"></span>
|
||||
<span class="bar-segment" [class.filled]="item.remainings === 'high'" [class.high]="item.remainings === 'high'"></span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<button class="add-to-cart-btn" (click)="addToCart(item.itemID, $event)" [attr.aria-label]="('search.addToCart' | translate) + ': ' + item.name">
|
||||
{{ 'search.addToCart' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<app-product-card
|
||||
[item]="item"
|
||||
[title]="itemName(item)"
|
||||
[description]="itemDesc(item)"
|
||||
[showDescription]="true"
|
||||
[addToCartLabel]="'search.addToCart' | translate"
|
||||
(addToCart)="addToCart($event.itemID, $event.event)"
|
||||
(preview)="onItemHover($event)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Component, signal, HostListener, OnDestroy, ChangeDetectionStrategy, inject } from '@angular/core';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { CartService } from '../../services';
|
||||
import { PrefetchService } from '../../services/prefetch.service';
|
||||
import { Item } from '../../models';
|
||||
@@ -9,15 +7,15 @@ import { Subject, Subscription } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
import { getDiscountedPrice, getMainImage, trackByItemId, getBadgeClass, getTranslatedField } from '../../utils/item.utils';
|
||||
import { LanguageService } from '../../services/language.service';
|
||||
import { LangRoutePipe } from '../../pipes/lang-route.pipe';
|
||||
import { TranslatePipe } from '../../i18n/translate.pipe';
|
||||
import { TranslateService } from '../../i18n/translate.service';
|
||||
import { SEARCH_DEBOUNCE_MS, ITEMS_PER_PAGE, SCROLL_THRESHOLD_PX, SCROLL_DEBOUNCE_MS } from '../../config/constants';
|
||||
import { ProductFacade } from '../../facades/platform/product.facade';
|
||||
import { ProductCardComponent } from '../../components/product-card/product-card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search',
|
||||
imports: [DecimalPipe, FormsModule, RouterLink, LangRoutePipe, TranslatePipe],
|
||||
imports: [FormsModule, TranslatePipe, ProductCardComponent],
|
||||
templateUrl: './search.component.html',
|
||||
styleUrls: ['./search.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
|
||||
Reference in New Issue
Block a user