From 0769bb8683f4a6af7fe4ca778e1fdf183f6b0e0c Mon Sep 17 00:00:00 2001 From: ncoronel Date: Mon, 10 Aug 2026 16:17:09 -0300 Subject: [PATCH] Revert "feat: enhance variant attribute handling across components for improved selection and display" This reverts commit 56f602d876951bbe1ba908bc0bcc26ec8b3c619e. --- .../store-layout/store-layout.component.ts | 11 +-- src/app/core/services/cart/cart.interface.ts | 4 +- .../services/catalog/catalog.interface.ts | 15 +--- .../product-attribute-selector.component.ts | 7 +- ...uct-vertical-with-cart-card.component.html | 4 +- ...-vertical-with-cart-card.component.spec.ts | 18 ---- ...oduct-vertical-with-cart-card.component.ts | 82 +++---------------- .../variant-selector.component.spec.ts | 18 ---- .../variant-selector.component.ts | 67 ++++----------- 9 files changed, 35 insertions(+), 191 deletions(-) diff --git a/src/app/core/layout/store-layout/store-layout.component.ts b/src/app/core/layout/store-layout/store-layout.component.ts index 7e69a81..98810f6 100644 --- a/src/app/core/layout/store-layout/store-layout.component.ts +++ b/src/app/core/layout/store-layout/store-layout.component.ts @@ -12,7 +12,6 @@ import { findMenu } from '../../services/menu.utils'; import { CheckoutService } from '../../services/checkout.service'; import { ToastService } from '../../services/toast.service'; import { Category } from '../../services/tenant.interface'; -import { VariantAttributeValue } from '../../services/catalog/catalog.interface'; @Component({ selector: 'app-store-layout', @@ -81,7 +80,7 @@ export class StoreLayoutComponent implements OnInit { if (selectedVariant) { attributes = Object.entries(selectedVariant.values).map(([label, value]) => ({ label: this.formatAttributeLabel(label), - value: this.variantAttributeLabel(value), + value: Array.isArray(value) ? value.join(', ') : value, })); } @@ -104,14 +103,6 @@ export class StoreLayoutComponent implements OnInit { return label.charAt(0).toUpperCase() + label.slice(1); } - private variantAttributeLabel(value: VariantAttributeValue): string { - if (typeof value === 'string') return value; - if (Array.isArray(value)) { - return value.map((item) => (typeof item === 'string' ? item : item.label)).join(', '); - } - return value.label; - } - protected readonly currentYear = new Date().getFullYear(); protected readonly tenant = this.tenantService.tenant; protected readonly user = this.authService.user; diff --git a/src/app/core/services/cart/cart.interface.ts b/src/app/core/services/cart/cart.interface.ts index bc5b8ea..3145a6c 100644 --- a/src/app/core/services/cart/cart.interface.ts +++ b/src/app/core/services/cart/cart.interface.ts @@ -1,5 +1,3 @@ -import { VariantAttributeValue } from '../catalog/catalog.interface'; - export interface CartItemProduct { nombre: string; imagen: string | null; @@ -10,7 +8,7 @@ export interface CartItemVariant { id: number; precio: string; stock_tecnico: number | null; - values: Record; + values: Record; } export interface CartItem { diff --git a/src/app/core/services/catalog/catalog.interface.ts b/src/app/core/services/catalog/catalog.interface.ts index 663c398..913546c 100644 --- a/src/app/core/services/catalog/catalog.interface.ts +++ b/src/app/core/services/catalog/catalog.interface.ts @@ -47,17 +47,6 @@ export interface ProductAttribute { export type InventoryPolicy = 'tracked' | 'unlimited'; -export interface VariantAttributeOptionValue { - value: string; - label: string; -} - -export type VariantAttributeValue = - | VariantAttributeOptionValue - | VariantAttributeOptionValue[] - | string - | string[]; - export interface CatalogItemVariant { id: number; descripcion?: string | null; @@ -70,7 +59,7 @@ export interface CatalogItemVariant { maximum_use_date?: string | null; effective_minimum_use_date?: string | null; effective_maximum_use_date?: string | null; - values: Record; + values: Record; } export interface SelectedCatalogItemVariant extends CatalogItemVariant { @@ -109,7 +98,7 @@ export interface CatalogFeaturedItemVariant { descripcion?: string | null; precio?: string; stock_tecnico: number | null; - values: Record; + values: Record; } export interface CatalogFeaturedItem { diff --git a/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts b/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts index ca37395..3df839d 100644 --- a/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts +++ b/src/app/features/store/components/product-attribute-selector/product-attribute-selector.component.ts @@ -14,7 +14,6 @@ import { InventoryPolicy, ProductAttribute, ProductAttributeOption, - VariantAttributeValue, } from '../../../../core/services/catalog/catalog.interface'; @Component({ @@ -199,7 +198,7 @@ export class ProductAttributeSelectorComponent { private getVariantAttributeValues( attribute: ProductAttribute, - variantAttributes: Record, + variantAttributes: Record, ): string[] { const normalizedCodigo = this.normalizeText(attribute.codigo); const normalizedNombre = this.normalizeText(attribute.nombre); @@ -208,9 +207,7 @@ export class ProductAttributeSelectorComponent { const normalizedKey = this.normalizeText(key); if (normalizedKey === normalizedCodigo || normalizedKey === normalizedNombre) { - return (Array.isArray(value) ? value : [value]).map((item) => - this.normalizeText(typeof item === 'string' ? item : item.value), - ); + return (Array.isArray(value) ? value : [value]).map((item) => this.normalizeText(item)); } } diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html index ec3531d..267dd4b 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.html @@ -28,8 +28,8 @@ [ngModel]="selectedValues()[selector.key]" (ngModelChange)="onVariantValueChange(selector.key, $event)" > - @for (option of selector.options; track option.key) { - + @for (option of selector.options; track option) { + } } diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts index f5db669..ef6fdca 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.spec.ts @@ -136,24 +136,6 @@ describe('ProductVerticalWithCartCardComponent', () => { expect(selectors?.querySelector('app-quantity-selector')).toBeNull(); }); - it('uses the variant value for selection and renders its label', async () => { - const fixture = await createComponent(); - fixture.componentRef.setInput('variants', [ - { - id: 10, - values: { event_date: { value: '2', label: '10/10/2026' } }, - }, - ]); - fixture.detectChanges(); - - const option = fixture.nativeElement.querySelector( - '.product-vertical-with-cart-card__variant-select option', - ) as HTMLOptionElement; - - expect(option.textContent?.trim()).toBe('10/10/2026'); - expect((fixture.componentInstance as any).selectedValues()).toEqual({ event_date: '2' }); - }); - it('places all variant selectors together below price and quantity', async () => { const fixture = await createComponent(); fixture.componentRef.setInput('variants', [ diff --git a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts index 19e5dc6..c4a06e7 100644 --- a/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts +++ b/src/app/shared/components/product-vertical-with-cart-card/product-vertical-with-cart-card.component.ts @@ -13,27 +13,18 @@ import { FormsModule } from '@angular/forms'; import { ButtonComponent } from '../button/button.component'; import { QuantitySelectorComponent } from '../quantity-selector/quantity-selector.component'; -import { VariantAttributeValue } from '../../../core/services/catalog/catalog.interface'; - -type VariantSelectionValue = string | string[]; export interface VerticalCartVariant { id: number; descripcion?: string | null; precio?: string | number; - values: Record; -} - -interface VariantSelectorOption { - key: string; - value: VariantSelectionValue; - label: string; + values: Record; } interface VariantSelector { key: string; label: string; - options: VariantSelectorOption[]; + options: string[]; } @Component({ @@ -55,7 +46,7 @@ export class ProductVerticalWithCartCardComponent { readonly buy = output<{ quantity: number; variant: number | null }>(); readonly addToCart = output<{ quantity: number; variant: number | null }>(); - protected readonly selectedValues = signal>({}); + protected readonly selectedValues = signal>({}); protected readonly selectedVariantData = computed(() => this.variants().find((variant) => variant.id === this.selectedVariant()), @@ -76,7 +67,13 @@ export class ProductVerticalWithCartCardComponent { return keys.map((key) => ({ key, label: this.formatVariantLabel(key), - options: this.optionsFor(variants, key), + options: Array.from( + new Set( + variants + .map((variant) => variant.values[key]) + .filter((value): value is string => Boolean(value)), + ), + ), })); }); @@ -95,21 +92,17 @@ export class ProductVerticalWithCartCardComponent { const selected = variants.find((variant) => variant.id === this.selectedVariant()) ?? variants[0]; - this.selectedValues.set(this.selectionValues(selected.values)); + this.selectedValues.set({ ...selected.values }); this.selectedVariant.set(selected.id); }); }); } - protected onVariantValueChange(key: string, value: VariantSelectionValue): void { + protected onVariantValueChange(key: string, value: string): void { const values = { ...this.selectedValues(), [key]: value }; const selectors = this.variantSelectors(); const matchingVariant = this.variants().find((variant) => - selectors.every( - (selector) => - this.valueKey(this.selectionValue(variant.values[selector.key])) === - this.valueKey(values[selector.key]), - ), + selectors.every((selector) => variant.values[selector.key] === values[selector.key]), ); this.selectedValues.set(values); @@ -124,55 +117,6 @@ export class ProductVerticalWithCartCardComponent { this.buy.emit({ quantity: this.quantity(), variant: this.selectedVariant() }); } - private optionsFor(variants: VerticalCartVariant[], key: string): VariantSelectorOption[] { - const options = new Map(); - - for (const variant of variants) { - const attributeValue = variant.values[key]; - if (attributeValue === undefined) continue; - - const value = this.selectionValue(attributeValue); - const optionKey = this.valueKey(value); - if (!options.has(optionKey)) { - options.set(optionKey, { - key: optionKey, - value, - label: this.selectionLabel(attributeValue), - }); - } - } - - return Array.from(options.values()); - } - - private selectionValues( - values: Record, - ): Record { - return Object.fromEntries( - Object.entries(values).map(([key, value]) => [key, this.selectionValue(value)]), - ); - } - - private selectionValue(value: VariantAttributeValue): VariantSelectionValue { - if (typeof value === 'string') return value; - if (Array.isArray(value)) { - return value.map((item) => (typeof item === 'string' ? item : item.value)); - } - return value.value; - } - - private selectionLabel(value: VariantAttributeValue): string { - if (typeof value === 'string') return value; - if (Array.isArray(value)) { - return value.map((item) => (typeof item === 'string' ? item : item.label)).join(', '); - } - return value.label; - } - - private valueKey(value: VariantSelectionValue | undefined): string { - return JSON.stringify(value); - } - private formatVariantLabel(key: string): string { const label = key.replace(/[_-]+/g, ' '); return label.charAt(0).toUpperCase() + label.slice(1); diff --git a/src/app/shared/components/variant-selector/variant-selector.component.spec.ts b/src/app/shared/components/variant-selector/variant-selector.component.spec.ts index e495add..2440b05 100644 --- a/src/app/shared/components/variant-selector/variant-selector.component.spec.ts +++ b/src/app/shared/components/variant-selector/variant-selector.component.spec.ts @@ -38,22 +38,4 @@ describe('VariantSelectorComponent', () => { servicio: 'Cena', }); }); - - it('renders labels while matching variants by value', async () => { - await TestBed.configureTestingModule({ - imports: [VariantSelectorComponent], - }).compileComponents(); - const fixture = TestBed.createComponent(VariantSelectorComponent); - fixture.componentRef.setInput('variants', [ - { - value: 1, - values: { event_date: { value: '2', label: '10/10/2026' } }, - }, - ]); - fixture.detectChanges(); - - const option = fixture.nativeElement.querySelector('option') as HTMLOptionElement; - expect(option.textContent?.trim()).toBe('10/10/2026'); - expect((fixture.componentInstance as any).selectedValues()).toEqual({ event_date: '2' }); - }); }); diff --git a/src/app/shared/components/variant-selector/variant-selector.component.ts b/src/app/shared/components/variant-selector/variant-selector.component.ts index 78fa991..0eaaf9b 100644 --- a/src/app/shared/components/variant-selector/variant-selector.component.ts +++ b/src/app/shared/components/variant-selector/variant-selector.component.ts @@ -9,9 +9,8 @@ import { untracked, } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { VariantAttributeValue } from '../../../core/services/catalog/catalog.interface'; -type VariantSelectionValue = string | string[]; +export type VariantAttributeValue = string | string[]; export interface VariantSelectorVariant { value: unknown; @@ -21,7 +20,7 @@ export interface VariantSelectorVariant { interface VariantSelectorOption { key: string; label: string; - value: VariantSelectionValue; + value: VariantAttributeValue; } interface VariantSelectorGroup { @@ -44,7 +43,7 @@ export class VariantSelectorComponent { readonly disabled = input(false); readonly compact = input(false); - protected readonly selectedValues = signal>({}); + protected readonly selectedValues = signal>({}); protected readonly attributeKeys = computed(() => Array.from(new Set(this.variants().flatMap((variant) => Object.keys(variant.values)))), ); @@ -57,10 +56,7 @@ export class VariantSelectorComponent { const previousKeys = keys.slice(0, index); const compatibleVariants = variants.filter((variant) => previousKeys.every((previousKey) => - this.sameValue( - this.selectionValue(variant.values[previousKey]), - selectedValues[previousKey], - ), + this.sameValue(variant.values[previousKey], selectedValues[previousKey]), ), ); @@ -86,13 +82,13 @@ export class VariantSelectorComponent { const selected = variants.find((variant) => Object.is(variant.value, selectedVariant)) ?? variants[0]; - this.selectedValues.set(this.selectionValues(selected.values)); + this.selectedValues.set({ ...selected.values }); if (!Object.is(selected.value, selectedVariant)) this.selectedVariant.set(selected.value); }); }); } - protected onValueChange(key: string, value: VariantSelectionValue): void { + protected onValueChange(key: string, value: VariantAttributeValue): void { const variants = this.variants(); const keys = this.attributeKeys(); const changedIndex = keys.indexOf(key); @@ -103,7 +99,7 @@ export class VariantSelectorComponent { const previousKeys = keys.slice(0, index); const compatibleVariants = variants.filter((variant) => previousKeys.every((previousKey) => - this.sameValue(this.selectionValue(variant.values[previousKey]), values[previousKey]), + this.sameValue(variant.values[previousKey], values[previousKey]), ), ); const options = this.optionsFor(compatibleVariants, currentKey); @@ -117,7 +113,7 @@ export class VariantSelectorComponent { const matchingVariant = variants.find((variant) => keys.every((attributeKey) => - this.sameValue(this.selectionValue(variant.values[attributeKey]), values[attributeKey]), + this.sameValue(variant.values[attributeKey], values[attributeKey]), ), ); @@ -129,19 +125,14 @@ export class VariantSelectorComponent { const options = new Map(); for (const variant of variants) { - const attributeValue = variant.values[key]; - if (attributeValue === undefined) continue; - - const value = this.selectionValue(attributeValue); - if (value === undefined || value === '' || (Array.isArray(value) && value.length === 0)) { - continue; - } + const value = variant.values[key]; + if (value === undefined || value === '') continue; const optionKey = this.valueKey(value); if (!options.has(optionKey)) { options.set(optionKey, { key: optionKey, - label: this.selectionLabel(attributeValue), + label: Array.isArray(value) ? value.join(', ') : value, value, }); } @@ -151,48 +142,18 @@ export class VariantSelectorComponent { } private sameValue( - left: VariantSelectionValue | undefined, - right: VariantSelectionValue | undefined, + left: VariantAttributeValue | undefined, + right: VariantAttributeValue | undefined, ): boolean { return ( left !== undefined && right !== undefined && this.valueKey(left) === this.valueKey(right) ); } - private valueKey(value: VariantSelectionValue): string { + private valueKey(value: VariantAttributeValue): string { return JSON.stringify(value); } - private selectionValues( - values: Record, - ): Record { - return Object.fromEntries( - Object.entries(values).flatMap(([key, value]) => { - const selectionValue = this.selectionValue(value); - return selectionValue === undefined ? [] : [[key, selectionValue]]; - }), - ); - } - - private selectionValue( - value: VariantAttributeValue | undefined, - ): VariantSelectionValue | undefined { - if (value === undefined) return undefined; - if (typeof value === 'string') return value; - if (Array.isArray(value)) { - return value.map((item) => (typeof item === 'string' ? item : item.value)); - } - return value.value; - } - - private selectionLabel(value: VariantAttributeValue): string { - if (typeof value === 'string') return value; - if (Array.isArray(value)) { - return value.map((item) => (typeof item === 'string' ? item : item.label)).join(', '); - } - return value.label; - } - private formatVariantLabel(key: string): string { const label = key.replace(/[_-]+/g, ' '); return label.charAt(0).toUpperCase() + label.slice(1);