diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.html b/src/app/features/store/pages/checkout-page/checkout-page.component.html index 8fb9344..50990be 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.html +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.html @@ -5,12 +5,7 @@ } @else {
-
+
- @if (isEditingItems()) { -
-

Terminá de modificar las cantidades para continuar con el pago.

-
- } -
diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.scss b/src/app/features/store/pages/checkout-page/checkout-page.component.scss index 04b30ae..33c677c 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.scss +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.scss @@ -11,8 +11,7 @@ order: 1; } - .checkout-page__stepper-col, - .checkout-page__editing-notice { + .checkout-page__stepper-col { order: 2; } } @@ -21,27 +20,6 @@ min-width: 0; border-radius: 4px; min-height: 420px; - - &--editing { - display: none; - } - } - - &__editing-notice { - display: grid; - min-height: 420px; - place-items: center; - padding: 2rem; - border-radius: 4px; - background: #f5f5f5; - color: #666666; - text-align: center; - - p { - max-width: 360px; - margin: 0; - font-size: 14px; - } } &__cart-col { diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts b/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts index c874f2e..2d312c5 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts @@ -10,9 +10,11 @@ import { AuthService } from '../../../../core/services/auth/auth.service'; import { CartService } from '../../../../core/services/cart/cart.service'; import { CheckoutService } from '../../../../core/services/checkout.service'; import { CatalogService } from '../../../../core/services/catalog/catalog.service'; +import { GlobalLoadingService } from '../../../../core/services/global-loading/global-loading.service'; import { TenantService } from '../../../../core/services/tenant.service'; import { ToastService } from '../../../../core/services/toast.service'; import { CartEditingPolicy } from '../../../../core/services/tenant.interface'; +import { ToastService } from '../../../../core/services/toast.service'; import { CheckoutPageComponent } from './checkout-page.component'; describe('CheckoutPageComponent payment validation', () => { @@ -22,7 +24,6 @@ describe('CheckoutPageComponent payment validation', () => { updateItemQuantity: ReturnType; updateItemVariant: ReturnType; removeItem: ReturnType; - prepareItemEditing: ReturnType; cancelPurchase: ReturnType; generatePaymentIntent: ReturnType; submitPurchaseForReview: ReturnType; @@ -37,6 +38,10 @@ describe('CheckoutPageComponent payment validation', () => { }; let routerStub: { navigate: ReturnType }; let toastServiceStub: { danger: ReturnType }; + let globalLoadingServiceStub: { + start: ReturnType; + stop: ReturnType; + }; let routeQueryParamMap: ReturnType; let authUserState: ReturnType; let tenantState: ReturnType< @@ -68,7 +73,6 @@ describe('CheckoutPageComponent payment validation', () => { updateItemQuantity: vi.fn(), updateItemVariant: vi.fn(), removeItem: vi.fn(), - prepareItemEditing: vi.fn(), cancelPurchase: vi.fn().mockResolvedValue({ status: 'cancelled' }), generatePaymentIntent: vi.fn().mockResolvedValue({ qr_data: { qr_code: 'qr-value' }, @@ -101,6 +105,7 @@ describe('CheckoutPageComponent payment validation', () => { }); routerStub = { navigate: vi.fn() }; toastServiceStub = { danger: vi.fn() }; + globalLoadingServiceStub = { start: vi.fn(), stop: vi.fn() }; routeQueryParamMap = convertToParamMap({}); authUserState = signal(null); tenantState = signal({ @@ -122,6 +127,7 @@ describe('CheckoutPageComponent payment validation', () => { { provide: CartService, useValue: cartServiceStub }, { provide: TenantService, useValue: { tenant: tenantState } }, { provide: AuthService, useValue: { user: authUserState } }, + { provide: GlobalLoadingService, useValue: globalLoadingServiceStub }, { provide: ToastService, useValue: toastServiceStub }, { provide: ActivatedRoute, @@ -477,83 +483,30 @@ describe('CheckoutPageComponent payment validation', () => { expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout/status', 25]); }); - it('updates a purchase item while editing and refreshes checkout totals', async () => { - const updatedPurchase = { - id: 25, - items: [], - subtotal: '300.00', - total: '300.00', - }; - checkoutServiceStub.updateItemQuantity.mockResolvedValue(updatedPurchase); - const { component } = createComponent(); - component.isEditingItems.set(true); - - await component.onPurchaseItemQuantityChange({ - item: { - cartItemId: 91, - imageUrl: null, - product: 'Remera', - originalPrice: null, - discountedPrice: 100, - discountPercentage: null, - attributes: [], - quantity: 2, - }, - quantity: 3, + it('shows the API error in a toast when restoring the cart fails', async () => { + const message = 'La compra venció. Iniciá una nueva compra.'; + checkoutServiceStub.cancelPurchase.mockRejectedValue({ + error: { code: 'purchase.expired', message }, }); + const { component } = createComponent(); - expect(checkoutServiceStub.updateItemQuantity).toHaveBeenCalledWith( - 'tenant-test', - 25, - 91, - 3, - null, - 'purchase', + await component.onModifyPurchase(); + + expect(toastServiceStub.danger).toHaveBeenCalledWith(message); + expect(routerStub.navigate).not.toHaveBeenCalled(); + expect(globalLoadingServiceStub.start).toHaveBeenCalledOnce(); + expect(globalLoadingServiceStub.stop).toHaveBeenCalledOnce(); + expect(component.isRestoringCart()).toBe(false); + }); + + it('restores the previous cart and opens it when Modificar is clicked', async () => { + let finishNavigation!: (navigated: boolean) => void; + routerStub.navigate.mockReturnValue( + new Promise((resolve) => { + finishNavigation = resolve; + }), ); - expect(component.createdPurchase()).toBe(updatedPurchase); - expect(component.isUpdatingItem()).toBe(false); - }); - - it('keeps the payment step selected while editing and regenerates payment afterward', async () => { - const editablePurchase = { - id: 25, - status: 'created', - items: [], - subtotal: '100.00', - total: '100.00', - }; - checkoutServiceStub.prepareItemEditing.mockResolvedValue(editablePurchase); const { component } = createComponent(); - component.stepper = { currentStepIndex: signal(1) }; - const selectPaymentMethod = vi - .spyOn(component, 'selectPaymentMethod') - .mockResolvedValue(undefined); - - await component.onEditingItemsChange(true); - - expect(checkoutServiceStub.prepareItemEditing).toHaveBeenCalledWith('tenant-test', 25); - expect(component.isEditingItems()).toBe(true); - expect(component.createdPurchase()).toBe(editablePurchase); - - await component.onEditingItemsChange(false); - - expect(component.stepper.currentStepIndex()).toBe(1); - expect(selectPaymentMethod).toHaveBeenCalledWith('qr'); - expect(component.isEditingItems()).toBe(false); - }); - - it('shows Modificar and returns to the home cart when checkout editing is disabled', async () => { - tenantState.set({ - codigo: 'tenant-test', - checkout_editing_policy: { - code: 'disabled', - allow_modify: false, - allow_delete: false, - allow_update_quantity: false, - allow_update_variant: false, - }, - }); - const { fixture, component } = createComponent(); component.createdPurchase.set({ id: 25, status: 'created', @@ -577,23 +530,28 @@ describe('CheckoutPageComponent payment validation', () => { subtotal: '200.00', total: '200.00', }); - fixture.detectChanges(); + const modification = component.onModifyPurchase(); + await Promise.resolve(); + await Promise.resolve(); - const modifyButton = (fixture.nativeElement as HTMLElement).querySelector('.cart-edit-btn'); - expect(modifyButton?.textContent?.trim()).toBe('Modificar'); - expect( - (fixture.nativeElement as HTMLElement).querySelector('app-quantity-selector'), - ).toBeNull(); - - await component.onEditingItemsChange(true); - - expect(component.canUpdateCartQuantity()).toBe(false); - expect(component.canModifyCart()).toBe(false); - expect(component.isEditingItems()).toBe(false); - expect(checkoutServiceStub.prepareItemEditing).not.toHaveBeenCalled(); + expect(checkoutServiceStub.cancelPurchase).toHaveBeenCalledWith('tenant-test', 25); + expect(globalLoadingServiceStub.start).toHaveBeenCalledOnce(); + expect(globalLoadingServiceStub.stop).not.toHaveBeenCalled(); + expect(cartServiceStub.loadCart).toHaveBeenCalledOnce(); + expect(component.createdPurchaseId()).toBeNull(); + expect(component.createdPurchase()).toBeNull(); expect(routerStub.navigate).toHaveBeenCalledWith(['/'], { queryParams: { openCart: true }, }); + + finishNavigation(true); + await modification; + + expect(globalLoadingServiceStub.stop).toHaveBeenCalledOnce(); + + await component.canDeactivate(); + + expect(cartServiceStub.loadCart).toHaveBeenCalledOnce(); }); it('updates customer data on the existing purchase before payment', async () => { diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.ts b/src/app/features/store/pages/checkout-page/checkout-page.component.ts index 5524da0..aedcefa 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.ts +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.ts @@ -21,8 +21,8 @@ import { } from '../../../../core/services/checkout.service'; import { AuthService } from '../../../../core/services/auth/auth.service'; import { CartService } from '../../../../core/services/cart/cart.service'; +import { GlobalLoadingService } from '../../../../core/services/global-loading/global-loading.service'; import { ToastService } from '../../../../core/services/toast.service'; -import { BankAccount } from '../../../../core/services/tenant.interface'; import { CartComponent, CartItemMock } from '../../../../shared/components/cart/cart.component'; import { StepComponent } from '../../../../shared/components/stepper/step.component'; import { StepperComponent } from '../../../../shared/components/stepper/stepper.component'; @@ -66,6 +66,8 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { private readonly checkoutService = inject(CheckoutService); private readonly authService = inject(AuthService); private readonly cartService = inject(CartService); + private readonly globalLoadingService = inject(GlobalLoadingService); + private readonly toastService = inject(ToastService); private readonly qrPollingIntervalMs = 5_000; private readonly toastService = inject(ToastService); private readonly qrPollingMaxAttempts = 9; @@ -93,19 +95,6 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { protected readonly createdPurchase = signal(null); protected readonly isLoadingPurchase = signal(true); protected readonly checkoutStepIndex = signal(0); - protected readonly canUpdateCartQuantity = computed( - () => this.tenantService.tenant()?.checkout_editing_policy?.allow_update_quantity ?? false, - ); - protected readonly canUpdateCartVariant = computed( - () => this.tenantService.tenant()?.checkout_editing_policy?.allow_update_variant ?? false, - ); - protected readonly canDeleteCartItems = computed( - () => this.tenantService.tenant()?.checkout_editing_policy?.allow_delete ?? false, - ); - protected readonly canModifyCart = computed( - () => this.tenantService.tenant()?.checkout_editing_policy?.allow_modify ?? false, - ); - protected readonly cartSubtotal = computed(() => { const purchase = this.createdPurchase(); return purchase ? parseFloat(purchase.subtotal) : 0; @@ -134,9 +123,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { protected readonly transferDni = signal(''); protected readonly isUpdatingPurchase = signal(false); - protected readonly isEditingItems = signal(false); - protected readonly isUpdatingItem = signal(false); - protected readonly isPreparingItemEdit = signal(false); + protected readonly isRestoringCart = signal(false); protected readonly createdPurchaseId = signal(null); protected readonly isGeneratingIntent = signal(false); protected readonly isPaymentLoading = computed(() => this.isGeneratingIntent()); @@ -202,187 +189,39 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { }; } - protected async onEditingItemsChange(editing: boolean): Promise { - if (this.isUpdatingItem() || this.isPreparingItemEdit()) { + protected async onModifyPurchase(): Promise { + if (this.isRestoringCart()) { return; } +<<<<<<< HEAD if (editing && !this.canModifyCart()) { const tenant = this.tenantService.tenant(); - const purchaseId = this.createdPurchaseId(); - - if (!tenant || !purchaseId) { - return; - } - - this.isPreparingItemEdit.set(true); - try { - await this.checkoutService.cancelPurchase(tenant.codigo, purchaseId); - this.createdPurchaseId.set(null); - this.createdPurchase.set(null); - await firstValueFrom(this.cartService.loadCart()); - await this.router.navigate(['/'], { - queryParams: { openCart: true }, - }); - } catch (error) { - this.handleCheckoutError(error, 'No se pudo restaurar el carrito para editarlo.'); - } finally { - this.isPreparingItemEdit.set(false); - } - return; } - if (!editing) { - this.isEditingItems.set(false); - - if (this.stepper?.currentStepIndex() === 1) { - void this.selectPaymentMethod(this.selectedPaymentMethod()); - } - - return; - } - - this.isEditingItems.set(true); - this.stopQrPolling(); - this.stopTransferPolling(); - this.qrData.set(null); - this.qrPaymentStatus.set('idle'); - this.transferAccount.set(null); - this.transferValidationStatus.set('idle'); - - const tenant = this.tenantService.tenant(); - const purchaseId = this.createdPurchaseId(); - - if (!tenant || !purchaseId) { - this.isEditingItems.set(false); - return; - } - - this.isPreparingItemEdit.set(true); + this.isRestoringCart.set(true); + this.globalLoadingService.start(); try { - const purchase = await this.checkoutService.prepareItemEditing(tenant.codigo, purchaseId); - this.createdPurchase.set(purchase); + await this.checkoutService.cancelPurchase(tenant.codigo, purchaseId); + this.createdPurchaseId.set(null); + this.createdPurchase.set(null); + await firstValueFrom(this.cartService.loadCart()); + this.navigationStarted = true; + await this.router.navigate(['/'], { + queryParams: { openCart: true }, + }); } catch (error) { - this.handleCheckoutError(error, 'No se pudo preparar la compra para editarla.'); - this.isEditingItems.set(false); + console.error('Failed to restore cart for editing:', error); + this.showRequestError(error, 'No se pudo recuperar el carrito para modificar la compra.'); } finally { - this.isPreparingItemEdit.set(false); + this.globalLoadingService.stop(); + this.isRestoringCart.set(false); +>>>>>>> feature/restore-cart-on-modify } } - - protected async onPurchaseItemQuantityChange(event: { - item: CartItemMock; - quantity: number; - }): Promise { - const tenant = this.tenantService.tenant(); - const purchaseId = this.createdPurchaseId(); - const itemId = event.item.cartItemId; - - if ( - !this.canUpdateCartQuantity() || - !tenant || - !purchaseId || - !itemId || - this.isUpdatingItem() || - !this.isEditingItems() - ) { - return; - } - - this.isUpdatingItem.set(true); - try { - const purchase = await this.checkoutService.updateItemQuantity( - tenant.codigo, - purchaseId, - itemId, - event.quantity, - this.createdPurchase()?.cart_id ?? null, - this.createdPurchase()?.items_source ?? 'purchase', - ); - this.createdPurchase.set(purchase); - } catch (error) { - this.handleCheckoutError(error, 'No se pudo actualizar la cantidad del producto.'); - } finally { - this.isUpdatingItem.set(false); - } - } - - protected async onPurchaseItemVariantChange(event: { - item: CartItemMock; - variantId: number; - }): Promise { - const tenant = this.tenantService.tenant(); - const purchase = this.createdPurchase(); - const itemId = event.item.cartItemId; - - if ( - !this.canUpdateCartVariant() || - !tenant || - !purchase || - !itemId || - this.isUpdatingItem() || - !this.isEditingItems() - ) { - return; - } - - this.isUpdatingItem.set(true); - try { - this.createdPurchase.set( - await this.checkoutService.updateItemVariant( - tenant.codigo, - purchase.id, - itemId, - event.variantId, - event.item.quantity, - purchase.cart_id, - purchase.items_source, - ), - ); - } catch (error) { - this.handleCheckoutError(error, 'No se pudo actualizar la variante del producto.'); - } finally { - this.isUpdatingItem.set(false); - } - } - - protected async onPurchaseItemRemove(event: { item: CartItemMock }): Promise { - const tenant = this.tenantService.tenant(); - const purchase = this.createdPurchase(); - const itemId = event.item.cartItemId; - - if ( - !this.canDeleteCartItems() || - !tenant || - !purchase || - !itemId || - this.isUpdatingItem() || - !this.isEditingItems() - ) { - return; - } - - this.isUpdatingItem.set(true); - try { - this.createdPurchase.set( - await this.checkoutService.removeItem( - tenant.codigo, - purchase.id, - itemId, - purchase.cart_id, - purchase.items_source, - ), - ); - } catch (error) { - this.handleCheckoutError(error, 'No se pudo eliminar el producto de la compra.'); - } finally { - this.isUpdatingItem.set(false); - } - } - protected async onStep1Continue(): Promise { - if (this.form.invalid || this.isUpdatingPurchase() || this.isEditingItems()) return; + if (this.form.invalid || this.isUpdatingPurchase()) return; const tenant = this.tenantService.tenant(); const purchaseId = this.createdPurchaseId(); @@ -405,7 +244,8 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { // Auto trigger intent for default option void this.selectPaymentMethod(this.selectedPaymentMethod()); } catch (error) { - this.handleCheckoutError(error, 'No se pudieron actualizar los datos de la compra.'); + console.error('Failed to create purchase:', error); + this.showRequestError(error, 'No se pudieron actualizar los datos de la compra.'); } finally { this.isUpdatingPurchase.set(false); } @@ -431,13 +271,14 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { await firstValueFrom(this.cartService.loadCart()); } catch (error) { console.error('Failed to load the active cart after leaving checkout:', error); + this.showRequestError(error, 'No se pudo cargar el carrito.'); } return true; } protected async selectPaymentMethod(method: PaymentMethod): Promise { - if (this.navigationStarted || this.isEditingItems()) { + if (this.navigationStarted) { return; } @@ -481,17 +322,14 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { this.startQrPolling(); } } catch (error) { - this.handleCheckoutError(error, 'No se pudo generar la intenci\u00f3n de pago.'); + console.error('Failed to generate payment intent:', error); + this.showRequestError(error, 'No se pudo generar el pago.'); } finally { this.isGeneratingIntent.set(false); } } protected async generateTransferIntent(dni: string): Promise { - if (this.isEditingItems()) { - return; - } - const purchaseId = this.createdPurchaseId(); const tenant = this.tenantService.tenant(); @@ -518,7 +356,8 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { }); } } catch (error) { - this.handleCheckoutError(error, 'No se pudo generar la intenci\u00f3n de transferencia.'); + console.error('Failed to generate transfer payment intent:', error); + this.showRequestError(error, 'No se pudo generar el pago por transferencia.'); } finally { this.isGeneratingIntent.set(false); } @@ -549,7 +388,6 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { !purchaseId || !tenant || this.navigationStarted || - this.isEditingItems() || this.transferValidationStatus() === 'checking' ) { return; @@ -571,16 +409,10 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { } if (purchase.status === 'paid') { - this.navigateToPurchaseStatus(purchaseId); - return; - } this.scheduleTransferPoll(runId); - } catch (error) { - const expired = this.handleCheckoutError( - error, - 'No se pudo enviar la compra a revisi\u00f3n.', - ); + console.error('Failed to submit transfer payment for review:', error); + this.showRequestError(error, 'No se pudo enviar el pago para su validación.'); if (!expired && runId === this.transferPollingRunId) { this.transferValidationStatus.set('error'); @@ -811,57 +643,21 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { } } catch (error) { console.error('Failed to load purchase:', error); + this.showRequestError(error, 'No se pudo cargar la compra.'); void this.router.navigate(['/']); } } - private handleCheckoutError(error: unknown, fallbackMessage: string): boolean { - if (!(error instanceof HttpErrorResponse)) { - this.toastService.danger(fallbackMessage); + private showRequestError(error: unknown, fallbackMessage: string): void { + const payload = + typeof error === 'object' && error !== null && 'error' in error + ? (error as { error?: { message?: unknown } }).error + : undefined; + const message = + typeof payload?.message === 'string' && payload.message.trim() + ? payload.message + : fallbackMessage; - return false; - } - - const response = error.error as ApiErrorResponse | null; - - if (response?.code === 'purchase.expired' || this.hasExpiredPurchase()) { - this.navigationStarted = true; - this.stopQrPolling(); - this.stopTransferPolling(); - this.toastService.danger( - response?.code === 'purchase.expired' && response.message - ? response.message - : 'La compra venci\u00f3. Inici\u00e1 una nueva compra.', - ); - void this.router.navigate(['/']); - - return true; - } - - const validationMessage = response?.errors - ? Object.values(response.errors).flat().find(Boolean) - : undefined; - - this.toastService.danger(validationMessage ?? response?.message ?? fallbackMessage); - - return false; - } - - private hasExpiredPurchase(): boolean { - const purchase = this.createdPurchase(); - - if (!purchase) { - return false; - } - - if (purchase.status === 'expired') { - return true; - } - - if (!purchase.expires_at) { - return false; - } - - return Date.parse(purchase.expires_at) <= Date.now(); + this.toastService.danger(message); } } diff --git a/src/app/shared/components/cart/cart.component.html b/src/app/shared/components/cart/cart.component.html index 715f1de..66dab93 100644 --- a/src/app/shared/components/cart/cart.component.html +++ b/src/app/shared/components/cart/cart.component.html @@ -9,17 +9,17 @@ @if ( allowModify() && (!readonly() || showModifyWhenReadonly()) && - requireEditingMode() && + (requireEditingMode() || modifyAsAction()) && items().length > 0 ) { } diff --git a/src/app/shared/components/cart/cart.component.spec.ts b/src/app/shared/components/cart/cart.component.spec.ts index 29dc1ad..7aaf644 100644 --- a/src/app/shared/components/cart/cart.component.spec.ts +++ b/src/app/shared/components/cart/cart.component.spec.ts @@ -367,6 +367,57 @@ describe('CartComponent', () => { expect(editingChange).toHaveBeenLastCalledWith(false); }); + it('emits Modificar as an action without toggling to Listo', async () => { + await TestBed.configureTestingModule({ + imports: [CartComponent], + providers: [ + { + provide: CartService, + useValue: { + cart: signal(null).asReadonly(), + updateItemQuantity: vi.fn(), + removeItem: vi.fn(), + }, + }, + { provide: ModalService, useValue: {} }, + { + provide: ToastService, + useValue: { success: vi.fn(), info: vi.fn(), danger: vi.fn() }, + }, + ], + }).compileComponents(); + + const fixture = TestBed.createComponent(CartComponent); + fixture.componentRef.setInput('items', [ + { + cartItemId: 10, + imageUrl: null, + product: 'Producto de prueba', + originalPrice: null, + discountedPrice: 1000, + discountPercentage: null, + attributes: [], + quantity: 1, + }, + ]); + fixture.componentRef.setInput('readonly', true); + fixture.componentRef.setInput('showModifyWhenReadonly', true); + fixture.componentRef.setInput('modifyAsAction', true); + const modify = vi.fn(); + fixture.componentInstance.modify.subscribe(modify); + fixture.detectChanges(); + + const modifyButton = fixture.debugElement.query(By.css('.cart-edit-btn')); + expect(modifyButton.nativeElement.textContent.trim()).toBe('Modificar'); + + modifyButton.nativeElement.click(); + fixture.detectChanges(); + + expect(modify).toHaveBeenCalledOnce(); + expect(modifyButton.nativeElement.textContent.trim()).toBe('Modificar'); + expect(fixture.componentInstance.editing()).toBe(false); + }); + it('allows editing directly when the optional Modificar toggle is disabled', async () => { await TestBed.configureTestingModule({ imports: [CartComponent], diff --git a/src/app/shared/components/cart/cart.component.ts b/src/app/shared/components/cart/cart.component.ts index 6b907ff..bcd1bb1 100644 --- a/src/app/shared/components/cart/cart.component.ts +++ b/src/app/shared/components/cart/cart.component.ts @@ -56,6 +56,7 @@ export class CartComponent { readonly allowUpdateQuantity = input(true); readonly allowModify = input(true); readonly showModifyWhenReadonly = input(false); + readonly modifyAsAction = input(false); readonly requireEditingMode = input(false); readonly allowUpdateVariant = input(true); readonly allowDelete = input(true); @@ -66,6 +67,7 @@ export class CartComponent { readonly editing = model(false); readonly closed = output(); + readonly modify = output(); readonly itemQuantityChange = output<{ item: CartItemMock; index: number; @@ -257,6 +259,11 @@ export class CartComponent { return; } + if (this.modifyAsAction()) { + this.modify.emit(); + return; + } + const editing = !this.editing(); this.editing.set(editing); }