@@ -31,6 +45,9 @@
[discountPercentage]="item.discountPercentage"
[attributes]="item.attributes"
[quantity]="getItemQuantity(item)"
+ [readonly]="readonly()"
+ [quantityDisabled]="editingDisabled() || (allowEditing() && !editing())"
+ [showRemove]="allowRemove()"
(quantityChange)="onItemQuantityChange(idx, $event)"
(remove)="onItemRemove(idx)"
/>
diff --git a/src/app/shared/components/cart/cart.component.scss b/src/app/shared/components/cart/cart.component.scss
index b80a2c0..f38f1c5 100644
--- a/src/app/shared/components/cart/cart.component.scss
+++ b/src/app/shared/components/cart/cart.component.scss
@@ -21,6 +21,17 @@
color: #a0a0a0;
}
+.cart-header-actions {
+ gap: 14px;
+}
+
+.cart-edit-btn {
+ color: var(--bs-primary);
+ font-size: 11px;
+ font-weight: 500;
+ text-decoration: none;
+}
+
.cart-items-container {
min-height: 0;
gap: 0;
diff --git a/src/app/shared/components/cart/cart.component.spec.ts b/src/app/shared/components/cart/cart.component.spec.ts
index fe8ec93..d7a1d49 100644
--- a/src/app/shared/components/cart/cart.component.spec.ts
+++ b/src/app/shared/components/cart/cart.component.spec.ts
@@ -306,4 +306,105 @@ describe('CartComponent', () => {
vi.useRealTimers();
});
+
+ it('enables quantity editing only while Modificar mode is active', 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('allowEditing', true);
+ const editingChange = vi.fn();
+ fixture.componentInstance.editing.subscribe(editingChange);
+ fixture.detectChanges();
+
+ const editButton = fixture.debugElement.query(By.css('.cart-edit-btn'));
+ expect(editButton.nativeElement.textContent.trim()).toBe('Modificar');
+ expect(
+ fixture.debugElement.query(By.css('app-cart-item')).componentInstance.quantityDisabled(),
+ ).toBe(true);
+
+ editButton.nativeElement.click();
+ fixture.detectChanges();
+
+ expect(editButton.nativeElement.textContent.trim()).toBe('Listo');
+ expect(
+ fixture.debugElement.query(By.css('app-cart-item')).componentInstance.quantityDisabled(),
+ ).toBe(false);
+ expect(editingChange).toHaveBeenCalledWith(true);
+
+ editButton.nativeElement.click();
+ fixture.detectChanges();
+
+ expect(editButton.nativeElement.textContent.trim()).toBe('Modificar');
+ expect(editingChange).toHaveBeenLastCalledWith(false);
+ });
+
+ it('allows editing directly when the optional Modificar toggle is disabled', 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.detectChanges();
+
+ expect(fixture.debugElement.query(By.css('.cart-edit-btn'))).toBeNull();
+ expect(
+ fixture.debugElement.query(By.css('app-cart-item')).componentInstance.quantityDisabled(),
+ ).toBe(false);
+ });
});
diff --git a/src/app/shared/components/cart/cart.component.ts b/src/app/shared/components/cart/cart.component.ts
index f68b331..7a78e16 100644
--- a/src/app/shared/components/cart/cart.component.ts
+++ b/src/app/shared/components/cart/cart.component.ts
@@ -4,6 +4,7 @@ import {
computed,
inject,
input,
+ model,
output,
signal,
} from '@angular/core';
@@ -48,11 +49,21 @@ export class CartComponent {
readonly discount = input
(0);
readonly total = input(0);
readonly backgroundColor = input('#ffffff');
+ readonly readonly = input(false);
+ readonly allowEditing = input(false);
+ readonly allowRemove = input(true);
+ readonly persistQuantityChanges = input(true);
+ readonly editingDisabled = input(false);
+ readonly editing = model(false);
readonly closed = output();
+ readonly itemQuantityChange = output<{
+ item: CartItemMock;
+ index: number;
+ quantity: number;
+ }>();
protected readonly quantityOverrides = signal>({});
-
constructor() {
this.quantityUpdates$
.pipe(
@@ -103,6 +114,20 @@ export class CartComponent {
protected onItemQuantityChange(index: number, newQuantity: number): void {
const mockItem = this.items()[index];
+ if (!mockItem) {
+ return;
+ }
+
+ this.itemQuantityChange.emit({
+ item: mockItem,
+ index,
+ quantity: newQuantity,
+ });
+
+ if (!this.persistQuantityChanges()) {
+ return;
+ }
+
const cartItemId = mockItem?.cartItemId;
if (cartItemId) {
this.quantityOverrides.update((overrides) => ({
@@ -153,6 +178,15 @@ export class CartComponent {
protected readonly formattedDiscount = computed(() => this.formatCurrency(this.discount()));
protected readonly formattedTotal = computed(() => this.formatCurrency(this.total()));
+ protected toggleEditing(): void {
+ if (this.editingDisabled()) {
+ return;
+ }
+
+ const editing = !this.editing();
+ this.editing.set(editing);
+ }
+
private formatCurrency(value: number): string {
const rounded = Math.round(value);
const parts = rounded.toString().split('.');
diff --git a/src/app/shared/components/quantity-selector/quantity-selector.component.html b/src/app/shared/components/quantity-selector/quantity-selector.component.html
index f5aeabe..4bd1383 100644
--- a/src/app/shared/components/quantity-selector/quantity-selector.component.html
+++ b/src/app/shared/components/quantity-selector/quantity-selector.component.html
@@ -1,13 +1,15 @@