147 lines
4.9 KiB
TypeScript
147 lines
4.9 KiB
TypeScript
|
|
import { BillingCycle, BasketItem, ProductDto } from "@/proxy/order/models";
|
||
|
|
|
||
|
|
export interface CartState {
|
||
|
|
items: BasketItem[];
|
||
|
|
total: number;
|
||
|
|
globalBillingCycle: BillingCycle;
|
||
|
|
globalPeriod: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const initialCartState: CartState = {
|
||
|
|
items: [],
|
||
|
|
total: 0,
|
||
|
|
globalBillingCycle: 'yearly',
|
||
|
|
globalPeriod: 1
|
||
|
|
};
|
||
|
|
|
||
|
|
// Key for localStorage
|
||
|
|
const CART_STORAGE_KEY = 'cartState';
|
||
|
|
|
||
|
|
export const calculateItemPrice = (
|
||
|
|
product: ProductDto,
|
||
|
|
quantity: number,
|
||
|
|
billingCycle: BillingCycle,
|
||
|
|
period: number = 1
|
||
|
|
): number => {
|
||
|
|
const price = billingCycle === 'monthly' ? product.monthlyPrice! : product.yearlyPrice!;
|
||
|
|
return price * quantity * period;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const addItemToCart = (
|
||
|
|
cartState: CartState,
|
||
|
|
product: ProductDto,
|
||
|
|
quantity: number,
|
||
|
|
billingCycle: BillingCycle
|
||
|
|
): CartState => {
|
||
|
|
const isAlreadyInCart = isItemInCartState(cartState.items, product.id, billingCycle);
|
||
|
|
|
||
|
|
if (!product.isQuantityBased && isAlreadyInCart) {
|
||
|
|
return cartState;
|
||
|
|
}
|
||
|
|
|
||
|
|
const existingItemIndex = cartState.items.findIndex(
|
||
|
|
item => item.product.id === product.id && item.billingCycle === billingCycle
|
||
|
|
);
|
||
|
|
|
||
|
|
let newItems;
|
||
|
|
if (existingItemIndex > -1) {
|
||
|
|
newItems = [...cartState.items];
|
||
|
|
newItems[existingItemIndex].quantity += quantity;
|
||
|
|
newItems[existingItemIndex].totalPrice = calculateItemPrice(
|
||
|
|
product,
|
||
|
|
newItems[existingItemIndex].quantity,
|
||
|
|
billingCycle,
|
||
|
|
cartState.globalPeriod
|
||
|
|
);
|
||
|
|
} else {
|
||
|
|
const actualQuantity = !product.isQuantityBased ? 1 : quantity;
|
||
|
|
const totalPrice = calculateItemPrice(product, actualQuantity, billingCycle, cartState.globalPeriod);
|
||
|
|
newItems = [...cartState.items, { product, quantity: actualQuantity, billingCycle, totalPrice }];
|
||
|
|
}
|
||
|
|
|
||
|
|
const total = newItems.reduce((sum, item) => sum + item.totalPrice, 0);
|
||
|
|
const updatedState = { ...cartState, items: newItems, total };
|
||
|
|
saveCartToLocalStorage(updatedState);
|
||
|
|
return updatedState;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const removeItemFromCart = (cartState: CartState, id: string): CartState => {
|
||
|
|
const newItems = cartState.items.filter(item => item.product.id !== id);
|
||
|
|
const total = newItems.reduce((sum, item) => sum + item.totalPrice, 0);
|
||
|
|
const updatedState = { ...cartState, items: newItems, total };
|
||
|
|
saveCartToLocalStorage(updatedState);
|
||
|
|
return updatedState;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const updateCartItemQuantity = (cartState: CartState, id: string, quantity: number): CartState => {
|
||
|
|
const newItems = cartState.items.map(item => {
|
||
|
|
if (item.product.id === id) {
|
||
|
|
if (quantity <= 0) return null;
|
||
|
|
if (!item.product.isQuantityBased) return item;
|
||
|
|
|
||
|
|
return {
|
||
|
|
...item,
|
||
|
|
quantity,
|
||
|
|
totalPrice: calculateItemPrice(item.product, quantity, item.billingCycle, cartState.globalPeriod)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
return item;
|
||
|
|
}).filter(Boolean) as BasketItem[];
|
||
|
|
|
||
|
|
const total = newItems.reduce((sum, item) => sum + item.totalPrice, 0);
|
||
|
|
const updatedState = { ...cartState, items: newItems, total };
|
||
|
|
saveCartToLocalStorage(updatedState);
|
||
|
|
return updatedState;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const clearCart = (): CartState => {
|
||
|
|
localStorage.removeItem(CART_STORAGE_KEY);
|
||
|
|
return initialCartState;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const setCartGlobalBillingCycle = (cartState: CartState, cycle: BillingCycle): CartState => {
|
||
|
|
const newItems = cartState.items.map(item => ({
|
||
|
|
...item,
|
||
|
|
billingCycle: cycle,
|
||
|
|
totalPrice: calculateItemPrice(item.product, item.quantity, cycle, cartState.globalPeriod)
|
||
|
|
}));
|
||
|
|
const total = newItems.reduce((sum, item) => sum + item.totalPrice, 0);
|
||
|
|
const updatedState = { ...cartState, globalBillingCycle: cycle, items: newItems, total };
|
||
|
|
saveCartToLocalStorage(updatedState);
|
||
|
|
return updatedState;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const setCartGlobalPeriod = (cartState: CartState, period: number): CartState => {
|
||
|
|
const newItems = cartState.items.map(item => ({
|
||
|
|
...item,
|
||
|
|
totalPrice: calculateItemPrice(item.product, item.quantity, item.billingCycle, period)
|
||
|
|
}));
|
||
|
|
const total = newItems.reduce((sum, item) => sum + item.totalPrice, 0);
|
||
|
|
const updatedState = { ...cartState, globalPeriod: period, items: newItems, total };
|
||
|
|
saveCartToLocalStorage(updatedState);
|
||
|
|
return updatedState;
|
||
|
|
};
|
||
|
|
|
||
|
|
export const isItemInCart = (cartState: CartState, productId: string, billingCycle: BillingCycle): boolean => {
|
||
|
|
return cartState.items.some(
|
||
|
|
item => item.product.id === productId && item.billingCycle === billingCycle
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export const isItemInCartState = (items: BasketItem[], productId: string, billingCycle: BillingCycle): boolean => {
|
||
|
|
return items.some(
|
||
|
|
item => item.product.id === productId && item.billingCycle === billingCycle
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
// Utility: Save to localStorage
|
||
|
|
const saveCartToLocalStorage = (cartState: CartState) => {
|
||
|
|
localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(cartState));
|
||
|
|
};
|
||
|
|
|
||
|
|
// Utility: Load from localStorage
|
||
|
|
export const loadCartFromLocalStorage = (): CartState => {
|
||
|
|
const saved = localStorage.getItem(CART_STORAGE_KEY);
|
||
|
|
return saved ? JSON.parse(saved) : initialCartState;
|
||
|
|
};
|