u
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user