'use client' import React, { createContext, useContext, useReducer, useEffect, useState } from 'react' import { CartState, CartContextType, CartItem, DeliveryInfo } from '@/types/cart' // Начальное состояние корзины const initialState: CartState = { items: [], summary: { totalItems: 0, totalPrice: 0, totalDiscount: 0, deliveryPrice: 39, finalPrice: 0 }, delivery: { type: 'Доставка курьером', address: 'Калининградская область, Калиниград, улица Понартская, 5, кв./офис 1, Подъезд 1, этаж 1', price: 39 }, orderComment: '', isLoading: false } // Типы действий type CartAction = | { type: 'ADD_ITEM'; payload: Omit } | { type: 'ADD_ITEM_SUCCESS'; payload: { items: CartItem[]; summary: any } } | { type: 'ADD_ITEM_ERROR'; payload: string } | { type: 'REMOVE_ITEM'; payload: string } | { type: 'UPDATE_QUANTITY'; payload: { id: string; quantity: number } } | { type: 'TOGGLE_SELECT'; payload: string } | { type: 'TOGGLE_FAVORITE'; payload: string } | { type: 'UPDATE_COMMENT'; payload: { id: string; comment: string } } | { type: 'UPDATE_ORDER_COMMENT'; payload: string } | { type: 'SELECT_ALL' } | { type: 'REMOVE_ALL' } | { type: 'REMOVE_SELECTED' } | { type: 'UPDATE_DELIVERY'; payload: Partial } | { type: 'CLEAR_CART' } | { type: 'LOAD_CART'; payload: CartItem[] } | { type: 'LOAD_FULL_STATE'; payload: { items: CartItem[]; delivery: DeliveryInfo; orderComment: string } } | { type: 'SET_LOADING'; payload: boolean } | { type: 'SET_ERROR'; payload: string } // Функция для генерации ID const generateId = () => Math.random().toString(36).substr(2, 9) // Утилитарная функция для парсинга количества в наличии const parseStock = (stockStr: string | number | undefined): number => { if (typeof stockStr === 'number') return stockStr; if (typeof stockStr === 'string') { const match = stockStr.match(/\d+/); return match ? parseInt(match[0]) : 0; } return 0; }; // Функция для расчета итогов const calculateSummary = (items: CartItem[], deliveryPrice: number) => { const selectedItems = items.filter(item => item.selected) const totalItems = selectedItems.reduce((sum, item) => sum + item.quantity, 0) const totalPrice = selectedItems.reduce((sum, item) => sum + (item.price * item.quantity), 0) const totalDiscount = selectedItems.reduce((sum, item) => { const discount = item.originalPrice ? (item.originalPrice - item.price) * item.quantity : 0 return sum + discount }, 0) // Доставка включена в стоимость товаров, поэтому добавляем её только если есть товары const finalPrice = totalPrice + (totalPrice > 0 ? 0 : 0) // Доставка всегда включена в цену товаров return { totalItems, totalPrice, totalDiscount, deliveryPrice, finalPrice } } // Редьюсер корзины const cartReducer = (state: CartState, action: CartAction): CartState => { switch (action.type) { case 'ADD_ITEM': { const existingItemIndex = state.items.findIndex( item => (item.productId && item.productId === action.payload.productId) || (item.offerKey && item.offerKey === action.payload.offerKey) ) let newItems: CartItem[] if (existingItemIndex >= 0) { // Увеличиваем количество существующего товара const existingItem = state.items[existingItemIndex]; const totalQuantity = existingItem.quantity + action.payload.quantity; newItems = state.items.map((item, index) => index === existingItemIndex ? { ...item, quantity: totalQuantity } : item ) } else { // Добавляем новый товар const newItem: CartItem = { ...action.payload, id: generateId(), selected: true, favorite: false } newItems = [...state.items, newItem] } const newSummary = calculateSummary(newItems, state.delivery.price) return { ...state, items: newItems, summary: newSummary } } case 'REMOVE_ITEM': { const newItems = state.items.filter(item => item.id !== action.payload) const newSummary = calculateSummary(newItems, state.delivery.price) return { ...state, items: newItems, summary: newSummary } } case 'UPDATE_QUANTITY': { const newItems = state.items.map(item => item.id === action.payload.id ? { ...item, quantity: Math.max(1, action.payload.quantity) } : item ) const newSummary = calculateSummary(newItems, state.delivery.price) return { ...state, items: newItems, summary: newSummary } } case 'TOGGLE_SELECT': { const newItems = state.items.map(item => item.id === action.payload ? { ...item, selected: !item.selected } : item ) const newSummary = calculateSummary(newItems, state.delivery.price) return { ...state, items: newItems, summary: newSummary } } case 'TOGGLE_FAVORITE': { const newItems = state.items.map(item => item.id === action.payload ? { ...item, favorite: !item.favorite } : item ) return { ...state, items: newItems } } case 'UPDATE_COMMENT': { const newItems = state.items.map(item => item.id === action.payload.id ? { ...item, comment: action.payload.comment } : item ) return { ...state, items: newItems } } case 'UPDATE_ORDER_COMMENT': { return { ...state, orderComment: action.payload } } case 'SELECT_ALL': { const allSelected = state.items.every(item => item.selected) const newItems = state.items.map(item => ({ ...item, selected: !allSelected })) const newSummary = calculateSummary(newItems, state.delivery.price) return { ...state, items: newItems, summary: newSummary } } case 'REMOVE_ALL': { const newSummary = calculateSummary([], state.delivery.price) return { ...state, items: [], summary: newSummary } } case 'REMOVE_SELECTED': { const newItems = state.items.filter(item => !item.selected) const newSummary = calculateSummary(newItems, state.delivery.price) return { ...state, items: newItems, summary: newSummary } } case 'UPDATE_DELIVERY': { const newDelivery = { ...state.delivery, ...action.payload } const newSummary = calculateSummary(state.items, newDelivery.price) return { ...state, delivery: newDelivery, summary: newSummary } } case 'CLEAR_CART': { const newSummary = calculateSummary([], state.delivery.price) return { ...state, items: [], summary: newSummary } } case 'LOAD_CART': { const newSummary = calculateSummary(action.payload, state.delivery.price) return { ...state, items: action.payload, summary: newSummary } } case 'LOAD_FULL_STATE': { const newSummary = calculateSummary(action.payload.items, action.payload.delivery.price || state.delivery.price) return { ...state, items: action.payload.items, delivery: action.payload.delivery, orderComment: action.payload.orderComment, summary: newSummary } } case 'SET_LOADING': { return { ...state, isLoading: action.payload } } case 'SET_ERROR': { return { ...state, error: action.payload, isLoading: false } } default: return state } } // Создание контекста const CartContext = createContext(undefined) // Провайдер корзины export const CartProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [state, dispatch] = useReducer(cartReducer, initialState) const [isInitialized, setIsInitialized] = useState(false) // Загрузка корзины из localStorage при инициализации useEffect(() => { if (typeof window === 'undefined') return console.log('🔄 Загружаем состояние корзины из localStorage...') const savedCartState = localStorage.getItem('cartState') if (savedCartState) { try { const cartState = JSON.parse(savedCartState) console.log('✅ Найдено сохраненное состояние корзины:', cartState) // Загружаем полное состояние корзины dispatch({ type: 'LOAD_FULL_STATE', payload: cartState }) } catch (error) { console.error('❌ Ошибка загрузки корзины из localStorage:', error) // Попытаемся загрузить старый формат (только товары) const savedCart = localStorage.getItem('cart') if (savedCart) { try { const cartItems = JSON.parse(savedCart) console.log('✅ Найдены товары в старом формате:', cartItems) dispatch({ type: 'LOAD_CART', payload: cartItems }) } catch (error) { console.error('❌ Ошибка загрузки старой корзины:', error) } } } } else { console.log('ℹ️ Сохраненное состояние корзины не найдено') } setIsInitialized(true) }, []) // Сохранение полного состояния корзины в localStorage при изменении (только после инициализации) useEffect(() => { if (!isInitialized || typeof window === 'undefined') return const stateToSave = { items: state.items, delivery: state.delivery, orderComment: state.orderComment } console.log('💾 Сохраняем состояние корзины:', stateToSave) localStorage.setItem('cartState', JSON.stringify(stateToSave)) // Сохраняем также старый формат для совместимости localStorage.setItem('cart', JSON.stringify(state.items)) }, [state.items, state.delivery, state.orderComment, isInitialized]) // Функции для работы с корзиной const addItem = async (item: Omit) => { // Проверяем наличие товара на складе перед добавлением const existingItemIndex = state.items.findIndex( existingItem => (existingItem.productId && existingItem.productId === item.productId) || (existingItem.offerKey && existingItem.offerKey === item.offerKey) ) let totalQuantity = item.quantity; if (existingItemIndex >= 0) { const existingItem = state.items[existingItemIndex]; totalQuantity = existingItem.quantity + item.quantity; } // Проверяем наличие товара на складе const availableStock = parseStock(item.stock); if (availableStock > 0 && totalQuantity > availableStock) { const errorMessage = `Недостаточно товара в наличии. Доступно: ${availableStock} шт., запрошено: ${totalQuantity} шт.`; dispatch({ type: 'SET_ERROR', payload: errorMessage }); return { success: false, error: errorMessage }; } // Если проверка прошла успешно, добавляем товар dispatch({ type: 'ADD_ITEM', payload: item }) return { success: true } } const removeItem = (id: string) => { dispatch({ type: 'REMOVE_ITEM', payload: id }) } const updateQuantity = (id: string, quantity: number) => { // Найдем товар для проверки наличия const item = state.items.find(item => item.id === id); if (item) { const availableStock = parseStock(item.stock); if (availableStock > 0 && quantity > availableStock) { // Показываем ошибку, но не изменяем количество dispatch({ type: 'SET_ERROR', payload: `Недостаточно товара в наличии. Доступно: ${availableStock} шт.` }); return; } } dispatch({ type: 'UPDATE_QUANTITY', payload: { id, quantity } }) } const toggleSelect = (id: string) => { dispatch({ type: 'TOGGLE_SELECT', payload: id }) } const toggleFavorite = (id: string) => { dispatch({ type: 'TOGGLE_FAVORITE', payload: id }) } const updateComment = (id: string, comment: string) => { dispatch({ type: 'UPDATE_COMMENT', payload: { id, comment } }) } const updateOrderComment = (comment: string) => { dispatch({ type: 'UPDATE_ORDER_COMMENT', payload: comment }) } const selectAll = () => { dispatch({ type: 'SELECT_ALL' }) } const removeAll = () => { dispatch({ type: 'REMOVE_ALL' }) } const removeSelected = () => { dispatch({ type: 'REMOVE_SELECTED' }) } const updateDelivery = (delivery: Partial) => { dispatch({ type: 'UPDATE_DELIVERY', payload: delivery }) } const clearCart = () => { dispatch({ type: 'CLEAR_CART' }) // Очищаем localStorage при очистке корзины if (typeof window !== 'undefined') { localStorage.removeItem('cartState') localStorage.removeItem('cart') } } const clearError = () => { dispatch({ type: 'SET_ERROR', payload: '' }) } const contextValue: CartContextType = { state, addItem, removeItem, updateQuantity, toggleSelect, toggleFavorite, updateComment, updateOrderComment, selectAll, removeAll, removeSelected, updateDelivery, clearCart, clearError } return ( {children} ) } // Хук для использования контекста корзины export const useCart = (): CartContextType => { const context = useContext(CartContext) if (!context) { throw new Error('useCart должен использоваться внутри CartProvider') } return context }