198 lines
5.5 KiB
JavaScript
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>
|
|
);
|
|
};
|