From 40cc2b840d48b40defd0f787c5bec858ade8d71b Mon Sep 17 00:00:00 2001 From: ncoronel Date: Fri, 17 Jul 2026 11:54:12 -0300 Subject: [PATCH] feat(product-row-card): add reusable product row card component with styling and functionality --- .../reutilizables-test-page.component.html | 24 +++++++ .../reutilizables-test-page.component.ts | 12 ++++ .../product-row-card.component.html | 47 ++++++++++++++ .../product-row-card.component.scss | 63 +++++++++++++++++++ .../product-row-card.component.ts | 55 ++++++++++++++++ 5 files changed, 201 insertions(+) create mode 100644 src/app/shared/components/product-row-card/product-row-card.component.html create mode 100644 src/app/shared/components/product-row-card/product-row-card.component.scss create mode 100644 src/app/shared/components/product-row-card/product-row-card.component.ts diff --git a/src/app/features/componentes-test/pages/reutilizables-test-page/reutilizables-test-page.component.html b/src/app/features/componentes-test/pages/reutilizables-test-page/reutilizables-test-page.component.html index fd4cf52..831ea18 100644 --- a/src/app/features/componentes-test/pages/reutilizables-test-page/reutilizables-test-page.component.html +++ b/src/app/features/componentes-test/pages/reutilizables-test-page/reutilizables-test-page.component.html @@ -631,6 +631,30 @@ +
+
+ Reusable Component +

Tarjetas de Producto (Row Layout)

+ +

+ Diseño horizontal que ocupa el 100% del ancho disponible. +

+ +
+
+ +
+
+
+
+ + +
+

+ {{ title() }} +

+ @if (description()) { +

+ {{ description() }} +

+ } +
+ + +
+ +
+ + {{ formattedPrice() }} + +
+ + Comprar + +
+
+ + +
+ @if (variants().length > 0) { + + } + + + +
+ + Agregar al carrito + +
+
+
+ diff --git a/src/app/shared/components/product-row-card/product-row-card.component.scss b/src/app/shared/components/product-row-card/product-row-card.component.scss new file mode 100644 index 0000000..e1dd7a7 --- /dev/null +++ b/src/app/shared/components/product-row-card/product-row-card.component.scss @@ -0,0 +1,63 @@ +:host { + display: block; + width: 100%; +} + +.product-row-card { + width: 100%; + background-color: #f8f9fa; /* slightly light bg matching the screenshot if needed, but let's use white or #f8f9fa based on the border */ + border-color: #e9ecef !important; + + &__title { + font-size: 16px; + font-weight: 700; + color: #555; /* dark grey matching screenshot */ + letter-spacing: 0.5px; + } + + &__description { + font-size: 13px; + color: #777; /* lighter grey for text */ + line-height: 1.4; + max-width: 600px; + + /* Simulate the bold text for 'Niños menores...' if it was HTML. + Since it's passed as string we just let it be, unless we parse it. */ + } + + &__price { + font-size: 20px; + font-weight: 800; + color: var(--tenant-primary, #009933) !important; /* Green matching screenshot */ + } + + &__select { + width: auto; + min-width: 150px; + font-size: 14px; + height: 38px; + color: #666; + border-color: #ccc; + cursor: pointer; + + &:focus { + border-color: var(--tenant-primary); + box-shadow: 0 0 0 0.25rem rgba(0, 153, 51, 0.25); + } + } + + &__btn-wrapper { + min-width: 160px; /* To make both buttons equal width as in screenshot */ + + app-button { + display: block; + width: 100%; + + ::ng-deep .btn { + width: 100%; + font-weight: 600; + border-radius: 4px; + } + } + } +} diff --git a/src/app/shared/components/product-row-card/product-row-card.component.ts b/src/app/shared/components/product-row-card/product-row-card.component.ts new file mode 100644 index 0000000..5a8448d --- /dev/null +++ b/src/app/shared/components/product-row-card/product-row-card.component.ts @@ -0,0 +1,55 @@ +import { ChangeDetectionStrategy, Component, computed, input, output, model } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ButtonComponent } from '../button/button.component'; +import { QuantitySelectorComponent } from '../quantity-selector/quantity-selector.component'; + +export interface Variant { + label: string; + value: any; +} + +@Component({ + selector: 'app-product-row-card', + standalone: true, + imports: [ButtonComponent, QuantitySelectorComponent, FormsModule], + templateUrl: './product-row-card.component.html', + styleUrl: './product-row-card.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProductRowCardComponent { + // Configurable inputs + readonly title = input(''); + readonly description = input(''); + readonly price = input(0); + readonly variants = input([]); + + // Internal state models + readonly quantity = model(1); + readonly selectedVariant = model(null); + + // Interactive events + readonly buy = output(); + readonly addToCart = output<{ quantity: number; variant: any }>(); + + // Formatted string for price: "$ 10.000" + readonly formattedPrice = computed(() => { + return this.formatCurrency(this.price()); + }); + + protected onAddToCart(): void { + this.addToCart.emit({ + quantity: this.quantity(), + variant: this.selectedVariant() + }); + } + + /** + * Helper method to format currency numbers to Argentine Pesos style "$ XX.XXX". + */ + 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(',')}`; + } +}