234 lines
6.7 KiB
TypeScript
234 lines
6.7 KiB
TypeScript
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
import { RootState } from '../store';
|
|
|
|
// Define the type for a box item
|
|
export interface BoxItem {
|
|
id: string;
|
|
name: string;
|
|
price: number;
|
|
image: string;
|
|
quantity: number;
|
|
variantId?: string;
|
|
color?: string;
|
|
compositeKey?: string;
|
|
}
|
|
|
|
// Define the type for the box state
|
|
interface BoxState {
|
|
items: BoxItem[];
|
|
total: number;
|
|
editingBoxGroupId?: string;
|
|
isInitialized: boolean;
|
|
}
|
|
|
|
// Helper function to load state from localStorage
|
|
const loadState = (): BoxState | undefined => {
|
|
try {
|
|
// Check if we're in a browser environment
|
|
if (typeof window === 'undefined') {
|
|
return undefined;
|
|
}
|
|
|
|
const serializedState = localStorage.getItem('boxState');
|
|
if (serializedState === null) {
|
|
return undefined;
|
|
}
|
|
return JSON.parse(serializedState);
|
|
} catch (err) {
|
|
console.error('Error loading box state from localStorage', err);
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
// Helper function to save state to localStorage
|
|
const saveState = (state: BoxState) => {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const serializedState = JSON.stringify(state);
|
|
localStorage.setItem('boxState', serializedState);
|
|
} catch (err) {
|
|
console.error('Error saving box state to localStorage', err);
|
|
}
|
|
};
|
|
|
|
// Initial state without loading from localStorage to avoid hydration mismatch
|
|
const initialState: BoxState = {
|
|
items: [],
|
|
total: 0,
|
|
editingBoxGroupId: undefined,
|
|
isInitialized: false
|
|
};
|
|
|
|
// Helper to generate a unique key for an item based on ID and color
|
|
const generateItemKey = (item: { id: string; color?: string }): string => {
|
|
return `${item.id}-${item.color || 'no-color'}`;
|
|
};
|
|
|
|
export const boxSlice = createSlice({
|
|
name: 'box',
|
|
initialState,
|
|
reducers: {
|
|
// Initialize state from localStorage (to be called in useEffect)
|
|
initializeState: (state) => {
|
|
// Skip if already initialized
|
|
if (state.isInitialized) return;
|
|
|
|
try {
|
|
const savedState = loadState();
|
|
if (savedState) {
|
|
state.items = savedState.items || [];
|
|
state.total = savedState.total || 0;
|
|
state.editingBoxGroupId = savedState.editingBoxGroupId;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error initializing state from localStorage', error);
|
|
}
|
|
|
|
state.isInitialized = true;
|
|
},
|
|
|
|
// Add an item to the box
|
|
addToBox: (state, action: PayloadAction<BoxItem>) => {
|
|
const newItem = { ...action.payload };
|
|
|
|
// Check if the variantId should be used as a color
|
|
if (newItem.variantId && !newItem.color) {
|
|
newItem.color = newItem.variantId;
|
|
}
|
|
|
|
// Generate composite key
|
|
const compositeKey = generateItemKey(newItem);
|
|
newItem.compositeKey = compositeKey;
|
|
|
|
const existingItemIndex = state.items.findIndex(
|
|
item => generateItemKey(item) === compositeKey
|
|
);
|
|
|
|
if (existingItemIndex >= 0 && state.items[existingItemIndex]) {
|
|
// If item with the same key exists, increment quantity
|
|
const quantity = newItem.quantity || 1;
|
|
state.items[existingItemIndex].quantity += quantity;
|
|
} else {
|
|
// Otherwise add the new item (or item with a different color)
|
|
state.items.push({
|
|
...newItem,
|
|
quantity: newItem.quantity || 1,
|
|
compositeKey
|
|
});
|
|
}
|
|
|
|
// Recalculate total
|
|
state.total = state.items.reduce(
|
|
(sum, item) => sum + item.price * item.quantity, 0
|
|
);
|
|
|
|
// Save to localStorage
|
|
saveState(state);
|
|
},
|
|
|
|
// Update item quantity
|
|
updateQuantity: (state, action: PayloadAction<{ id: string; color?: string; quantity: number }>) => {
|
|
const { id, color, quantity } = action.payload;
|
|
const itemKey = generateItemKey({ id, color });
|
|
const item = state.items.find(item => generateItemKey(item) === itemKey);
|
|
|
|
if (item) {
|
|
item.quantity = quantity;
|
|
|
|
// Recalculate total
|
|
state.total = state.items.reduce(
|
|
(sum, item) => sum + item.price * item.quantity, 0
|
|
);
|
|
}
|
|
|
|
// Save to localStorage
|
|
saveState(state);
|
|
},
|
|
|
|
// Remove an item from the box
|
|
removeFromBox: (state, action: PayloadAction<{ id: string; color?: string }>) => {
|
|
const { id, color } = action.payload;
|
|
const itemKeyToRemove = generateItemKey({ id, color });
|
|
state.items = state.items.filter(item => generateItemKey(item) !== itemKeyToRemove);
|
|
|
|
// Recalculate total
|
|
state.total = state.items.reduce(
|
|
(sum, item) => sum + item.price * item.quantity, 0
|
|
);
|
|
|
|
// Save to localStorage
|
|
saveState(state);
|
|
},
|
|
|
|
// Clear the entire box
|
|
clearBox: (state) => {
|
|
state.items = [];
|
|
state.total = 0;
|
|
state.editingBoxGroupId = undefined;
|
|
|
|
// Save to localStorage
|
|
saveState(state);
|
|
},
|
|
|
|
// Load a box for editing
|
|
loadBoxForEditing: (state) => {
|
|
try {
|
|
if (typeof window === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
const boxStateString = localStorage.getItem('lastBoxState');
|
|
if (!boxStateString) {
|
|
return;
|
|
}
|
|
|
|
const boxState = JSON.parse(boxStateString);
|
|
|
|
// Spremi originalni boxGroupId ako postoji
|
|
if (boxState && boxState.originalBoxGroupId) {
|
|
state.editingBoxGroupId = boxState.originalBoxGroupId;
|
|
}
|
|
|
|
if (boxState && boxState.boxItems && boxState.productItems) {
|
|
// Reset current state
|
|
state.items = [];
|
|
|
|
// Add box items
|
|
boxState.boxItems.forEach((item: BoxItem) => {
|
|
state.items.push(item);
|
|
});
|
|
|
|
// Add product items
|
|
boxState.productItems.forEach((item: BoxItem) => {
|
|
state.items.push(item);
|
|
});
|
|
|
|
// Recalculate total
|
|
state.total = state.items.reduce(
|
|
(sum, item) => sum + item.price * item.quantity, 0
|
|
);
|
|
|
|
// Save to localStorage
|
|
saveState(state);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading box for editing', error);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
// Export actions
|
|
export const { initializeState, addToBox, updateQuantity, removeFromBox, clearBox, loadBoxForEditing } = boxSlice.actions;
|
|
|
|
// Selectors
|
|
export const selectBoxItems = (state: RootState) => state.box.items;
|
|
export const selectBoxTotal = (state: RootState) => state.box.total;
|
|
export const selectBoxItemsCount = (state: RootState) =>
|
|
state.box.items.reduce((total, item) => total + item.quantity, 0);
|
|
export const selectEditingBoxGroupId = (state: RootState) => state.box.editingBoxGroupId;
|
|
|
|
export default boxSlice.reducer;
|