grocery-app/frontend/src/context/HouseholdContext.jsx

198 lines
5.5 KiB
JavaScript

import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import {
createHousehold as createHouseholdApi,
getUserHouseholds,
reorderHouseholds as reorderHouseholdsApi,
} from '../api/households';
import { isTransientApiError } from '../api/offlineCache';
import { AuthContext } from './AuthContext';
const ACTIVE_HOUSEHOLD_STORAGE_KEY = 'activeHouseholdId';
export const HouseholdContext = createContext({
households: [],
activeHousehold: null,
loading: false,
hasLoaded: false,
error: null,
setActiveHousehold: () => { },
refreshHouseholds: () => { },
createHousehold: () => { },
reorderHouseholds: () => { },
});
export const HouseholdProvider = ({ children }) => {
const { token } = useContext(AuthContext);
const [households, setHouseholds] = useState([]);
const [activeHousehold, setActiveHouseholdState] = useState(null);
const [loading, setLoading] = useState(false);
const [hasLoaded, setHasLoaded] = useState(false);
const [error, setError] = useState(null);
const clearActiveHousehold = useCallback(() => {
setActiveHouseholdState(null);
localStorage.removeItem(ACTIVE_HOUSEHOLD_STORAGE_KEY);
}, []);
const loadHouseholds = useCallback(async () => {
if (!token) return;
setLoading(true);
setError(null);
try {
const response = await getUserHouseholds();
const nextHouseholds = Array.isArray(response.data) ? response.data : [];
setHouseholds(nextHouseholds);
if (nextHouseholds.length === 0) {
clearActiveHousehold();
}
} catch (err) {
console.error('[HouseholdContext] Failed to load households:', err);
setError(
err.response?.data?.error?.message ||
err.response?.data?.message ||
'Failed to load households'
);
if (!isTransientApiError(err)) {
setHouseholds([]);
clearActiveHousehold();
}
} finally {
setLoading(false);
setHasLoaded(true);
}
}, [clearActiveHousehold, token]);
// Load households on mount and when token changes
useEffect(() => {
if (token) {
setHasLoaded(false);
loadHouseholds();
} else {
setHouseholds([]);
clearActiveHousehold();
setError(null);
setLoading(false);
setHasLoaded(false);
}
}, [clearActiveHousehold, loadHouseholds, token]);
// Load active household from localStorage on mount
useEffect(() => {
if (households.length === 0) {
setActiveHouseholdState(null);
return;
}
const savedHouseholdId = localStorage.getItem(ACTIVE_HOUSEHOLD_STORAGE_KEY);
if (savedHouseholdId) {
const household = households.find((candidate) => String(candidate.id) === savedHouseholdId);
if (household) {
setActiveHouseholdState(household);
return;
}
}
// No saved household or not found, use first one
setActiveHouseholdState(households[0]);
localStorage.setItem(ACTIVE_HOUSEHOLD_STORAGE_KEY, String(households[0].id));
}, [households]);
const setActiveHousehold = (household) => {
setActiveHouseholdState(household);
if (household) {
localStorage.setItem(ACTIVE_HOUSEHOLD_STORAGE_KEY, String(household.id));
} else {
localStorage.removeItem(ACTIVE_HOUSEHOLD_STORAGE_KEY);
}
};
const createHousehold = async (name) => {
try {
const response = await createHouseholdApi(name);
const newHousehold = response.data.household;
// Refresh households list
await loadHouseholds();
// Set new household as active
setActiveHousehold(newHousehold);
return newHousehold;
} catch (err) {
console.error('Failed to create household:', err);
throw err;
}
};
const reorderHouseholds = async (orderedHouseholdIds) => {
const householdById = new Map(
households.map((household) => [String(household.id), household])
);
const nextHouseholds = orderedHouseholdIds
.map((householdId) => householdById.get(String(householdId)))
.filter(Boolean);
if (nextHouseholds.length !== households.length) {
throw new Error("Household order is out of date");
}
const previousHouseholds = households;
setHouseholds(nextHouseholds);
if (activeHousehold) {
const nextActiveHousehold = nextHouseholds.find(
(household) => String(household.id) === String(activeHousehold.id)
);
if (nextActiveHousehold) {
setActiveHouseholdState(nextActiveHousehold);
}
}
try {
const response = await reorderHouseholdsApi(orderedHouseholdIds);
const savedHouseholds = Array.isArray(response.data.households)
? response.data.households
: [];
if (savedHouseholds.length > 0) {
setHouseholds(savedHouseholds);
if (activeHousehold) {
const savedActiveHousehold = savedHouseholds.find(
(household) => String(household.id) === String(activeHousehold.id)
);
if (savedActiveHousehold) {
setActiveHouseholdState(savedActiveHousehold);
}
}
}
return savedHouseholds;
} catch (err) {
setHouseholds(previousHouseholds);
if (activeHousehold) {
setActiveHouseholdState(activeHousehold);
}
throw err;
}
};
const value = {
households,
activeHousehold,
loading,
hasLoaded,
error,
setActiveHousehold,
refreshHouseholds: loadHouseholds,
createHousehold,
reorderHouseholds,
};
return (
<HouseholdContext.Provider value={value}>
{children}
</HouseholdContext.Provider>
);
};