initial commit

This commit is contained in:
2026-08-26 15:01:15 +05:30
parent aa42d9ee81
commit cf4f4ecd9c
87 changed files with 12814 additions and 2328 deletions

View File

@@ -66,39 +66,47 @@ const getPageCached = (page) => {
return promise;
};
export const fetchBookingsInRange = async (start, end) => {
const firstPage = await getPageCached(1);
/**
* Drain pages up to the budget and return every row, unfiltered.
*
* `makeStop` receives page 1's rows once and returns the per-page early-stop
* predicate, so a range scan can bail as soon as it has read past its window
* while an unfiltered drain simply reads to the budget.
*
* `fetchPage` is injectable because the page cache below is the wrong layer for
* a caller that already has one. A TanStack-managed screen sets its own refetch
* interval, and serving it from a 20s module-level cache would silently cap how
* fresh that screen can ever be.
*/
const drainPages = async (makeStop, fetchPage = getPageCached) => {
const firstPage = await fetchPage(1);
const total = firstPage.total;
const pageCount = Math.max(1, Math.ceil(total / BULK_PAGESIZE));
const pageSize = Math.max(1, firstPage.rows?.length || 100);
const pageCount = Math.max(1, Math.ceil(total / pageSize));
const budget = Math.min(pageCount, MAX_PAGES);
const collected = [...firstPage.rows];
const descending = isDescendingByCreatedAt(firstPage.rows);
const collected = [...(firstPage.rows || [])];
const shouldStop = makeStop ? makeStop(firstPage.rows) : () => false;
/* Newest-first and this page already ends before the window opens → every
later page is older still, so there is nothing left to find. */
const pageEndsBeforeRange = (rows) => {
if (!descending || !rows.length) return false;
const oldest = parseDoormileTimestamp(rows[rows.length - 1].createdat);
return oldest.isValid() && oldest.format('YYYY-MM-DD') < start;
};
let stoppedEarly = pageEndsBeforeRange(firstPage.rows);
let stoppedEarly = shouldStop(firstPage.rows) || collected.length >= total;
let lastPageFetched = 1;
for (let page = 2; page <= budget && !stoppedEarly; page += 1) {
const next = await getPageCached(page);
const next = await fetchPage(page);
lastPageFetched = page;
if (!next.rows.length) {
if (!next.rows?.length) {
stoppedEarly = true;
break;
}
collected.push(...next.rows);
stoppedEarly = pageEndsBeforeRange(next.rows);
if (collected.length >= total) {
break;
}
stoppedEarly = shouldStop(next.rows);
}
return {
rows: collected.filter((booking) => inRange(booking, start, end)),
rows: collected,
/* Truncated only if the budget ran out with pages still unread AND the scan
did not stop early because it had already passed the window. */
truncated: !stoppedEarly && pageCount > budget,
@@ -108,6 +116,35 @@ export const fetchBookingsInRange = async (start, end) => {
};
};
export const fetchBookingsInRange = async (start, end) => {
const scan = await drainPages((firstRows) => {
const descending = isDescendingByCreatedAt(firstRows);
/* Newest-first and this page already ends before the window opens → every
later page is older still, so there is nothing left to find. */
return (rows) => {
if (!descending || !rows.length) return false;
const oldest = parseDoormileTimestamp(rows[rows.length - 1].createdat);
return oldest.isValid() && oldest.format('YYYY-MM-DD') < start;
};
});
/* `scanned` deliberately stays the pre-filter count — it describes how much of
the account was read, which is what `truncated` has to be judged against. */
return { ...scan, rows: scan.rows.filter((booking) => inRange(booking, start, end)) };
};
/**
* Every booking the account has, up to the page budget, with **no date filter**.
*
* For screens that show the whole list rather than a window. Deliberately not
* `fetchBookingsInRange` with sentinel bounds: that path still runs `inRange`,
* which drops any row whose `createdat` will not parse. Dropping a row from a
* date-scoped answer is defensible; dropping it from "every order" is not — the
* row exists and the operator has to be able to see it.
*/
export const drainBookings = ({ cached = true } = {}) =>
drainPages(undefined, cached ? getPageCached : (page) => getBookingsPage(page, BULK_PAGESIZE));
export const fetchBookingsForDay = (day) => fetchBookingsInRange(day, day);
/**