feature: user can change quantity and remove products

This commit is contained in:
kusowl 2026-03-10 19:07:42 +05:30
parent 9000ea0052
commit 2b88cee10b
6 changed files with 85 additions and 14 deletions

View File

@ -13,3 +13,8 @@ export interface CartModel {
totalPrice: number; totalPrice: number;
items: CartItemModel[]; items: CartItemModel[];
} }
export interface CartItemRequest {
productId: number;
quantity: number;
}

View File

@ -1,8 +1,9 @@
import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { effect, inject, Injectable, signal } from "@angular/core"; import { effect, inject, Injectable, signal } from "@angular/core";
import { API_URL } from "../tokens/api-url-tokens"; import { API_URL } from "../tokens/api-url-tokens";
import { CartModel } from "../models/cart.model"; import { CartItemModel, CartItemRequest, CartModel } from "../models/cart.model";
import { AuthService, AuthState } from "@app/features/auth/services/auth-service"; import { AuthService, AuthState } from "@app/features/auth/services/auth-service";
import { Cart } from "@app/shared/components/cart/cart";
@Injectable({ @Injectable({
providedIn: "root", providedIn: "root",
@ -36,4 +37,12 @@ export class CartService {
}, },
}); });
} }
updateCart(data: CartItemRequest) {
return this.http.patch<CartModel>(this.apiUrl + "/cart", data);
}
removeFromCart(productId: number) {
return this.http.delete<CartModel>(this.apiUrl + "/cart", { body: { productId: productId } });
}
} }

View File

@ -7,17 +7,19 @@
<p class="text-sm truncate max-w-30 mt-2">{{ cartItem.title }}</p> <p class="text-sm truncate max-w-30 mt-2">{{ cartItem.title }}</p>
<div class="mt-auto flex space-x-4 items-center text-gray-600"> <div class="mt-auto flex space-x-4 items-center text-gray-600">
<p class="text-sm">Rs. {{ cartItem.price }} x {{ cartItem.quantity }}</p> <p class="text-sm">Rs. {{ cartItem.price }} x {{ cartItem.quantity }}</p>
<button class="active:scale-80" title="Remove Item"> </div>
</div>
<div class="flex flex-col justify-between">
<div class="mt-2 flex space-x-2 items-center">
<p class="font-medium">Rs. {{ cartItem.subtotal }}</p>
<button (click)="removeProduct()" class="active:scale-80 py-0" title="Remove Item">
<lucide-angular [name]="TrashIcon" class="w-3" /> <lucide-angular [name]="TrashIcon" class="w-3" />
</button> </button>
</div> </div>
</div>
<div class="flex flex-col">
<p class="mt-2 font-medium flex-1">Rs. {{ cartItem.subtotal }}</p>
<div class="flex max-h-7 text-xs text-center text-gray-700"> <div class="flex max-h-7 text-xs text-center text-gray-700">
<button class="btn btn-ghost py-1! px-2 rounded-r-none!">-</button> <button (click)="decrementQty()" class="btn btn-ghost py-1! px-2 rounded-r-none!">-</button>
<p class="py-1 px-2 border-y border-y-gray-300">{{ cartItem.quantity }}</p> <p class="w-7 text-center py-1 px-2 border-y border-y-gray-300">{{ cartItem.quantity }}</p>
<button class="btn py-1! btn-ghost px-2 rounded-l-none!">+</button> <button (click)="incrementQty()" class="btn py-1! btn-ghost px-2 rounded-l-none!">+</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
import { Component, Input } from "@angular/core"; import { Component, EventEmitter, Input, Output, signal } from "@angular/core";
import { CartItemModel } from "@app/core/models/cart.model"; import { CartItemModel, CartItemRequest } from "@app/core/models/cart.model";
import { LucideAngularModule, Trash } from "lucide-angular"; import { LucideAngularModule, Trash } from "lucide-angular";
@Component({ @Component({
@ -10,5 +10,25 @@ import { LucideAngularModule, Trash } from "lucide-angular";
}) })
export class CartItem { export class CartItem {
@Input() cartItem!: CartItemModel; @Input() cartItem!: CartItemModel;
@Output() qtyChangeEvent = new EventEmitter<CartItemRequest>();
@Output() productDeleteEvent = new EventEmitter<number>();
TrashIcon = Trash; TrashIcon = Trash;
incrementQty() {
if (this.cartItem.quantity < 10) {
this.cartItem.quantity += 1;
this.qtyChangeEvent.emit({ productId: this.cartItem.id, quantity: this.cartItem.quantity });
}
}
decrementQty() {
if (this.cartItem.quantity > 1) {
this.cartItem.quantity -= 1;
this.qtyChangeEvent.emit({ productId: this.cartItem.id, quantity: this.cartItem.quantity });
}
}
removeProduct() {
this.productDeleteEvent.emit(this.cartItem.id);
}
} }

View File

@ -4,9 +4,17 @@
} @else if (authService.authState() === AuthState.Loading) { } @else if (authService.authState() === AuthState.Loading) {
<li><a class="block h-full w-full">Loading</a></li> <li><a class="block h-full w-full">Loading</a></li>
} @else { } @else {
<ol> <ol
[class.pointer-events-none]="isLoading()"
[class.opacity-40]="isLoading()"
[class.cursor-block]="isLoading()"
>
@for (item of cart.items; track item.id) { @for (item of cart.items; track item.id) {
<app-cart-item [cartItem]="item" /> <app-cart-item
(qtyChangeEvent)="updateProductQty($event)"
(productDeleteEvent)="removeProduct($event)"
[cartItem]="item"
/>
} }
</ol> </ol>

View File

@ -1,7 +1,9 @@
import { Component, computed, inject, Input } from "@angular/core"; import { Component, computed, inject, Input, signal } from "@angular/core";
import { CartModel } from "@app/core/models/cart.model"; import { CartItemModel, CartItemRequest, CartModel } from "@app/core/models/cart.model";
import { CartItem } from "../cart-item/cart-item"; import { CartItem } from "../cart-item/cart-item";
import { AuthService, AuthState } from "@app/features/auth/services/auth-service"; import { AuthService, AuthState } from "@app/features/auth/services/auth-service";
import { CartService } from "@app/core/services/cart-service";
import { finalize, tap } from "rxjs";
@Component({ @Component({
selector: "app-cart", selector: "app-cart",
@ -12,6 +14,31 @@ import { AuthService, AuthState } from "@app/features/auth/services/auth-service
export class Cart { export class Cart {
@Input() cart!: CartModel; @Input() cart!: CartModel;
isLoading = signal(false);
protected readonly authService = inject(AuthService); protected readonly authService = inject(AuthService);
protected readonly cartService = inject(CartService);
protected readonly AuthState = AuthState; protected readonly AuthState = AuthState;
updateProductQty(cartItem: CartItemRequest) {
this.isLoading.set(true);
this.cartService
.updateCart(cartItem)
.pipe(finalize(() => this.isLoading.set(false)))
.subscribe((cartdata) => {
this.cart = cartdata;
});
}
removeProduct(productId: number) {
this.isLoading.set(true);
this.cartService
.removeFromCart(productId)
.pipe(finalize(() => this.isLoading.set(false)))
.subscribe((cartData) => {
this.cart = cartData;
});
}
} }