@zeniai/client-epic-state 5.1.16-betaRD4 → 5.1.16-betaRD5

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.
@@ -18,11 +18,6 @@ groupBy((action) => action.payload.selectedTab), mergeMap((group$) => group$.pip
18
18
  const { pageToken, sortOrder, sortKey } = uiState;
19
19
  const currentTenant = getCurrentTenant(state$.value);
20
20
  const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
21
- console.log('[EA-SEARCH] fetchTransactionCategorizationEpic HTTP call', {
22
- selectedTab,
23
- refreshViewInBackground,
24
- search_text: uiState.searchString,
25
- });
26
21
  const queryParam = {
27
22
  start_date: toString(period.start),
28
23
  end_date: toString(period.end),
@@ -6,25 +6,25 @@ import { fetchAccountList } from '../../../accountList/accountListReducer';
6
6
  import { fetchClassList } from '../../../classList/classListReducer';
7
7
  import { fetchOwnerList } from '../../../ownerList/ownerListReducer';
8
8
  import { fetchProjectList } from '../../../projectList/projectListReducer';
9
- import { fetchTransactionCategorization, fetchTransactionCategorizationView, } from '../../reducers/transactionsViewReducer';
10
- import { TRANSACTIONS_TABS } from '../../types/transactionsViewState';
9
+ import { fetchTransactionCategorization, fetchTransactionCategorizationView, restoreFullDatasetSnapshot, } from '../../reducers/transactionsViewReducer';
10
+ import { TRANSACTIONS_TABS, } from '../../types/transactionsViewState';
11
11
  export const fetchTransactionCategorizationViewEpic = (actions$, state$) => actions$.pipe(filter(fetchTransactionCategorizationView.match), mergeMap((action) => {
12
12
  const { selectedTab, cacheOverride, keepExistingListItems, pageToken, period, refreshViewInBackground, searchString, resetListItems, isUncategorizedExpenseCategoryEnabled, } = action.payload;
13
13
  const updateActions = [];
14
14
  const { expenseAutomationTransactionsViewState } = state$.value;
15
15
  const { fetchState, transactionIdsBySelectedPeriod } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTab];
16
- console.log('[EA-SEARCH] fetchTransactionCategorizationViewEpic received', {
17
- selectedTab,
18
- cacheOverride,
19
- searchString,
20
- fetchState,
21
- });
22
16
  const monthYearPeriod = {
23
17
  month: period.start.month,
24
18
  year: period.start.year,
25
19
  };
26
- const transactionIds = transactionIdsBySelectedPeriod[toMonthYearPeriodId(monthYearPeriod)] ??
27
- [];
20
+ const periodId = toMonthYearPeriodId(monthYearPeriod);
21
+ const transactionIds = transactionIdsBySelectedPeriod[periodId] ?? [];
22
+ const hasUsableSnapshot = (tabKey) => {
23
+ const snapshot = expenseAutomationTransactionsViewState.transactionCategorizationView[tabKey].fullDatasetSnapshot;
24
+ return (snapshot != null &&
25
+ snapshot.periodId === periodId &&
26
+ snapshot.transactionIds.length > 0);
27
+ };
28
28
  const accountList = state$.value.accountListState.byReportId['accountList'];
29
29
  if (accountList.hasValidState() === false &&
30
30
  accountList.fetchState === 'Not-Started') {
@@ -46,37 +46,33 @@ export const fetchTransactionCategorizationViewEpic = (actions$, state$) => acti
46
46
  vendorOwnerList.fetchState === 'Not-Started') {
47
47
  updateActions.push(fetchOwnerList());
48
48
  }
49
- console.log('[EA-SEARCH] view epic cache check', {
50
- selectedTab,
51
- cacheOverride,
52
- fetchState,
53
- transactionIdsBySelectedPeriod
54
- });
55
- if (cacheOverride === true ||
49
+ // When clearing search (searchString === ''), restore from the pre-search
50
+ // snapshot if one exists — avoids a round-trip since all data is already
51
+ // in the entity store. Fall back to a normal fetch if no snapshot (the
52
+ // tab was never loaded before the search was applied).
53
+ if (searchString === '' && hasUsableSnapshot(selectedTab)) {
54
+ updateActions.push(restoreFullDatasetSnapshot({ selectedTab, period }));
55
+ }
56
+ else if (cacheOverride === true ||
56
57
  fetchState === 'Not-Started' ||
57
58
  transactionIds.length === 0) {
58
- console.log('[EA-SEARCH] dispatching fetchTransactionCategorization for active tab', { selectedTab, searchString });
59
59
  updateActions.push(fetchTransactionCategorization(selectedTab, period, cacheOverride, keepExistingListItems, searchString, pageToken, refreshViewInBackground, resetListItems, isUncategorizedExpenseCategoryEnabled));
60
60
  }
61
61
  // When search changes (searchString defined, including empty string to
62
- // clear), also fetch every other tab so all tabs always reflect the same
62
+ // clear), also update every other tab so all tabs always reflect the same
63
63
  // search query. Skip if the other tab already has this search string to
64
- // avoid redundant requests on noop updates. cacheOverride=true bypasses
65
- // the cache check; pageToken reset to undefined so the other tab starts
66
- // from page 1; refreshViewInBackground=true because the user isn't
67
- // looking at the other tab, so a silent background fetch is correct.
64
+ // avoid redundant work on noop updates. On clear, restore from snapshot
65
+ // if available; otherwise issue a background fetch.
68
66
  if (searchString !== undefined) {
69
67
  TRANSACTIONS_TABS.filter((tab) => tab !== selectedTab).forEach((otherTab) => {
70
68
  const otherTabSearchString = expenseAutomationTransactionsViewState.transactionCategorizationView[otherTab].uiState.searchString;
71
- console.log('[EA-SEARCH] dual-fetch check for other tab', {
72
- otherTab,
73
- searchString,
74
- otherTabSearchString,
75
- willDispatch: searchString !== otherTabSearchString,
76
- });
77
69
  if (searchString !== otherTabSearchString) {
78
- console.log('[EA-SEARCH] dispatching fetchTransactionCategorization for other tab', { otherTab, searchString });
79
- updateActions.push(fetchTransactionCategorization(otherTab, period, true, keepExistingListItems, searchString, undefined, true, true, isUncategorizedExpenseCategoryEnabled));
70
+ if (searchString === '' && hasUsableSnapshot(otherTab)) {
71
+ updateActions.push(restoreFullDatasetSnapshot({ selectedTab: otherTab, period }));
72
+ }
73
+ else {
74
+ updateActions.push(fetchTransactionCategorization(otherTab, period, true, keepExistingListItems, searchString, undefined, true, true, isUncategorizedExpenseCategoryEnabled));
75
+ }
80
76
  }
81
77
  });
82
78
  }
@@ -38,6 +38,7 @@ export const initialTransactionTabViewState = {
38
38
  hasValidState() {
39
39
  return this.fetchState == 'Completed';
40
40
  },
41
+ fullDatasetSnapshot: undefined,
41
42
  transactionReviewLocalDataById: {},
42
43
  transactionIdsBySelectedPeriod: {},
43
44
  selectedCheckBoxTransactionIds: [],
@@ -169,8 +170,29 @@ const expenseAutomationTransactionsView = createSlice({
169
170
  month: period.start.month,
170
171
  year: period.start.year,
171
172
  };
173
+ const periodId = toMonthYearPeriodId(monthYearPeriod);
172
174
  const transactionIdsBySelectedPeriod = draft.transactionCategorizationView[selectedTab]
173
- .transactionIdsBySelectedPeriod[toMonthYearPeriodId(monthYearPeriod)] ?? [];
175
+ .transactionIdsBySelectedPeriod[periodId] ?? [];
176
+ // When starting a non-empty search, snapshot the current full dataset
177
+ // so clearing the search can restore instantly without an API round-trip.
178
+ // Only save the snapshot when there are rows to restore; skip for tabs
179
+ // that have never been loaded (empty list → no useful snapshot).
180
+ if (searchString !== undefined &&
181
+ searchString !== '' &&
182
+ transactionIdsBySelectedPeriod.length > 0) {
183
+ const snapshot = {
184
+ periodId,
185
+ totalCount: draft.transactionCategorizationView[selectedTab].uiState
186
+ .totalCount[periodId],
187
+ transactionIds: [...transactionIdsBySelectedPeriod],
188
+ transactionReviewLocalDataById: {
189
+ ...draft.transactionCategorizationView[selectedTab]
190
+ .transactionReviewLocalDataById,
191
+ },
192
+ };
193
+ draft.transactionCategorizationView[selectedTab].fullDatasetSnapshot =
194
+ snapshot;
195
+ }
174
196
  transactionIdsBySelectedPeriod.forEach((transactionId) => {
175
197
  delete draft.transactionCategorizationView[selectedTab]
176
198
  .transactionReviewLocalDataById[transactionId];
@@ -686,6 +708,39 @@ const expenseAutomationTransactionsView = createSlice({
686
708
  // leave the other tab's state untouched — it has its own active fetch.
687
709
  });
688
710
  },
711
+ restoreFullDatasetSnapshot(draft, action) {
712
+ const { selectedTab, period } = action.payload;
713
+ const periodId = toMonthYearPeriodId({
714
+ month: period.start.month,
715
+ year: period.start.year,
716
+ });
717
+ const snapshot = draft.transactionCategorizationView[selectedTab].fullDatasetSnapshot;
718
+ if (snapshot == null || snapshot.periodId !== periodId) {
719
+ return;
720
+ }
721
+ draft.transactionCategorizationView[selectedTab]
722
+ .transactionIdsBySelectedPeriod[periodId] = snapshot.transactionIds;
723
+ // Merge back per-transaction local data; skip entries that were updated
724
+ // during the search (e.g. a save that happened while filtered results
725
+ // were showing) so those saves are not overwritten.
726
+ Object.entries(snapshot.transactionReviewLocalDataById).forEach(([transactionId, localData]) => {
727
+ if (draft.transactionCategorizationView[selectedTab]
728
+ .transactionReviewLocalDataById[transactionId] == null) {
729
+ draft.transactionCategorizationView[selectedTab]
730
+ .transactionReviewLocalDataById[transactionId] = localData;
731
+ }
732
+ });
733
+ if (snapshot.totalCount != null) {
734
+ draft.transactionCategorizationView[selectedTab].uiState.totalCount[periodId] = snapshot.totalCount;
735
+ }
736
+ draft.transactionCategorizationView[selectedTab].uiState.searchString =
737
+ '';
738
+ draft.transactionCategorizationView[selectedTab].uiState.pageToken = null;
739
+ draft.transactionCategorizationView[selectedTab].fetchState = 'Completed';
740
+ draft.transactionCategorizationView[selectedTab].error = undefined;
741
+ draft.transactionCategorizationView[selectedTab].fullDatasetSnapshot =
742
+ undefined;
743
+ },
689
744
  fetchTransactionCategorizationFailure(draft, action) {
690
745
  const { selectedTab, selectedPeriod } = action.payload;
691
746
  draft.transactionCategorizationView[selectedTab].transactionIdsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)] =
@@ -1066,5 +1121,5 @@ const expenseAutomationTransactionsView = createSlice({
1066
1121
  },
1067
1122
  },
1068
1123
  });
1069
- export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, updateTransactionFilters, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, updateParentTotalCountForTab, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, markCategoryClassRecommendationsInProgressForCategorization, markCategoryClassRecommendationsCompletedForCategorization, markCategoryClassRecommendationsFailureForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, removeTransactionFromAllTabs, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
1124
+ export const { fetchTransactionCategorization, updateTransactionCategorizationUIState, updateTransactionFilters, initializeTransactionCategorizationViewLocalData, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateStatusForTransactionNotMiscategorizedUpdateForCategorization, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, setAllItemsToCategoryClassInLocalDataForCategorization, updateTotalCountForTransactionCategorization, updateParentTotalCountForTab, fetchTransactionCategorizationSuccess, resetOtherTabsFetchState, restoreFullDatasetSnapshot, fetchTransactionCategorizationView, updateSelectedCheckboxTransactionIds, clearExpenseAutomationTransactionsViewPerTabView, clearExpenseAutomationTransactionsView, setEntityRecommendationForLineIdsForCategorization, markCategoryClassRecommendationsInProgressForCategorization, markCategoryClassRecommendationsCompletedForCategorization, markCategoryClassRecommendationsFailureForCategorization, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, removeTransactionFromAllTabs, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, } = expenseAutomationTransactionsView.actions;
1070
1125
  export default expenseAutomationTransactionsView.reducer;
@@ -21,11 +21,6 @@ const fetchTransactionCategorizationEpic = (actions$, state$, zeniAPI) => action
21
21
  const { pageToken, sortOrder, sortKey } = uiState;
22
22
  const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state$.value);
23
23
  const selectedPeriod = selectedPeriodByTenantId[currentTenant.tenantId];
24
- console.log('[EA-SEARCH] fetchTransactionCategorizationEpic HTTP call', {
25
- selectedTab,
26
- refreshViewInBackground,
27
- search_text: uiState.searchString,
28
- });
29
24
  const queryParam = {
30
25
  start_date: (0, timePeriod_1.toString)(period.start),
31
26
  end_date: (0, timePeriod_1.toString)(period.end),
@@ -5,6 +5,6 @@ import { fetchAccountList } from '../../../accountList/accountListReducer';
5
5
  import { fetchClassList } from '../../../classList/classListReducer';
6
6
  import { fetchOwnerList } from '../../../ownerList/ownerListReducer';
7
7
  import { fetchProjectList } from '../../../projectList/projectListReducer';
8
- import { fetchTransactionCategorization, fetchTransactionCategorizationView } from '../../reducers/transactionsViewReducer';
9
- export type ActionType = ReturnType<typeof fetchTransactionCategorizationView> | ReturnType<typeof fetchTransactionCategorization> | ReturnType<typeof fetchAccountList> | ReturnType<typeof fetchClassList> | ReturnType<typeof fetchOwnerList> | ReturnType<typeof fetchProjectList>;
8
+ import { fetchTransactionCategorization, fetchTransactionCategorizationView, restoreFullDatasetSnapshot } from '../../reducers/transactionsViewReducer';
9
+ export type ActionType = ReturnType<typeof fetchTransactionCategorizationView> | ReturnType<typeof fetchTransactionCategorization> | ReturnType<typeof restoreFullDatasetSnapshot> | ReturnType<typeof fetchAccountList> | ReturnType<typeof fetchClassList> | ReturnType<typeof fetchOwnerList> | ReturnType<typeof fetchProjectList>;
10
10
  export declare const fetchTransactionCategorizationViewEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>) => Observable<ActionType>;
@@ -16,18 +16,18 @@ const fetchTransactionCategorizationViewEpic = (actions$, state$) => actions$.pi
16
16
  const updateActions = [];
17
17
  const { expenseAutomationTransactionsViewState } = state$.value;
18
18
  const { fetchState, transactionIdsBySelectedPeriod } = expenseAutomationTransactionsViewState.transactionCategorizationView[selectedTab];
19
- console.log('[EA-SEARCH] fetchTransactionCategorizationViewEpic received', {
20
- selectedTab,
21
- cacheOverride,
22
- searchString,
23
- fetchState,
24
- });
25
19
  const monthYearPeriod = {
26
20
  month: period.start.month,
27
21
  year: period.start.year,
28
22
  };
29
- const transactionIds = transactionIdsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(monthYearPeriod)] ??
30
- [];
23
+ const periodId = (0, timePeriod_1.toMonthYearPeriodId)(monthYearPeriod);
24
+ const transactionIds = transactionIdsBySelectedPeriod[periodId] ?? [];
25
+ const hasUsableSnapshot = (tabKey) => {
26
+ const snapshot = expenseAutomationTransactionsViewState.transactionCategorizationView[tabKey].fullDatasetSnapshot;
27
+ return (snapshot != null &&
28
+ snapshot.periodId === periodId &&
29
+ snapshot.transactionIds.length > 0);
30
+ };
31
31
  const accountList = state$.value.accountListState.byReportId['accountList'];
32
32
  if (accountList.hasValidState() === false &&
33
33
  accountList.fetchState === 'Not-Started') {
@@ -49,37 +49,33 @@ const fetchTransactionCategorizationViewEpic = (actions$, state$) => actions$.pi
49
49
  vendorOwnerList.fetchState === 'Not-Started') {
50
50
  updateActions.push((0, ownerListReducer_1.fetchOwnerList)());
51
51
  }
52
- console.log('[EA-SEARCH] view epic cache check', {
53
- selectedTab,
54
- cacheOverride,
55
- fetchState,
56
- transactionIdsBySelectedPeriod
57
- });
58
- if (cacheOverride === true ||
52
+ // When clearing search (searchString === ''), restore from the pre-search
53
+ // snapshot if one exists — avoids a round-trip since all data is already
54
+ // in the entity store. Fall back to a normal fetch if no snapshot (the
55
+ // tab was never loaded before the search was applied).
56
+ if (searchString === '' && hasUsableSnapshot(selectedTab)) {
57
+ updateActions.push((0, transactionsViewReducer_1.restoreFullDatasetSnapshot)({ selectedTab, period }));
58
+ }
59
+ else if (cacheOverride === true ||
59
60
  fetchState === 'Not-Started' ||
60
61
  transactionIds.length === 0) {
61
- console.log('[EA-SEARCH] dispatching fetchTransactionCategorization for active tab', { selectedTab, searchString });
62
62
  updateActions.push((0, transactionsViewReducer_1.fetchTransactionCategorization)(selectedTab, period, cacheOverride, keepExistingListItems, searchString, pageToken, refreshViewInBackground, resetListItems, isUncategorizedExpenseCategoryEnabled));
63
63
  }
64
64
  // When search changes (searchString defined, including empty string to
65
- // clear), also fetch every other tab so all tabs always reflect the same
65
+ // clear), also update every other tab so all tabs always reflect the same
66
66
  // search query. Skip if the other tab already has this search string to
67
- // avoid redundant requests on noop updates. cacheOverride=true bypasses
68
- // the cache check; pageToken reset to undefined so the other tab starts
69
- // from page 1; refreshViewInBackground=true because the user isn't
70
- // looking at the other tab, so a silent background fetch is correct.
67
+ // avoid redundant work on noop updates. On clear, restore from snapshot
68
+ // if available; otherwise issue a background fetch.
71
69
  if (searchString !== undefined) {
72
70
  transactionsViewState_1.TRANSACTIONS_TABS.filter((tab) => tab !== selectedTab).forEach((otherTab) => {
73
71
  const otherTabSearchString = expenseAutomationTransactionsViewState.transactionCategorizationView[otherTab].uiState.searchString;
74
- console.log('[EA-SEARCH] dual-fetch check for other tab', {
75
- otherTab,
76
- searchString,
77
- otherTabSearchString,
78
- willDispatch: searchString !== otherTabSearchString,
79
- });
80
72
  if (searchString !== otherTabSearchString) {
81
- console.log('[EA-SEARCH] dispatching fetchTransactionCategorization for other tab', { otherTab, searchString });
82
- updateActions.push((0, transactionsViewReducer_1.fetchTransactionCategorization)(otherTab, period, true, keepExistingListItems, searchString, undefined, true, true, isUncategorizedExpenseCategoryEnabled));
73
+ if (searchString === '' && hasUsableSnapshot(otherTab)) {
74
+ updateActions.push((0, transactionsViewReducer_1.restoreFullDatasetSnapshot)({ selectedTab: otherTab, period }));
75
+ }
76
+ else {
77
+ updateActions.push((0, transactionsViewReducer_1.fetchTransactionCategorization)(otherTab, period, true, keepExistingListItems, searchString, undefined, true, true, isUncategorizedExpenseCategoryEnabled));
78
+ }
83
79
  }
84
80
  });
85
81
  }
@@ -137,7 +137,10 @@ export declare const fetchTransactionCategorization: import("@reduxjs/toolkit").
137
137
  }, "expenseAutomationTransactionsView/fetchTransactionCategorizationSuccess">, resetOtherTabsFetchState: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
138
138
  tabs: TransactionsTab[];
139
139
  refreshViewInBackground?: boolean;
140
- }, "expenseAutomationTransactionsView/resetOtherTabsFetchState">, fetchTransactionCategorizationView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[selectedTab: "review" | "autoCategorized", period: TimePeriod, cacheOverride?: any, keepExistingListItems?: any, searchString?: string | undefined, pageToken?: PageToken | undefined, refreshViewInBackground?: any, resetListItems?: any, isUncategorizedExpenseCategoryEnabled?: boolean | undefined], {
140
+ }, "expenseAutomationTransactionsView/resetOtherTabsFetchState">, restoreFullDatasetSnapshot: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
141
+ period: TimePeriod;
142
+ selectedTab: TransactionsTab;
143
+ }, "expenseAutomationTransactionsView/restoreFullDatasetSnapshot">, fetchTransactionCategorizationView: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[selectedTab: "review" | "autoCategorized", period: TimePeriod, cacheOverride?: any, keepExistingListItems?: any, searchString?: string | undefined, pageToken?: PageToken | undefined, refreshViewInBackground?: any, resetListItems?: any, isUncategorizedExpenseCategoryEnabled?: boolean | undefined], {
141
144
  selectedTab: "review" | "autoCategorized";
142
145
  period: TimePeriod;
143
146
  searchString: string | undefined;
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  var _a;
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.uploadTransactionCategorizationReceiptSuccess = exports.updateTransactionCategorizationUploadReceiptState = exports.removeTransactionFromAllTabs = exports.backgroundRefetchReviewTab = exports.syncTransactionCategorizationFromDetailSave = exports.updateSelectedTransactionId = exports.markCategoryClassRecommendationsFailureForCategorization = exports.markCategoryClassRecommendationsCompletedForCategorization = exports.markCategoryClassRecommendationsInProgressForCategorization = exports.setEntityRecommendationForLineIdsForCategorization = exports.clearExpenseAutomationTransactionsView = exports.clearExpenseAutomationTransactionsViewPerTabView = exports.updateSelectedCheckboxTransactionIds = exports.fetchTransactionCategorizationView = exports.resetOtherTabsFetchState = exports.fetchTransactionCategorizationSuccess = exports.updateParentTotalCountForTab = exports.updateTotalCountForTransactionCategorization = exports.setAllItemsToCategoryClassInLocalDataForCategorization = exports.updateSelectedCustomerForTransaction = exports.updateSelectedVendorForTransaction = exports.updateStatusForTransactionNotMiscategorizedUpdateForCategorization = exports.markTransactionAsNotMiscategorized = exports.updateTransactionCategorizationSaveStatus = exports.updateTransactionCategorizationCompletedSubTab = exports.updateCurrentSelectedTransactionCategorizationTab = exports.updateTransactionCategorization = exports.saveTransactionCategorization = exports.fetchTransactionCategorizationFailure = exports.saveTransactionCategorizationLocalData = exports.initializeTransactionCategorizationViewLocalData = exports.updateTransactionFilters = exports.updateTransactionCategorizationUIState = exports.fetchTransactionCategorization = exports.initialState = exports.initialTransactionTabViewState = void 0;
7
+ exports.uploadTransactionCategorizationReceiptSuccess = exports.updateTransactionCategorizationUploadReceiptState = exports.removeTransactionFromAllTabs = exports.backgroundRefetchReviewTab = exports.syncTransactionCategorizationFromDetailSave = exports.updateSelectedTransactionId = exports.markCategoryClassRecommendationsFailureForCategorization = exports.markCategoryClassRecommendationsCompletedForCategorization = exports.markCategoryClassRecommendationsInProgressForCategorization = exports.setEntityRecommendationForLineIdsForCategorization = exports.clearExpenseAutomationTransactionsView = exports.clearExpenseAutomationTransactionsViewPerTabView = exports.updateSelectedCheckboxTransactionIds = exports.fetchTransactionCategorizationView = exports.restoreFullDatasetSnapshot = exports.resetOtherTabsFetchState = exports.fetchTransactionCategorizationSuccess = exports.updateParentTotalCountForTab = exports.updateTotalCountForTransactionCategorization = exports.setAllItemsToCategoryClassInLocalDataForCategorization = exports.updateSelectedCustomerForTransaction = exports.updateSelectedVendorForTransaction = exports.updateStatusForTransactionNotMiscategorizedUpdateForCategorization = exports.markTransactionAsNotMiscategorized = exports.updateTransactionCategorizationSaveStatus = exports.updateTransactionCategorizationCompletedSubTab = exports.updateCurrentSelectedTransactionCategorizationTab = exports.updateTransactionCategorization = exports.saveTransactionCategorization = exports.fetchTransactionCategorizationFailure = exports.saveTransactionCategorizationLocalData = exports.initializeTransactionCategorizationViewLocalData = exports.updateTransactionFilters = exports.updateTransactionCategorizationUIState = exports.fetchTransactionCategorization = exports.initialState = exports.initialTransactionTabViewState = void 0;
8
8
  const toolkit_1 = require("@reduxjs/toolkit");
9
9
  const get_1 = __importDefault(require("lodash/get"));
10
10
  const uniq_1 = __importDefault(require("lodash/uniq"));
@@ -45,6 +45,7 @@ exports.initialTransactionTabViewState = {
45
45
  hasValidState() {
46
46
  return this.fetchState == 'Completed';
47
47
  },
48
+ fullDatasetSnapshot: undefined,
48
49
  transactionReviewLocalDataById: {},
49
50
  transactionIdsBySelectedPeriod: {},
50
51
  selectedCheckBoxTransactionIds: [],
@@ -176,8 +177,29 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
176
177
  month: period.start.month,
177
178
  year: period.start.year,
178
179
  };
180
+ const periodId = (0, timePeriod_1.toMonthYearPeriodId)(monthYearPeriod);
179
181
  const transactionIdsBySelectedPeriod = draft.transactionCategorizationView[selectedTab]
180
- .transactionIdsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(monthYearPeriod)] ?? [];
182
+ .transactionIdsBySelectedPeriod[periodId] ?? [];
183
+ // When starting a non-empty search, snapshot the current full dataset
184
+ // so clearing the search can restore instantly without an API round-trip.
185
+ // Only save the snapshot when there are rows to restore; skip for tabs
186
+ // that have never been loaded (empty list → no useful snapshot).
187
+ if (searchString !== undefined &&
188
+ searchString !== '' &&
189
+ transactionIdsBySelectedPeriod.length > 0) {
190
+ const snapshot = {
191
+ periodId,
192
+ totalCount: draft.transactionCategorizationView[selectedTab].uiState
193
+ .totalCount[periodId],
194
+ transactionIds: [...transactionIdsBySelectedPeriod],
195
+ transactionReviewLocalDataById: {
196
+ ...draft.transactionCategorizationView[selectedTab]
197
+ .transactionReviewLocalDataById,
198
+ },
199
+ };
200
+ draft.transactionCategorizationView[selectedTab].fullDatasetSnapshot =
201
+ snapshot;
202
+ }
181
203
  transactionIdsBySelectedPeriod.forEach((transactionId) => {
182
204
  delete draft.transactionCategorizationView[selectedTab]
183
205
  .transactionReviewLocalDataById[transactionId];
@@ -693,6 +715,39 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
693
715
  // leave the other tab's state untouched — it has its own active fetch.
694
716
  });
695
717
  },
718
+ restoreFullDatasetSnapshot(draft, action) {
719
+ const { selectedTab, period } = action.payload;
720
+ const periodId = (0, timePeriod_1.toMonthYearPeriodId)({
721
+ month: period.start.month,
722
+ year: period.start.year,
723
+ });
724
+ const snapshot = draft.transactionCategorizationView[selectedTab].fullDatasetSnapshot;
725
+ if (snapshot == null || snapshot.periodId !== periodId) {
726
+ return;
727
+ }
728
+ draft.transactionCategorizationView[selectedTab]
729
+ .transactionIdsBySelectedPeriod[periodId] = snapshot.transactionIds;
730
+ // Merge back per-transaction local data; skip entries that were updated
731
+ // during the search (e.g. a save that happened while filtered results
732
+ // were showing) so those saves are not overwritten.
733
+ Object.entries(snapshot.transactionReviewLocalDataById).forEach(([transactionId, localData]) => {
734
+ if (draft.transactionCategorizationView[selectedTab]
735
+ .transactionReviewLocalDataById[transactionId] == null) {
736
+ draft.transactionCategorizationView[selectedTab]
737
+ .transactionReviewLocalDataById[transactionId] = localData;
738
+ }
739
+ });
740
+ if (snapshot.totalCount != null) {
741
+ draft.transactionCategorizationView[selectedTab].uiState.totalCount[periodId] = snapshot.totalCount;
742
+ }
743
+ draft.transactionCategorizationView[selectedTab].uiState.searchString =
744
+ '';
745
+ draft.transactionCategorizationView[selectedTab].uiState.pageToken = null;
746
+ draft.transactionCategorizationView[selectedTab].fetchState = 'Completed';
747
+ draft.transactionCategorizationView[selectedTab].error = undefined;
748
+ draft.transactionCategorizationView[selectedTab].fullDatasetSnapshot =
749
+ undefined;
750
+ },
696
751
  fetchTransactionCategorizationFailure(draft, action) {
697
752
  const { selectedTab, selectedPeriod } = action.payload;
698
753
  draft.transactionCategorizationView[selectedTab].transactionIdsBySelectedPeriod[(0, timePeriod_1.toMonthYearPeriodId)(selectedPeriod)] =
@@ -1073,5 +1128,5 @@ const expenseAutomationTransactionsView = (0, toolkit_1.createSlice)({
1073
1128
  },
1074
1129
  },
1075
1130
  });
1076
- _a = expenseAutomationTransactionsView.actions, exports.fetchTransactionCategorization = _a.fetchTransactionCategorization, exports.updateTransactionCategorizationUIState = _a.updateTransactionCategorizationUIState, exports.updateTransactionFilters = _a.updateTransactionFilters, exports.initializeTransactionCategorizationViewLocalData = _a.initializeTransactionCategorizationViewLocalData, exports.saveTransactionCategorizationLocalData = _a.saveTransactionCategorizationLocalData, exports.fetchTransactionCategorizationFailure = _a.fetchTransactionCategorizationFailure, exports.saveTransactionCategorization = _a.saveTransactionCategorization, exports.updateTransactionCategorization = _a.updateTransactionCategorization, exports.updateCurrentSelectedTransactionCategorizationTab = _a.updateCurrentSelectedTransactionCategorizationTab, exports.updateTransactionCategorizationCompletedSubTab = _a.updateTransactionCategorizationCompletedSubTab, exports.updateTransactionCategorizationSaveStatus = _a.updateTransactionCategorizationSaveStatus, exports.markTransactionAsNotMiscategorized = _a.markTransactionAsNotMiscategorized, exports.updateStatusForTransactionNotMiscategorizedUpdateForCategorization = _a.updateStatusForTransactionNotMiscategorizedUpdateForCategorization, exports.updateSelectedVendorForTransaction = _a.updateSelectedVendorForTransaction, exports.updateSelectedCustomerForTransaction = _a.updateSelectedCustomerForTransaction, exports.setAllItemsToCategoryClassInLocalDataForCategorization = _a.setAllItemsToCategoryClassInLocalDataForCategorization, exports.updateTotalCountForTransactionCategorization = _a.updateTotalCountForTransactionCategorization, exports.updateParentTotalCountForTab = _a.updateParentTotalCountForTab, exports.fetchTransactionCategorizationSuccess = _a.fetchTransactionCategorizationSuccess, exports.resetOtherTabsFetchState = _a.resetOtherTabsFetchState, exports.fetchTransactionCategorizationView = _a.fetchTransactionCategorizationView, exports.updateSelectedCheckboxTransactionIds = _a.updateSelectedCheckboxTransactionIds, exports.clearExpenseAutomationTransactionsViewPerTabView = _a.clearExpenseAutomationTransactionsViewPerTabView, exports.clearExpenseAutomationTransactionsView = _a.clearExpenseAutomationTransactionsView, exports.setEntityRecommendationForLineIdsForCategorization = _a.setEntityRecommendationForLineIdsForCategorization, exports.markCategoryClassRecommendationsInProgressForCategorization = _a.markCategoryClassRecommendationsInProgressForCategorization, exports.markCategoryClassRecommendationsCompletedForCategorization = _a.markCategoryClassRecommendationsCompletedForCategorization, exports.markCategoryClassRecommendationsFailureForCategorization = _a.markCategoryClassRecommendationsFailureForCategorization, exports.updateSelectedTransactionId = _a.updateSelectedTransactionId, exports.syncTransactionCategorizationFromDetailSave = _a.syncTransactionCategorizationFromDetailSave, exports.backgroundRefetchReviewTab = _a.backgroundRefetchReviewTab, exports.removeTransactionFromAllTabs = _a.removeTransactionFromAllTabs, exports.updateTransactionCategorizationUploadReceiptState = _a.updateTransactionCategorizationUploadReceiptState, exports.uploadTransactionCategorizationReceiptSuccess = _a.uploadTransactionCategorizationReceiptSuccess;
1131
+ _a = expenseAutomationTransactionsView.actions, exports.fetchTransactionCategorization = _a.fetchTransactionCategorization, exports.updateTransactionCategorizationUIState = _a.updateTransactionCategorizationUIState, exports.updateTransactionFilters = _a.updateTransactionFilters, exports.initializeTransactionCategorizationViewLocalData = _a.initializeTransactionCategorizationViewLocalData, exports.saveTransactionCategorizationLocalData = _a.saveTransactionCategorizationLocalData, exports.fetchTransactionCategorizationFailure = _a.fetchTransactionCategorizationFailure, exports.saveTransactionCategorization = _a.saveTransactionCategorization, exports.updateTransactionCategorization = _a.updateTransactionCategorization, exports.updateCurrentSelectedTransactionCategorizationTab = _a.updateCurrentSelectedTransactionCategorizationTab, exports.updateTransactionCategorizationCompletedSubTab = _a.updateTransactionCategorizationCompletedSubTab, exports.updateTransactionCategorizationSaveStatus = _a.updateTransactionCategorizationSaveStatus, exports.markTransactionAsNotMiscategorized = _a.markTransactionAsNotMiscategorized, exports.updateStatusForTransactionNotMiscategorizedUpdateForCategorization = _a.updateStatusForTransactionNotMiscategorizedUpdateForCategorization, exports.updateSelectedVendorForTransaction = _a.updateSelectedVendorForTransaction, exports.updateSelectedCustomerForTransaction = _a.updateSelectedCustomerForTransaction, exports.setAllItemsToCategoryClassInLocalDataForCategorization = _a.setAllItemsToCategoryClassInLocalDataForCategorization, exports.updateTotalCountForTransactionCategorization = _a.updateTotalCountForTransactionCategorization, exports.updateParentTotalCountForTab = _a.updateParentTotalCountForTab, exports.fetchTransactionCategorizationSuccess = _a.fetchTransactionCategorizationSuccess, exports.resetOtherTabsFetchState = _a.resetOtherTabsFetchState, exports.restoreFullDatasetSnapshot = _a.restoreFullDatasetSnapshot, exports.fetchTransactionCategorizationView = _a.fetchTransactionCategorizationView, exports.updateSelectedCheckboxTransactionIds = _a.updateSelectedCheckboxTransactionIds, exports.clearExpenseAutomationTransactionsViewPerTabView = _a.clearExpenseAutomationTransactionsViewPerTabView, exports.clearExpenseAutomationTransactionsView = _a.clearExpenseAutomationTransactionsView, exports.setEntityRecommendationForLineIdsForCategorization = _a.setEntityRecommendationForLineIdsForCategorization, exports.markCategoryClassRecommendationsInProgressForCategorization = _a.markCategoryClassRecommendationsInProgressForCategorization, exports.markCategoryClassRecommendationsCompletedForCategorization = _a.markCategoryClassRecommendationsCompletedForCategorization, exports.markCategoryClassRecommendationsFailureForCategorization = _a.markCategoryClassRecommendationsFailureForCategorization, exports.updateSelectedTransactionId = _a.updateSelectedTransactionId, exports.syncTransactionCategorizationFromDetailSave = _a.syncTransactionCategorizationFromDetailSave, exports.backgroundRefetchReviewTab = _a.backgroundRefetchReviewTab, exports.removeTransactionFromAllTabs = _a.removeTransactionFromAllTabs, exports.updateTransactionCategorizationUploadReceiptState = _a.updateTransactionCategorizationUploadReceiptState, exports.uploadTransactionCategorizationReceiptSuccess = _a.uploadTransactionCategorizationReceiptSuccess;
1077
1132
  exports.default = expenseAutomationTransactionsView.reducer;
@@ -94,8 +94,23 @@ export interface SupportedTransactionCategorization {
94
94
  transactionId?: ID;
95
95
  }
96
96
  export declare const initialSupportedTransactionCategorization: SupportedTransactionCategorization;
97
+ /**
98
+ * Snapshot of a tab's full (pre-search) dataset taken the moment the user
99
+ * first applies a non-empty search string. When the search is cleared the
100
+ * view epic restores from here instead of issuing a new API request.
101
+ *
102
+ * Only saved when there are actual rows to restore (`transactionIds` is
103
+ * non-empty); cleared once consumed or replaced by a fresh search.
104
+ */
105
+ export interface FullDatasetSnapshot {
106
+ periodId: MonthYearPeriodId;
107
+ totalCount: number | undefined;
108
+ transactionIds: ID[];
109
+ transactionReviewLocalDataById: Record<ID | TransactionDetailKey, SupportedTransactionCategorization>;
110
+ }
97
111
  export interface TransactionsTabViewState extends FetchedState {
98
112
  filters: TransactionFilters;
113
+ fullDatasetSnapshot: FullDatasetSnapshot | undefined;
99
114
  markAsNotMiscategorizedStatus: FetchStateAndError;
100
115
  refreshStatus: FetchStateAndError;
101
116
  saveStatus: FetchStateAndError;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.1.16-betaRD4",
3
+ "version": "5.1.16-betaRD5",
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",