211 lines
6.4 KiB
JavaScript
211 lines
6.4 KiB
JavaScript
import { createContext, useContext, useEffect, useState } from 'react';
|
|
import { isTransientApiError } from '../api/offlineCache';
|
|
import { getHouseholdStores, getLocationZones } from '../api/stores';
|
|
import { AuthContext } from './AuthContext';
|
|
import { HouseholdContext } from './HouseholdContext';
|
|
|
|
const DEFAULT_LOCATION_NAME = "Default Location";
|
|
|
|
function getHouseholdStoreKey(store) {
|
|
return String(store?.household_store_id ?? store?.store_id ?? store?.id ?? "");
|
|
}
|
|
|
|
function isDefaultLocationName(store) {
|
|
return store?.location_name === DEFAULT_LOCATION_NAME || store?.display_name === store?.name;
|
|
}
|
|
|
|
function chooseStoreLocation(locations) {
|
|
if (!locations || locations.length === 0) return null;
|
|
return (
|
|
locations.find(isDefaultLocationName) ||
|
|
locations.find((store) => store.is_default) ||
|
|
locations[0]
|
|
);
|
|
}
|
|
|
|
function chooseStoreByHouseholdStoreId(stores, householdStoreId) {
|
|
const matchingLocations = stores.filter(
|
|
(store) => getHouseholdStoreKey(store) === String(householdStoreId)
|
|
);
|
|
return chooseStoreLocation(matchingLocations);
|
|
}
|
|
|
|
function getLocationStorageKey(householdId, householdStoreId) {
|
|
return `activeStoreLocationId_${householdId}_${householdStoreId}`;
|
|
}
|
|
|
|
function chooseLocationByStoredIds(stores, householdId, householdStoreId) {
|
|
const storageKey = getLocationStorageKey(householdId, householdStoreId);
|
|
const savedLocationId = localStorage.getItem(storageKey);
|
|
if (!savedLocationId) return null;
|
|
|
|
return stores.find(
|
|
(store) =>
|
|
String(store.id) === String(savedLocationId) &&
|
|
getHouseholdStoreKey(store) === String(householdStoreId)
|
|
);
|
|
}
|
|
|
|
export const StoreContext = createContext({
|
|
stores: [],
|
|
activeStore: null,
|
|
zones: [],
|
|
loading: false,
|
|
zonesLoading: false,
|
|
error: null,
|
|
setActiveStore: () => { },
|
|
refreshStores: () => { },
|
|
refreshZones: () => { },
|
|
});
|
|
|
|
export const StoreProvider = ({ children }) => {
|
|
const { token } = useContext(AuthContext);
|
|
const { activeHousehold } = useContext(HouseholdContext);
|
|
const [stores, setStores] = useState([]);
|
|
const [activeStore, setActiveStoreState] = useState(null);
|
|
const [zones, setZones] = useState([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [zonesLoading, setZonesLoading] = useState(false);
|
|
const [error, setError] = useState(null);
|
|
|
|
// Load stores when household changes
|
|
useEffect(() => {
|
|
if (token && activeHousehold) {
|
|
loadStores();
|
|
} else {
|
|
// Clear state when logged out or no household
|
|
setStores([]);
|
|
setActiveStoreState(null);
|
|
setZones([]);
|
|
}
|
|
}, [token, activeHousehold?.id]);
|
|
|
|
// Load active store from localStorage on mount (per household)
|
|
useEffect(() => {
|
|
if (!activeHousehold || stores.length === 0) return;
|
|
|
|
const householdStoreStorageKey = `activeHouseholdStoreId_${activeHousehold.id}`;
|
|
const legacyLocationStorageKey = `activeStoreId_${activeHousehold.id}`;
|
|
const savedHouseholdStoreId = localStorage.getItem(householdStoreStorageKey);
|
|
|
|
if (savedHouseholdStoreId) {
|
|
const store =
|
|
chooseLocationByStoredIds(stores, activeHousehold.id, savedHouseholdStoreId) ||
|
|
chooseStoreByHouseholdStoreId(stores, savedHouseholdStoreId);
|
|
if (store) {
|
|
setActiveStoreState(store);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const savedLocationId = localStorage.getItem(legacyLocationStorageKey);
|
|
if (savedLocationId) {
|
|
const savedLocation = stores.find(s => String(s.id) === String(savedLocationId));
|
|
const store = savedLocation || chooseStoreByHouseholdStoreId(stores, getHouseholdStoreKey(savedLocation));
|
|
if (store) {
|
|
const householdStoreId = getHouseholdStoreKey(store);
|
|
setActiveStoreState(store);
|
|
localStorage.setItem(householdStoreStorageKey, householdStoreId);
|
|
localStorage.setItem(
|
|
getLocationStorageKey(activeHousehold.id, householdStoreId),
|
|
String(store.id)
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No saved store or not found, use the default store's representative location.
|
|
const defaultStore = chooseStoreByHouseholdStoreId(
|
|
stores,
|
|
getHouseholdStoreKey(stores.find(s => s.is_default) || stores[0])
|
|
);
|
|
setActiveStoreState(defaultStore);
|
|
const householdStoreId = getHouseholdStoreKey(defaultStore);
|
|
localStorage.setItem(householdStoreStorageKey, householdStoreId);
|
|
localStorage.setItem(
|
|
getLocationStorageKey(activeHousehold.id, householdStoreId),
|
|
String(defaultStore.id)
|
|
);
|
|
}, [stores, activeHousehold]);
|
|
|
|
useEffect(() => {
|
|
if (token && activeHousehold?.id && activeStore?.id) {
|
|
loadZones();
|
|
} else {
|
|
setZones([]);
|
|
}
|
|
}, [token, activeHousehold?.id, activeStore?.id]);
|
|
|
|
const loadStores = async () => {
|
|
if (!token || !activeHousehold) return;
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const response = await getHouseholdStores(activeHousehold.id);
|
|
setStores(response.data);
|
|
} catch (err) {
|
|
console.error('[StoreContext] Failed to load stores:', err);
|
|
setError(
|
|
err.response?.data?.error?.message ||
|
|
err.response?.data?.message ||
|
|
'Failed to load stores'
|
|
);
|
|
if (!isTransientApiError(err)) {
|
|
setStores([]);
|
|
setActiveStoreState(null);
|
|
}
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const setActiveStore = (store) => {
|
|
setActiveStoreState(store);
|
|
if (store && activeHousehold) {
|
|
const storageKey = `activeHouseholdStoreId_${activeHousehold.id}`;
|
|
const householdStoreId = getHouseholdStoreKey(store);
|
|
localStorage.setItem(storageKey, householdStoreId);
|
|
localStorage.setItem(
|
|
getLocationStorageKey(activeHousehold.id, householdStoreId),
|
|
String(store.id)
|
|
);
|
|
}
|
|
};
|
|
|
|
const loadZones = async () => {
|
|
if (!token || !activeHousehold?.id || !activeStore?.id) return;
|
|
|
|
setZonesLoading(true);
|
|
try {
|
|
const response = await getLocationZones(activeHousehold.id, activeStore.id);
|
|
setZones(response.data?.zones || []);
|
|
} catch (err) {
|
|
console.error('[StoreContext] Failed to load zones:', err);
|
|
if (!isTransientApiError(err)) {
|
|
setZones([]);
|
|
}
|
|
} finally {
|
|
setZonesLoading(false);
|
|
}
|
|
};
|
|
|
|
const value = {
|
|
stores,
|
|
activeStore,
|
|
zones,
|
|
loading,
|
|
zonesLoading,
|
|
error,
|
|
setActiveStore,
|
|
refreshStores: loadStores,
|
|
refreshZones: loadZones,
|
|
};
|
|
|
|
return (
|
|
<StoreContext.Provider value={value}>
|
|
{children}
|
|
</StoreContext.Provider>
|
|
);
|
|
};
|