@zeniai/client-epic-state 5.0.71-betaVR1 → 5.0.71-betaVR3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/epic.d.ts +3 -1
- package/lib/epic.js +3 -1
- package/lib/esm/epic.js +3 -1
- package/lib/esm/index.js +2 -2
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +17 -7
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/refreshBatchDetailsForBatchIdEpic.js +52 -0
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/syncTabsAfterAutomatchEpic.js +53 -0
- package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +13 -9
- package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +75 -6
- package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +2 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +37 -34
- package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +17 -7
- package/lib/view/expenseAutomationView/epics/missingReceipts/refreshBatchDetailsForBatchIdEpic.d.ts +18 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/refreshBatchDetailsForBatchIdEpic.js +56 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/syncTabsAfterAutomatchEpic.d.ts +28 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/syncTabsAfterAutomatchEpic.js +57 -0
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +11 -5
- package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +12 -8
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +13 -1
- package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +77 -7
- package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +12 -0
- package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +2 -0
- package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +19 -0
- package/package.json +1 -1
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts
CHANGED
|
@@ -4,13 +4,19 @@ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
|
4
4
|
import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
|
|
5
5
|
import { RootState } from '../../../../reducer';
|
|
6
6
|
import { ZeniAPI } from '../../../../zeniAPI';
|
|
7
|
-
import { bulkUploadAutomatchingTimedOut, bulkUploadReceiptsSuccess, clearBulkUpload, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, pusherBatchStatusUpdate, restoreBulkUploadMatchingState } from '../../reducers/missingReceiptsViewReducer';
|
|
8
|
-
export type ActionType = ReturnType<typeof bulkUploadReceiptsSuccess> | ReturnType<typeof restoreBulkUploadMatchingState> | ReturnType<typeof pusherBatchStatusUpdate> | ReturnType<typeof fetchBulkUploadBatchDetails> | ReturnType<typeof fetchBulkUploadBatchDetailsSuccess> | ReturnType<typeof fetchBulkUploadBatches> | ReturnType<typeof updateTransactions> | ReturnType<typeof clearBulkUpload> | ReturnType<typeof openSnackbar> | ReturnType<typeof bulkUploadAutomatchingTimedOut>;
|
|
7
|
+
import { bulkUploadAutomatchingTimedOut, bulkUploadReceiptsSuccess, clearBulkUpload, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, pusherBatchStatusUpdate, refreshBatchDetailsForBatchId, restoreBulkUploadMatchingState } from '../../reducers/missingReceiptsViewReducer';
|
|
8
|
+
export type ActionType = ReturnType<typeof bulkUploadReceiptsSuccess> | ReturnType<typeof restoreBulkUploadMatchingState> | ReturnType<typeof pusherBatchStatusUpdate> | ReturnType<typeof fetchBulkUploadBatchDetails> | ReturnType<typeof fetchBulkUploadBatchDetailsSuccess> | ReturnType<typeof fetchBulkUploadBatches> | ReturnType<typeof refreshBatchDetailsForBatchId> | ReturnType<typeof updateTransactions> | ReturnType<typeof clearBulkUpload> | ReturnType<typeof openSnackbar> | ReturnType<typeof bulkUploadAutomatchingTimedOut>;
|
|
9
9
|
/**
|
|
10
|
-
* On Pusher batch completion: refresh batch list (
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* On Pusher batch completion: refresh the batch list (so summary counts update) and dispatch a
|
|
11
|
+
* targeted single-batch details refresh for the batch that just completed. Stale-while-revalidate:
|
|
12
|
+
* we no longer pass `invalidateBatchDetailsCache: true` — existing `batchDetailsById` entries stay
|
|
13
|
+
* on screen and the just-completed row replaces in place via `storeBatchDetails`. The post-match
|
|
14
|
+
* tab decision (Unmatched vs Completed) is made client-side in `ExpenseAutomationPage` once fresh
|
|
13
15
|
* batch details land -- do not dispatch `requestMissingReceiptsTabNavigation` here.
|
|
16
|
+
*
|
|
17
|
+
* Pusher resend storms (same batch completing twice within the dedupe window) are throttled in
|
|
18
|
+
* `refreshBatchDetailsForBatchIdEpic` via `lastBatchDetailRefreshAtById`, so this epic stays
|
|
19
|
+
* stateless and just forwards the event.
|
|
14
20
|
*/
|
|
15
21
|
export declare const pusherBatchStatusCompletionEpic: (actions$: ActionsObservable<ActionType>) => Observable<ActionType>;
|
|
16
22
|
/**
|
package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js
CHANGED
|
@@ -15,16 +15,20 @@ const FALLBACK_FIRST_DELAY_MS = 30000;
|
|
|
15
15
|
const RESTORE_FIRST_DELAY_MS = 5000;
|
|
16
16
|
const FALLBACK_POLL_INTERVAL_MS = 10000;
|
|
17
17
|
/**
|
|
18
|
-
* On Pusher batch completion: refresh batch list (
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* On Pusher batch completion: refresh the batch list (so summary counts update) and dispatch a
|
|
19
|
+
* targeted single-batch details refresh for the batch that just completed. Stale-while-revalidate:
|
|
20
|
+
* we no longer pass `invalidateBatchDetailsCache: true` — existing `batchDetailsById` entries stay
|
|
21
|
+
* on screen and the just-completed row replaces in place via `storeBatchDetails`. The post-match
|
|
22
|
+
* tab decision (Unmatched vs Completed) is made client-side in `ExpenseAutomationPage` once fresh
|
|
21
23
|
* batch details land -- do not dispatch `requestMissingReceiptsTabNavigation` here.
|
|
24
|
+
*
|
|
25
|
+
* Pusher resend storms (same batch completing twice within the dedupe window) are throttled in
|
|
26
|
+
* `refreshBatchDetailsForBatchIdEpic` via `lastBatchDetailRefreshAtById`, so this epic stays
|
|
27
|
+
* stateless and just forwards the event.
|
|
22
28
|
*/
|
|
23
|
-
const pusherBatchStatusCompletionEpic = (actions$) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.pusherBatchStatusUpdate.match), (0, operators_1.filter)((action) => action.payload.status === 'completed'), (0, operators_1.debounceTime)(300), (0, operators_1.mergeMap)(() => (0, rxjs_1.from)([
|
|
24
|
-
(0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)({
|
|
25
|
-
|
|
26
|
-
invalidateBatchDetailsCache: true,
|
|
27
|
-
}),
|
|
29
|
+
const pusherBatchStatusCompletionEpic = (actions$) => actions$.pipe((0, operators_1.filter)(missingReceiptsViewReducer_1.pusherBatchStatusUpdate.match), (0, operators_1.filter)((action) => action.payload.status === 'completed'), (0, operators_1.debounceTime)(300), (0, operators_1.mergeMap)((action) => (0, rxjs_1.from)([
|
|
30
|
+
(0, missingReceiptsViewReducer_1.fetchBulkUploadBatches)({ cacheOverride: true }),
|
|
31
|
+
(0, missingReceiptsViewReducer_1.refreshBatchDetailsForBatchId)({ batchId: action.payload.batchId }),
|
|
28
32
|
])));
|
|
29
33
|
exports.pusherBatchStatusCompletionEpic = pusherBatchStatusCompletionEpic;
|
|
30
34
|
/**
|
|
@@ -19,6 +19,15 @@ export declare const fetchBulkUploadBatchDetails: import("@reduxjs/toolkit").Act
|
|
|
19
19
|
* after a hard refresh. The epic owns the list + details API calls end-to-end.
|
|
20
20
|
*/
|
|
21
21
|
export declare const restoreBulkUploadAutomatchingOnMount: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/restoreBulkUploadAutomatchingOnMount">;
|
|
22
|
+
/**
|
|
23
|
+
* Epic trigger only. Targeted stale-while-revalidate refresh for a single batch's details — used
|
|
24
|
+
* by `pusherBatchStatusCompletionEpic` so a Pusher 'completed' event re-fetches only that one
|
|
25
|
+
* batch instead of wiping the entire `batchDetailsById` cache. The epic dedupes via
|
|
26
|
+
* `lastBatchDetailRefreshAtById` so resend storms don't trigger waves of API calls.
|
|
27
|
+
*/
|
|
28
|
+
export declare const refreshBatchDetailsForBatchId: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
29
|
+
batchId: ID;
|
|
30
|
+
}, string>;
|
|
22
31
|
export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[period: TimePeriod, minimumTransactionAmount: Amount, cacheOverride?: any, refreshViewInBackground?: any], {
|
|
23
32
|
period: TimePeriod;
|
|
24
33
|
minimumTransactionAmount: Amount;
|
|
@@ -73,7 +82,10 @@ export declare const fetchMissingReceipts: import("@reduxjs/toolkit").ActionCrea
|
|
|
73
82
|
}, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatchesSuccess">, fetchBulkUploadBatchesFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
74
83
|
error: ZeniAPIStatus;
|
|
75
84
|
selectedPeriod: MonthYearPeriod;
|
|
76
|
-
}, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatchesFailure">,
|
|
85
|
+
}, "expenseAutomationMissingReceiptsView/fetchBulkUploadBatchesFailure">, clearBulkUploadBatchDetailsForScopeChange: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"expenseAutomationMissingReceiptsView/clearBulkUploadBatchDetailsForScopeChange">, markBatchDetailRefreshAttempted: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
86
|
+
batchId: ID;
|
|
87
|
+
timestamp: number;
|
|
88
|
+
}, "expenseAutomationMissingReceiptsView/markBatchDetailRefreshAttempted">, confirmBulkUploadMatch: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[batchId: string, attachmentId: string, transactionId: string, transactionType: string, fileName: string], {
|
|
77
89
|
batchId: string;
|
|
78
90
|
attachmentId: string;
|
|
79
91
|
transactionId: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.
|
|
4
|
+
exports.fetchCompletedTransactions = exports.acknowledgeBulkUploadConfirmMatchComplete = exports.clearManualSearchResults = exports.searchTransactionsForManualMatchFailure = exports.searchTransactionsForManualMatchSuccess = exports.searchTransactionsForManualMatch = exports.clearBulkUpload = exports.setBulkUploadSortConfig = exports.setBulkUploadCompletedSubTab = exports.setBulkUploadResultsTab = exports.confirmBulkUploadMatchFailure = exports.confirmBulkUploadMatchSuccess = exports.confirmBulkUploadMatch = exports.markBatchDetailRefreshAttempted = exports.clearBulkUploadBatchDetailsForScopeChange = exports.fetchBulkUploadBatchesFailure = exports.fetchBulkUploadBatchesSuccess = exports.fetchBulkUploadBatches = exports.fetchMoreBatchDetailsFailure = exports.fetchMoreBatchDetailsComplete = exports.fetchMoreBatchDetails = exports.setInitialBatchDetailsLoading = exports.batchDetailFetchFailed = exports.storeBatchDetails = exports.fetchBulkUploadBatchDetailsFailure = exports.fetchBulkUploadBatchDetailsSuccess = exports.clearMissingReceiptsTabNavigation = exports.requestMissingReceiptsTabNavigation = exports.pusherBatchStatusUpdate = exports.bulkUploadAutomatchingTimedOut = exports.restoreBulkUploadMatchingState = exports.bulkUploadReceiptsFailure = exports.bulkUploadReceiptsSuccess = exports.setBulkUploadUploadedFileCount = exports.updateBulkUploadProgress = exports.bulkUploadReceipts = exports.clearExpenseAutomationMissingReceiptsView = exports.markMissingReceiptAsDone = exports.updateMissingReceiptsUIState = exports.updateMissingReceiptUploadState = exports.uploadMissingReceiptSuccess = exports.fetchMissingReceiptsFailure = exports.fetchMissingReceiptsSuccess = exports.fetchMissingReceipts = exports.refreshBatchDetailsForBatchId = exports.restoreBulkUploadAutomatchingOnMount = exports.fetchBulkUploadBatchDetails = exports.initialState = exports.initialBulkUploadState = exports.getCompletedTransactionsCacheKey = void 0;
|
|
5
|
+
exports.fetchCompletedTransactionsFailure = exports.fetchCompletedTransactionsSuccess = void 0;
|
|
5
6
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
7
|
const timePeriod_1 = require("../../../commonStateTypes/timePeriod");
|
|
7
8
|
const completedSubTab_1 = require("../types/completedSubTab");
|
|
@@ -15,6 +16,7 @@ exports.getCompletedTransactionsCacheKey = getCompletedTransactionsCacheKey;
|
|
|
15
16
|
exports.initialBulkUploadState = {
|
|
16
17
|
batchDetailsById: {},
|
|
17
18
|
batchDetailsPaginationState: { fetchState: 'Not-Started', error: undefined },
|
|
19
|
+
batchDetailsRevalidating: false,
|
|
18
20
|
batchListByPeriod: {},
|
|
19
21
|
batchListFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
20
22
|
batchStatusById: {},
|
|
@@ -24,6 +26,8 @@ exports.initialBulkUploadState = {
|
|
|
24
26
|
currentBatchId: undefined,
|
|
25
27
|
currentResultsTab: 'unmatched',
|
|
26
28
|
failedBatchDetailIds: [],
|
|
29
|
+
initialBatchDetailsLoaded: false,
|
|
30
|
+
lastBatchDetailRefreshAtById: {},
|
|
27
31
|
manualSearchUiResetKey: 0,
|
|
28
32
|
manualSearch: {
|
|
29
33
|
fetchState: { fetchState: 'Not-Started', error: undefined },
|
|
@@ -70,6 +74,13 @@ exports.fetchBulkUploadBatchDetails = (0, toolkit_1.createAction)('expenseAutoma
|
|
|
70
74
|
* after a hard refresh. The epic owns the list + details API calls end-to-end.
|
|
71
75
|
*/
|
|
72
76
|
exports.restoreBulkUploadAutomatchingOnMount = (0, toolkit_1.createAction)('expenseAutomationMissingReceiptsView/restoreBulkUploadAutomatchingOnMount');
|
|
77
|
+
/**
|
|
78
|
+
* Epic trigger only. Targeted stale-while-revalidate refresh for a single batch's details — used
|
|
79
|
+
* by `pusherBatchStatusCompletionEpic` so a Pusher 'completed' event re-fetches only that one
|
|
80
|
+
* batch instead of wiping the entire `batchDetailsById` cache. The epic dedupes via
|
|
81
|
+
* `lastBatchDetailRefreshAtById` so resend storms don't trigger waves of API calls.
|
|
82
|
+
*/
|
|
83
|
+
exports.refreshBatchDetailsForBatchId = (0, toolkit_1.createAction)('expenseAutomationMissingReceiptsView/refreshBatchDetailsForBatchId');
|
|
73
84
|
const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
74
85
|
name: 'expenseAutomationMissingReceiptsView',
|
|
75
86
|
initialState: exports.initialState,
|
|
@@ -256,6 +267,16 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
256
267
|
const details = action.payload;
|
|
257
268
|
draft.bulkUpload.batchDetailsById[details.batchId] = details;
|
|
258
269
|
draft.bulkUpload.phase = 'completed';
|
|
270
|
+
draft.bulkUpload.initialBatchDetailsLoaded = true;
|
|
271
|
+
/**
|
|
272
|
+
* A successful detail load supersedes any previous failure for this batch — without this,
|
|
273
|
+
* `getPrimaryBatchIdFromBatchListOrder` (and pagination) would keep skipping the batch
|
|
274
|
+
* across the recovery, hiding now-valid Unmatched-tab work.
|
|
275
|
+
*/
|
|
276
|
+
const failedIndex = draft.bulkUpload.failedBatchDetailIds.indexOf(details.batchId);
|
|
277
|
+
if (failedIndex >= 0) {
|
|
278
|
+
draft.bulkUpload.failedBatchDetailIds.splice(failedIndex, 1);
|
|
279
|
+
}
|
|
259
280
|
},
|
|
260
281
|
fetchBulkUploadBatchDetailsFailure: {
|
|
261
282
|
reducer(draft) {
|
|
@@ -277,6 +298,19 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
277
298
|
draft.bulkUpload.currentBatchId === details.batchId) {
|
|
278
299
|
draft.bulkUpload.phase = 'completed';
|
|
279
300
|
}
|
|
301
|
+
/** Stale-while-revalidate: any successful detail load means we have rendered data. */
|
|
302
|
+
draft.bulkUpload.initialBatchDetailsLoaded = true;
|
|
303
|
+
/**
|
|
304
|
+
* Stale-while-revalidate recovery: if this batch had previously failed, a successful
|
|
305
|
+
* targeted refresh (Pusher `completed` → `refreshBatchDetailsForBatchIdEpic`) or
|
|
306
|
+
* multi-batch revalidation (manual refresh) supersedes the failure. Selectors that gate
|
|
307
|
+
* on `failedBatchDetailIds` (primary-batch resolution, `hasMoreBatchDetails`) must see the
|
|
308
|
+
* batch as recovered so it can show fresh Unmatched work.
|
|
309
|
+
*/
|
|
310
|
+
const failedIndex = draft.bulkUpload.failedBatchDetailIds.indexOf(details.batchId);
|
|
311
|
+
if (failedIndex >= 0) {
|
|
312
|
+
draft.bulkUpload.failedBatchDetailIds.splice(failedIndex, 1);
|
|
313
|
+
}
|
|
280
314
|
},
|
|
281
315
|
batchDetailFetchFailed(draft, action) {
|
|
282
316
|
const { batchId } = action.payload;
|
|
@@ -305,6 +339,14 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
305
339
|
fetchState: 'Completed',
|
|
306
340
|
error: undefined,
|
|
307
341
|
};
|
|
342
|
+
/**
|
|
343
|
+
* Stale-while-revalidate: a completed pagination round means the initial page (or a
|
|
344
|
+
* subsequent revalidation) has fully resolved. Once `initialBatchDetailsLoaded` flips to
|
|
345
|
+
* `true` it stays `true` for the session — only `clearBulkUpload` /
|
|
346
|
+
* `clearBulkUploadBatchDetailsForScopeChange` can reset it.
|
|
347
|
+
*/
|
|
348
|
+
draft.bulkUpload.initialBatchDetailsLoaded = true;
|
|
349
|
+
draft.bulkUpload.batchDetailsRevalidating = false;
|
|
308
350
|
const currentId = draft.bulkUpload.currentBatchId;
|
|
309
351
|
const currentDetails = currentId != null
|
|
310
352
|
? draft.bulkUpload.batchDetailsById[currentId]
|
|
@@ -320,17 +362,20 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
320
362
|
fetchState: 'Error',
|
|
321
363
|
error: action.payload.error,
|
|
322
364
|
};
|
|
365
|
+
draft.bulkUpload.batchDetailsRevalidating = false;
|
|
323
366
|
},
|
|
324
367
|
fetchBulkUploadBatches: {
|
|
325
368
|
reducer(draft, action) {
|
|
369
|
+
/**
|
|
370
|
+
* Manual refresh button (and any other caller passing `invalidateBatchDetailsCache: true`):
|
|
371
|
+
* wipe the details cache, failed-id list, and pagination state so the Unmatched tab drops
|
|
372
|
+
* its existing rows and re-renders the initial-load skeleton while the next list +
|
|
373
|
+
* details fetches resolve. The post-success epic (`fetchMultipleBatchDetailsEpic`) then
|
|
374
|
+
* re-runs the details chain from a clean slate.
|
|
375
|
+
*/
|
|
326
376
|
if (action.payload.invalidateBatchDetailsCache === true) {
|
|
327
377
|
draft.bulkUpload.batchDetailsById = {};
|
|
328
378
|
draft.bulkUpload.failedBatchDetailIds = [];
|
|
329
|
-
/**
|
|
330
|
-
* Also reset pagination so the post-success epic re-runs the details chain from a
|
|
331
|
-
* clean slate. Otherwise a previously `Completed` pagination state survives the cache
|
|
332
|
-
* wipe and the Unmatched skeleton can stay up while no new details actually load.
|
|
333
|
-
*/
|
|
334
379
|
draft.bulkUpload.batchDetailsPaginationState = {
|
|
335
380
|
fetchState: 'Not-Started',
|
|
336
381
|
error: undefined,
|
|
@@ -351,6 +396,31 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
351
396
|
};
|
|
352
397
|
},
|
|
353
398
|
},
|
|
399
|
+
/**
|
|
400
|
+
* Tenant/period scope change — the new scope's data is genuinely unknown so the existing
|
|
401
|
+
* details cache and `initialBatchDetailsLoaded` flag must be reset (the Unmatched skeleton
|
|
402
|
+
* is acceptable on scope change). Distinct from a stale-while-revalidate refresh.
|
|
403
|
+
*/
|
|
404
|
+
clearBulkUploadBatchDetailsForScopeChange(draft) {
|
|
405
|
+
draft.bulkUpload.batchDetailsById = {};
|
|
406
|
+
draft.bulkUpload.failedBatchDetailIds = [];
|
|
407
|
+
draft.bulkUpload.batchDetailsPaginationState = {
|
|
408
|
+
fetchState: 'Not-Started',
|
|
409
|
+
error: undefined,
|
|
410
|
+
};
|
|
411
|
+
draft.bulkUpload.batchDetailsRevalidating = false;
|
|
412
|
+
draft.bulkUpload.initialBatchDetailsLoaded = false;
|
|
413
|
+
draft.bulkUpload.lastBatchDetailRefreshAtById = {};
|
|
414
|
+
},
|
|
415
|
+
/**
|
|
416
|
+
* Records the last time we issued a targeted refresh for `batchId`. Set by
|
|
417
|
+
* `refreshBatchDetailsForBatchIdEpic` before each API call so subsequent Pusher resends in the
|
|
418
|
+
* dedupe window are skipped.
|
|
419
|
+
*/
|
|
420
|
+
markBatchDetailRefreshAttempted(draft, action) {
|
|
421
|
+
draft.bulkUpload.lastBatchDetailRefreshAtById[action.payload.batchId] =
|
|
422
|
+
action.payload.timestamp;
|
|
423
|
+
},
|
|
354
424
|
fetchBulkUploadBatchesSuccess(draft, action) {
|
|
355
425
|
const periodId = (0, timePeriod_1.toMonthYearPeriodId)(action.payload.selectedPeriod);
|
|
356
426
|
draft.bulkUpload.batchListByPeriod[periodId] = action.payload.batchList;
|
|
@@ -593,7 +663,7 @@ const expenseAutomationMissingReceiptsView = (0, toolkit_1.createSlice)({
|
|
|
593
663
|
});
|
|
594
664
|
_a = expenseAutomationMissingReceiptsView.actions, exports.fetchMissingReceipts = _a.fetchMissingReceipts, exports.fetchMissingReceiptsSuccess = _a.fetchMissingReceiptsSuccess, exports.fetchMissingReceiptsFailure = _a.fetchMissingReceiptsFailure, exports.uploadMissingReceiptSuccess = _a.uploadMissingReceiptSuccess, exports.updateMissingReceiptUploadState = _a.updateMissingReceiptUploadState, exports.updateMissingReceiptsUIState = _a.updateMissingReceiptsUIState, exports.markMissingReceiptAsDone = _a.markMissingReceiptAsDone, exports.clearExpenseAutomationMissingReceiptsView = _a.clearExpenseAutomationMissingReceiptsView,
|
|
595
665
|
// Bulk Upload
|
|
596
|
-
exports.bulkUploadReceipts = _a.bulkUploadReceipts, exports.updateBulkUploadProgress = _a.updateBulkUploadProgress, exports.setBulkUploadUploadedFileCount = _a.setBulkUploadUploadedFileCount, exports.bulkUploadReceiptsSuccess = _a.bulkUploadReceiptsSuccess, exports.bulkUploadReceiptsFailure = _a.bulkUploadReceiptsFailure, exports.restoreBulkUploadMatchingState = _a.restoreBulkUploadMatchingState, exports.bulkUploadAutomatchingTimedOut = _a.bulkUploadAutomatchingTimedOut, exports.pusherBatchStatusUpdate = _a.pusherBatchStatusUpdate, exports.requestMissingReceiptsTabNavigation = _a.requestMissingReceiptsTabNavigation, exports.clearMissingReceiptsTabNavigation = _a.clearMissingReceiptsTabNavigation, exports.fetchBulkUploadBatchDetailsSuccess = _a.fetchBulkUploadBatchDetailsSuccess, exports.fetchBulkUploadBatchDetailsFailure = _a.fetchBulkUploadBatchDetailsFailure, exports.storeBatchDetails = _a.storeBatchDetails, exports.batchDetailFetchFailed = _a.batchDetailFetchFailed, exports.setInitialBatchDetailsLoading = _a.setInitialBatchDetailsLoading, exports.fetchMoreBatchDetails = _a.fetchMoreBatchDetails, exports.fetchMoreBatchDetailsComplete = _a.fetchMoreBatchDetailsComplete, exports.fetchMoreBatchDetailsFailure = _a.fetchMoreBatchDetailsFailure, exports.fetchBulkUploadBatches = _a.fetchBulkUploadBatches, exports.fetchBulkUploadBatchesSuccess = _a.fetchBulkUploadBatchesSuccess, exports.fetchBulkUploadBatchesFailure = _a.fetchBulkUploadBatchesFailure, exports.confirmBulkUploadMatch = _a.confirmBulkUploadMatch, exports.confirmBulkUploadMatchSuccess = _a.confirmBulkUploadMatchSuccess, exports.confirmBulkUploadMatchFailure = _a.confirmBulkUploadMatchFailure, exports.setBulkUploadResultsTab = _a.setBulkUploadResultsTab, exports.setBulkUploadCompletedSubTab = _a.setBulkUploadCompletedSubTab, exports.setBulkUploadSortConfig = _a.setBulkUploadSortConfig, exports.clearBulkUpload = _a.clearBulkUpload, exports.searchTransactionsForManualMatch = _a.searchTransactionsForManualMatch, exports.searchTransactionsForManualMatchSuccess = _a.searchTransactionsForManualMatchSuccess, exports.searchTransactionsForManualMatchFailure = _a.searchTransactionsForManualMatchFailure, exports.clearManualSearchResults = _a.clearManualSearchResults, exports.acknowledgeBulkUploadConfirmMatchComplete = _a.acknowledgeBulkUploadConfirmMatchComplete,
|
|
666
|
+
exports.bulkUploadReceipts = _a.bulkUploadReceipts, exports.updateBulkUploadProgress = _a.updateBulkUploadProgress, exports.setBulkUploadUploadedFileCount = _a.setBulkUploadUploadedFileCount, exports.bulkUploadReceiptsSuccess = _a.bulkUploadReceiptsSuccess, exports.bulkUploadReceiptsFailure = _a.bulkUploadReceiptsFailure, exports.restoreBulkUploadMatchingState = _a.restoreBulkUploadMatchingState, exports.bulkUploadAutomatchingTimedOut = _a.bulkUploadAutomatchingTimedOut, exports.pusherBatchStatusUpdate = _a.pusherBatchStatusUpdate, exports.requestMissingReceiptsTabNavigation = _a.requestMissingReceiptsTabNavigation, exports.clearMissingReceiptsTabNavigation = _a.clearMissingReceiptsTabNavigation, exports.fetchBulkUploadBatchDetailsSuccess = _a.fetchBulkUploadBatchDetailsSuccess, exports.fetchBulkUploadBatchDetailsFailure = _a.fetchBulkUploadBatchDetailsFailure, exports.storeBatchDetails = _a.storeBatchDetails, exports.batchDetailFetchFailed = _a.batchDetailFetchFailed, exports.setInitialBatchDetailsLoading = _a.setInitialBatchDetailsLoading, exports.fetchMoreBatchDetails = _a.fetchMoreBatchDetails, exports.fetchMoreBatchDetailsComplete = _a.fetchMoreBatchDetailsComplete, exports.fetchMoreBatchDetailsFailure = _a.fetchMoreBatchDetailsFailure, exports.fetchBulkUploadBatches = _a.fetchBulkUploadBatches, exports.fetchBulkUploadBatchesSuccess = _a.fetchBulkUploadBatchesSuccess, exports.fetchBulkUploadBatchesFailure = _a.fetchBulkUploadBatchesFailure, exports.clearBulkUploadBatchDetailsForScopeChange = _a.clearBulkUploadBatchDetailsForScopeChange, exports.markBatchDetailRefreshAttempted = _a.markBatchDetailRefreshAttempted, exports.confirmBulkUploadMatch = _a.confirmBulkUploadMatch, exports.confirmBulkUploadMatchSuccess = _a.confirmBulkUploadMatchSuccess, exports.confirmBulkUploadMatchFailure = _a.confirmBulkUploadMatchFailure, exports.setBulkUploadResultsTab = _a.setBulkUploadResultsTab, exports.setBulkUploadCompletedSubTab = _a.setBulkUploadCompletedSubTab, exports.setBulkUploadSortConfig = _a.setBulkUploadSortConfig, exports.clearBulkUpload = _a.clearBulkUpload, exports.searchTransactionsForManualMatch = _a.searchTransactionsForManualMatch, exports.searchTransactionsForManualMatchSuccess = _a.searchTransactionsForManualMatchSuccess, exports.searchTransactionsForManualMatchFailure = _a.searchTransactionsForManualMatchFailure, exports.clearManualSearchResults = _a.clearManualSearchResults, exports.acknowledgeBulkUploadConfirmMatchComplete = _a.acknowledgeBulkUploadConfirmMatchComplete,
|
|
597
667
|
// Completed Transactions
|
|
598
668
|
exports.fetchCompletedTransactions = _a.fetchCompletedTransactions, exports.fetchCompletedTransactionsSuccess = _a.fetchCompletedTransactionsSuccess, exports.fetchCompletedTransactionsFailure = _a.fetchCompletedTransactionsFailure;
|
|
599
669
|
exports.default = expenseAutomationMissingReceiptsView.reducer;
|
|
@@ -29,6 +29,12 @@ export interface ResolvedBatchFile {
|
|
|
29
29
|
export interface BulkUploadSelectorData {
|
|
30
30
|
batchDetails: ResolvedBatchDetails | undefined;
|
|
31
31
|
batchDetailsPaginationState: FetchStateAndError;
|
|
32
|
+
/**
|
|
33
|
+
* True while a stale-while-revalidate refresh of `batchDetailsById` is in flight (e.g. manual
|
|
34
|
+
* refresh button). UI should keep rendering existing data and show only a lightweight refresh
|
|
35
|
+
* indicator — never the full skeleton.
|
|
36
|
+
*/
|
|
37
|
+
batchDetailsRevalidating: boolean;
|
|
32
38
|
batchList: BatchListItem[];
|
|
33
39
|
batchListFetchState: FetchStateAndError;
|
|
34
40
|
batchStatus: BatchStatus | undefined;
|
|
@@ -37,6 +43,12 @@ export interface BulkUploadSelectorData {
|
|
|
37
43
|
confirmMatchStatus: FetchStateAndError;
|
|
38
44
|
currentResultsTab: BulkUploadResultsTab;
|
|
39
45
|
hasMoreBatchDetails: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* `true` once the first batch-details page has loaded for the current scope (period/tenant).
|
|
48
|
+
* Stays `true` across refreshes — flips back to `false` only on `clearBulkUpload` or on an
|
|
49
|
+
* explicit scope change. Used by the Unmatched tab to gate the initial-load skeleton.
|
|
50
|
+
*/
|
|
51
|
+
initialBatchDetailsLoaded: boolean;
|
|
40
52
|
manualSearch: ManualSearchState;
|
|
41
53
|
/** Bumps when manual search state is cleared — remount manual search inputs. */
|
|
42
54
|
manualSearchUiResetKey: number;
|
|
@@ -230,6 +230,8 @@ function getExpenseAutomationMissingReceiptsView(state) {
|
|
|
230
230
|
manualSearchUiResetKey: bulkUpload.manualSearchUiResetKey,
|
|
231
231
|
hasMoreBatchDetails,
|
|
232
232
|
batchDetailsPaginationState: bulkUpload.batchDetailsPaginationState,
|
|
233
|
+
batchDetailsRevalidating: bulkUpload.batchDetailsRevalidating,
|
|
234
|
+
initialBatchDetailsLoaded: bulkUpload.initialBatchDetailsLoaded,
|
|
233
235
|
manualSearch: {
|
|
234
236
|
...bulkUpload.manualSearch,
|
|
235
237
|
results: manualSearchResults,
|
|
@@ -129,6 +129,13 @@ export interface CompletedTransactionsState {
|
|
|
129
129
|
export interface BulkUploadState {
|
|
130
130
|
batchDetailsById: Record<ID, BatchDetails>;
|
|
131
131
|
batchDetailsPaginationState: FetchStateAndError;
|
|
132
|
+
/**
|
|
133
|
+
* True while a stale-while-revalidate refresh of `batchDetailsById` is in flight (manual refresh
|
|
134
|
+
* or scope-change replacement). Existing entries are kept on screen during this window; the UI
|
|
135
|
+
* can surface a lightweight refresh indicator. Cleared on `fetchMoreBatchDetailsComplete` /
|
|
136
|
+
* `fetchMoreBatchDetailsFailure`.
|
|
137
|
+
*/
|
|
138
|
+
batchDetailsRevalidating: boolean;
|
|
132
139
|
batchListByPeriod: Record<MonthYearPeriodId, BatchListItem[]>;
|
|
133
140
|
batchListFetchState: FetchStateAndError;
|
|
134
141
|
batchStatusById: Record<ID, BatchStatus>;
|
|
@@ -137,6 +144,18 @@ export interface BulkUploadState {
|
|
|
137
144
|
confirmMatchStatus: FetchStateAndError;
|
|
138
145
|
currentResultsTab: BulkUploadResultsTab;
|
|
139
146
|
failedBatchDetailIds: ID[];
|
|
147
|
+
/**
|
|
148
|
+
* Set to `true` once the first batch-details page has resolved (success or empty completion).
|
|
149
|
+
* Drives stale-while-revalidate: the Unmatched skeleton is shown only while this is `false`.
|
|
150
|
+
* Reset only by `clearBulkUpload` or by an explicit scope-change (period/tenant) clear.
|
|
151
|
+
*/
|
|
152
|
+
initialBatchDetailsLoaded: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Last time `GET /1.0/batches/:id` was issued for a given batch via the targeted refresh epic.
|
|
155
|
+
* Used to throttle Pusher resend storms — repeat completion events for the same batchId within
|
|
156
|
+
* the dedupe window are dropped.
|
|
157
|
+
*/
|
|
158
|
+
lastBatchDetailRefreshAtById: Record<ID, number>;
|
|
140
159
|
/**
|
|
141
160
|
* Incremented when manual search Redux state is cleared so Unmatched `ManualSearchInput`
|
|
142
161
|
* instances remount and drop local query text.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.0.71-
|
|
3
|
+
"version": "5.0.71-betaVR3",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|