feat: update cart service and components to handle productVariantId, improve item removal and addition feedback

This commit is contained in:
2026-07-02 11:00:40 -03:00
parent c9d8265790
commit d31cb65d65
6 changed files with 43 additions and 14 deletions

View File

@@ -54,6 +54,7 @@ export class StoreLayoutComponent implements OnInit {
} }
return { return {
productVariantId: item.product_variant_id,
imageUrl: item.product?.imagen ?? null, imageUrl: item.product?.imagen ?? null,
product, product,
originalPrice: null, originalPrice: null,

View File

@@ -75,8 +75,8 @@ describe('CartService', () => {
}); });
it('should add item and update signal', () => { it('should add item and update signal', () => {
service.addItem(10, 2).subscribe((cart) => { service.addItem(10, 2).subscribe((res) => {
expect(cart).toEqual(mockCart); expect(res.data).toEqual(mockCart);
expect(service.cart()).toEqual(mockCart); expect(service.cart()).toEqual(mockCart);
}); });

View File

@@ -31,7 +31,7 @@ export class CartService {
); );
} }
addItem(productVariantId: number, cantidad: number): Observable<Cart> { addItem(productVariantId: number, cantidad: number): Observable<ApiResponse<Cart>> {
return this.http return this.http
.post<ApiResponse<Cart>>( .post<ApiResponse<Cart>>(
`${this.tenantApiUrl}/cart/items`, `${this.tenantApiUrl}/cart/items`,
@@ -39,8 +39,7 @@ export class CartService {
{ withCredentials: true } { withCredentials: true }
) )
.pipe( .pipe(
map((response) => response.data), tap((response) => this.cartState.set(response.data))
tap((cart) => this.cartState.set(cart))
); );
} }

View File

@@ -59,7 +59,7 @@ describe('ProductDetailPageComponent', () => {
navigate: vi.fn() navigate: vi.fn()
}; };
cartServiceStub = { cartServiceStub = {
addItem: vi.fn().mockReturnValue(of({})) addItem: vi.fn().mockReturnValue(of({ message: 'Producto agregado al carrito' }))
}; };
toastServiceStub = { toastServiceStub = {
success: vi.fn(), success: vi.fn(),
@@ -277,6 +277,8 @@ describe('ProductDetailPageComponent', () => {
it('keeps quantity at a minimum of one and increments locally', async () => { it('keeps quantity at a minimum of one and increments locally', async () => {
await configureTestingModule(); await configureTestingModule();
const fixture = TestBed.createComponent(ProductDetailPageComponent); const fixture = TestBed.createComponent(ProductDetailPageComponent);
fixture.detectChanges();
fixture.componentInstance['selectedVariant'].set({ fixture.componentInstance['selectedVariant'].set({
variant_id: 1, variant_id: 1,
cantidad_maxima: 10, cantidad_maxima: 10,

View File

@@ -230,8 +230,9 @@ export class ProductDetailPageComponent implements OnInit, OnDestroy {
this.addingToCart.set(true); this.addingToCart.set(true);
this.cartService.addItem(variant.variant_id, this.quantity()).subscribe({ this.cartService.addItem(variant.variant_id, this.quantity()).subscribe({
next: () => { next: (res) => {
this.toastService.success('Producto agregado al carrito'); const msg = res.message || 'Producto agregado al carrito';
this.toastService.success(msg);
this.addingToCart.set(false); this.addingToCart.set(false);
}, },
error: (err: HttpErrorResponse) => { error: (err: HttpErrorResponse) => {

View File

@@ -8,6 +8,7 @@ import { CartService } from '../../../core/services/cart/cart.service';
import { ToastService } from '../../../core/services/toast.service'; import { ToastService } from '../../../core/services/toast.service';
export interface CartItemMock { export interface CartItemMock {
productVariantId?: number;
imageUrl: string | null; imageUrl: string | null;
product: string; product: string;
originalPrice: number | null; originalPrice: number | null;
@@ -68,19 +69,29 @@ export class CartComponent {
} }
protected onItemQuantityChange(index: number, newQuantity: number): void { protected onItemQuantityChange(index: number, newQuantity: number): void {
const item = this.cartService.cart()?.items[index]; const mockItem = this.items()[index];
if (item) { const productVariantId = mockItem?.productVariantId;
if (productVariantId) {
this.quantityUpdates$.next({ this.quantityUpdates$.next({
productVariantId: item.product_variant_id, productVariantId,
quantity: newQuantity quantity: newQuantity
}); });
} else {
const item = this.cartService.cart()?.items[index];
if (item) {
this.quantityUpdates$.next({
productVariantId: item.product_variant_id,
quantity: newQuantity
});
}
} }
} }
protected onItemRemove(index: number): void { protected onItemRemove(index: number): void {
const item = this.cartService.cart()?.items[index]; const mockItem = this.items()[index];
if (item) { const productVariantId = mockItem?.productVariantId;
this.cartService.removeItem(item.product_variant_id).subscribe({ if (productVariantId) {
this.cartService.removeItem(productVariantId).subscribe({
next: (res) => { next: (res) => {
const msg = res.message || 'Producto eliminado del carrito.'; const msg = res.message || 'Producto eliminado del carrito.';
this.toastService.success(msg); this.toastService.success(msg);
@@ -91,6 +102,21 @@ export class CartComponent {
this.toastService.danger(msg); this.toastService.danger(msg);
} }
}); });
} else {
const item = this.cartService.cart()?.items[index];
if (item) {
this.cartService.removeItem(item.product_variant_id).subscribe({
next: (res) => {
const msg = res.message || 'Producto eliminado del carrito.';
this.toastService.success(msg);
},
error: (err: HttpErrorResponse) => {
console.error('Error removing item from cart', err);
const msg = err.error?.message || 'Error al eliminar el producto del carrito.';
this.toastService.danger(msg);
}
});
}
} }
} }