@zeniai/client-epic-state 5.0.71 → 5.0.72

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 (125) hide show
  1. package/lib/coreEpics.js +1 -1
  2. package/lib/entity/account/accountSelector.d.ts +1 -1
  3. package/lib/entity/account/accountState.d.ts +1 -1
  4. package/lib/entity/account/subAccountSelector.d.ts +1 -1
  5. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +3 -1
  6. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +2 -1
  7. package/lib/entity/aiAccountantCustomer/aiAccountantCustomerSelector.d.ts +1 -1
  8. package/lib/entity/chargeCard/chargeCardReducer.d.ts +2 -19
  9. package/lib/entity/chargeCard/chargeCardReducer.js +2 -35
  10. package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.d.ts +1 -14
  11. package/lib/entity/chargeCardTransaction/chargeCardTransactionReducer.js +2 -36
  12. package/lib/entity/sectionProjectView/sectionProjectViewReducer.d.ts +1 -1
  13. package/lib/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
  14. package/lib/entity/tenant/SessionManager.js +3 -6
  15. package/lib/entity/tenant/clearAllEpic.d.ts +2 -2
  16. package/lib/entity/tenant/clearAllEpic.js +2 -2
  17. package/lib/entity/tenant/epic/saveAPIKeyConnectionEpic.js +2 -6
  18. package/lib/entity/tenant/epic/saveOAuthConnectionEpic.js +2 -6
  19. package/lib/entity/tenant/tenantReducer.js +1 -6
  20. package/lib/entity/transaction/payloadTypes/transactionLinePayload.js +1 -3
  21. package/lib/epic.d.ts +9 -9
  22. package/lib/epic.js +10 -10
  23. package/lib/esm/coreEpics.js +1 -1
  24. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerPayload.js +3 -1
  25. package/lib/esm/entity/aiAccountantCustomer/aiAccountantCustomerReducer.js +2 -1
  26. package/lib/esm/entity/chargeCard/chargeCardReducer.js +1 -34
  27. package/lib/esm/entity/chargeCardTransaction/chargeCardTransactionReducer.js +1 -35
  28. package/lib/esm/entity/sectionProjectView/sectionProjectViewSelector.js +1 -1
  29. package/lib/esm/entity/tenant/SessionManager.js +1 -4
  30. package/lib/esm/entity/tenant/clearAllEpic.js +2 -2
  31. package/lib/esm/entity/tenant/epic/saveAPIKeyConnectionEpic.js +2 -6
  32. package/lib/esm/entity/tenant/epic/saveOAuthConnectionEpic.js +2 -6
  33. package/lib/esm/entity/tenant/tenantReducer.js +1 -6
  34. package/lib/esm/entity/transaction/payloadTypes/transactionLinePayload.js +1 -3
  35. package/lib/esm/epic.js +10 -10
  36. package/lib/esm/index.js +13 -17
  37. package/lib/esm/reducer.js +2 -2
  38. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +5 -2
  39. package/lib/esm/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +5 -2
  40. package/lib/esm/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +1 -4
  41. package/lib/esm/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
  42. package/lib/esm/view/companyTaskManagerView/epics/fetchTaskManagerMetricsEpic.js +1 -1
  43. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
  44. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +1 -2
  45. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +6 -8
  46. package/lib/esm/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +1 -7
  47. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +1 -11
  48. package/lib/esm/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +1 -2
  49. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +1 -127
  50. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +1 -20
  51. package/lib/esm/view/expenseAutomationView/types/missingReceiptsViewState.js +2 -0
  52. package/lib/esm/view/financeStatement/financeStatementSelector.js +1 -1
  53. package/lib/esm/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +2 -3
  54. package/lib/esm/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +2 -3
  55. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +18 -55
  56. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +2 -16
  57. package/lib/esm/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +4 -5
  58. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +9 -26
  59. package/lib/esm/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +6 -9
  60. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +2 -3
  61. package/lib/esm/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
  62. package/lib/esm/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +3 -7
  63. package/lib/esm/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.js +1 -1
  64. package/lib/esm/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.js +1 -1
  65. package/lib/esm/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +1 -1
  66. package/lib/esm/view/transactionDetail/transactionDetailSelector.js +1 -7
  67. package/lib/index.d.ts +15 -19
  68. package/lib/index.js +58 -72
  69. package/lib/reducer.d.ts +2 -2
  70. package/lib/reducer.js +2 -2
  71. package/lib/tsconfig.typecheck.tsbuildinfo +1 -0
  72. package/lib/view/aiAccountantView/aiAccountantViewPayload.d.ts +2 -2
  73. package/lib/view/aiAccountantView/aiAccountantViewReducer.d.ts +1 -1
  74. package/lib/view/aiAccountantView/epics/fetchAiAccountantCustomersEpic.js +5 -2
  75. package/lib/view/aiAccountantView/epics/fetchAiAccountantJobsEpic.js +5 -2
  76. package/lib/view/aiAccountantView/epics/triggerAiAccountantJobEpic.js +1 -4
  77. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.d.ts +1 -1
  78. package/lib/view/companyTaskManagerView/companyTaskManagerViewSelector.js +1 -1
  79. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchBulkUploadBatchesEpic.js +2 -2
  80. package/lib/view/expenseAutomationView/epics/missingReceipts/fetchCompletedTransactionsEpic.js +1 -2
  81. package/lib/view/expenseAutomationView/epics/missingReceipts/restoreBulkUploadAutomatchingOnMountEpic.js +6 -8
  82. package/lib/view/expenseAutomationView/epics/missingReceipts/searchTransactionsForManualMatchEpic.js +1 -7
  83. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.d.ts +2 -2
  84. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +0 -10
  85. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  86. package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +0 -21
  87. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +1 -2
  88. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.js +1 -2
  89. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +3 -9
  90. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +2 -128
  91. package/lib/view/expenseAutomationView/selectorTypes/missingReceiptsSelectorTypes.d.ts +1 -2
  92. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +0 -14
  93. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +1 -20
  94. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.d.ts +2 -1
  95. package/lib/view/expenseAutomationView/types/missingReceiptsViewState.js +4 -1
  96. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +2 -41
  97. package/lib/view/financeStatement/financeStatementSelector.js +1 -1
  98. package/lib/view/profitAndLossProjectView/profitAndLossProjectViewSelectorTypes.d.ts +1 -1
  99. package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.d.ts +1 -3
  100. package/lib/view/spendManagement/chargeCards/cashbackDetail/cashbackDetailReducer.js +2 -3
  101. package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.d.ts +1 -3
  102. package/lib/view/spendManagement/chargeCards/chargeCardConfig/chargeCardConfigReducer.js +2 -3
  103. package/lib/view/spendManagement/chargeCards/chargeCardDetail/chargeCardDetailReducer.js +18 -55
  104. package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailEpic.js +2 -16
  105. package/lib/view/spendManagement/chargeCards/chargeCardDetail/fetchChargeCardDetailPageEpic.js +4 -5
  106. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.d.ts +3 -18
  107. package/lib/view/spendManagement/chargeCards/chargeCardList/chargeCardListReducer.js +10 -27
  108. package/lib/view/spendManagement/chargeCards/chargeCardList/fetchChargeCardListPageEpic.js +6 -9
  109. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.d.ts +1 -3
  110. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/chargeCardPaymentHistoryReducer.js +2 -3
  111. package/lib/view/spendManagement/chargeCards/chargeCardPaymentHistory/fetchChargeCardPaymentPageEpic.js +1 -1
  112. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.d.ts +1 -3
  113. package/lib/view/spendManagement/chargeCards/chargeCardRepaymentDetail/chargeCardRepaymentDetailReducer.js +3 -7
  114. package/lib/view/transactionDetail/transactionDetailSelector.js +1 -7
  115. package/lib/view/zeniOAuthView/zeniOAuthSelector.d.ts +1 -1
  116. package/package.json +4 -9
  117. package/lib/commonStateTypes/cacheOverrideFetchReducer.d.ts +0 -17
  118. package/lib/commonStateTypes/cacheOverrideFetchReducer.js +0 -20
  119. package/lib/commonStateTypes/pusherActions.d.ts +0 -11
  120. package/lib/commonStateTypes/pusherActions.js +0 -6
  121. package/lib/esm/commonStateTypes/cacheOverrideFetchReducer.js +0 -16
  122. package/lib/esm/commonStateTypes/pusherActions.js +0 -3
  123. package/lib/esm/view/expenseAutomationView/types/completedSubTab.js +0 -17
  124. package/lib/view/expenseAutomationView/types/completedSubTab.d.ts +0 -10
  125. package/lib/view/expenseAutomationView/types/completedSubTab.js +0 -21
@@ -1,6 +1,6 @@
1
1
  import { of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
- import { isSuccessResponse } from '../../../responsePayload';
3
+ import { isSuccessResponse, } from '../../../responsePayload';
4
4
  import { fetchTaskManagerMetrics, updateTaskManagerMetrics, updateTaskManagerMetricsOnFailure, } from '../companyTaskManagerViewReducer';
5
5
  export const fetchTaskManagerMetricsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchTaskManagerMetrics.match), switchMap(() => {
6
6
  const query = JSON.stringify({ is_filter_by_logged_in_user: true });
@@ -3,10 +3,10 @@ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
- import { BULK_UPLOAD_BATCH_LIST_DATE_RANGE_DAYS } from '../../helpers/bulkUploadTiming';
7
- import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
8
6
  import { toBatchListItem, } from '../../payload/missingReceiptsPayload';
9
7
  import { fetchBulkUploadBatches, fetchBulkUploadBatchesFailure, fetchBulkUploadBatchesSuccess, } from '../../reducers/missingReceiptsViewReducer';
8
+ import { BULK_UPLOAD_BATCH_LIST_DATE_RANGE_DAYS } from '../../helpers/bulkUploadTiming';
9
+ import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
10
10
  export const fetchBulkUploadBatchesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchBulkUploadBatches.match), switchMap((action) => {
11
11
  const { cacheOverride } = action.payload;
12
12
  const state = state$.value;
@@ -5,7 +5,6 @@ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
6
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
7
7
  import { fetchCompletedTransactions, fetchCompletedTransactionsFailure, fetchCompletedTransactionsSuccess, getCompletedTransactionsCacheKey, } from '../../reducers/missingReceiptsViewReducer';
8
- import { DEFAULT_COMPLETED_SUB_TAB } from '../../types/completedSubTab';
9
8
  export const fetchCompletedTransactionsEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchCompletedTransactions.match), switchMap((action) => {
10
9
  const state = state$.value;
11
10
  const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationMissingReceiptsViewState: { bulkUpload }, } = state;
@@ -40,7 +39,7 @@ export const fetchCompletedTransactionsEpic = (actions$, state$, zeniAPI) => act
40
39
  if (sortOrder != null) {
41
40
  queryPayload.sort_order = sortOrder === 'ascending' ? 'asc' : 'desc';
42
41
  }
43
- if (completedSubTab !== DEFAULT_COMPLETED_SUB_TAB) {
42
+ if (completedSubTab !== 'all') {
44
43
  queryPayload.match_type = completedSubTab;
45
44
  }
46
45
  const encodedQuery = encodeURIComponent(JSON.stringify(queryPayload));
@@ -1,12 +1,12 @@
1
1
  import { EMPTY, concat, from, of } from 'rxjs';
2
2
  import { catchError, concatMap, filter, mergeMap, take } from 'rxjs/operators';
3
- import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
4
- import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
3
  import { isSuccessResponse } from '../../../../responsePayload';
6
- import { BULK_UPLOAD_BATCH_LIST_DATE_RANGE_DAYS } from '../../helpers/bulkUploadTiming';
7
- import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
8
4
  import { isBatchDetailsStatusPendingOrProcessing, toBatchListItem, } from '../../payload/missingReceiptsPayload';
9
5
  import { fetchBulkUploadBatchesSuccess, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, } from '../../reducers/missingReceiptsViewReducer';
6
+ import { BULK_UPLOAD_BATCH_LIST_DATE_RANGE_DAYS } from '../../helpers/bulkUploadTiming';
7
+ import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
8
+ import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
9
+ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
10
10
  /**
11
11
  * On missing-receipts entry / period switch, hydrate the automatching banner for the logged-in
12
12
  * user without relying on UI-driven list fetches (the Missing tab does not fetch the batch list
@@ -49,12 +49,10 @@ export const restoreBulkUploadAutomatchingOnMountEpic = (actions$, state$, zeniA
49
49
  bulkUpload.batchListFetchState.fetchState === 'Completed';
50
50
  const listSource = alreadyCached
51
51
  ? of(null)
52
- : zeniAPI
53
- .getJSON(listUrl)
54
- .pipe(catchError(() => of(null)));
52
+ : zeniAPI.getJSON(listUrl).pipe(catchError(() => of(null)));
55
53
  return listSource.pipe(mergeMap((listResponse) => {
56
54
  const batchList = alreadyCached
57
- ? (bulkUpload.batchListByPeriod[periodId] ?? [])
55
+ ? bulkUpload.batchListByPeriod[periodId] ?? []
58
56
  : listResponse != null &&
59
57
  isSuccessResponse(listResponse) &&
60
58
  listResponse.data != null
@@ -2,11 +2,10 @@ import { EMPTY, from, merge, of } from 'rxjs';
2
2
  import { catchError, debounceTime, filter, mergeMap, switchMap, } from 'rxjs/operators';
3
3
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
- import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
6
5
  import { supportedTransactionPayloadToManualSearchResult } from '../../payload/missingReceiptsPayload';
7
6
  import { searchTransactionsForManualMatch, searchTransactionsForManualMatchFailure, searchTransactionsForManualMatchSuccess, } from '../../reducers/missingReceiptsViewReducer';
8
- import { DEFAULT_COMPLETED_SUB_TAB } from '../../types/completedSubTab';
9
7
  import { toTransactionsSortKey } from '../../types/transactionsViewState';
8
+ import { rollingCalendarDateRangeInclusive } from '../../helpers/rollingCalendarDateRangeInclusive';
10
9
  /**
11
10
  * `auto_categorized` for manual transaction search (Unmatched).
12
11
  * Backend contract TBD — align with `fetchTransactionCategorizationEpic` (`selectedTab === 'autoCategorized'`)
@@ -47,11 +46,6 @@ export const searchTransactionsForManualMatchEpic = (actions$, state$, zeniAPI)
47
46
  page_token: pageToken ?? null,
48
47
  page_size: manualSearch.pageSize,
49
48
  search_text: query,
50
- // Manual search ignores the Completed sub-tab axis — always span the
51
- // default sentinel so the receipt-matching UX returns results
52
- // regardless of where the transaction would otherwise be bucketed on
53
- // the Completed tab.
54
- sub_tab: DEFAULT_COMPLETED_SUB_TAB,
55
49
  };
56
50
  return zeniAPI
57
51
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/transactions?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
@@ -5,8 +5,7 @@ import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
5
  import { updateTransactions } from '../../../../entity/transaction/transactionReducer';
6
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
7
7
  import { getLineItemsByTransactionsIdsFromResponse } from '../../helpers/transactionCategorizationLocalDataHelper';
8
- import { fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationSuccess, initializeTransactionCategorizationViewLocalData, resetOtherTabsFetchState, updateParentTotalCountForTab, updateTotalCountForTransactionCategorization, updateTransactionCategorizationUIState, } from '../../reducers/transactionsViewReducer';
9
- import { DEFAULT_COMPLETED_SUB_TAB } from '../../types/completedSubTab';
8
+ import { fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationSuccess, initializeTransactionCategorizationViewLocalData, resetOtherTabsFetchState, updateTotalCountForTransactionCategorization, updateTransactionCategorizationUIState, } from '../../reducers/transactionsViewReducer';
10
9
  import { TRANSACTIONS_TABS, toTransactionsSortKey, } from '../../types/transactionsViewState';
11
10
  export const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchTransactionCategorization.match), switchMap((action) => {
12
11
  const { expenseAutomationViewState: { selectedPeriodByTenantId }, expenseAutomationTransactionsViewState, } = state$.value;
@@ -24,9 +23,6 @@ export const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) =>
24
23
  page_token: pageToken,
25
24
  page_size: 25,
26
25
  search_text: uiState.searchString,
27
- sub_tab: selectedTab === 'autoCategorized'
28
- ? expenseAutomationTransactionsViewState.selectedTransactionCategorizationCompletedSubTab
29
- : DEFAULT_COMPLETED_SUB_TAB,
30
26
  };
31
27
  return zeniAPI
32
28
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/expense-automation/transactions?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
@@ -49,12 +45,6 @@ export const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) =>
49
45
  selectedTab,
50
46
  totalCount: response.data.total_count,
51
47
  }));
52
- updateActions.push(updateParentTotalCountForTab({
53
- selectedPeriod,
54
- selectedTab,
55
- parentTotalCount: response.data.parent_tab_total_count ??
56
- response.data.total_count,
57
- }));
58
48
  updateActions.push(resetOtherTabsFetchState({
59
49
  refreshViewInBackground,
60
50
  tabs: TRANSACTIONS_TABS.filter((tab) => selectedTab !== tab),
@@ -1,6 +1,5 @@
1
1
  import { createAction, createSlice } from '@reduxjs/toolkit';
2
2
  import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
3
- import { DEFAULT_COMPLETED_SUB_TAB, } from '../types/completedSubTab';
4
3
  import { MIN_MANUAL_TRANSACTION_SEARCH_LENGTH, } from '../types/missingReceiptsViewState';
5
4
  export const getCompletedTransactionsCacheKey = (periodId, sortKey, sortOrder, subTab) => {
6
5
  const sortKeyPart = sortKey ?? 'none';
@@ -13,7 +12,7 @@ export const initialBulkUploadState = {
13
12
  batchListByPeriod: {},
14
13
  batchListFetchState: { fetchState: 'Not-Started', error: undefined },
15
14
  batchStatusById: {},
16
- completedSubTab: DEFAULT_COMPLETED_SUB_TAB,
15
+ completedSubTab: 'all',
17
16
  completedTransactionsByPeriod: {},
18
17
  confirmMatchStatus: { fetchState: 'Not-Started', error: undefined },
19
18
  currentBatchId: undefined,
@@ -4,7 +4,6 @@ import uniq from 'lodash/uniq';
4
4
  import { toMonthYearPeriodId, } from '../../../commonStateTypes/timePeriod';
5
5
  import { isVendorTransaction } from '../../../entity/transaction/stateTypes/vendorTransaction';
6
6
  import { MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, filterAutoTabLineItems, mergeTabSpecificLineItems, removeTransactionFromTabView, setEntityRecommendationForLineIdInLocalData, toSetAllLineItemsToCategoryClass, toTransactionDetailLocalData, } from '../helpers/transactionCategorizationLocalDataHelper';
7
- import { DEFAULT_COMPLETED_SUB_TAB, } from '../types/completedSubTab';
8
7
  export const initialTransactionTabViewState = {
9
8
  saveStatus: {
10
9
  fetchState: 'Not-Started',
@@ -42,74 +41,12 @@ export const initialTransactionTabViewState = {
42
41
  selectedTransactionLineId: undefined,
43
42
  };
44
43
  export const initialState = {
45
- autoCategorizedSubTabCache: {},
46
- parentTotalCountByTab: {
47
- review: {},
48
- autoCategorized: {},
49
- },
50
44
  selectedTransactionCategorizationTab: 'review',
51
- selectedTransactionCategorizationCompletedSubTab: DEFAULT_COMPLETED_SUB_TAB,
52
45
  transactionCategorizationView: {
53
46
  review: { ...initialTransactionTabViewState },
54
47
  autoCategorized: { ...initialTransactionTabViewState },
55
48
  },
56
49
  };
57
- /**
58
- * Capture the autoCategorized tab's current paginated dataset for a sub-tab.
59
- * The mid-flight `'In-Progress'` state is normalised to `'Completed'` because
60
- * the in-flight request is cancelled by `switchMap` in the fetch epic when the
61
- * sub-tab changes — restoring `'In-Progress'` later would leave the UI stuck
62
- * showing a phantom loading state with no fetch actually running. Length-zero
63
- * datasets degrade to `'Not-Started'` so the next visit triggers a fresh
64
- * fetch from page 1 instead of short-circuiting on stale empty caches.
65
- *
66
- * Snapshot scope (deliberately narrow):
67
- * - Captured: `pageToken`, `totalCount`, `transactionIdsBySelectedPeriod`,
68
- * `fetchState`, `error`, `scrollPosition`. The first five are the keys the
69
- * fetch epic's cache check reads to decide whether to short-circuit a
70
- * sub-tab revisit; `scrollPosition` is captured per sub-tab so revisiting
71
- * restores the user's last offset and switching to a never-visited sub-tab
72
- * starts at the top instead of inheriting the outgoing sub-tab's offset
73
- * (which would land the user at a meaningless row index against a
74
- * completely different list).
75
- * - Intentionally NOT captured (left in place on `autoCat` and inherited by
76
- * the incoming sub-tab):
77
- * - `uiState.searchString`, `uiState.sortKey`, `uiState.sortOrder`,
78
- * `uiState.nodeCollapseState`, `uiState.limit` — search and sort are
79
- * user-applied preferences that persist across sub-tabs by design (a
80
- * search query stays active when the user toggles between Manual / AI
81
- * Accountant).
82
- * - `selectedCheckBoxTransactionIds` — selection state is intentionally
83
- * cleared at the action source (the fetch epic / save flow), not
84
- * by the snapshot path. Carrying selection across sub-tabs is safe
85
- * because every sub-tab change goes through a fetch round-trip that
86
- * reconciles selection against the new row IDs.
87
- * - `transactionReviewLocalDataById`, `transactionIdsWithUnsavedData`,
88
- * `uploadReceiptStatusById`, `saveStatus`, `refreshStatus`,
89
- * `markAsNotMiscategorizedStatus` — these are per-row / per-flight
90
- * caches that are write-through global state, not sub-tab-scoped.
91
- *
92
- * If the contract changes (e.g. selection should reset on every sub-tab
93
- * switch), update both this comment and the snapshot return shape together.
94
- */
95
- function snapshotAutoCategorizedTab(autoCat) {
96
- const transactionIdsBySelectedPeriod = {
97
- ...autoCat.transactionIdsBySelectedPeriod,
98
- };
99
- const hasAnyLoadedRows = Object.values(transactionIdsBySelectedPeriod).some((ids) => ids.length > 0);
100
- let normalisedFetchState = autoCat.fetchState;
101
- if (normalisedFetchState === 'In-Progress') {
102
- normalisedFetchState = hasAnyLoadedRows ? 'Completed' : 'Not-Started';
103
- }
104
- return {
105
- error: autoCat.error,
106
- fetchState: normalisedFetchState,
107
- pageToken: autoCat.uiState.pageToken,
108
- scrollPosition: autoCat.uiState.scrollPosition,
109
- totalCount: { ...autoCat.uiState.totalCount },
110
- transactionIdsBySelectedPeriod,
111
- };
112
- }
113
50
  const expenseAutomationTransactionsView = createSlice({
114
51
  name: 'expenseAutomationTransactionsView',
115
52
  initialState,
@@ -323,55 +260,6 @@ const expenseAutomationTransactionsView = createSlice({
323
260
  return { payload: { newSelectedTab } };
324
261
  },
325
262
  },
326
- updateTransactionCategorizationCompletedSubTab: {
327
- reducer(draft, action) {
328
- const { newSubTab } = action.payload;
329
- const previousSubTab = draft.selectedTransactionCategorizationCompletedSubTab;
330
- if (previousSubTab === newSubTab) {
331
- return;
332
- }
333
- const autoCat = draft.transactionCategorizationView.autoCategorized;
334
- // Snapshot the outgoing sub-tab so a future revisit can short-circuit
335
- // the fetch epic (mirrors the parent-tab "don't refetch what's
336
- // already cached" behaviour). Period-scoped data is preserved as-is
337
- // because the snapshot itself is keyed only by sub-tab; the active
338
- // tab's per-period maps remain the source of truth while live.
339
- draft.autoCategorizedSubTabCache[previousSubTab] =
340
- snapshotAutoCategorizedTab(autoCat);
341
- const cached = draft.autoCategorizedSubTabCache[newSubTab];
342
- if (cached != null) {
343
- autoCat.uiState.pageToken = cached.pageToken;
344
- autoCat.uiState.totalCount = { ...cached.totalCount };
345
- autoCat.uiState.scrollPosition = cached.scrollPosition;
346
- autoCat.transactionIdsBySelectedPeriod = {
347
- ...cached.transactionIdsBySelectedPeriod,
348
- };
349
- autoCat.fetchState = cached.fetchState;
350
- autoCat.error = cached.error;
351
- }
352
- else {
353
- // Sub-tab has never been visited — clear the active slot so the
354
- // view-epic's `fetchState === 'Not-Started' || transactionIds.length
355
- // === 0` cache check falls through and triggers a fresh page-1
356
- // fetch for the new dataset. `scrollPosition` is reset to the top
357
- // so the incoming list isn't anchored to the outgoing sub-tab's
358
- // offset (different rows → previous offset is meaningless).
359
- autoCat.uiState.pageToken = null;
360
- autoCat.uiState.totalCount = {};
361
- autoCat.uiState.scrollPosition = {
362
- scrollTop: 0,
363
- scrollLeft: undefined,
364
- };
365
- autoCat.transactionIdsBySelectedPeriod = {};
366
- autoCat.fetchState = 'Not-Started';
367
- autoCat.error = undefined;
368
- }
369
- draft.selectedTransactionCategorizationCompletedSubTab = newSubTab;
370
- },
371
- prepare(newSubTab) {
372
- return { payload: { newSubTab } };
373
- },
374
- },
375
263
  updateTransactionCategorization: {
376
264
  reducer(draft, action) {
377
265
  const selectedTab = action.payload.selectedTab;
@@ -809,10 +697,6 @@ const expenseAutomationTransactionsView = createSlice({
809
697
  const { selectedTab, selectedPeriod, totalCount } = action.payload;
810
698
  draft.transactionCategorizationView[selectedTab].uiState.totalCount[toMonthYearPeriodId(selectedPeriod)] = totalCount;
811
699
  },
812
- updateParentTotalCountForTab(draft, action) {
813
- const { selectedTab, selectedPeriod, parentTotalCount } = action.payload;
814
- draft.parentTotalCountByTab[selectedTab][toMonthYearPeriodId(selectedPeriod)] = parentTotalCount;
815
- },
816
700
  updateSelectedCheckboxTransactionIds(draft, action) {
817
701
  const selectedTab = action.payload.selectedTab;
818
702
  const transactionIds = action.payload.transactionIds;
@@ -823,16 +707,6 @@ const expenseAutomationTransactionsView = createSlice({
823
707
  draft.transactionCategorizationView[selectedTab] = {
824
708
  ...initialTransactionTabViewState,
825
709
  };
826
- draft.parentTotalCountByTab[selectedTab] = {};
827
- // The per-sub-tab snapshot cache lives at the slice root and is
828
- // exclusively populated by the autoCategorized tab's sub-tab switching
829
- // (see updateTransactionCategorizationCompletedSubTab). When that tab
830
- // is reset, leaving stale snapshots behind would short-circuit the
831
- // next sub-tab visit with phantom row IDs / page tokens that no longer
832
- // correspond to anything in state — drop them in lockstep.
833
- if (selectedTab === 'autoCategorized') {
834
- draft.autoCategorizedSubTabCache = {};
835
- }
836
710
  },
837
711
  clearExpenseAutomationTransactionsView(draft) {
838
712
  Object.assign(draft, initialState);
@@ -929,5 +803,5 @@ const expenseAutomationTransactionsView = createSlice({
929
803
  },
930
804
  },
931
805
  });
932
- export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, updateParentTotalCountForTab, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
806
+ export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
933
807
  export default expenseAutomationTransactionsView.reducer;
@@ -10,7 +10,7 @@ import { getProjectList } from '../../projectList/projectListSelector';
10
10
  import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
11
11
  export function getExpenseAutomationTransactionView(state) {
12
12
  const { accountState, classState, classListState, accountListState, expenseAutomationTransactionsViewState, expenseAutomationViewState, projectState, projectListState, transactionState, monthEndCloseChecksState, monthEndCloseChecksViewState, } = state;
13
- const { selectedTransactionCategorizationCompletedSubTab, selectedTransactionCategorizationTab, } = expenseAutomationTransactionsViewState;
13
+ const { selectedTransactionCategorizationTab } = expenseAutomationTransactionsViewState;
14
14
  const { uiState, refreshStatus, saveStatus, markAsNotMiscategorizedStatus, transactionIdsBySelectedPeriod, transactionReviewLocalDataById, selectedCheckBoxTransactionIds, transactionIdsWithUnsavedData, uploadReceiptStatusById, selectedTransactionId, selectedTransactionLineId, } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTransactionCategorizationTab];
15
15
  const uncategorizedIncomeExpense = getUncategorizedAccounts(accountState, accountListState, 'accountList');
16
16
  const accountList = getAccountList(accountState, accountListState, 'accountList');
@@ -63,27 +63,10 @@ export function getExpenseAutomationTransactionView(state) {
63
63
  .uiState.totalCount;
64
64
  const totalCountByAutoCat = expenseAutomationTransactionsViewState.transactionCategorizationView
65
65
  .autoCategorized.uiState.totalCount;
66
- // Per-(sub-)tab counts powering pagination, in-list "showing X of Y", and
67
- // any save/refresh round-trips. Sourced from `uiState.totalCount` which
68
- // mirrors the response's `total_count` for whatever tab/sub-tab the user is
69
- // viewing (e.g. only the Manual sub-tab rows for Completed → Manual).
70
66
  const totalCountByTab = {
71
67
  review: monthYearPeriodId != null ? totalCountByReview[monthYearPeriodId] : 0,
72
68
  autoCategorized: monthYearPeriodId != null ? totalCountByAutoCat[monthYearPeriodId] : 0,
73
69
  };
74
- // Parent-tab badge counts. Sourced from `parent_tab_total_count` in the
75
- // listing response and used exclusively by the navbar / tab strip badges.
76
- // Independent of which Completed sub-tab is active so the badge stays
77
- // stable across sub-tab switches.
78
- const parentTotalCountByTab = expenseAutomationTransactionsViewState.parentTotalCountByTab;
79
- const parentTabTotalCountByTab = {
80
- review: monthYearPeriodId != null
81
- ? (parentTotalCountByTab.review[monthYearPeriodId] ?? 0)
82
- : 0,
83
- autoCategorized: monthYearPeriodId != null
84
- ? (parentTotalCountByTab.autoCategorized[monthYearPeriodId] ?? 0)
85
- : 0,
86
- };
87
70
  const fetchStateByTab = {
88
71
  review: {
89
72
  fetchState: expenseAutomationTransactionsViewState.transactionCategorizationView
@@ -125,11 +108,9 @@ export function getExpenseAutomationTransactionView(state) {
125
108
  markAsNotMiscategorizedStatus,
126
109
  completionStatus,
127
110
  totalCountByTab,
128
- parentTabTotalCountByTab,
129
111
  fetchStateByTransactionTabs: fetchStateByTab,
130
112
  uploadReceiptStatusById,
131
113
  selectedTransactionId,
132
114
  selectedTransactionLineId,
133
- selectedTransactionCategorizationCompletedSubTab,
134
115
  };
135
116
  }
@@ -39,6 +39,8 @@ const BULK_UPLOAD_RESULTS_TABS = ['completed', 'unmatched'];
39
39
  export const toBulkUploadResultsTab = (v) => stringToUnion(v.trim().toLowerCase(), BULK_UPLOAD_RESULTS_TABS);
40
40
  const MISSING_RECEIPTS_TABS = ['missing', 'completed', 'unmatched'];
41
41
  export const toMissingReceiptsTab = (v) => stringToUnion(v.trim().toLowerCase(), MISSING_RECEIPTS_TABS);
42
+ const COMPLETED_SUB_TABS = ['all', 'ai_accountant', 'manual'];
43
+ export const toCompletedSubTab = (v) => stringToUnion(v.trim().toLowerCase(), COMPLETED_SUB_TABS);
42
44
  const MATCH_SOURCES = ['ai', 'manual'];
43
45
  export const toMatchSource = (v) => stringToUnion(v.trim().toLowerCase(), MATCH_SOURCES);
44
46
  const BULK_UPLOAD_SORT_KEYS = [
@@ -2,7 +2,6 @@ import { createSelector } from '@reduxjs/toolkit';
2
2
  import { stripCompareAdditionalBalanceTypes } from '../../commonStateTypes/coaBalance/coaBalanceType';
3
3
  import { newBalancesFilter, newBalancesFilterForDateRange, } from '../../commonStateTypes/coaBalance/coaBalancesFilter';
4
4
  import { newBalancesFilterClassesView } from '../../commonStateTypes/coaBalance/coaBalancesFilterClassesView';
5
- import { newBalancesFilterProjectView } from '../../commonStateTypes/coaBalance/coaBalancesFilterProjectView';
6
5
  import { getNumberOfPeriods, } from '../../commonStateTypes/dataAvailable';
7
6
  import { reduceAnyCompletedFetchState } from '../../commonStateTypes/reduceFetchState';
8
7
  import { toTimeframeTick, } from '../../commonStateTypes/timeframeTick';
@@ -10,6 +9,7 @@ import { extractThisPeriod } from '../../commonStateTypes/viewAndReport/thisPeri
10
9
  import { getBalanceSheet } from '../balanceSheet/balanceSheetSelector';
11
10
  import { getCashFlow } from '../cashFlow/cashFlowSelector';
12
11
  import { getProfitAndLossReport, } from '../profitAndLoss/profitAndLossSelector';
12
+ import { newBalancesFilterProjectView } from '../../commonStateTypes/coaBalance/coaBalancesFilterProjectView';
13
13
  import { getProfitAndLossClassesHorizontalView } from '../profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector';
14
14
  import { getProfitAndLossClassesView } from '../profitAndLossClassesView/profitAndLossClassesViewSelector';
15
15
  import { getProfitAndLossProjectView } from '../profitAndLossProjectView/profitAndLossProjectViewSelector';
@@ -1,7 +1,6 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { mapCurrencyPayloadToCurrency } from '../../../../commonPayloadTypes/v2/viewAndReportPayloadv2';
3
3
  import { toAmount } from '../../../../commonStateTypes/amount';
4
- import { createCacheOverrideFetchReducer } from '../../../../commonStateTypes/cacheOverrideFetchReducer';
5
4
  import { initialCashbackAnalytics, } from './cashbackDetailState';
6
5
  export const initialState = {
7
6
  fetchState: 'Not-Started',
@@ -28,10 +27,10 @@ const cashbackDetail = createSlice({
28
27
  name: 'cashbackDetail',
29
28
  initialState,
30
29
  reducers: {
31
- fetchCashbackDetail: createCacheOverrideFetchReducer((draft) => {
30
+ fetchCashbackDetail(draft) {
32
31
  draft.fetchState = 'In-Progress';
33
32
  draft.error = undefined;
34
- }),
33
+ },
35
34
  updateCashbackDetail(draft, action) {
36
35
  draft.cashbackByPeriod = toCashbackByPeriod(action.payload);
37
36
  draft.cashbackSummary = toCashbackSummary(action.payload);
@@ -1,6 +1,5 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { toAmount } from '../../../../commonStateTypes/amount';
3
- import { createCacheOverrideFetchReducer } from '../../../../commonStateTypes/cacheOverrideFetchReducer';
4
3
  import { toCreditLimitFrequencyCodeType } from '../../../../entity/chargeCard/chargeCard';
5
4
  import { toCreditTerm, toRePaymentInfo, } from './chargeCardConfigPayload';
6
5
  export const initialState = {
@@ -31,10 +30,10 @@ const chargeCardConfig = createSlice({
31
30
  name: 'chargeCardConfig',
32
31
  initialState,
33
32
  reducers: {
34
- fetchChargeCardConfig: createCacheOverrideFetchReducer((draft) => {
33
+ fetchChargeCardConfig(draft) {
35
34
  draft.fetchState = 'In-Progress';
36
35
  draft.error = undefined;
37
- }),
36
+ },
38
37
  updateChargeCardConfig(draft, action) {
39
38
  const { payload } = action;
40
39
  draft.creditLimitFrequency =
@@ -1,6 +1,5 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
2
  import { mapSortOrderPayloadToSortOrder, } from '../../../../commonPayloadTypes/sortOrderPayload';
3
- import { addChargeCardTransactionFromPusher, addDeclinedChargeCardTransactionFromPusher, } from '../../../../commonStateTypes/pusherActions';
4
3
  import { toAttachment, } from '../../../../entity/transaction/payloadTypes/attachmentPayload';
5
4
  import { emptyChargeCardDetail, mapChargeCardTransactionStatisticsPayloadToChargeCardTransactionStatistics, toChargeCardTransactionSortKeyType, } from './chargeCardDetail';
6
5
  export const initialState = {
@@ -65,30 +64,19 @@ const chargeCardDetail = createSlice({
65
64
  },
66
65
  fetchChargeCardTransactionList: {
67
66
  reducer(draft, action) {
68
- const { chargeCardId, pageToken, pageTokenType, cacheOverride } = action.payload;
67
+ const { chargeCardId, pageToken, pageTokenType } = action.payload;
69
68
  const existingCardDetail = draft.detailById[chargeCardId] ?? emptyChargeCardDetail;
70
- const existingUi = existingCardDetail.transactionList.uiState;
71
- // cacheOverride=true (Pusher): preserve fetchState, error, and scroll
72
- // to avoid skeleton flash. Normal fetches reset as before.
73
69
  draft.detailById[chargeCardId] = {
74
70
  ...existingCardDetail,
75
71
  transactionList: {
76
72
  ...existingCardDetail.transactionList,
77
- fetchState: cacheOverride
78
- ? existingCardDetail.transactionList.fetchState
79
- : 'In-Progress',
80
- error: cacheOverride
81
- ? existingCardDetail.transactionList.error
82
- : undefined,
73
+ fetchState: 'In-Progress',
74
+ error: undefined,
83
75
  uiState: {
84
- ...existingUi,
85
- // Preserve scroll on silent refresh so users aren't snapped
86
- // away from their reading position.
87
- scrollYOffset: cacheOverride
88
- ? existingUi.scrollYOffset
89
- : existingUi.pageToken === null
90
- ? 0
91
- : existingUi.scrollYOffset,
76
+ ...existingCardDetail.transactionList.uiState,
77
+ scrollYOffset: existingCardDetail.transactionList.uiState.pageToken === null
78
+ ? 0
79
+ : existingCardDetail.transactionList.uiState.scrollYOffset,
92
80
  pageToken: pageToken,
93
81
  pageTokenType: pageTokenType,
94
82
  },
@@ -105,24 +93,18 @@ const chargeCardDetail = createSlice({
105
93
  const { chargeCardId, data, cacheOverride } = action.payload;
106
94
  const existingCardDetail = draft.detailById[chargeCardId] ?? emptyChargeCardDetail;
107
95
  const newTransactionIds = data.transactions.map((tran) => tran.transaction_id);
108
- const existingIds = existingCardDetail.transactionList.transactionIds;
109
- // cacheOverride (Pusher): refresh page 1 but keep page 2+ IDs.
110
- // Normal pagination: append new page, deduped.
111
- const newIdSet = new Set(newTransactionIds);
112
- const mergedTransactionIds = cacheOverride
113
- ? [
114
- ...newTransactionIds,
115
- ...existingIds.filter((id) => !newIdSet.has(id)),
116
- ]
117
- : [
118
- ...existingIds,
119
- ...newTransactionIds.filter((id) => !existingIds.includes(id)),
120
- ];
121
96
  draft.detailById[chargeCardId] = {
122
97
  ...existingCardDetail,
123
98
  transactionList: {
124
99
  ...existingCardDetail.transactionList,
125
- transactionIds: mergedTransactionIds,
100
+ transactionIds: [
101
+ ...new Set([
102
+ ...(cacheOverride
103
+ ? []
104
+ : existingCardDetail.transactionList.transactionIds),
105
+ ...newTransactionIds,
106
+ ]),
107
+ ],
126
108
  fetchState: 'Completed',
127
109
  error: undefined,
128
110
  uiState: {
@@ -134,12 +116,9 @@ const chargeCardDetail = createSlice({
134
116
  limit: data.query.limit,
135
117
  pageToken: data.query.page_token,
136
118
  pageTokenType: data.query.page_token_type,
137
- // Preserve scroll on silent refresh; reset on user-triggered first page.
138
- scrollYOffset: cacheOverride
139
- ? existingCardDetail.transactionList.uiState.scrollYOffset
140
- : existingCardDetail.transactionList.uiState.pageToken === null
141
- ? 0
142
- : existingCardDetail.transactionList.uiState.scrollYOffset,
119
+ scrollYOffset: existingCardDetail.transactionList.uiState.pageToken === null
120
+ ? 0
121
+ : existingCardDetail.transactionList.uiState.scrollYOffset,
143
122
  },
144
123
  },
145
124
  };
@@ -653,22 +632,6 @@ const chargeCardDetail = createSlice({
653
632
  Object.assign(draft, initialState);
654
633
  },
655
634
  },
656
- extraReducers: (builder) => {
657
- const prependTransaction = (draft, cardId, txId) => {
658
- const detail = draft.detailById[cardId];
659
- if (detail != null &&
660
- !detail.transactionList.transactionIds.includes(txId)) {
661
- detail.transactionList.transactionIds.unshift(txId);
662
- }
663
- };
664
- builder
665
- .addCase(addChargeCardTransactionFromPusher, (draft, action) => {
666
- prependTransaction(draft, action.payload.cardId, action.payload.transaction.transaction_id);
667
- })
668
- .addCase(addDeclinedChargeCardTransactionFromPusher, (draft, action) => {
669
- prependTransaction(draft, action.payload.cardId, action.payload.transaction.transaction_id);
670
- });
671
- },
672
635
  });
673
636
  export const { fetchChargeCardDetailPage, fetchChargeCardDetail, updateChargeCardDetailFetchState, fetchChargeCardTransactionList, updateChargeCardDetailUIState, updateChargeCardTransactionList, updateChargeCardTransactionUploadReceiptsFetchStatus, fetchChargeCardTransactionAttachments, updateChargeCardTransactionIsViewReceiptClicked, updateChargeCardTransactionAttachmentsFailure, updatePhysicalChargeCardAttemptSuccessOrFailure, updatePhysicalChargeCardAttempt, updateDebitCardPinAttempt, updateDebitCardPinAttemptSuccessOrFailure, updateChargeCardTransactionReceiptsShowTickFetchStatus, updateChargeCardTransactionAttachmentsInCardDetail, updateChargeCardTransactionListFailure, updateChargeCardDetail, startCVVActivationWait, completeCVVActivationWait, startDebitCardSetPinWait, completeDebitCardSetPinWait, updateChargeCardDetailUpdateStatus, updateChargeCardSpendLimit, updateChargeCardSpendLimitSuccessOrFailure, updateChargeCardName, updateChargeCardNameSuccessOrFailure, lockChargeCard, lockChargeCardSuccessOrFailure, unlockChargeCard, unlockChargeCardSuccessOrFailure, closeChargeCard, closeChargeCardSuccessOrFailure, fetchDepositAccountLimit, updateDepositAccountLimitFetchStatus, fetchChargeCardTransactionStatistics, updateChargeCardTransactionStatistics, updateChargeCardTransactionStatisticsFetchState, fetchChargeCardsRecurringExpenses, updateChargeCardsRecurringExpenses, updateChargeCardsRecurringExpensesFetchState, sendOtpCardHolder, resendOtpCardHolder, sendOtpCardHolderSuccess, sendOtpCardHolderFailure, verifyOtpCardHolder, verifyOtpCardHolderSuccess, verifyOtpCardHolderFailure, clearChargeCardDetail, } = chargeCardDetail.actions;
674
637
  export default chargeCardDetail.reducer;
@@ -13,24 +13,13 @@ export const fetchChargeCardDetailEpic = (actions$, state$, zeniAPI) => actions$
13
13
  const chargeCardAction = [];
14
14
  const state = state$.value;
15
15
  const chargeCardDetail = recordGet(state.chargeCardDetailState.detailById, chargeCardId, undefined);
16
- // Skip if a user-triggered fetch is already in-flight (not applicable
17
- // to cacheOverride/Pusher refreshes, which always proceed).
18
16
  if (cacheOverride === true ||
19
17
  chargeCardDetail == null ||
20
18
  chargeCardDetail.fetchState !== 'In-Progress') {
21
- // Silent refresh: preserve fetchState to avoid skeleton flash.
22
- // Discriminate via fetchState (not null-check) because the reducer
23
- // always creates an entry before the epic runs.
24
- const isSilentRefresh = cacheOverride === true &&
25
- chargeCardDetail != null &&
26
- chargeCardDetail.fetchState !== 'Not-Started';
27
19
  chargeCardAction.push(updateChargeCardDetailFetchState({
28
20
  chargeCardId,
29
- isRefreshingViewInBackground: isSilentRefresh,
30
- fetchState: isSilentRefresh
31
- ? chargeCardDetail.fetchState
32
- : 'In-Progress',
33
- error: isSilentRefresh ? chargeCardDetail.error : undefined,
21
+ isRefreshingViewInBackground: cacheOverride,
22
+ fetchState: 'In-Progress',
34
23
  }));
35
24
  const query = {
36
25
  is_include_card_activation_events: true,
@@ -48,8 +37,6 @@ export const fetchChargeCardDetailEpic = (actions$, state$, zeniAPI) => actions$
48
37
  }));
49
38
  }
50
39
  else {
51
- // Surface errors even on silent refresh so the user
52
- // knows data is stale (follow-up: consider toast instead).
53
40
  return of(updateChargeCardDetailFetchState({
54
41
  chargeCardId,
55
42
  fetchState: 'Error',
@@ -58,7 +45,6 @@ export const fetchChargeCardDetailEpic = (actions$, state$, zeniAPI) => actions$
58
45
  }));
59
46
  }
60
47
  }), catchError((error) => {
61
- // Surface network failures even on silent refresh.
62
48
  return of(updateChargeCardDetailFetchState({
63
49
  chargeCardId,
64
50
  fetchState: 'Error',
@@ -15,16 +15,15 @@ export const fetchChargeCardDetailPageEpic = (actions$, state$) => actions$.pipe
15
15
  ? exsitingChargeCardDetail.transactionList.uiState.pageTokenType
16
16
  : null;
17
17
  if (cacheOverride === true) {
18
- // Forward cacheOverride to all sub-fetches to prevent skeleton flash.
19
18
  chargeCardTransactionListAction.push(fetchChargeCardDetail(chargeCardId, cacheOverride));
20
19
  //latest transactions need to be fetched hence removing pageToken and pageTokenType
21
- chargeCardTransactionListAction.push(fetchChargeCardTransactionList(chargeCardId, null, null, true));
22
- chargeCardTransactionListAction.push(fetchChargeCardConfig(true));
20
+ chargeCardTransactionListAction.push(fetchChargeCardTransactionList(chargeCardId, null, null));
21
+ chargeCardTransactionListAction.push(fetchChargeCardConfig());
23
22
  if (doFetchCreditAccounts && isCreditCardEnabled) {
24
- chargeCardTransactionListAction.push(fetchCreditAccount(true));
23
+ chargeCardTransactionListAction.push(fetchCreditAccount());
25
24
  }
26
25
  if (doFetchCreditAccounts && isCreditCardEnabled) {
27
- chargeCardTransactionListAction.push(fetchCreditAccountRepayment(true));
26
+ chargeCardTransactionListAction.push(fetchCreditAccountRepayment());
28
27
  }
29
28
  }
30
29
  else {