feat(product-list): replace ProductCard with ProductColumnWithImage and update related components
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<article
|
||||
class="product-column-with-image border rounded shadow-sm bg-white overflow-hidden d-flex flex-column h-100"
|
||||
>
|
||||
<div class="product-column-with-image__image-container position-relative bg-light">
|
||||
@if (imageUrl(); as imageSrc) {
|
||||
<img
|
||||
[ngSrc]="imageSrc"
|
||||
[alt]="title()"
|
||||
[priority]="imagePriority()"
|
||||
fill
|
||||
sizes="(min-width: 1200px) 25vw, (min-width: 768px) 50vw, 100vw"
|
||||
class="product-column-with-image__image w-100 h-100 object-fit-cover"
|
||||
/>
|
||||
} @else {
|
||||
<div
|
||||
class="product-column-with-image__image-placeholder w-100 h-100 d-flex align-items-center justify-content-center bg-light text-muted"
|
||||
>
|
||||
<i class="fa-solid fa-image fa-2x"></i>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (discount() && discount()! > 0) {
|
||||
<span
|
||||
class="product-column-with-image__discount-badge position-absolute top-0 end-0 bg-primary text-white px-2 py-1"
|
||||
>
|
||||
-{{ discount() }}%
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="product-column-with-image__body p-3 d-flex flex-column align-items-center text-center flex-grow-1"
|
||||
>
|
||||
<h3 class="product-column-with-image__title text-uppercase text-black mb-4">{{ title() }}</h3>
|
||||
|
||||
<div class="product-column-with-image__prices d-flex align-items-center justify-content-center gap-2">
|
||||
<span class="product-column-with-image__price-discounted text-primary">
|
||||
{{ formattedDiscountedPrice() }}
|
||||
</span>
|
||||
|
||||
@if (discount() && discount()! > 0) {
|
||||
<span class="product-column-with-image__price-original text-decoration-line-through">
|
||||
{{ formattedOriginalPrice() }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="product-column-with-image__price-transfer mb-4">
|
||||
<span class="product-column-with-image__price-transfer-value">{{
|
||||
formattedTransferPrice()
|
||||
}}</span>
|
||||
<span class="product-column-with-image__price-transfer-label"> con transferencia</span>
|
||||
</div>
|
||||
|
||||
<div class="product-column-with-image__action mt-auto w-100">
|
||||
<app-button variant="primary" class="w-100" (click)="buy.emit()">
|
||||
{{ buttonText() }}
|
||||
</app-button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@@ -0,0 +1,77 @@
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.product-column-with-image {
|
||||
width: 100%;
|
||||
max-height: 411px;
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
|
||||
&__image-container {
|
||||
width: 100%;
|
||||
max-height: 50%;
|
||||
aspect-ratio: 1 / 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__discount-badge {
|
||||
background-color: var(--tenant-primary) !important;
|
||||
font-size: 13px;
|
||||
font-weight: 325;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 12px;
|
||||
font-weight: 325;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
&__price-discounted {
|
||||
color: var(--tenant-primary) !important;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&__price-original {
|
||||
font-size: 11px;
|
||||
font-weight: 325;
|
||||
color: #dddddd;
|
||||
}
|
||||
|
||||
&__price-transfer {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
&__price-transfer-value {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&__price-transfer-label {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
&__action {
|
||||
app-button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
::ng-deep .btn {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { NgOptimizedImage } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
|
||||
|
||||
import { ButtonComponent } from '../button/button.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-column-with-image',
|
||||
imports: [ButtonComponent, NgOptimizedImage],
|
||||
templateUrl: './product-column-with-image.component.html',
|
||||
styleUrl: './product-column-with-image.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ProductColumnWithImageComponent {
|
||||
readonly imageUrl = input<string | null>(null);
|
||||
readonly title = input<string>('');
|
||||
readonly originalPrice = input<number>(0);
|
||||
readonly discount = input<number | null>(null);
|
||||
readonly transferPrice = input<number | null>(null);
|
||||
readonly buttonText = input<string>('Comprar');
|
||||
readonly imagePriority = input<boolean>(false);
|
||||
|
||||
readonly buy = output<void>();
|
||||
|
||||
readonly discountedPrice = computed(() => {
|
||||
const original = this.originalPrice();
|
||||
const discount = this.discount();
|
||||
|
||||
return !discount || discount <= 0 ? original : original * (1 - discount / 100);
|
||||
});
|
||||
|
||||
readonly computedTransferPrice = computed(() => {
|
||||
return this.transferPrice() ?? this.discountedPrice();
|
||||
});
|
||||
|
||||
readonly formattedDiscountedPrice = computed(() => this.formatCurrency(this.discountedPrice()));
|
||||
readonly formattedOriginalPrice = computed(() => this.formatCurrency(this.originalPrice()));
|
||||
readonly formattedTransferPrice = computed(() =>
|
||||
this.formatCurrency(this.computedTransferPrice()),
|
||||
);
|
||||
|
||||
private formatCurrency(value: number): string {
|
||||
const rounded = Math.round(value);
|
||||
const parts = rounded.toString().split('.');
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
||||
|
||||
return `$${parts.join(',')}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user