@zeniai/client-epic-state 5.2.11-betaRD2 → 5.2.11-betaRD3

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 (19) hide show
  1. package/lib/entity/jeSchedules/jeSchedulesPayload.js +3 -0
  2. package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +3 -0
  3. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +7 -2
  4. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +31 -5
  5. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +33 -15
  6. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +7 -2
  7. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +6 -0
  8. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +3 -0
  9. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.d.ts +2 -1
  10. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +6 -1
  11. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.d.ts +4 -40
  12. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +29 -3
  13. package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.d.ts +2 -1
  14. package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +32 -14
  15. package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.d.ts +2 -1
  16. package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +6 -1
  17. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +6 -0
  18. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +3 -0
  19. package/package.json +1 -1
@@ -329,6 +329,9 @@ function toJEAccruedScheduledTransaction(payload) {
329
329
  endDate: payload.end_date != null ? (0, zeniDayJS_1.date)(payload.end_date) : undefined,
330
330
  period: payload.period != null ? Number(payload.period) : undefined,
331
331
  updatedByUserID: payload.updated_by.user_id,
332
+ // The accrued joiner resolves createdByUserID into the "Created by" cell. Without this the
333
+ // payload's created_by was dropped here and every accrued row rendered a hyphen.
334
+ createdByUserID: payload.created_by?.user_id ?? undefined,
332
335
  balanceAsOfToday: payload.balance ?? undefined,
333
336
  updatedAt: payload.updated_at != null ? (0, zeniDayJS_1.date)(payload.updated_at) : undefined,
334
337
  jeCredit: {
@@ -318,6 +318,9 @@ export function toJEAccruedScheduledTransaction(payload) {
318
318
  endDate: payload.end_date != null ? date(payload.end_date) : undefined,
319
319
  period: payload.period != null ? Number(payload.period) : undefined,
320
320
  updatedByUserID: payload.updated_by.user_id,
321
+ // The accrued joiner resolves createdByUserID into the "Created by" cell. Without this the
322
+ // payload's created_by was dropped here and every accrued row rendered a hyphen.
323
+ createdByUserID: payload.created_by?.user_id ?? undefined,
321
324
  balanceAsOfToday: payload.balance ?? undefined,
322
325
  updatedAt: payload.updated_at != null ? date(payload.updated_at) : undefined,
323
326
  jeCredit: {
@@ -1,9 +1,10 @@
1
1
  import { from } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
3
  import { convertToPeriod, toAbsoluteDay, } from '../../../../../commonStateTypes/timePeriod';
4
4
  import { updateJEOneTimeAccruals } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
5
5
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
6
6
  import { getCurrentTenant } from '../../../../../entity/tenant/tenantSelector';
7
+ import { clearAll } from '../../../../../rootActions';
7
8
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../../responsePayload';
8
9
  import { prepareCreateOneTimeAccrualPayload, } from '../../../payload/jeSchedulesPayload';
9
10
  import { createOneTimeAccrual, createOneTimeAccrualFailure, createOneTimeAccrualSuccess, fetchJeSchedules, } from '../../../reducers/jeSchedulesViewReducer';
@@ -95,5 +96,9 @@ export const createOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.
95
96
  createOneTimeAccrualFailure({
96
97
  status: createZeniAPIStatus('Unexpected Error', `Create One Time Accrual REST API call errored out ${JSON.stringify(error)}`),
97
98
  }),
98
- ])));
99
+ ])),
100
+ // A tenant switch clears the store while this request is still in flight.
101
+ // Without this the old tenant's response is written back into the freshly
102
+ // cleared store and stays on screen under the new tenant.
103
+ takeUntil(actions$.pipe(filter(clearAll.match))));
99
104
  }));
@@ -1,12 +1,15 @@
1
1
  import { from } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
+ import { convertToPeriod, toAbsoluteDay, } from '../../../../../commonStateTypes/timePeriod';
4
+ import { getCurrentTenant } from '../../../../../entity/tenant/tenantSelector';
5
+ import { clearAll } from '../../../../../rootActions';
3
6
  import { updateJESchedules, updateJESchedulesDetails, } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
4
7
  import { getJEScheduleTransactionKey } from '../../../../../entity/jeSchedules/jeSchedulesTypes';
5
8
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
6
9
  import { getScheduleTransactionById } from '../../../../../entity/transaction/transactionSelector';
7
10
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../../responsePayload';
8
11
  import { prepareNewSchedulePayload, } from '../../../helpers/newScheduleLocalDataHelper';
9
- import { resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess, } from '../../../reducers/jeSchedulesViewReducer';
12
+ import { fetchJeSchedules, resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess, } from '../../../reducers/jeSchedulesViewReducer';
10
13
  const FAILURE_REASON_MESSAGES = {
11
14
  missing_third_party_id: 'This transaction is missing a QuickBooks reference and cannot be scheduled.',
12
15
  missing_vendor: 'Select a vendor before saving this schedule.',
@@ -58,11 +61,21 @@ export const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe(
58
61
  je_schedule_type: scheduleType,
59
62
  };
60
63
  const payload = { ...result.payload };
64
+ // convertToPeriod destructures, so an unset period would throw here and take the epic
65
+ // subscription down for the whole session -- a missed refresh is the cheaper failure.
66
+ const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[getCurrentTenant(state).tenantId];
67
+ const selectedPeriod = selectedMonthYear != null
68
+ ? convertToPeriod(selectedMonthYear)
69
+ : undefined;
70
+ const periodStart = selectedPeriod != null
71
+ ? toAbsoluteDay(selectedPeriod.start)
72
+ : undefined;
73
+ const periodEnd = selectedPeriod != null ? toAbsoluteDay(selectedPeriod.end) : undefined;
61
74
  return zeniAPI
62
75
  .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/schedules?query=${encodeURIComponent(JSON.stringify(queryParam))}`, payload)
63
76
  .pipe(mergeMap((response) => {
64
77
  if (isSuccessResponse(response) && response.data != null) {
65
- return from([
78
+ const successActions = [
66
79
  updateJESchedules(response.data.je_schedules),
67
80
  updateJESchedulesDetails(response.data.je_schedules, response.data.scheduled_journal_entries),
68
81
  resetNewScheduleLocalData({ scheduleType }),
@@ -75,7 +88,16 @@ export const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe(
75
88
  { variableName: '_vendor_name_', variableValue: vendorName },
76
89
  ],
77
90
  }),
78
- ]);
91
+ ];
92
+ // Writing the entity is not enough: the list reads
93
+ // jeScheduleTransactionKeysByPeriodAndTab, which is rebuilt wholesale per fetch, so
94
+ // without this the new schedule stays invisible until some later fetch. The page
95
+ // only refetches when the save also changes tab, so saving from the tab the row
96
+ // lands on showed nothing. Same reasoning as createOneTimeAccrualEpic.
97
+ if (periodStart != null && periodEnd != null) {
98
+ successActions.push(fetchJeSchedules({ start: periodStart, end: periodEnd }, true));
99
+ }
100
+ return from(successActions);
79
101
  }
80
102
  return from([
81
103
  saveNewScheduleFailure({ status: response.status }),
@@ -106,5 +128,9 @@ export const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe(
106
128
  saveNewScheduleFailure({
107
129
  status: createZeniAPIStatus('Unexpected Error', `Save New Schedule REST API call errored out ${JSON.stringify(error)}`),
108
130
  }),
109
- ])));
131
+ ])),
132
+ // A tenant switch clears the store while this request is still in flight.
133
+ // Without this the old tenant's response is written back into the freshly
134
+ // cleared store and stays on screen under the new tenant.
135
+ takeUntil(actions$.pipe(filter(clearAll.match))));
110
136
  }));
@@ -1,17 +1,22 @@
1
1
  import { from, merge } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
3
  import { convertToPeriod, toAbsoluteDay, } from '../../../../commonStateTypes/timePeriod';
4
4
  import { generateJEOneTimeAccrualKey } from '../../../../entity/jeSchedules/jeScheduleHelper';
5
5
  import { updateJEOneTimeAccruals } from '../../../../entity/jeSchedules/jeSchedulesReducer';
6
6
  import { getJEOneTimeAccrualByKey } from '../../../../entity/jeSchedules/jeSchedulesSelector';
7
7
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
8
8
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
9
+ import { clearAll } from '../../../../rootActions';
9
10
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
10
11
  import { fetchJeSchedules, ignoreOneTimeAccrual, oneTimeAccrualActionFailure, oneTimeAccrualActionSuccess, promoteOneTimeAccrual, revertOneTimeAccrual, } from '../../reducers/jeSchedulesViewReducer';
11
12
  /**
12
13
  * The page the accrual came from, so a promoted or ignored row lands in the right bucket. Guarded
13
14
  * because convertToPeriod destructures: an unset period would throw and take the epic subscription
14
15
  * down for the session, and a missed refresh is the cheaper failure.
16
+ *
17
+ * Read state$.value at response time rather than the snapshot taken when the action was dispatched:
18
+ * if the user changes month while the request is in flight, the snapshot refreshes the month they
19
+ * have left and the month they are now looking at stays stale.
15
20
  */
16
21
  function refreshCurrentPage(state) {
17
22
  const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[getCurrentTenant(state).tenantId];
@@ -26,14 +31,17 @@ function refreshCurrentPage(state) {
26
31
  }
27
32
  return [fetchJeSchedules({ start, end }, true)];
28
33
  }
29
- function failureActions(accrualId, message, status) {
34
+ function failureActions(
35
+ // Per action, not fixed: every failure used to read "Save schedule failed", so skipping or
36
+ // reverting an accrual reported an operation the user had not performed.
37
+ messageSection, accrualId, message, status) {
30
38
  return [
31
39
  oneTimeAccrualActionFailure({
32
40
  accrualId,
33
41
  status: status ?? createZeniAPIStatus('Unexpected Error', message),
34
42
  }),
35
43
  openSnackbar({
36
- messageSection: 'save_schedule',
44
+ messageSection,
37
45
  messageText: 'failed',
38
46
  type: 'error',
39
47
  variables: [{ variableName: '_api-error_', variableValue: message }],
@@ -47,7 +55,7 @@ export const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$
47
55
  if (accrual?.jeDebitAccountId == null ||
48
56
  accrual.jeCreditAccountId == null) {
49
57
  // Promote posts a journal entry, so it cannot proceed without both sides of it.
50
- return from(failureActions(accrualId, 'This accrual is missing an expense or liability account.'));
58
+ return from(failureActions('save_schedule', accrualId, 'This accrual is missing an expense or liability account.'));
51
59
  }
52
60
  // vendor_id, vendor_name and service_month are deliberately absent: promote takes them from
53
61
  // the stored draft and ignores them in the body, so sending them would imply a reassignment
@@ -76,14 +84,17 @@ export const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$
76
84
  },
77
85
  ],
78
86
  }),
79
- ...refreshCurrentPage(state),
87
+ ...refreshCurrentPage(state$.value),
80
88
  ]);
81
89
  }
82
- return from(failureActions(accrualId, response.status.message, response.status));
83
- }), catchError((error) => from(failureActions(accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))));
90
+ return from(failureActions('save_schedule', accrualId, response.status.message, response.status));
91
+ }), catchError((error) => from(failureActions('save_schedule', accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))),
92
+ // A tenant switch clears the store while this request is still in flight.
93
+ // Without this the old tenant's response is written back into the freshly
94
+ // cleared store and stays on screen under the new tenant.
95
+ takeUntil(actions$.pipe(filter(clearAll.match))));
84
96
  }));
85
97
  export const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(ignoreOneTimeAccrual.match), mergeMap((action) => {
86
- const state = state$.value;
87
98
  const { accrualId, reason, note } = action.payload;
88
99
  // `scope` is deliberately never sent: the API returns 400 on it, which is why the design's
89
100
  // "Apply to" selector cannot be built yet.
@@ -103,14 +114,17 @@ export const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.
103
114
  messageText: 'success',
104
115
  type: 'success',
105
116
  }),
106
- ...refreshCurrentPage(state),
117
+ ...refreshCurrentPage(state$.value),
107
118
  ]);
108
119
  }
109
- return from(failureActions(accrualId, response.status.message, response.status));
110
- }), catchError((error) => from(failureActions(accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))));
120
+ return from(failureActions('ignore_accrual', accrualId, response.status.message, response.status));
121
+ }), catchError((error) => from(failureActions('ignore_accrual', accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))),
122
+ // A tenant switch clears the store while this request is still in flight.
123
+ // Without this the old tenant's response is written back into the freshly
124
+ // cleared store and stays on screen under the new tenant.
125
+ takeUntil(actions$.pipe(filter(clearAll.match))));
111
126
  }));
112
127
  export const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(revertOneTimeAccrual.match), mergeMap((action) => {
113
- const state = state$.value;
114
128
  const { accrualId, reason, note } = action.payload;
115
129
  // Deletes the accrual's journal entry in QBO and soft-deletes the row. `scope` is never sent:
116
130
  // the endpoint 400s on it, exactly as ignore does.
@@ -130,10 +144,14 @@ export const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.
130
144
  messageText: 'success',
131
145
  type: 'success',
132
146
  }),
133
- ...refreshCurrentPage(state),
147
+ ...refreshCurrentPage(state$.value),
134
148
  ]);
135
149
  }
136
- return from(failureActions(accrualId, response.status.message, response.status));
137
- }), catchError((error) => from(failureActions(accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))));
150
+ return from(failureActions('revert_accrual', accrualId, response.status.message, response.status));
151
+ }), catchError((error) => from(failureActions('revert_accrual', accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))),
152
+ // A tenant switch clears the store while this request is still in flight.
153
+ // Without this the old tenant's response is written back into the freshly
154
+ // cleared store and stays on screen under the new tenant.
155
+ takeUntil(actions$.pipe(filter(clearAll.match))));
138
156
  }));
139
157
  export const oneTimeAccrualActionsEpic = (actions$, state$, zeniAPI) => merge(promoteOneTimeAccrualEpic(actions$, state$, zeniAPI), ignoreOneTimeAccrualEpic(actions$, state$, zeniAPI), revertOneTimeAccrualEpic(actions$, state$, zeniAPI));
@@ -1,7 +1,8 @@
1
1
  import { from } from 'rxjs';
2
- import { catchError, filter, mergeMap } from 'rxjs/operators';
2
+ import { catchError, filter, mergeMap, takeUntil } from 'rxjs/operators';
3
3
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
4
  import { getCurrentTenant } from '../../../../entity/tenant/tenantSelector';
5
+ import { clearAll } from '../../../../rootActions';
5
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
7
  import { fetchMonthEndCloseChecks } from '../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer';
7
8
  import { removeJeAccruedScheduleKey, removeJeScheduleTransactionKey, revertJeSchedule, revertJeScheduleFailure, revertJeScheduleSuccess, } from '../../reducers/jeSchedulesViewReducer';
@@ -102,5 +103,9 @@ export const revertJeScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe
102
103
  messageText: 'failed',
103
104
  type: 'error',
104
105
  }),
105
- ])));
106
+ ])),
107
+ // A tenant switch clears the store while this request is still in flight.
108
+ // Without this the old tenant's response is written back into the freshly
109
+ // cleared store and stays on screen under the new tenant.
110
+ takeUntil(actions$.pipe(filter(clearAll.match))));
106
111
  }));
@@ -12,6 +12,12 @@ export const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe(filt
12
12
  }
13
13
  const currentTenant = getCurrentTenant(state);
14
14
  const selectedPeriod = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant.tenantId];
15
+ // convertToPeriod destructures, so an unset period throws and takes this epic's subscription
16
+ // down for the rest of the session -- every later tab switch would then stop refreshing. A
17
+ // skipped refresh is the cheaper failure, as in refreshCurrentPage.
18
+ if (selectedPeriod == null) {
19
+ return EMPTY;
20
+ }
15
21
  const period = convertToPeriod(selectedPeriod);
16
22
  const timePeriodStart = toAbsoluteDay(period.start);
17
23
  const timePeriodEnd = toAbsoluteDay(period.end);
@@ -172,6 +172,9 @@ const expenseAutomationJESchedulesView = createSlice({
172
172
  draft.jeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
173
173
  draft.jeScheduleAccruedKeysByPeriodAndTab[periodTabKey] = [];
174
174
  draft.jeScheduleOneTimeAccrualKeysByPeriodAndTab[periodTabKey] = [];
175
+ // Cleared with the rest: leaving it behind kept the previous fetch's resolve rows on an
176
+ // otherwise empty errored tab, which reads as "these are all the schedules there are".
177
+ draft.failedJeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
175
178
  }
176
179
  },
177
180
  retryJeSchedule(draft, action) {
@@ -2,7 +2,8 @@ import { ActionsObservable, StateObservable } from 'redux-observable';
2
2
  import { updateJEOneTimeAccruals } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
3
3
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
4
4
  import { RootState } from '../../../../../reducer';
5
+ import { clearAll } from '../../../../../rootActions';
5
6
  import { ZeniAPI } from '../../../../../zeniAPI';
6
7
  import { createOneTimeAccrual, createOneTimeAccrualFailure, createOneTimeAccrualSuccess, fetchJeSchedules } from '../../../reducers/jeSchedulesViewReducer';
7
- export type ActionType = ReturnType<typeof createOneTimeAccrual> | ReturnType<typeof createOneTimeAccrualSuccess> | ReturnType<typeof createOneTimeAccrualFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
8
+ export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof createOneTimeAccrual> | ReturnType<typeof createOneTimeAccrualSuccess> | ReturnType<typeof createOneTimeAccrualFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
8
9
  export declare const createOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<ActionType>;
@@ -7,6 +7,7 @@ const timePeriod_1 = require("../../../../../commonStateTypes/timePeriod");
7
7
  const jeSchedulesReducer_1 = require("../../../../../entity/jeSchedules/jeSchedulesReducer");
8
8
  const snackbarReducer_1 = require("../../../../../entity/snackbar/snackbarReducer");
9
9
  const tenantSelector_1 = require("../../../../../entity/tenant/tenantSelector");
10
+ const rootActions_1 = require("../../../../../rootActions");
10
11
  const responsePayload_1 = require("../../../../../responsePayload");
11
12
  const jeSchedulesPayload_1 = require("../../../payload/jeSchedulesPayload");
12
13
  const jeSchedulesViewReducer_1 = require("../../../reducers/jeSchedulesViewReducer");
@@ -98,6 +99,10 @@ const createOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0
98
99
  (0, jeSchedulesViewReducer_1.createOneTimeAccrualFailure)({
99
100
  status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', `Create One Time Accrual REST API call errored out ${JSON.stringify(error)}`),
100
101
  }),
101
- ])));
102
+ ])),
103
+ // A tenant switch clears the store while this request is still in flight.
104
+ // Without this the old tenant's response is written back into the freshly
105
+ // cleared store and stays on screen under the new tenant.
106
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
102
107
  }));
103
108
  exports.createOneTimeAccrualEpic = createOneTimeAccrualEpic;
@@ -1,45 +1,9 @@
1
1
  import { ActionsObservable, StateObservable } from 'redux-observable';
2
+ import { clearAll } from '../../../../../rootActions';
2
3
  import { updateJESchedules, updateJESchedulesDetails } from '../../../../../entity/jeSchedules/jeSchedulesReducer';
3
4
  import { openSnackbar } from '../../../../../entity/snackbar/snackbarReducer';
4
5
  import { RootState } from '../../../../../reducer';
5
6
  import { ZeniAPI } from '../../../../../zeniAPI';
6
- import { resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess } from '../../../reducers/jeSchedulesViewReducer';
7
- export type ActionType = ReturnType<typeof saveNewSchedule> | ReturnType<typeof saveNewScheduleSuccess> | ReturnType<typeof saveNewScheduleFailure> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails> | ReturnType<typeof resetNewScheduleLocalData> | ReturnType<typeof openSnackbar>;
8
- export declare const saveNewScheduleEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<{
9
- payload: {
10
- messageSection: import("../../../../..").SnackbarMessageSections;
11
- messageText: import("../../../../..").SnackbarMessageSectionTexts;
12
- type: import("../../../../..").SnackbarMessageType;
13
- showStatusIcon?: boolean;
14
- variables?: {
15
- variableName: string;
16
- variableValue: string;
17
- }[];
18
- };
19
- type: "snackbar/openSnackbar";
20
- } | {
21
- payload: {
22
- jeSchedulesTransactionsPayload: import("../../../../../entity/jeSchedules/jeSchedulesPayload").JEScheduledTransactionPayload[];
23
- };
24
- type: "jeSchedules/updateJESchedules";
25
- } | {
26
- payload: {
27
- jeSchedulesPayload: import("../../../../../entity/jeSchedules/jeSchedulesPayload").JEScheduledTransactionPayload[];
28
- scheduledJEntriesPayload: import("../../../../../entity/jeSchedules/jeSchedulesPayload").ScheduledJournalEntryPayload[] | undefined;
29
- updateType: import("../../../../..").UpdateType;
30
- };
31
- type: "jeSchedules/updateJESchedulesDetails";
32
- } | {
33
- payload: {
34
- scheduleType: "prepaid_expenses" | "fixed_assets";
35
- };
36
- type: "expenseAutomationJESchedulesView/resetNewScheduleLocalData";
37
- } | {
38
- payload: undefined;
39
- type: "expenseAutomationJESchedulesView/saveNewScheduleSuccess";
40
- } | {
41
- payload: {
42
- status: import("../../../../../zeniAPIStatus").ZeniAPIStatus;
43
- };
44
- type: "expenseAutomationJESchedulesView/saveNewScheduleFailure";
45
- }>;
7
+ import { fetchJeSchedules, resetNewScheduleLocalData, saveNewSchedule, saveNewScheduleFailure, saveNewScheduleSuccess } from '../../../reducers/jeSchedulesViewReducer';
8
+ export type ActionType = ReturnType<typeof saveNewSchedule> | ReturnType<typeof saveNewScheduleSuccess> | ReturnType<typeof saveNewScheduleFailure> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails> | ReturnType<typeof resetNewScheduleLocalData> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof clearAll> | ReturnType<typeof openSnackbar>;
9
+ export declare const saveNewScheduleEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<ActionType>;
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.saveNewScheduleEpic = void 0;
4
4
  const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
+ const timePeriod_1 = require("../../../../../commonStateTypes/timePeriod");
7
+ const tenantSelector_1 = require("../../../../../entity/tenant/tenantSelector");
8
+ const rootActions_1 = require("../../../../../rootActions");
6
9
  const jeSchedulesReducer_1 = require("../../../../../entity/jeSchedules/jeSchedulesReducer");
7
10
  const jeSchedulesTypes_1 = require("../../../../../entity/jeSchedules/jeSchedulesTypes");
8
11
  const snackbarReducer_1 = require("../../../../../entity/snackbar/snackbarReducer");
@@ -61,11 +64,21 @@ const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
61
64
  je_schedule_type: scheduleType,
62
65
  };
63
66
  const payload = { ...result.payload };
67
+ // convertToPeriod destructures, so an unset period would throw here and take the epic
68
+ // subscription down for the whole session -- a missed refresh is the cheaper failure.
69
+ const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[(0, tenantSelector_1.getCurrentTenant)(state).tenantId];
70
+ const selectedPeriod = selectedMonthYear != null
71
+ ? (0, timePeriod_1.convertToPeriod)(selectedMonthYear)
72
+ : undefined;
73
+ const periodStart = selectedPeriod != null
74
+ ? (0, timePeriod_1.toAbsoluteDay)(selectedPeriod.start)
75
+ : undefined;
76
+ const periodEnd = selectedPeriod != null ? (0, timePeriod_1.toAbsoluteDay)(selectedPeriod.end) : undefined;
64
77
  return zeniAPI
65
78
  .postAndGetJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/schedules?query=${encodeURIComponent(JSON.stringify(queryParam))}`, payload)
66
79
  .pipe((0, operators_1.mergeMap)((response) => {
67
80
  if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
68
- return (0, rxjs_1.from)([
81
+ const successActions = [
69
82
  (0, jeSchedulesReducer_1.updateJESchedules)(response.data.je_schedules),
70
83
  (0, jeSchedulesReducer_1.updateJESchedulesDetails)(response.data.je_schedules, response.data.scheduled_journal_entries),
71
84
  (0, jeSchedulesViewReducer_1.resetNewScheduleLocalData)({ scheduleType }),
@@ -78,7 +91,16 @@ const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
78
91
  { variableName: '_vendor_name_', variableValue: vendorName },
79
92
  ],
80
93
  }),
81
- ]);
94
+ ];
95
+ // Writing the entity is not enough: the list reads
96
+ // jeScheduleTransactionKeysByPeriodAndTab, which is rebuilt wholesale per fetch, so
97
+ // without this the new schedule stays invisible until some later fetch. The page
98
+ // only refetches when the save also changes tab, so saving from the tab the row
99
+ // lands on showed nothing. Same reasoning as createOneTimeAccrualEpic.
100
+ if (periodStart != null && periodEnd != null) {
101
+ successActions.push((0, jeSchedulesViewReducer_1.fetchJeSchedules)({ start: periodStart, end: periodEnd }, true));
102
+ }
103
+ return (0, rxjs_1.from)(successActions);
82
104
  }
83
105
  return (0, rxjs_1.from)([
84
106
  (0, jeSchedulesViewReducer_1.saveNewScheduleFailure)({ status: response.status }),
@@ -109,6 +131,10 @@ const saveNewScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, ope
109
131
  (0, jeSchedulesViewReducer_1.saveNewScheduleFailure)({
110
132
  status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', `Save New Schedule REST API call errored out ${JSON.stringify(error)}`),
111
133
  }),
112
- ])));
134
+ ])),
135
+ // A tenant switch clears the store while this request is still in flight.
136
+ // Without this the old tenant's response is written back into the freshly
137
+ // cleared store and stays on screen under the new tenant.
138
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
113
139
  }));
114
140
  exports.saveNewScheduleEpic = saveNewScheduleEpic;
@@ -3,9 +3,10 @@ import { Observable } from 'rxjs';
3
3
  import { updateJEOneTimeAccruals } from '../../../../entity/jeSchedules/jeSchedulesReducer';
4
4
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
5
5
  import { RootState } from '../../../../reducer';
6
+ import { clearAll } from '../../../../rootActions';
6
7
  import { ZeniAPI } from '../../../../zeniAPI';
7
8
  import { fetchJeSchedules, ignoreOneTimeAccrual, oneTimeAccrualActionFailure, oneTimeAccrualActionSuccess, promoteOneTimeAccrual, revertOneTimeAccrual } from '../../reducers/jeSchedulesViewReducer';
8
- export type ActionType = ReturnType<typeof promoteOneTimeAccrual> | ReturnType<typeof revertOneTimeAccrual> | ReturnType<typeof ignoreOneTimeAccrual> | ReturnType<typeof oneTimeAccrualActionSuccess> | ReturnType<typeof oneTimeAccrualActionFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
9
+ export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof promoteOneTimeAccrual> | ReturnType<typeof revertOneTimeAccrual> | ReturnType<typeof ignoreOneTimeAccrual> | ReturnType<typeof oneTimeAccrualActionSuccess> | ReturnType<typeof oneTimeAccrualActionFailure> | ReturnType<typeof updateJEOneTimeAccruals> | ReturnType<typeof fetchJeSchedules> | ReturnType<typeof openSnackbar>;
9
10
  export declare const promoteOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
10
11
  export declare const ignoreOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
11
12
  export declare const revertOneTimeAccrualEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
@@ -9,12 +9,17 @@ const jeSchedulesReducer_1 = require("../../../../entity/jeSchedules/jeSchedules
9
9
  const jeSchedulesSelector_1 = require("../../../../entity/jeSchedules/jeSchedulesSelector");
10
10
  const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
11
11
  const tenantSelector_1 = require("../../../../entity/tenant/tenantSelector");
12
+ const rootActions_1 = require("../../../../rootActions");
12
13
  const responsePayload_1 = require("../../../../responsePayload");
13
14
  const jeSchedulesViewReducer_1 = require("../../reducers/jeSchedulesViewReducer");
14
15
  /**
15
16
  * The page the accrual came from, so a promoted or ignored row lands in the right bucket. Guarded
16
17
  * because convertToPeriod destructures: an unset period would throw and take the epic subscription
17
18
  * down for the session, and a missed refresh is the cheaper failure.
19
+ *
20
+ * Read state$.value at response time rather than the snapshot taken when the action was dispatched:
21
+ * if the user changes month while the request is in flight, the snapshot refreshes the month they
22
+ * have left and the month they are now looking at stays stale.
18
23
  */
19
24
  function refreshCurrentPage(state) {
20
25
  const selectedMonthYear = state.expenseAutomationViewState.selectedPeriodByTenantId[(0, tenantSelector_1.getCurrentTenant)(state).tenantId];
@@ -29,14 +34,17 @@ function refreshCurrentPage(state) {
29
34
  }
30
35
  return [(0, jeSchedulesViewReducer_1.fetchJeSchedules)({ start, end }, true)];
31
36
  }
32
- function failureActions(accrualId, message, status) {
37
+ function failureActions(
38
+ // Per action, not fixed: every failure used to read "Save schedule failed", so skipping or
39
+ // reverting an accrual reported an operation the user had not performed.
40
+ messageSection, accrualId, message, status) {
33
41
  return [
34
42
  (0, jeSchedulesViewReducer_1.oneTimeAccrualActionFailure)({
35
43
  accrualId,
36
44
  status: status ?? (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', message),
37
45
  }),
38
46
  (0, snackbarReducer_1.openSnackbar)({
39
- messageSection: 'save_schedule',
47
+ messageSection,
40
48
  messageText: 'failed',
41
49
  type: 'error',
42
50
  variables: [{ variableName: '_api-error_', variableValue: message }],
@@ -50,7 +58,7 @@ const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((
50
58
  if (accrual?.jeDebitAccountId == null ||
51
59
  accrual.jeCreditAccountId == null) {
52
60
  // Promote posts a journal entry, so it cannot proceed without both sides of it.
53
- return (0, rxjs_1.from)(failureActions(accrualId, 'This accrual is missing an expense or liability account.'));
61
+ return (0, rxjs_1.from)(failureActions('save_schedule', accrualId, 'This accrual is missing an expense or liability account.'));
54
62
  }
55
63
  // vendor_id, vendor_name and service_month are deliberately absent: promote takes them from
56
64
  // the stored draft and ignores them in the body, so sending them would imply a reassignment
@@ -79,15 +87,18 @@ const promoteOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((
79
87
  },
80
88
  ],
81
89
  }),
82
- ...refreshCurrentPage(state),
90
+ ...refreshCurrentPage(state$.value),
83
91
  ]);
84
92
  }
85
- return (0, rxjs_1.from)(failureActions(accrualId, response.status.message, response.status));
86
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions(accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))));
93
+ return (0, rxjs_1.from)(failureActions('save_schedule', accrualId, response.status.message, response.status));
94
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions('save_schedule', accrualId, `Promote accrual REST API call errored out ${JSON.stringify(error)}`))),
95
+ // A tenant switch clears the store while this request is still in flight.
96
+ // Without this the old tenant's response is written back into the freshly
97
+ // cleared store and stays on screen under the new tenant.
98
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
87
99
  }));
88
100
  exports.promoteOneTimeAccrualEpic = promoteOneTimeAccrualEpic;
89
101
  const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(jeSchedulesViewReducer_1.ignoreOneTimeAccrual.match), (0, operators_1.mergeMap)((action) => {
90
- const state = state$.value;
91
102
  const { accrualId, reason, note } = action.payload;
92
103
  // `scope` is deliberately never sent: the API returns 400 on it, which is why the design's
93
104
  // "Apply to" selector cannot be built yet.
@@ -107,15 +118,18 @@ const ignoreOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0
107
118
  messageText: 'success',
108
119
  type: 'success',
109
120
  }),
110
- ...refreshCurrentPage(state),
121
+ ...refreshCurrentPage(state$.value),
111
122
  ]);
112
123
  }
113
- return (0, rxjs_1.from)(failureActions(accrualId, response.status.message, response.status));
114
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions(accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))));
124
+ return (0, rxjs_1.from)(failureActions('ignore_accrual', accrualId, response.status.message, response.status));
125
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions('ignore_accrual', accrualId, `Ignore accrual REST API call errored out ${JSON.stringify(error)}`))),
126
+ // A tenant switch clears the store while this request is still in flight.
127
+ // Without this the old tenant's response is written back into the freshly
128
+ // cleared store and stays on screen under the new tenant.
129
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
115
130
  }));
116
131
  exports.ignoreOneTimeAccrualEpic = ignoreOneTimeAccrualEpic;
117
132
  const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(jeSchedulesViewReducer_1.revertOneTimeAccrual.match), (0, operators_1.mergeMap)((action) => {
118
- const state = state$.value;
119
133
  const { accrualId, reason, note } = action.payload;
120
134
  // Deletes the accrual's journal entry in QBO and soft-deletes the row. `scope` is never sent:
121
135
  // the endpoint 400s on it, exactly as ignore does.
@@ -135,11 +149,15 @@ const revertOneTimeAccrualEpic = (actions$, state$, zeniAPI) => actions$.pipe((0
135
149
  messageText: 'success',
136
150
  type: 'success',
137
151
  }),
138
- ...refreshCurrentPage(state),
152
+ ...refreshCurrentPage(state$.value),
139
153
  ]);
140
154
  }
141
- return (0, rxjs_1.from)(failureActions(accrualId, response.status.message, response.status));
142
- }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions(accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))));
155
+ return (0, rxjs_1.from)(failureActions('revert_accrual', accrualId, response.status.message, response.status));
156
+ }), (0, operators_1.catchError)((error) => (0, rxjs_1.from)(failureActions('revert_accrual', accrualId, `Revert accrual REST API call errored out ${JSON.stringify(error)}`))),
157
+ // A tenant switch clears the store while this request is still in flight.
158
+ // Without this the old tenant's response is written back into the freshly
159
+ // cleared store and stays on screen under the new tenant.
160
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
143
161
  }));
144
162
  exports.revertOneTimeAccrualEpic = revertOneTimeAccrualEpic;
145
163
  const oneTimeAccrualActionsEpic = (actions$, state$, zeniAPI) => (0, rxjs_1.merge)((0, exports.promoteOneTimeAccrualEpic)(actions$, state$, zeniAPI), (0, exports.ignoreOneTimeAccrualEpic)(actions$, state$, zeniAPI), (0, exports.revertOneTimeAccrualEpic)(actions$, state$, zeniAPI));
@@ -3,10 +3,11 @@ import { updateJESchedules, updateJESchedulesDetails } from '../../../../entity/
3
3
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
4
  import { JEScheduleKey, JEScheduleTransactionKey } from '../../../../entity/jeSchedules/jeSchedulesTypes';
5
5
  import { RootState } from '../../../../reducer';
6
+ import { clearAll } from '../../../../rootActions';
6
7
  import { ZeniAPI } from '../../../../zeniAPI';
7
8
  import { fetchMonthEndCloseChecks } from '../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer';
8
9
  import { removeJeAccruedScheduleKey, removeJeScheduleTransactionKey, revertJeSchedule, revertJeScheduleFailure, revertJeScheduleSuccess } from '../../reducers/jeSchedulesViewReducer';
9
- export type ActionType = ReturnType<typeof fetchMonthEndCloseChecks> | ReturnType<typeof openSnackbar> | ReturnType<typeof removeJeAccruedScheduleKey> | ReturnType<typeof removeJeScheduleTransactionKey> | ReturnType<typeof revertJeSchedule> | ReturnType<typeof revertJeScheduleFailure> | ReturnType<typeof revertJeScheduleSuccess> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails>;
10
+ export type ActionType = ReturnType<typeof clearAll> | ReturnType<typeof fetchMonthEndCloseChecks> | ReturnType<typeof openSnackbar> | ReturnType<typeof removeJeAccruedScheduleKey> | ReturnType<typeof removeJeScheduleTransactionKey> | ReturnType<typeof revertJeSchedule> | ReturnType<typeof revertJeScheduleFailure> | ReturnType<typeof revertJeScheduleSuccess> | ReturnType<typeof updateJESchedules> | ReturnType<typeof updateJESchedulesDetails>;
10
11
  /**
11
12
  * Undo a posted prepaid / fixed-asset / accrued schedule: deletes its posted QBO journal entry, then
12
13
  * soft-deletes the scheduled entries and the schedule itself.
@@ -5,6 +5,7 @@ const rxjs_1 = require("rxjs");
5
5
  const operators_1 = require("rxjs/operators");
6
6
  const snackbarReducer_1 = require("../../../../entity/snackbar/snackbarReducer");
7
7
  const tenantSelector_1 = require("../../../../entity/tenant/tenantSelector");
8
+ const rootActions_1 = require("../../../../rootActions");
8
9
  const responsePayload_1 = require("../../../../responsePayload");
9
10
  const monthEndCloseChecksViewReducer_1 = require("../../../monthEndCloseChecksView/monthEndCloseChecksViewReducer");
10
11
  const jeSchedulesViewReducer_1 = require("../../reducers/jeSchedulesViewReducer");
@@ -105,6 +106,10 @@ const revertJeScheduleEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, op
105
106
  messageText: 'failed',
106
107
  type: 'error',
107
108
  }),
108
- ])));
109
+ ])),
110
+ // A tenant switch clears the store while this request is still in flight.
111
+ // Without this the old tenant's response is written back into the freshly
112
+ // cleared store and stays on screen under the new tenant.
113
+ (0, operators_1.takeUntil)(actions$.pipe((0, operators_1.filter)(rootActions_1.clearAll.match))));
109
114
  }));
110
115
  exports.revertJeScheduleEpic = revertJeScheduleEpic;
@@ -15,6 +15,12 @@ const switchJESchedulesTabEpic = (actions$, state$) => actions$.pipe((0, operato
15
15
  }
16
16
  const currentTenant = (0, tenantSelector_1.getCurrentTenant)(state);
17
17
  const selectedPeriod = state.expenseAutomationViewState.selectedPeriodByTenantId[currentTenant.tenantId];
18
+ // convertToPeriod destructures, so an unset period throws and takes this epic's subscription
19
+ // down for the rest of the session -- every later tab switch would then stop refreshing. A
20
+ // skipped refresh is the cheaper failure, as in refreshCurrentPage.
21
+ if (selectedPeriod == null) {
22
+ return rxjs_1.EMPTY;
23
+ }
18
24
  const period = (0, timePeriod_1.convertToPeriod)(selectedPeriod);
19
25
  const timePeriodStart = (0, timePeriod_1.toAbsoluteDay)(period.start);
20
26
  const timePeriodEnd = (0, timePeriod_1.toAbsoluteDay)(period.end);
@@ -178,6 +178,9 @@ const expenseAutomationJESchedulesView = (0, toolkit_1.createSlice)({
178
178
  draft.jeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
179
179
  draft.jeScheduleAccruedKeysByPeriodAndTab[periodTabKey] = [];
180
180
  draft.jeScheduleOneTimeAccrualKeysByPeriodAndTab[periodTabKey] = [];
181
+ // Cleared with the rest: leaving it behind kept the previous fetch's resolve rows on an
182
+ // otherwise empty errored tab, which reads as "these are all the schedules there are".
183
+ draft.failedJeScheduleTransactionKeysByPeriodAndTab[periodTabKey] = [];
181
184
  }
182
185
  },
183
186
  retryJeSchedule(draft, action) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.11-betaRD2",
3
+ "version": "5.2.11-betaRD3",
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",