feature: add to cart
- make the cart service dependable on BehavorialSubject, migrated from siganls - implement add to cart service
This commit is contained in:
parent
27a04c6458
commit
ad957efcf0
@ -32,7 +32,7 @@
|
||||
style="anchor-name: --anchor-2"
|
||||
>
|
||||
<lucide-angular [img]="CartIcon" class="w-5" />
|
||||
<span class="absolute top-0 text-xs ml-1">{{ cartItemCount() }}</span>
|
||||
<span class="absolute top-0 text-xs ml-1">{{ cartItemCount | async }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -53,7 +53,7 @@
|
||||
</ul>
|
||||
|
||||
<app-cart
|
||||
[cart]="cartItem()"
|
||||
[cart]="(cartItem$ | async)!"
|
||||
id="popover-2"
|
||||
class="dropdown"
|
||||
popover
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import { Component, computed, inject } from "@angular/core";
|
||||
import { Component, inject } from "@angular/core";
|
||||
import { LucideAngularModule, Search, ShoppingCart, User } from "lucide-angular";
|
||||
import { RouterLink } from "@angular/router";
|
||||
import { AuthService, AuthState } from "../../../features/auth/services/auth-service";
|
||||
import { CartService } from "@app/core/services/cart-service";
|
||||
import { Cart } from "@app/shared/components/cart/cart";
|
||||
import { CartModel } from "@app/core/models/cart.model";
|
||||
import { map } from "rxjs";
|
||||
import { AsyncPipe } from "@angular/common";
|
||||
|
||||
@Component({
|
||||
selector: "app-header",
|
||||
imports: [LucideAngularModule, RouterLink, Cart],
|
||||
imports: [LucideAngularModule, RouterLink, Cart, AsyncPipe],
|
||||
templateUrl: "./header.html",
|
||||
styleUrl: "./header.css",
|
||||
})
|
||||
@ -19,6 +22,6 @@ export class Header {
|
||||
readonly cartService = inject(CartService);
|
||||
protected readonly AuthState = AuthState;
|
||||
|
||||
cartItem = this.cartService.cartItem;
|
||||
cartItemCount = computed(() => this.cartItem().itemsCount ?? 0);
|
||||
cartItem$ = this.cartService.cartItem$;
|
||||
cartItemCount = this.cartItem$.pipe(map((cart: CartModel) => cart.itemsCount ?? 0));
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { API_URL } from "../tokens/api-url-tokens";
|
||||
import { CartItemModel, CartItemRequest, CartModel } from "../models/cart.model";
|
||||
import { AuthService, AuthState } from "@app/features/auth/services/auth-service";
|
||||
import { Cart } from "@app/shared/components/cart/cart";
|
||||
import { BehaviorSubject, tap } from "rxjs";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
@ -14,21 +15,23 @@ export class CartService {
|
||||
private http = inject(HttpClient);
|
||||
private apiUrl = inject(API_URL);
|
||||
|
||||
cartItem = signal<CartModel>({} as CartModel);
|
||||
private _cartItem = new BehaviorSubject<CartModel>({} as CartModel);
|
||||
|
||||
cartItem$ = this._cartItem.asObservable();
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
if (this.authService.isAuthenticated()) {
|
||||
this.fetchCart();
|
||||
} else {
|
||||
this.cartItem.set({} as CartModel);
|
||||
this._cartItem.next({} as CartModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchCart() {
|
||||
return this.http.get<CartModel>(this.apiUrl + "/cart").subscribe({
|
||||
next: (data) => this.cartItem.set(data),
|
||||
next: (data) => this._cartItem.next(data),
|
||||
error: (error: HttpErrorResponse) => {
|
||||
if (error.status === 401) {
|
||||
this.authService.purgeAuth();
|
||||
@ -38,11 +41,21 @@ export class CartService {
|
||||
});
|
||||
}
|
||||
|
||||
addToCart(data: CartItemRequest) {
|
||||
return this.http
|
||||
.post<CartModel>(this.apiUrl + "/cart", data)
|
||||
.pipe(tap((updatedCart: CartModel) => this._cartItem.next(updatedCart)));
|
||||
}
|
||||
|
||||
updateCart(data: CartItemRequest) {
|
||||
return this.http.patch<CartModel>(this.apiUrl + "/cart", data);
|
||||
return this.http
|
||||
.patch<CartModel>(this.apiUrl + "/cart", data)
|
||||
.pipe(tap((updatedCart: CartModel) => this._cartItem.next(updatedCart)));
|
||||
}
|
||||
|
||||
removeFromCart(productId: number) {
|
||||
return this.http.delete<CartModel>(this.apiUrl + "/cart", { body: { productId: productId } });
|
||||
return this.http
|
||||
.delete<CartModel>(this.apiUrl + "/cart", { body: { productId: productId } })
|
||||
.pipe(tap((updatedCart: CartModel) => this._cartItem.next(updatedCart)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<p class="font-medium text-lg">Rs. {{ product.actualPrice }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary p-3" title="Add to cart">
|
||||
<button (click)="addToCart($event)" class="btn btn-primary p-3" title="Add to cart">
|
||||
<lucide-angular [img]="ShoppingCartIcon" class="w-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,7 @@ import { ProductModel } from "../../../../core/models/product.model";
|
||||
import { Router } from "@angular/router";
|
||||
import { FavoriteButton } from "../../../../shared/components/favorite-button/favorite-button";
|
||||
import { LucideAngularModule, ShoppingCart } from "lucide-angular";
|
||||
import { CartService } from "@app/core/services/cart-service";
|
||||
|
||||
@Component({
|
||||
selector: "app-product-card",
|
||||
@ -13,10 +14,16 @@ import { LucideAngularModule, ShoppingCart } from "lucide-angular";
|
||||
export class ProductCard {
|
||||
readonly router = inject(Router);
|
||||
readonly ShoppingCartIcon = ShoppingCart;
|
||||
readonly cartService = inject(CartService);
|
||||
|
||||
@Input() product!: ProductModel;
|
||||
|
||||
goToProductDetails() {
|
||||
this.router.navigate(["/products", this.product.slug]);
|
||||
}
|
||||
|
||||
addToCart(event: Event) {
|
||||
event.stopPropagation();
|
||||
this.cartService.addToCart({ productId: this.product.id, quantity: 1 }).subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,28 +90,16 @@
|
||||
<div class="lg:col-span-3 flex flex-col gap-6">
|
||||
<div class="card">
|
||||
<div class="flex justify-between items-start mb-6">
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-xl text-gray-400 line-through decoration-1"
|
||||
>Rs.{{ product()?.actualPrice }}</span
|
||||
>
|
||||
<span class="text-3xl font-bold text-gray-900">Rs.{{ product()?.listPrice }}</span>
|
||||
<span class="text-xs text-gray-400 ml-1"></span>
|
||||
<div class="">
|
||||
<p class="text-sm text-gray-400 line-through decoration-1">
|
||||
Rs.{{ product()?.listPrice }}
|
||||
</p>
|
||||
<p class="text-3xl font-bold text-gray-900">Rs.{{ product()?.actualPrice }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-6 overflow-hidden">
|
||||
<button class="px-4 py-2 btn btn-ghost rounded-r-none!">−</button>
|
||||
<input
|
||||
type="text"
|
||||
value="1"
|
||||
class="w-12 text-center border-y border-gray-300"
|
||||
readonly
|
||||
/>
|
||||
<button class="px-4 py-2 btn btn-ghost rounded-l-none!">+</button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<button class="w-full btn btn-ghost">Add to Cart</button>
|
||||
<button (click)="addToCart()" class="w-full btn btn-ghost">Add to Cart</button>
|
||||
<button class="w-full btn btn-primary">Buy Now</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,7 @@ import { ProductModel } from "../../../core/models/product.model";
|
||||
import { ProductService } from "../services/product-service";
|
||||
import { LucideAngularModule, Heart, ArrowRight, ArrowLeft } from "lucide-angular";
|
||||
import { FavoriteButton } from "../../../shared/components/favorite-button/favorite-button";
|
||||
import { CartService } from "@app/core/services/cart-service";
|
||||
|
||||
@Component({
|
||||
selector: "app-show-product",
|
||||
@ -17,6 +18,7 @@ export class ShowProduct {
|
||||
ArrowRightIcon = ArrowRight;
|
||||
ArrowLeftIcon = ArrowLeft;
|
||||
productService = inject(ProductService);
|
||||
cartService = inject(CartService);
|
||||
product = signal<ProductModel | null>(null);
|
||||
activeImageIndex: WritableSignal<number> = signal(0);
|
||||
totalImageCount: number = 0;
|
||||
@ -29,9 +31,14 @@ export class ShowProduct {
|
||||
});
|
||||
}
|
||||
|
||||
addToCart() {
|
||||
this.cartService.addToCart({ productId: this.product()!.id, quantity: 1 }).subscribe();
|
||||
}
|
||||
|
||||
nextImage() {
|
||||
this.activeImageIndex.update((index) => (index + 1) % this.totalImageCount);
|
||||
}
|
||||
|
||||
prevImage() {
|
||||
this.activeImageIndex.update(
|
||||
(index) =>
|
||||
|
||||
@ -26,9 +26,7 @@ export class Cart {
|
||||
this.cartService
|
||||
.updateCart(cartItem)
|
||||
.pipe(finalize(() => this.isLoading.set(false)))
|
||||
.subscribe((cartdata) => {
|
||||
this.cart = cartdata;
|
||||
});
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
removeProduct(productId: number) {
|
||||
@ -37,8 +35,6 @@ export class Cart {
|
||||
this.cartService
|
||||
.removeFromCart(productId)
|
||||
.pipe(finalize(() => this.isLoading.set(false)))
|
||||
.subscribe((cartData) => {
|
||||
this.cart = cartData;
|
||||
});
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user