This commit is contained in:
seb
2026-10-04 09:21:45 +02:00
parent 4edd417dab
commit da318223c1
8 changed files with 315 additions and 166 deletions

View File

@@ -34,6 +34,36 @@ const initialState = {
};
let categoryAutomat;
let _treeLoadingPromise = null;
function waitForCategoryTree() {
if (categoryAutomat.state.treeStatus === 'succeeded') {
return Promise.resolve(categoryAutomat.state);
}
return new Promise((resolve, reject) => {
let unsubCat = null;
let unsubAuth = null;
const cleanup = () => {
unsubCat?.();
unsubAuth?.();
};
unsubCat = categoryAutomat.subscribe((state) => {
if (state.treeStatus === 'succeeded') {
cleanup();
resolve(state);
} else if (state.treeStatus === 'failed') {
cleanup();
reject(new Error(state.error || 'Fehler beim Laden des Kategoriebaums'));
}
});
unsubAuth = authAutomat.subscribe((auth) => {
if (auth.status === 'anon') {
cleanup();
reject(new Error('Nicht authentifiziert.'));
}
});
});
}
/**
* KeyedAutomat holding per-route category views indexed by slug path.
@@ -50,7 +80,10 @@ export const categoryViews = new KeyedAutomat(
// Ensure tree is loaded before resolving slug -> categoryId
if (categoryAutomat.state.treeStatus !== 'succeeded') {
await categoryAutomat.actions.loadTree();
if (categoryAutomat.state.treeStatus === 'idle') {
void categoryAutomat.actions.loadTree();
}
await waitForCategoryTree();
}
const { categories, rootCategoryId } = categoryAutomat.state;
@@ -117,31 +150,43 @@ export function disposeAllCategoryViewAutomats() {
const actions = {
/** Load the category tree for the currently selected company. */
async loadTree() {
const company = companyAutomat.state.current;
let company = companyAutomat.state.current;
if (!company && companyAutomat.state.status === 'idle') {
await companyAutomat.actions.load();
company = companyAutomat.state.current;
}
if (!company) return;
const { id: companyId, rootCategoryId: companyRootId } = company;
if (!companyRootId) return;
if (_treeLoadingPromise) return _treeLoadingPromise;
categoryAutomat.setState({ treeStatus: 'loading', error: null });
try {
const data = await categoryApi.getTree();
const parsed = parseCategoryTreeResponse(data, companyRootId);
categoryAutomat.setState({
treeStatus: 'succeeded',
companyId,
languageId: parsed.languageId,
categories: parsed.categories,
rootCategoryId: parsed.rootCategoryId,
error: null,
});
refreshLoadedCategoryViewChildren(parsed.categories);
} catch (err) {
categoryAutomat.setState({
treeStatus: 'failed',
error: err?.message ?? String(err),
});
throw err;
}
_treeLoadingPromise = (async () => {
try {
const data = await categoryApi.getTree();
const parsed = parseCategoryTreeResponse(data, companyRootId);
categoryAutomat.setState({
treeStatus: 'succeeded',
companyId,
languageId: parsed.languageId,
categories: parsed.categories,
rootCategoryId: parsed.rootCategoryId,
error: null,
});
refreshLoadedCategoryViewChildren(parsed.categories);
} catch (err) {
categoryAutomat.setState({
treeStatus: 'failed',
error: err?.message ?? String(err),
});
throw err;
}
})().finally(() => {
_treeLoadingPromise = null;
});
return _treeLoadingPromise;
},
/** Toggle rename/edit mode for the category grid. */
@@ -429,8 +474,11 @@ companyAutomat.subscribe((company) => {
const id = company.current?.id ?? null;
const rootCategoryId = company.current?.rootCategoryId ?? null;
if (id && rootCategoryId && id !== _lastCompanyId) {
const isCompanySwitch = _lastCompanyId !== null;
_lastCompanyId = id;
disposeAllCategoryViewAutomats();
if (isCompanySwitch) {
disposeAllCategoryViewAutomats();
}
void categoryAutomat.actions.loadTree();
}
if (!id && _lastCompanyId) {