exercises

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

cart.service.ts (2784B)


      1 import { Injectable, signal, computed } from '@angular/core';
      2 
      3 export interface CartItem {
      4   product: ProductCard;
      5   quantity: number;
      6   addedAt: Date;
      7 }
      8 
      9 @Injectable({
     10   providedIn: 'root'
     11 })
     12 export class CartService {
     13   // Private signal for cart items
     14   private readonly _cartItems = signal<CartItem[]>([]);
     15 
     16   // Public readonly signals
     17   readonly cartItems = this._cartItems.asReadonly();
     18   
     19   // Computed signals for derived state
     20   readonly totalItems = computed(() => 
     21     this._cartItems().reduce((total, item) => total + item.quantity, 0)
     22   );
     23   
     24   readonly totalPrice = computed(() => 
     25     this._cartItems().reduce((total, item) => 
     26       total + (parseFloat(item.product.final_price) * item.quantity), 0
     27     )
     28   );
     29   
     30   readonly isEmpty = computed(() => this._cartItems().length === 0);
     31 
     32   // Private helper methods
     33   private findItemIndex(productId: string): number {
     34     return this._cartItems().findIndex(item => item.product.eid === productId);
     35   }
     36 
     37   private findItem(productId: string): CartItem | undefined {
     38     return this._cartItems().find(item => item.product.eid === productId);
     39   }
     40 
     41   private updateItems(updater: (items: CartItem[]) => CartItem[]): void {
     42     this._cartItems.set(updater([...this._cartItems()]));
     43   }
     44 
     45   // Public methods
     46   addToCart(product: ProductCard, quantity: number = 1): void {
     47     const existingIndex = this.findItemIndex(product.eid);
     48     
     49     if (existingIndex >= 0) {
     50       // Update existing item
     51       this.updateItems(items => {
     52         items[existingIndex].quantity += quantity;
     53         return items;
     54       });
     55     } else {
     56       // Add new item
     57       this.updateItems(items => [
     58         ...items,
     59         { product, quantity, addedAt: new Date() }
     60       ]);
     61     }
     62   }
     63 
     64   removeFromCart(productId: string): void {
     65     this.updateItems(items => items.filter(item => item.product.eid !== productId));
     66   }
     67 
     68   updateQuantity(productId: string, quantity: number): void {
     69     if (quantity <= 0) {
     70       this.removeFromCart(productId);
     71       return;
     72     }
     73 
     74     this.updateItems(items => 
     75       items.map(item => 
     76         item.product.eid === productId 
     77           ? { ...item, quantity }
     78           : item
     79       )
     80     );
     81   }
     82 
     83   increaseQuantity(productId: string): void {
     84     const item = this.findItem(productId);
     85     if (item) {
     86       this.updateQuantity(productId, item.quantity + 1);
     87     }
     88   }
     89 
     90   decreaseQuantity(productId: string): void {
     91     const item = this.findItem(productId);
     92     if (item) {
     93       this.updateQuantity(productId, item.quantity - 1);
     94     }
     95   }
     96 
     97   clearCart(): void {
     98     this._cartItems.set([]);
     99   }
    100 
    101   // Query methods
    102   getItem(productId: string): CartItem | undefined {
    103     return this.findItem(productId);
    104   }
    105 
    106   hasItem(productId: string): boolean {
    107     return this.findItemIndex(productId) >= 0;
    108   }
    109 }