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"
|
style="anchor-name: --anchor-2"
|
||||||
>
|
>
|
||||||
<lucide-angular [img]="CartIcon" class="w-5" />
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<app-cart
|
<app-cart
|
||||||
[cart]="cartItem()"
|
[cart]="(cartItem$ | async)!"
|
||||||
id="popover-2"
|
id="popover-2"
|
||||||
class="dropdown"
|
class="dropdown"
|
||||||
popover
|
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 { LucideAngularModule, Search, ShoppingCart, User } from "lucide-angular";
|
||||||
import { RouterLink } from "@angular/router";
|
import { RouterLink } from "@angular/router";
|
||||||
import { AuthService, AuthState } from "../../../features/auth/services/auth-service";
|
import { AuthService, AuthState } from "../../../features/auth/services/auth-service";
|
||||||
import { CartService } from "@app/core/services/cart-service";
|
import { CartService } from "@app/core/services/cart-service";
|
||||||
import { Cart } from "@app/shared/components/cart/cart";
|
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({
|
@Component({
|
||||||
selector: "app-header",
|
selector: "app-header",
|
||||||
imports: [LucideAngularModule, RouterLink, Cart],
|
imports: [LucideAngularModule, RouterLink, Cart, AsyncPipe],
|
||||||
templateUrl: "./header.html",
|
templateUrl: "./header.html",
|
||||||
styleUrl: "./header.css",
|
styleUrl: "./header.css",
|
||||||
})
|
})
|
||||||
@ -19,6 +22,6 @@ export class Header {
|
|||||||
readonly cartService = inject(CartService);
|
readonly cartService = inject(CartService);
|
||||||
protected readonly AuthState = AuthState;
|
protected readonly AuthState = AuthState;
|
||||||
|
|
||||||
cartItem = this.cartService.cartItem;
|
cartItem$ = this.cartService.cartItem$;
|
||||||
cartItemCount = computed(() => this.cartItem().itemsCount ?? 0);
|
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 { 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";
|
import { Cart } from "@app/shared/components/cart/cart";
|
||||||
|
import { BehaviorSubject, tap } from "rxjs";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: "root",
|
providedIn: "root",
|
||||||
@ -14,21 +15,23 @@ export class CartService {
|
|||||||
private http = inject(HttpClient);
|
private http = inject(HttpClient);
|
||||||
private apiUrl = inject(API_URL);
|
private apiUrl = inject(API_URL);
|
||||||
|
|
||||||
cartItem = signal<CartModel>({} as CartModel);
|
private _cartItem = new BehaviorSubject<CartModel>({} as CartModel);
|
||||||
|
|
||||||
|
cartItem$ = this._cartItem.asObservable();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
if (this.authService.isAuthenticated()) {
|
if (this.authService.isAuthenticated()) {
|
||||||
this.fetchCart();
|
this.fetchCart();
|
||||||
} else {
|
} else {
|
||||||
this.cartItem.set({} as CartModel);
|
this._cartItem.next({} as CartModel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCart() {
|
fetchCart() {
|
||||||
return this.http.get<CartModel>(this.apiUrl + "/cart").subscribe({
|
return this.http.get<CartModel>(this.apiUrl + "/cart").subscribe({
|
||||||
next: (data) => this.cartItem.set(data),
|
next: (data) => this._cartItem.next(data),
|
||||||
error: (error: HttpErrorResponse) => {
|
error: (error: HttpErrorResponse) => {
|
||||||
if (error.status === 401) {
|
if (error.status === 401) {
|
||||||
this.authService.purgeAuth();
|
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) {
|
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) {
|
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>
|
<p class="font-medium text-lg">Rs. {{ product.actualPrice }}</p>
|
||||||
</div>
|
</div>
|
||||||
<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" />
|
<lucide-angular [img]="ShoppingCartIcon" class="w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { ProductModel } from "../../../../core/models/product.model";
|
|||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { FavoriteButton } from "../../../../shared/components/favorite-button/favorite-button";
|
import { FavoriteButton } from "../../../../shared/components/favorite-button/favorite-button";
|
||||||
import { LucideAngularModule, ShoppingCart } from "lucide-angular";
|
import { LucideAngularModule, ShoppingCart } from "lucide-angular";
|
||||||
|
import { CartService } from "@app/core/services/cart-service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-product-card",
|
selector: "app-product-card",
|
||||||
@ -13,10 +14,16 @@ import { LucideAngularModule, ShoppingCart } from "lucide-angular";
|
|||||||
export class ProductCard {
|
export class ProductCard {
|
||||||
readonly router = inject(Router);
|
readonly router = inject(Router);
|
||||||
readonly ShoppingCartIcon = ShoppingCart;
|
readonly ShoppingCartIcon = ShoppingCart;
|
||||||
|
readonly cartService = inject(CartService);
|
||||||
|
|
||||||
@Input() product!: ProductModel;
|
@Input() product!: ProductModel;
|
||||||
|
|
||||||
goToProductDetails() {
|
goToProductDetails() {
|
||||||
this.router.navigate(["/products", this.product.slug]);
|
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="lg:col-span-3 flex flex-col gap-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="flex justify-between items-start mb-6">
|
<div class="flex justify-between items-start mb-6">
|
||||||
<div class="flex items-baseline gap-2">
|
<div class="">
|
||||||
<span class="text-xl text-gray-400 line-through decoration-1"
|
<p class="text-sm text-gray-400 line-through decoration-1">
|
||||||
>Rs.{{ product()?.actualPrice }}</span
|
Rs.{{ product()?.listPrice }}
|
||||||
>
|
</p>
|
||||||
<span class="text-3xl font-bold text-gray-900">Rs.{{ product()?.listPrice }}</span>
|
<p class="text-3xl font-bold text-gray-900">Rs.{{ product()?.actualPrice }}</p>
|
||||||
<span class="text-xs text-gray-400 ml-1"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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>
|
<button class="w-full btn btn-primary">Buy Now</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { ProductModel } from "../../../core/models/product.model";
|
|||||||
import { ProductService } from "../services/product-service";
|
import { ProductService } from "../services/product-service";
|
||||||
import { LucideAngularModule, Heart, ArrowRight, ArrowLeft } from "lucide-angular";
|
import { LucideAngularModule, Heart, ArrowRight, ArrowLeft } from "lucide-angular";
|
||||||
import { FavoriteButton } from "../../../shared/components/favorite-button/favorite-button";
|
import { FavoriteButton } from "../../../shared/components/favorite-button/favorite-button";
|
||||||
|
import { CartService } from "@app/core/services/cart-service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-show-product",
|
selector: "app-show-product",
|
||||||
@ -17,6 +18,7 @@ export class ShowProduct {
|
|||||||
ArrowRightIcon = ArrowRight;
|
ArrowRightIcon = ArrowRight;
|
||||||
ArrowLeftIcon = ArrowLeft;
|
ArrowLeftIcon = ArrowLeft;
|
||||||
productService = inject(ProductService);
|
productService = inject(ProductService);
|
||||||
|
cartService = inject(CartService);
|
||||||
product = signal<ProductModel | null>(null);
|
product = signal<ProductModel | null>(null);
|
||||||
activeImageIndex: WritableSignal<number> = signal(0);
|
activeImageIndex: WritableSignal<number> = signal(0);
|
||||||
totalImageCount: number = 0;
|
totalImageCount: number = 0;
|
||||||
@ -29,9 +31,14 @@ export class ShowProduct {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addToCart() {
|
||||||
|
this.cartService.addToCart({ productId: this.product()!.id, quantity: 1 }).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
nextImage() {
|
nextImage() {
|
||||||
this.activeImageIndex.update((index) => (index + 1) % this.totalImageCount);
|
this.activeImageIndex.update((index) => (index + 1) % this.totalImageCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevImage() {
|
prevImage() {
|
||||||
this.activeImageIndex.update(
|
this.activeImageIndex.update(
|
||||||
(index) =>
|
(index) =>
|
||||||
|
|||||||
@ -26,9 +26,7 @@ export class Cart {
|
|||||||
this.cartService
|
this.cartService
|
||||||
.updateCart(cartItem)
|
.updateCart(cartItem)
|
||||||
.pipe(finalize(() => this.isLoading.set(false)))
|
.pipe(finalize(() => this.isLoading.set(false)))
|
||||||
.subscribe((cartdata) => {
|
.subscribe();
|
||||||
this.cart = cartdata;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeProduct(productId: number) {
|
removeProduct(productId: number) {
|
||||||
@ -37,8 +35,6 @@ export class Cart {
|
|||||||
this.cartService
|
this.cartService
|
||||||
.removeFromCart(productId)
|
.removeFromCart(productId)
|
||||||
.pipe(finalize(() => this.isLoading.set(false)))
|
.pipe(finalize(() => this.isLoading.set(false)))
|
||||||
.subscribe((cartData) => {
|
.subscribe();
|
||||||
this.cart = cartData;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user