@zeniai/client-epic-state 4.19.87-beta2AD → 4.19.87-betaVR2

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.
Files changed (88) hide show
  1. package/lib/entity/aiCfo/aiCfoSelector.d.ts +2 -1
  2. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  3. package/lib/entity/snackbar/snackbarTypes.js +1 -0
  4. package/lib/epic.d.ts +10 -2
  5. package/lib/epic.js +10 -2
  6. package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
  7. package/lib/esm/epic.js +10 -2
  8. package/lib/esm/index.js +10 -4
  9. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +1 -83
  10. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +2 -8
  11. package/lib/esm/view/expenseAutomationView/bulkUploadTiming.js +10 -0
  12. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +29 -0
  13. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +49 -0
  14. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +40 -0
  15. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +47 -0
  16. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +61 -0
  17. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +34 -0
  18. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +57 -0
  19. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +78 -0
  20. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +82 -0
  21. package/lib/esm/view/expenseAutomationView/payload/missingReceiptsPayload.js +189 -1
  22. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +426 -2
  23. package/lib/esm/view/expenseAutomationView/selectors/missingReceiptsSelector.js +246 -1
  24. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +39 -0
  25. package/lib/esm/view/spendManagement/billPay/billList/billListSelector.js +0 -10
  26. package/lib/esm/view/spendManagement/billPay/billList/billListState.js +0 -1
  27. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardList.js +0 -1
  28. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +0 -2
  29. package/lib/esm/view/spendManagement/reimbursement/remiListView/remiListSelector.js +0 -12
  30. package/lib/esm/view/spendManagement/reimbursement/remiListView/remiListState.js +0 -1
  31. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +2 -2
  32. package/lib/esm/zeniAPI.js +0 -2
  33. package/lib/index.d.ts +9 -8
  34. package/lib/index.js +80 -38
  35. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +0 -11
  36. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +1 -15
  37. package/lib/view/aiCfoView/aiCfoViewReducer.js +2 -84
  38. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +1 -4
  39. package/lib/view/aiCfoView/aiCfoViewSelector.js +2 -9
  40. package/lib/view/aiCfoView/aiCfoViewState.d.ts +0 -22
  41. package/lib/view/auditReportView/auditReportViewState.d.ts +1 -1
  42. package/lib/view/expenseAutomationView/bulkUploadTiming.d.ts +10 -0
  43. package/lib/view/expenseAutomationView/bulkUploadTiming.js +13 -0
  44. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.d.ts +18 -0
  45. package/lib/view/expenseAutomationView/epics/missingReceipts/bulkUploadReceiptsEpic.js +33 -0
  46. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.d.ts +7 -0
  47. package/lib/view/expenseAutomationView/epics/missingReceipts/confirmBulkUploadMatchEpic.js +53 -0
  48. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.d.ts +8 -0
  49. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchDetailsEpic.js +44 -0
  50. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.d.ts +7 -0
  51. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +51 -0
  52. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.d.ts +8 -0
  53. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +65 -0
  54. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.d.ts +8 -0
  55. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMoreBatchDetailsEpic.js +38 -0
  56. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.d.ts +9 -0
  57. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchMultipleBatchDetailsEpic.js +62 -0
  58. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.d.ts +14 -0
  59. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +82 -0
  60. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.d.ts +25 -0
  61. package/lib/view/expenseAutomationView/epics/missingReceipts/watchBulkUploadBatchStatusEpic.js +88 -0
  62. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  63. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.d.ts +118 -0
  64. package/lib/view/expenseAutomationView/payload/missingReceiptsPayload.js +201 -0
  65. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +76 -3
  66. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +427 -2
  67. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +69 -1
  68. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.d.ts +1 -1
  69. package/lib/view/expenseAutomationView/selectors/missingReceiptsSelector.js +245 -0
  70. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +142 -0
  71. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +49 -1
  72. package/lib/view/spendManagement/billPay/billList/billListSelector.js +0 -10
  73. package/lib/view/spendManagement/billPay/billList/billListState.d.ts +1 -1
  74. package/lib/view/spendManagement/billPay/billList/billListState.js +0 -1
  75. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.d.ts +1 -1
  76. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardList.js +0 -1
  77. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListSelector.js +0 -2
  78. package/lib/view/spendManagement/reimbursement/remiListView/remiListSelector.d.ts +0 -1
  79. package/lib/view/spendManagement/reimbursement/remiListView/remiListSelector.js +0 -12
  80. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.d.ts +1 -1
  81. package/lib/view/spendManagement/reimbursement/remiListView/remiListState.js +0 -1
  82. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +2 -2
  83. package/lib/zeniAPI.js +0 -2
  84. package/package.json +2 -2
  85. package/lib/esm/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +0 -26
  86. package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
  87. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.d.ts +0 -8
  88. package/lib/view/aiCfoView/epics/fetchSuggestedQuestionsEpic.js +0 -30
@@ -1,6 +1,38 @@
1
- import { createSlice } from '@reduxjs/toolkit';
1
+ import { createAction, createSlice } from '@reduxjs/toolkit';
2
2
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
+ import { MIN_MANUAL_TRANSACTION_SEARCH_LENGTH, } from '../types/missingReceiptsViewState';
4
+ export const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => `completed-${subTab}-${sortKey}-${sortOrder === 'ascending' ? 'asc' : 'desc'}-${periodId}`;
5
+ export const initialBulkUploadState = {
6
+ batchDetailsById: {},
7
+ batchDetailsPaginationState: { fetchState: 'Not-Started', error: undefined },
8
+ batchListByPeriod: {},
9
+ batchListFetchState: { fetchState: 'Not-Started', error: undefined },
10
+ batchStatusById: {},
11
+ completedSubTab: 'all',
12
+ completedTransactionsByPeriod: {},
13
+ confirmMatchStatus: { fetchState: 'Not-Started', error: undefined },
14
+ currentBatchId: undefined,
15
+ currentResultsTab: 'unmatched',
16
+ failedBatchDetailIds: [],
17
+ manualSearchUiResetKey: 0,
18
+ manualSearch: {
19
+ fetchState: { fetchState: 'Not-Started', error: undefined },
20
+ isLoadingMore: false,
21
+ nextPageToken: null,
22
+ pageSize: 25,
23
+ results: [],
24
+ searchQuery: '',
25
+ totalCount: 0,
26
+ },
27
+ phase: 'idle',
28
+ sortKey: 'date',
29
+ sortOrder: 'descending',
30
+ uploadProgress: 0,
31
+ uploadStatus: { fetchState: 'Not-Started', error: undefined },
32
+ };
3
33
  export const initialState = {
34
+ bulkUpload: initialBulkUploadState,
35
+ pendingMissingReceiptsTabNavigation: null,
4
36
  uploadReceiptStatusById: {},
5
37
  refreshStatus: {
6
38
  fetchState: 'Not-Started',
@@ -19,6 +51,8 @@ export const initialState = {
19
51
  return this.fetchState == 'Completed';
20
52
  },
21
53
  };
54
+ /** Epic trigger only; batch-details fetch is handled in epics. */
55
+ export const fetchBulkUploadBatchDetails = createAction('expenseAutomationMissingReceiptsView/fetchBulkUploadBatchDetails');
22
56
  const expenseAutomationMissingReceiptsView = createSlice({
23
57
  name: 'expenseAutomationMissingReceiptsView',
24
58
  initialState,
@@ -120,7 +154,397 @@ const expenseAutomationMissingReceiptsView = createSlice({
120
154
  clearExpenseAutomationMissingReceiptsView(draft) {
121
155
  Object.assign(draft, initialState);
122
156
  },
157
+ // -- Bulk Upload Actions --
158
+ bulkUploadReceipts: {
159
+ reducer(draft) {
160
+ draft.bulkUpload.phase = 'uploading';
161
+ draft.bulkUpload.uploadProgress = 0;
162
+ draft.bulkUpload.uploadStatus = {
163
+ fetchState: 'In-Progress',
164
+ error: undefined,
165
+ };
166
+ },
167
+ prepare(files) {
168
+ return { payload: { files } };
169
+ },
170
+ },
171
+ updateBulkUploadProgress(draft, action) {
172
+ draft.bulkUpload.uploadProgress = action.payload;
173
+ },
174
+ bulkUploadReceiptsSuccess(draft, action) {
175
+ draft.bulkUpload.currentBatchId = action.payload.batchId;
176
+ draft.bulkUpload.uploadProgress = 100;
177
+ draft.bulkUpload.uploadStatus = {
178
+ fetchState: 'Completed',
179
+ error: undefined,
180
+ };
181
+ draft.bulkUpload.phase = 'matching';
182
+ },
183
+ bulkUploadReceiptsFailure(draft, action) {
184
+ draft.bulkUpload.uploadStatus = {
185
+ fetchState: 'Error',
186
+ error: action.payload.error,
187
+ };
188
+ draft.bulkUpload.phase = 'idle';
189
+ },
190
+ bulkUploadAutomatchingTimedOut: {
191
+ reducer(draft, action) {
192
+ if (draft.bulkUpload.phase !== 'matching' ||
193
+ draft.bulkUpload.currentBatchId !== action.payload.batchId) {
194
+ return;
195
+ }
196
+ draft.bulkUpload.phase = 'completed';
197
+ },
198
+ prepare(batchId) {
199
+ return { payload: { batchId } };
200
+ },
201
+ },
202
+ pusherBatchStatusUpdate(draft, action) {
203
+ const batchStatus = action.payload;
204
+ draft.bulkUpload.batchStatusById[batchStatus.batchId] = batchStatus;
205
+ /**
206
+ * Auto-matching banner (`phase === 'matching'`) must clear when the server reports this
207
+ * upload batch is done — otherwise the Missing tab keeps MatchingProgressBanner until
208
+ * batch-details APIs run; EmailUploadBanner should show again for the next upload.
209
+ */
210
+ if (batchStatus.status === 'completed' &&
211
+ draft.bulkUpload.phase === 'matching' &&
212
+ draft.bulkUpload.currentBatchId === batchStatus.batchId) {
213
+ draft.bulkUpload.phase = 'completed';
214
+ }
215
+ },
216
+ requestMissingReceiptsTabNavigation(draft, action) {
217
+ draft.pendingMissingReceiptsTabNavigation = action.payload.tab;
218
+ },
219
+ clearMissingReceiptsTabNavigation(draft) {
220
+ draft.pendingMissingReceiptsTabNavigation = null;
221
+ },
222
+ fetchBulkUploadBatchDetailsSuccess(draft, action) {
223
+ const details = action.payload;
224
+ draft.bulkUpload.batchDetailsById[details.batchId] = details;
225
+ draft.bulkUpload.phase = 'completed';
226
+ },
227
+ fetchBulkUploadBatchDetailsFailure: {
228
+ reducer(draft) {
229
+ draft.bulkUpload.phase = 'completed';
230
+ },
231
+ prepare(payload) {
232
+ return { payload };
233
+ },
234
+ },
235
+ storeBatchDetails(draft, action) {
236
+ const details = action.payload;
237
+ draft.bulkUpload.batchDetailsById[details.batchId] = details;
238
+ /**
239
+ * Multi-batch fetch paths use `storeBatchDetails` (not `fetchBulkUploadBatchDetailsSuccess`).
240
+ * Without this, `phase` stays `matching` after upload forever and the Unmatched tab keeps
241
+ * showing the skeleton (`bulkUploadPhase === 'matching'` in web-components).
242
+ */
243
+ if (draft.bulkUpload.phase === 'matching' &&
244
+ draft.bulkUpload.currentBatchId === details.batchId) {
245
+ draft.bulkUpload.phase = 'completed';
246
+ }
247
+ },
248
+ batchDetailFetchFailed(draft, action) {
249
+ const { batchId } = action.payload;
250
+ if (!draft.bulkUpload.failedBatchDetailIds.includes(batchId)) {
251
+ draft.bulkUpload.failedBatchDetailIds.push(batchId);
252
+ }
253
+ if (draft.bulkUpload.phase === 'matching' &&
254
+ draft.bulkUpload.currentBatchId === batchId) {
255
+ draft.bulkUpload.phase = 'completed';
256
+ }
257
+ },
258
+ setInitialBatchDetailsLoading(draft) {
259
+ draft.bulkUpload.batchDetailsPaginationState = {
260
+ fetchState: 'In-Progress',
261
+ error: undefined,
262
+ };
263
+ },
264
+ fetchMoreBatchDetails(draft) {
265
+ draft.bulkUpload.batchDetailsPaginationState = {
266
+ fetchState: 'In-Progress',
267
+ error: undefined,
268
+ };
269
+ },
270
+ fetchMoreBatchDetailsComplete(draft) {
271
+ draft.bulkUpload.batchDetailsPaginationState = {
272
+ fetchState: 'Completed',
273
+ error: undefined,
274
+ };
275
+ const currentId = draft.bulkUpload.currentBatchId;
276
+ const currentDetails = currentId != null
277
+ ? draft.bulkUpload.batchDetailsById[currentId]
278
+ : undefined;
279
+ if (draft.bulkUpload.phase === 'matching' &&
280
+ currentId != null &&
281
+ currentDetails?.status === 'completed') {
282
+ draft.bulkUpload.phase = 'completed';
283
+ }
284
+ },
285
+ fetchMoreBatchDetailsFailure(draft, action) {
286
+ draft.bulkUpload.batchDetailsPaginationState = {
287
+ fetchState: 'Error',
288
+ error: action.payload.error,
289
+ };
290
+ },
291
+ fetchBulkUploadBatches: {
292
+ reducer(draft, action) {
293
+ if (action.payload.invalidateBatchDetailsCache === true) {
294
+ draft.bulkUpload.batchDetailsById = {};
295
+ draft.bulkUpload.failedBatchDetailIds = [];
296
+ }
297
+ draft.bulkUpload.batchListFetchState = {
298
+ fetchState: 'In-Progress',
299
+ error: undefined,
300
+ };
301
+ },
302
+ prepare(input) {
303
+ const fetchOptions = input ?? {};
304
+ return {
305
+ payload: {
306
+ cacheOverride: fetchOptions.cacheOverride === true,
307
+ invalidateBatchDetailsCache: fetchOptions.invalidateBatchDetailsCache === true,
308
+ },
309
+ };
310
+ },
311
+ },
312
+ fetchBulkUploadBatchesSuccess(draft, action) {
313
+ const periodId = toMonthYearPeriodId(action.payload.selectedPeriod);
314
+ draft.bulkUpload.batchListByPeriod[periodId] = action.payload.batchList;
315
+ draft.bulkUpload.batchListFetchState = {
316
+ fetchState: 'Completed',
317
+ error: undefined,
318
+ };
319
+ /**
320
+ * When the multi-batch-details epic has nothing to fetch (e.g. list rows not `completed`
321
+ * yet, or details already present), `phase` can stay `matching` even though we already
322
+ * have completed details for `currentBatchId` (from polling / Pusher). Sync so the UI can
323
+ * dismiss the Unmatched skeleton.
324
+ */
325
+ const currentId = draft.bulkUpload.currentBatchId;
326
+ if (draft.bulkUpload.phase === 'matching' &&
327
+ currentId != null) {
328
+ const existing = draft.bulkUpload.batchDetailsById[currentId];
329
+ if (existing?.status === 'completed') {
330
+ draft.bulkUpload.phase = 'completed';
331
+ }
332
+ }
333
+ },
334
+ fetchBulkUploadBatchesFailure(draft, action) {
335
+ draft.bulkUpload.batchListFetchState = {
336
+ fetchState: 'Error',
337
+ error: action.payload.error,
338
+ };
339
+ },
340
+ confirmBulkUploadMatch: {
341
+ reducer(draft) {
342
+ draft.bulkUpload.confirmMatchStatus = {
343
+ fetchState: 'In-Progress',
344
+ error: undefined,
345
+ };
346
+ },
347
+ prepare(batchId, attachmentId, transactionId, transactionType, fileName) {
348
+ return { payload: { batchId, attachmentId, transactionId, transactionType, fileName } };
349
+ },
350
+ },
351
+ confirmBulkUploadMatchSuccess(draft, action) {
352
+ const { batchId, attachmentId, transactionId } = action.payload;
353
+ draft.bulkUpload.confirmMatchStatus = {
354
+ fetchState: 'Completed',
355
+ error: undefined,
356
+ };
357
+ const details = draft.bulkUpload.batchDetailsById[batchId];
358
+ if (details != null) {
359
+ const file = details.files.find((batchFile) => batchFile.attachmentId === attachmentId);
360
+ if (file != null) {
361
+ const originalStatus = file.status;
362
+ file.status = 'exact_match';
363
+ file.matchedTransactionId = transactionId;
364
+ file.candidates = undefined;
365
+ details.summary.matched += 1;
366
+ if (originalStatus === 'no_match') {
367
+ details.summary.noMatch = Math.max(0, details.summary.noMatch - 1);
368
+ }
369
+ else if (originalStatus === 'possible_matches') {
370
+ details.summary.possibleMatches = Math.max(0, details.summary.possibleMatches - 1);
371
+ }
372
+ }
373
+ }
374
+ draft.bulkUpload.manualSearch = initialBulkUploadState.manualSearch;
375
+ draft.bulkUpload.manualSearchUiResetKey += 1;
376
+ },
377
+ confirmBulkUploadMatchFailure(draft, action) {
378
+ draft.bulkUpload.confirmMatchStatus = {
379
+ fetchState: 'Error',
380
+ error: action.payload.error,
381
+ };
382
+ },
383
+ setBulkUploadResultsTab(draft, action) {
384
+ draft.bulkUpload.currentResultsTab = action.payload;
385
+ },
386
+ setBulkUploadCompletedSubTab(draft, action) {
387
+ draft.bulkUpload.completedSubTab = action.payload;
388
+ },
389
+ setBulkUploadSortConfig(draft, action) {
390
+ draft.bulkUpload.sortKey = action.payload.sortKey;
391
+ draft.bulkUpload.sortOrder = action.payload.sortOrder;
392
+ },
393
+ clearBulkUpload(draft) {
394
+ Object.assign(draft.bulkUpload, initialBulkUploadState);
395
+ draft.pendingMissingReceiptsTabNavigation = null;
396
+ },
397
+ searchTransactionsForManualMatch: {
398
+ reducer(draft, action) {
399
+ const { query, pageToken } = action.payload;
400
+ const isLoadMore = pageToken != null;
401
+ const trimmed = query.trim();
402
+ /**
403
+ * Cleared or too-short query: no API (see epic). Reset fully (no In-Progress).
404
+ * Only bump `manualSearchUiResetKey` when clearing a real search — not when the UI
405
+ * dispatches `""` on every keystroke while length is below MIN (web-components always sends
406
+ * `onSearch("")` in that case), or ReceiptCard remounts and the input loses focus.
407
+ */
408
+ if (!isLoadMore &&
409
+ trimmed.length < MIN_MANUAL_TRANSACTION_SEARCH_LENGTH) {
410
+ const hadActiveSearch = draft.bulkUpload.manualSearch.searchQuery.trim().length >=
411
+ MIN_MANUAL_TRANSACTION_SEARCH_LENGTH ||
412
+ draft.bulkUpload.manualSearch.results.length > 0;
413
+ draft.bulkUpload.manualSearch = initialBulkUploadState.manualSearch;
414
+ if (hadActiveSearch) {
415
+ draft.bulkUpload.manualSearchUiResetKey += 1;
416
+ }
417
+ return;
418
+ }
419
+ draft.bulkUpload.manualSearch.searchQuery = query;
420
+ if (isLoadMore) {
421
+ draft.bulkUpload.manualSearch.isLoadingMore = true;
422
+ }
423
+ else {
424
+ draft.bulkUpload.manualSearch.isLoadingMore = false;
425
+ draft.bulkUpload.manualSearch.results = [];
426
+ draft.bulkUpload.manualSearch.nextPageToken = null;
427
+ draft.bulkUpload.manualSearch.totalCount = 0;
428
+ draft.bulkUpload.manualSearch.fetchState = {
429
+ fetchState: 'In-Progress',
430
+ error: undefined,
431
+ };
432
+ }
433
+ },
434
+ prepare(query, pageToken) {
435
+ return { payload: { query, pageToken: pageToken ?? undefined } };
436
+ },
437
+ },
438
+ searchTransactionsForManualMatchSuccess(draft, action) {
439
+ const { append, nextPageToken, results, totalCount } = action.payload;
440
+ if (append) {
441
+ const merged = [...draft.bulkUpload.manualSearch.results, ...results];
442
+ const byId = new Map(merged.map((result) => [result.transactionId, result]));
443
+ draft.bulkUpload.manualSearch.results = Array.from(byId.values());
444
+ }
445
+ else {
446
+ draft.bulkUpload.manualSearch.results = results;
447
+ }
448
+ draft.bulkUpload.manualSearch.nextPageToken = nextPageToken;
449
+ draft.bulkUpload.manualSearch.totalCount = totalCount;
450
+ draft.bulkUpload.manualSearch.isLoadingMore = false;
451
+ draft.bulkUpload.manualSearch.fetchState = {
452
+ fetchState: 'Completed',
453
+ error: undefined,
454
+ };
455
+ },
456
+ searchTransactionsForManualMatchFailure(draft, action) {
457
+ draft.bulkUpload.manualSearch.isLoadingMore = false;
458
+ draft.bulkUpload.manualSearch.fetchState = {
459
+ fetchState: 'Error',
460
+ error: action.payload.error,
461
+ };
462
+ },
463
+ clearManualSearchResults(draft) {
464
+ draft.bulkUpload.manualSearch = initialBulkUploadState.manualSearch;
465
+ draft.bulkUpload.manualSearchUiResetKey += 1;
466
+ },
467
+ acknowledgeBulkUploadConfirmMatchComplete(draft) {
468
+ draft.bulkUpload.confirmMatchStatus = {
469
+ fetchState: 'Not-Started',
470
+ error: undefined,
471
+ };
472
+ },
473
+ // -- Completed Transactions Actions --
474
+ fetchCompletedTransactions: {
475
+ reducer(draft, action) {
476
+ const { pageToken } = action.payload;
477
+ if (pageToken != null) {
478
+ const periods = draft.bulkUpload.completedTransactionsByPeriod;
479
+ for (const entry of Object.values(periods)) {
480
+ if (entry.nextPageToken === pageToken) {
481
+ entry.fetchState = {
482
+ fetchState: 'In-Progress',
483
+ error: undefined,
484
+ };
485
+ break;
486
+ }
487
+ }
488
+ }
489
+ },
490
+ prepare(pageToken, cacheOverride) {
491
+ return { payload: { pageToken, cacheOverride } };
492
+ },
493
+ },
494
+ fetchCompletedTransactionsSuccess(draft, action) {
495
+ const { transactionIds, nextPageToken, totalCount, selectedPeriod, isInitialLoad } = action.payload;
496
+ const periodId = toMonthYearPeriodId(selectedPeriod);
497
+ const cacheKey = getCompletedTransactionsCacheKey(periodId, draft.bulkUpload.sortKey, draft.bulkUpload.sortOrder, draft.bulkUpload.completedSubTab);
498
+ if (isInitialLoad) {
499
+ draft.bulkUpload.completedTransactionsByPeriod[cacheKey] = {
500
+ transactionIds,
501
+ nextPageToken,
502
+ totalCount,
503
+ fetchState: { fetchState: 'Completed', error: undefined },
504
+ };
505
+ }
506
+ else {
507
+ const existing = draft.bulkUpload.completedTransactionsByPeriod[cacheKey];
508
+ if (existing != null) {
509
+ existing.transactionIds.push(...transactionIds);
510
+ existing.nextPageToken = nextPageToken;
511
+ existing.totalCount = totalCount;
512
+ existing.fetchState = { fetchState: 'Completed', error: undefined };
513
+ }
514
+ else {
515
+ draft.bulkUpload.completedTransactionsByPeriod[cacheKey] = {
516
+ transactionIds,
517
+ nextPageToken,
518
+ totalCount,
519
+ fetchState: { fetchState: 'Completed', error: undefined },
520
+ };
521
+ }
522
+ }
523
+ },
524
+ fetchCompletedTransactionsFailure(draft, action) {
525
+ const periodId = toMonthYearPeriodId(action.payload.selectedPeriod);
526
+ const cacheKey = getCompletedTransactionsCacheKey(periodId, draft.bulkUpload.sortKey, draft.bulkUpload.sortOrder, draft.bulkUpload.completedSubTab);
527
+ const existing = draft.bulkUpload.completedTransactionsByPeriod[cacheKey];
528
+ if (existing != null) {
529
+ existing.fetchState = {
530
+ fetchState: 'Error',
531
+ error: action.payload.error,
532
+ };
533
+ }
534
+ else {
535
+ draft.bulkUpload.completedTransactionsByPeriod[cacheKey] = {
536
+ fetchState: { fetchState: 'Error', error: action.payload.error },
537
+ nextPageToken: null,
538
+ totalCount: 0,
539
+ transactionIds: [],
540
+ };
541
+ }
542
+ },
123
543
  },
124
544
  });
125
- export const { fetchMissingReceipts, fetchMissingReceiptsSuccess, fetchMissingReceiptsFailure, uploadMissingReceiptSuccess, updateMissingReceiptUploadState, updateMissingReceiptsUIState, markMissingReceiptAsDone, clearExpenseAutomationMissingReceiptsView, } = expenseAutomationMissingReceiptsView.actions;
545
+ export const { fetchMissingReceipts, fetchMissingReceiptsSuccess, fetchMissingReceiptsFailure, uploadMissingReceiptSuccess, updateMissingReceiptUploadState, updateMissingReceiptsUIState, markMissingReceiptAsDone, clearExpenseAutomationMissingReceiptsView,
546
+ // Bulk Upload
547
+ bulkUploadReceipts, updateBulkUploadProgress, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, bulkUploadAutomatchingTimedOut, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, batchDetailFetchFailed, setInitialBatchDetailsLoading, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete,
548
+ // Completed Transactions
549
+ fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, } = expenseAutomationMissingReceiptsView.actions;
126
550
  export default expenseAutomationMissingReceiptsView.reducer;