chore: transfer repo
This commit is contained in:
6
lib/redux/hooks.ts
Normal file
6
lib/redux/hooks.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||
import type { AppDispatch, RootState } from './store';
|
||||
|
||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
15
lib/redux/provider.tsx
Normal file
15
lib/redux/provider.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { initializeState } from './slices/boxSlice';
|
||||
import { store } from './store';
|
||||
|
||||
export function ReduxProvider({ children }: { children: React.ReactNode }) {
|
||||
// Inicijaliziraj stanje iz localStorage nakon što se komponenta montira
|
||||
useEffect(() => {
|
||||
store.dispatch(initializeState());
|
||||
}, []);
|
||||
|
||||
return <Provider store={store}>{children}</Provider>;
|
||||
}
|
||||
234
lib/redux/slices/boxSlice.ts
Normal file
234
lib/redux/slices/boxSlice.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
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;
|
||||
11
lib/redux/store.ts
Normal file
11
lib/redux/store.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import boxReducer from './slices/boxSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
box: boxReducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
Reference in New Issue
Block a user