This commit is contained in:
seb
2026-10-11 13:44:34 +02:00
parent e8b2ead83f
commit 1e2f57e941
37 changed files with 386 additions and 584 deletions

View File

@@ -49,6 +49,7 @@ import authAutomat from './authAutomat';
const initialState = {
items: [],
filter: '',
total: 0,
offset: 0,
limit: 20,
@@ -58,61 +59,83 @@ const initialState = {
let automat;
let inflightLoad = null;
const FETCH_PAGE_SIZE = 100;
// MARK: pages through GET /api/order until every order is collected
async function fetchAllOrders() {
let offset = 0;
let total = Infinity;
const collected = [];
while (offset === 0 || collected.length < total) {
const res = await orderApi.list({ offset, limit: FETCH_PAGE_SIZE });
if (offset === 0) total = Number(res?.total ?? res?.orders?.length ?? 0);
const page = Array.isArray(res?.orders) ? res.orders : [];
collected.push(...page);
offset += page.length;
if (page.length === 0) break;
}
return collected;
}
const applyAll = (orders) => ({
items: orders,
total: orders.length,
offset: 0,
limit: Math.max(orders.length, 1),
status: 'ready',
error: null,
});
/**
* Client-side filter over number, external reference and (resolved) customer name.
* @param {Order[]} items
* @param {string} filter
* @param {(order: Order) => string} [nameOf] Customer name resolver.
*/
export function filterOrders(items, filter, nameOf) {
const query = String(filter ?? '').trim().toLowerCase();
if (!query) return items;
return items.filter((o) =>
[o.number, o.externalNumber, nameOf?.(o)]
.some((v) => String(v ?? '').toLowerCase().includes(query)));
}
const actions = {
/** Sets the active search filter. */
filter(query) {
automat.setState({ filter: String(query ?? '') });
},
/**
* Loads paginated orders, deduplicating concurrent callers.
* Loads all orders, deduplicating concurrent callers.
*
* @param {{ offset?: number, limit?: number }} [params]
* @returns {Promise<Order[]>}
*/
load({ offset, limit } = {}) {
const currentOffset = offset ?? automat.state.offset ?? 0;
const currentLimit = limit ?? automat.state.limit ?? 20;
load() {
if (inflightLoad) return inflightLoad;
inflightLoad = (async () => {
automat.setState({ status: 'loading', error: null });
try {
const res = await orderApi.list({ offset: currentOffset, limit: currentLimit });
const orders = res?.orders ?? [];
automat.setState({
items: orders,
total: Number(res?.total ?? orders.length),
offset: Number(res?.offset ?? currentOffset),
limit: Number(res?.limit ?? currentLimit),
status: 'ready',
error: null,
});
automat.setState({ status: 'loading', error: null });
inflightLoad = fetchAllOrders()
.then((orders) => {
automat.setState(applyAll(orders));
return orders;
} catch (error) {
})
.catch((error) => {
automat.setState({ status: 'failed', error });
throw error;
} finally {
inflightLoad = null;
}
})();
})
.finally(() => { inflightLoad = null; });
return inflightLoad;
},
/**
* Silently re-fetches the current page of orders without flashing loading state.
* Silently re-fetches all orders without flashing loading state.
*
* @returns {Promise<Order[]>}
*/
async reload() {
const { offset, limit } = automat.state;
try {
const res = await orderApi.list({ offset, limit });
const orders = res?.orders ?? [];
automat.setState({
items: orders,
total: Number(res?.total ?? orders.length),
offset: Number(res?.offset ?? offset),
limit: Number(res?.limit ?? limit),
status: 'ready',
error: null,
});
const orders = await fetchAllOrders();
automat.setState(applyAll(orders));
return orders;
} catch (error) {
automat.setState({ error });
@@ -120,37 +143,6 @@ const actions = {
}
},
/**
* Navigates to a 1-based page using formula: offset = (page - 1) * limit
*
* @param {number} page 1-based page number.
* @returns {Promise<Order[]>}
*/
setPage(page) {
const limit = automat.state.limit || 20;
const offset = Math.max(0, (page - 1) * limit);
return actions.load({ offset, limit });
},
/**
* Sets pagination offset directly.
*
* @param {number} offset 0-based offset.
* @returns {Promise<Order[]>}
*/
setOffset(offset) {
return actions.load({ offset, limit: automat.state.limit });
},
/**
* Sets page limit (records to return) and resets offset to 0.
*
* @param {number} limit Records per page (1..100).
* @returns {Promise<Order[]>}
*/
setLimit(limit) {
return actions.load({ offset: 0, limit });
},
/**
* Ensures orders are loaded once the selected company is known.