@zeniai/client-epic-state 5.2.77 → 5.2.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/view/invoicing/couponView/couponViewReducer.js +14 -25
- package/lib/esm/view/invoicing/couponView/couponViewSelector.js +21 -22
- package/lib/esm/view/invoicing/couponView/couponViewState.js +2 -0
- package/lib/esm/view/invoicing/couponView/fetchInvoicingCouponListEpic.js +26 -8
- package/lib/esm/view/invoicing/couponView/fetchInvoicingCouponListPageEpic.js +16 -2
- package/lib/esm/view/invoicing/createInvoice/createInvoiceSelector.js +23 -15
- package/lib/esm/view/invoicing/createInvoice/epics/fetchCreateInvoiceFormPageEpic.js +5 -2
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +2 -2
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionDetailPageEpic.js +9 -6
- package/lib/esm/view/invoicing/invoicingCreditNoteListView/epics/fetchInvoicingCreditNoteListEpic.js +25 -8
- package/lib/esm/view/invoicing/invoicingCreditNoteListView/epics/fetchInvoicingCreditNoteListPageEpic.js +15 -2
- package/lib/esm/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewReducer.js +12 -23
- package/lib/esm/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewSelector.js +6 -9
- package/lib/esm/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListEpic.js +26 -8
- package/lib/esm/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListPageEpic.js +16 -2
- package/lib/esm/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewReducer.js +14 -25
- package/lib/esm/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewSelector.js +16 -13
- package/lib/esm/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.js +2 -0
- package/lib/esm/view/invoicing/invoicingDunningCaseListView/epics/fetchInvoicingDunningCaseListEpic.js +25 -8
- package/lib/esm/view/invoicing/invoicingDunningCaseListView/epics/fetchInvoicingDunningCaseListPageEpic.js +15 -2
- package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.js +12 -23
- package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewSelector.js +6 -9
- package/lib/esm/view/invoicing/invoicingListTabCache.js +98 -0
- package/lib/esm/view/invoicing/invoicingOverview/epics/fetchInvoicingOverviewEpic.js +4 -7
- package/lib/esm/view/invoicing/invoicingOverview/invoicingOverviewSelector.js +8 -5
- package/lib/esm/view/invoicing/invoicingSubscriptionListView/epics/fetchInvoicingSubscriptionListEpic.js +28 -9
- package/lib/esm/view/invoicing/invoicingSubscriptionListView/epics/fetchInvoicingSubscriptionListPageEpic.js +15 -2
- package/lib/esm/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.js +13 -25
- package/lib/esm/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewSelector.js +6 -9
- package/lib/esm/view/taskManager/taskDetailView/epics/fetchTaskDetailEpic.js +82 -41
- package/lib/esm/view/taskManager/taskDetailView/epics/fetchTaskDetailPageEpic.js +9 -1
- package/lib/esm/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +2 -23
- package/lib/esm/view/taskManager/taskDetailView/taskDetailLocalData.js +36 -0
- package/lib/esm/view/taskManager/taskDetailView/taskDetailReducer.js +11 -3
- package/lib/view/invoicing/couponView/couponViewReducer.d.ts +8 -1
- package/lib/view/invoicing/couponView/couponViewReducer.js +14 -25
- package/lib/view/invoicing/couponView/couponViewSelector.js +20 -21
- package/lib/view/invoicing/couponView/couponViewState.d.ts +7 -4
- package/lib/view/invoicing/couponView/couponViewState.js +3 -1
- package/lib/view/invoicing/couponView/fetchInvoicingCouponListEpic.d.ts +1 -1
- package/lib/view/invoicing/couponView/fetchInvoicingCouponListEpic.js +25 -7
- package/lib/view/invoicing/couponView/fetchInvoicingCouponListPageEpic.js +16 -2
- package/lib/view/invoicing/createInvoice/createInvoiceSelector.js +22 -14
- package/lib/view/invoicing/createInvoice/epics/fetchCreateInvoiceFormPageEpic.js +5 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +1 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionDetailPageEpic.js +9 -6
- package/lib/view/invoicing/invoicingCreditNoteListView/epics/fetchInvoicingCreditNoteListEpic.d.ts +1 -1
- package/lib/view/invoicing/invoicingCreditNoteListView/epics/fetchInvoicingCreditNoteListEpic.js +24 -7
- package/lib/view/invoicing/invoicingCreditNoteListView/epics/fetchInvoicingCreditNoteListPageEpic.js +15 -2
- package/lib/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewReducer.d.ts +8 -1
- package/lib/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewReducer.js +12 -23
- package/lib/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewSelector.js +6 -9
- package/lib/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewState.d.ts +5 -4
- package/lib/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListEpic.d.ts +1 -1
- package/lib/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListEpic.js +25 -7
- package/lib/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListPageEpic.js +16 -2
- package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewReducer.d.ts +8 -1
- package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewReducer.js +14 -25
- package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewSelector.d.ts +5 -0
- package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewSelector.js +17 -13
- package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.d.ts +7 -4
- package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.js +3 -1
- package/lib/view/invoicing/invoicingDunningCaseListView/epics/fetchInvoicingDunningCaseListEpic.d.ts +1 -1
- package/lib/view/invoicing/invoicingDunningCaseListView/epics/fetchInvoicingDunningCaseListEpic.js +24 -7
- package/lib/view/invoicing/invoicingDunningCaseListView/epics/fetchInvoicingDunningCaseListPageEpic.js +15 -2
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.d.ts +8 -1
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.js +12 -23
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewSelector.js +6 -9
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.d.ts +5 -4
- package/lib/view/invoicing/invoicingListTabCache.d.ts +71 -0
- package/lib/view/invoicing/invoicingListTabCache.js +111 -0
- package/lib/view/invoicing/invoicingOverview/epics/fetchInvoicingOverviewEpic.js +4 -7
- package/lib/view/invoicing/invoicingOverview/invoicingOverviewSelector.js +8 -5
- package/lib/view/invoicing/invoicingSubscriptionListView/epics/fetchInvoicingSubscriptionListEpic.d.ts +1 -1
- package/lib/view/invoicing/invoicingSubscriptionListView/epics/fetchInvoicingSubscriptionListEpic.js +27 -8
- package/lib/view/invoicing/invoicingSubscriptionListView/epics/fetchInvoicingSubscriptionListPageEpic.js +15 -2
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.d.ts +8 -1
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.js +13 -25
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewSelector.js +6 -9
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.d.ts +5 -5
- package/lib/view/taskManager/taskDetailView/epics/fetchTaskDetailEpic.d.ts +2 -2
- package/lib/view/taskManager/taskDetailView/epics/fetchTaskDetailEpic.js +80 -39
- package/lib/view/taskManager/taskDetailView/epics/fetchTaskDetailPageEpic.js +9 -1
- package/lib/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +2 -23
- package/lib/view/taskManager/taskDetailView/taskDetailLocalData.d.ts +5 -0
- package/lib/view/taskManager/taskDetailView/taskDetailLocalData.js +45 -0
- package/lib/view/taskManager/taskDetailView/taskDetailReducer.js +11 -3
- package/package.json +1 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { of } from 'rxjs';
|
|
2
|
-
import { catchError, filter, switchMap } from 'rxjs/operators';
|
|
2
|
+
import { catchError, filter, groupBy, map, mergeMap, switchMap, } from 'rxjs/operators';
|
|
3
3
|
import { updateAddresses } from '../../../../entity/address/addressReducer';
|
|
4
4
|
import { extractInvoicingSubscriptionAddresses } from '../../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload';
|
|
5
5
|
import { updateInvoicingSubscriptions } from '../../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionReducer';
|
|
6
6
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
7
7
|
import { INVOICING_LIST_PAGE_SIZE, applyListSortParams, withQuery, } from '../../invoicingApiHelper';
|
|
8
|
+
import { invoicingListFetchTab, invoicingListStatusParam, } from '../../invoicingListTabCache';
|
|
8
9
|
import { fetchInvoicingSubscriptionList, updateInvoicingSubscriptionList, updateInvoicingSubscriptionListFailure, } from '../invoicingSubscriptionListViewReducer';
|
|
9
|
-
const buildQuery = (filters, cursor, sort) => {
|
|
10
|
+
const buildQuery = (filters, cursor, sort, status) => {
|
|
10
11
|
const params = new URLSearchParams();
|
|
12
|
+
if (status != null) {
|
|
13
|
+
params.set('status', status);
|
|
14
|
+
}
|
|
11
15
|
if (filters.customerId != null) {
|
|
12
16
|
params.set('customer_id', filters.customerId);
|
|
13
17
|
}
|
|
@@ -27,10 +31,18 @@ const buildQuery = (filters, cursor, sort) => {
|
|
|
27
31
|
applyListSortParams(params, sort);
|
|
28
32
|
return params;
|
|
29
33
|
};
|
|
30
|
-
export const fetchInvoicingSubscriptionListEpic = (actions$,
|
|
34
|
+
export const fetchInvoicingSubscriptionListEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingSubscriptionList.match), map((action) => ({
|
|
35
|
+
action,
|
|
36
|
+
tabId: invoicingListFetchTab(state$.value.invoicingSubscriptionListViewState.activeTab, action.payload.tabId),
|
|
37
|
+
})),
|
|
38
|
+
// Per tab: a newer fetch of the same tab replaces its in-flight one, while
|
|
39
|
+
// another tab's request keeps running and lands in its own cache.
|
|
40
|
+
groupBy(({ tabId }) => tabId), mergeMap((tabFetches$) => tabFetches$.pipe(switchMap(({ action, tabId }) => {
|
|
31
41
|
const { filters, cursor, keepExistingListItems, sort } = action.payload;
|
|
32
|
-
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/subscriptions`, buildQuery(filters, cursor, sort));
|
|
33
|
-
return zeniAPI
|
|
42
|
+
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/subscriptions`, buildQuery(filters, cursor, sort, invoicingListStatusParam(tabId)));
|
|
43
|
+
return zeniAPI
|
|
44
|
+
.getJSON(apiUrl)
|
|
45
|
+
.pipe(switchMap((response) => {
|
|
34
46
|
if (isSuccessResponse(response)) {
|
|
35
47
|
const items = response.data?.items ?? [];
|
|
36
48
|
return [
|
|
@@ -40,10 +52,17 @@ export const fetchInvoicingSubscriptionListEpic = (actions$, _state$, zeniAPI) =
|
|
|
40
52
|
ids: items.map((item) => item.id),
|
|
41
53
|
nextCursor: response.data?.next_cursor ?? null,
|
|
42
54
|
keepExistingListItems,
|
|
55
|
+
tabId,
|
|
43
56
|
}),
|
|
44
57
|
];
|
|
45
58
|
}
|
|
46
|
-
return of(updateInvoicingSubscriptionListFailure(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}));
|
|
59
|
+
return of(updateInvoicingSubscriptionListFailure({
|
|
60
|
+
status: response.status,
|
|
61
|
+
tabId,
|
|
62
|
+
}));
|
|
63
|
+
}), catchError((error) => of(updateInvoicingSubscriptionListFailure({
|
|
64
|
+
status: createZeniAPIStatus('Unexpected Error', 'Fetch subscription list REST API call errored out ' +
|
|
65
|
+
JSON.stringify(error)),
|
|
66
|
+
tabId,
|
|
67
|
+
}))));
|
|
68
|
+
}))));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { concat, from } from 'rxjs';
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
|
+
import { getInvoicingListTab, invoicingListSourceTab, planInvoicingListTabFetch, } from '../../invoicingListTabCache';
|
|
3
4
|
import { fetchInvoicingSubscriptionCounts, fetchInvoicingSubscriptionList, fetchInvoicingSubscriptionListPage, } from '../invoicingSubscriptionListViewReducer';
|
|
4
5
|
/**
|
|
5
6
|
* Fetch unless the endpoint is already loaded or in flight — or always when the
|
|
@@ -17,8 +18,20 @@ export const fetchInvoicingSubscriptionListPageEpic = (actions$, state$) => acti
|
|
|
17
18
|
const { cacheOverride, filters, sort } = action.payload;
|
|
18
19
|
const state = state$.value.invoicingSubscriptionListViewState;
|
|
19
20
|
const pageActions = [];
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const tabId = invoicingListSourceTab(state.activeTab);
|
|
22
|
+
const tabFetch = planInvoicingListTabFetch(getInvoicingListTab(state.byTab, tabId), cacheOverride);
|
|
23
|
+
if (tabFetch.kind === 'firstPage') {
|
|
24
|
+
pageActions.push(fetchInvoicingSubscriptionList({ filters, sort, tabId }));
|
|
25
|
+
}
|
|
26
|
+
else if (tabFetch.kind === 'nextPage') {
|
|
27
|
+
// Less than a page cached and the server has more: top it up.
|
|
28
|
+
pageActions.push(fetchInvoicingSubscriptionList({
|
|
29
|
+
cursor: tabFetch.cursor,
|
|
30
|
+
filters,
|
|
31
|
+
keepExistingListItems: true,
|
|
32
|
+
sort,
|
|
33
|
+
tabId,
|
|
34
|
+
}));
|
|
22
35
|
}
|
|
23
36
|
if (shouldFetch(state.countsFetchState, cacheOverride)) {
|
|
24
37
|
pageActions.push(fetchInvoicingSubscriptionCounts());
|
package/lib/esm/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { clearAll } from '../../../rootActions';
|
|
3
|
+
import { addToInvoicingListTabs, completeInvoicingListTabFetch, failInvoicingListTabFetch, invoicingListFetchTab, isSameInvoicingListQuery, startInvoicingListTabFetch, } from '../invoicingListTabCache';
|
|
3
4
|
export const initialState = {
|
|
4
|
-
fetchState: 'Not-Started',
|
|
5
|
-
error: undefined,
|
|
6
5
|
activeTab: 'all',
|
|
7
|
-
|
|
8
|
-
nextCursor: null,
|
|
6
|
+
byTab: {},
|
|
9
7
|
filters: {},
|
|
10
8
|
counts: { all: 0 },
|
|
11
9
|
countsFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
12
|
-
hasValidState() {
|
|
13
|
-
return this.fetchState === 'Completed';
|
|
14
|
-
},
|
|
15
10
|
};
|
|
16
11
|
const invoicingSubscriptionListView = createSlice({
|
|
17
12
|
name: 'invoicingSubscriptionListView',
|
|
@@ -19,14 +14,9 @@ const invoicingSubscriptionListView = createSlice({
|
|
|
19
14
|
reducers: {
|
|
20
15
|
fetchInvoicingSubscriptionList: {
|
|
21
16
|
reducer(draft, action) {
|
|
22
|
-
|
|
23
|
-
draft.ids = [];
|
|
24
|
-
draft.nextCursor = null;
|
|
25
|
-
}
|
|
17
|
+
startInvoicingListTabFetch(draft.byTab, invoicingListFetchTab(draft.activeTab, action.payload.tabId), action.payload.keepExistingListItems);
|
|
26
18
|
draft.filters = action.payload.filters;
|
|
27
19
|
draft.sort = action.payload.sort;
|
|
28
|
-
draft.fetchState = 'In-Progress';
|
|
29
|
-
draft.error = undefined;
|
|
30
20
|
},
|
|
31
21
|
prepare(payload) {
|
|
32
22
|
return {
|
|
@@ -35,12 +25,17 @@ const invoicingSubscriptionListView = createSlice({
|
|
|
35
25
|
cursor: payload.cursor,
|
|
36
26
|
keepExistingListItems: payload.keepExistingListItems ?? false,
|
|
37
27
|
sort: payload.sort,
|
|
28
|
+
tabId: payload.tabId,
|
|
38
29
|
},
|
|
39
30
|
};
|
|
40
31
|
},
|
|
41
32
|
},
|
|
42
33
|
fetchInvoicingSubscriptionListPage: {
|
|
43
34
|
reducer(draft, action) {
|
|
35
|
+
// Another search or sort makes every cached tab stale.
|
|
36
|
+
if (!isSameInvoicingListQuery(draft, action.payload)) {
|
|
37
|
+
draft.byTab = {};
|
|
38
|
+
}
|
|
44
39
|
draft.filters = action.payload.filters;
|
|
45
40
|
draft.sort = action.payload.sort;
|
|
46
41
|
},
|
|
@@ -55,16 +50,10 @@ const invoicingSubscriptionListView = createSlice({
|
|
|
55
50
|
},
|
|
56
51
|
},
|
|
57
52
|
updateInvoicingSubscriptionList(draft, action) {
|
|
58
|
-
draft.
|
|
59
|
-
? [...new Set([...draft.ids, ...action.payload.ids])]
|
|
60
|
-
: action.payload.ids;
|
|
61
|
-
draft.nextCursor = action.payload.nextCursor;
|
|
62
|
-
draft.fetchState = 'Completed';
|
|
63
|
-
draft.error = undefined;
|
|
53
|
+
completeInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload);
|
|
64
54
|
},
|
|
65
55
|
updateInvoicingSubscriptionListFailure(draft, action) {
|
|
66
|
-
draft.
|
|
67
|
-
draft.error = action.payload;
|
|
56
|
+
failInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload.status);
|
|
68
57
|
},
|
|
69
58
|
fetchInvoicingSubscriptionCounts(draft) {
|
|
70
59
|
draft.countsFetchState.fetchState = 'In-Progress';
|
|
@@ -82,8 +71,8 @@ const invoicingSubscriptionListView = createSlice({
|
|
|
82
71
|
updateInvoicingSubscriptionFilters(draft, action) {
|
|
83
72
|
draft.filters = action.payload;
|
|
84
73
|
},
|
|
85
|
-
// Select a status tab. The
|
|
86
|
-
//
|
|
74
|
+
// Select a status tab. The view dispatches the page fetch after, which loads
|
|
75
|
+
// the tab from the server only when its cache is empty or failed.
|
|
87
76
|
setInvoicingSubscriptionActiveTab(draft, action) {
|
|
88
77
|
draft.activeTab = action.payload;
|
|
89
78
|
},
|
|
@@ -92,10 +81,9 @@ const invoicingSubscriptionListView = createSlice({
|
|
|
92
81
|
// doubles as the per-tab count key (tab ids mirror InvoicingSubscriptionStatus).
|
|
93
82
|
addInvoicingSubscriptionToList(draft, action) {
|
|
94
83
|
const { id, status } = action.payload;
|
|
95
|
-
if (draft.
|
|
84
|
+
if (!addToInvoicingListTabs(draft.byTab, id, status)) {
|
|
96
85
|
return;
|
|
97
86
|
}
|
|
98
|
-
draft.ids = [id, ...draft.ids];
|
|
99
87
|
draft.counts.all += 1;
|
|
100
88
|
if (status != null) {
|
|
101
89
|
draft.counts[status] = (draft.counts[status] ?? 0) + 1;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { getInvoicingSubscriptionsByIds } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionSelector';
|
|
3
|
+
import { getInvoicingListTab, invoicingListSourceTab, } from '../invoicingListTabCache';
|
|
3
4
|
import { invoicingTabIdsFromConfig } from '../invoicingTabHelper';
|
|
4
5
|
import { runningInvoicingActionId } from '../runningInvoicingActionId';
|
|
5
6
|
import { ALL_INVOICING_SUBSCRIPTION_TAB_IDS, INVOICING_SUBSCRIPTION_COLUMNS, } from './invoicingSubscriptionListViewState';
|
|
@@ -12,20 +13,16 @@ import { ALL_INVOICING_SUBSCRIPTION_TAB_IDS, INVOICING_SUBSCRIPTION_COLUMNS, } f
|
|
|
12
13
|
*/
|
|
13
14
|
export const getInvoicingSubscriptionListView = createSelector((state) => state.invoicingSubscriptionState, (state) => state.invoicingSubscriptionListViewState, (state) => state.invoicingConfigViewState.subscriptionStatusOptions, (state) => state.invoicingConfigViewState, (state) => state.editInvoicingSubscriptionDetailViewState.actionStateById, (subscriptionState, listViewState, statusOptions, configState, actionStateById) => {
|
|
14
15
|
const { activeTab } = listViewState;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
// active tab client-side ('all' shows everything).
|
|
18
|
-
const subscriptions = activeTab === 'all'
|
|
19
|
-
? allSubscriptions
|
|
20
|
-
: allSubscriptions.filter((subscription) => subscription.status === activeTab);
|
|
16
|
+
const tab = getInvoicingListTab(listViewState.byTab, invoicingListSourceTab(activeTab));
|
|
17
|
+
const subscriptions = getInvoicingSubscriptionsByIds(subscriptionState, tab.ids);
|
|
21
18
|
return {
|
|
22
19
|
runningActionId: runningInvoicingActionId(actionStateById),
|
|
23
20
|
activeTab,
|
|
24
21
|
columns: INVOICING_SUBSCRIPTION_COLUMNS,
|
|
25
22
|
subscriptions,
|
|
26
|
-
fetchState:
|
|
27
|
-
error:
|
|
28
|
-
nextCursor:
|
|
23
|
+
fetchState: tab.fetchState,
|
|
24
|
+
error: tab.error,
|
|
25
|
+
nextCursor: tab.nextCursor,
|
|
29
26
|
filters: listViewState.filters,
|
|
30
27
|
count: listViewState.counts,
|
|
31
28
|
countsFetchState: listViewState.countsFetchState,
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import recordGet from 'lodash/get';
|
|
2
|
-
import
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
import { EMPTY, concat, from, of } from 'rxjs';
|
|
3
4
|
import { catchError, filter, mergeMap } from 'rxjs/operators';
|
|
4
5
|
import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
|
|
5
6
|
import { updateTags } from '../../../../entity/tag/tagReducer';
|
|
7
|
+
import { mapTaskPayloadToTask } from '../../../../entity/task/taskPayload';
|
|
6
8
|
import { updateTasks } from '../../../../entity/task/taskReducer';
|
|
9
|
+
import { getTaskById } from '../../../../entity/task/taskSelector';
|
|
7
10
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
8
11
|
import { fetchFileList } from '../../../fileView/fileViewReducer';
|
|
9
|
-
import {
|
|
12
|
+
import { buildTaskDetailLocalData, isLoadedTaskDetail, rebaseTaskDetailLocalData, } from '../taskDetailLocalData';
|
|
13
|
+
import { fetchTaskDetail, initializeTaskToLocalStore, removeTaskDetail, saveTaskUpdatesToLocalStore, updateEditTaskFetchStatus, } from '../taskDetailReducer';
|
|
10
14
|
export const fetchTaskDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchTaskDetail.match), mergeMap((action) => {
|
|
11
15
|
const { taskId, cacheOverride } = action.payload;
|
|
12
16
|
const taskActions = [];
|
|
13
17
|
const state = state$.value;
|
|
14
18
|
const taskDetail = recordGet(state.taskDetailState.editTaskStateById, taskId, undefined);
|
|
19
|
+
if (cacheOverride === true && isLoadedTaskDetail(taskDetail)) {
|
|
20
|
+
return refreshLoadedTaskDetail(taskId, state$, zeniAPI);
|
|
21
|
+
}
|
|
15
22
|
if (cacheOverride === true) {
|
|
16
23
|
taskActions.push(removeTaskDetail(taskId));
|
|
17
24
|
}
|
|
@@ -20,7 +27,7 @@ export const fetchTaskDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(
|
|
|
20
27
|
taskDetail.fetchTaskStatus.fetchState !== 'In-Progress') {
|
|
21
28
|
taskActions.push(updateEditTaskFetchStatus({ taskId, fetchState: 'In-Progress' }));
|
|
22
29
|
const getTaskDetail$ = zeniAPI
|
|
23
|
-
.getJSON(
|
|
30
|
+
.getJSON(getTaskDetailUrl(zeniAPI, taskId))
|
|
24
31
|
.pipe(mergeMap((response) => {
|
|
25
32
|
if (isSuccessResponse(response) && response.data != null) {
|
|
26
33
|
const fetchActions = [
|
|
@@ -38,44 +45,7 @@ export const fetchTaskDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(
|
|
|
38
45
|
}
|
|
39
46
|
return from(fetchActions);
|
|
40
47
|
}
|
|
41
|
-
|
|
42
|
-
const fetchActions = [
|
|
43
|
-
updateEditTaskFetchStatus({
|
|
44
|
-
taskId,
|
|
45
|
-
fetchState: 'Error',
|
|
46
|
-
error: response.status,
|
|
47
|
-
}),
|
|
48
|
-
];
|
|
49
|
-
if (response.status.message.includes('delete')) {
|
|
50
|
-
fetchActions.push(openSnackbar({
|
|
51
|
-
messageSection: 'fetch_task_detail',
|
|
52
|
-
messageText: 'failed',
|
|
53
|
-
type: 'error',
|
|
54
|
-
showStatusIcon: true,
|
|
55
|
-
variables: [
|
|
56
|
-
{
|
|
57
|
-
variableName: '_api-error_',
|
|
58
|
-
variableValue: 'Cannot retrieve deleted task.',
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
fetchActions.push(openSnackbar({
|
|
65
|
-
messageSection: 'fetch_task_detail',
|
|
66
|
-
messageText: 'failed',
|
|
67
|
-
type: 'error',
|
|
68
|
-
showStatusIcon: true,
|
|
69
|
-
variables: [
|
|
70
|
-
{
|
|
71
|
-
variableName: '_api-error_',
|
|
72
|
-
variableValue: response.status.message,
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
return from(fetchActions);
|
|
78
|
-
}
|
|
48
|
+
return from(getFetchFailureActions(taskId, response.status));
|
|
79
49
|
}), catchError((error) => {
|
|
80
50
|
console.error(`REST API call errored out ${error}`);
|
|
81
51
|
return of(updateEditTaskFetchStatus({
|
|
@@ -91,3 +61,74 @@ export const fetchTaskDetailEpic = (actions$, state$, zeniAPI) => actions$.pipe(
|
|
|
91
61
|
return from(taskActions);
|
|
92
62
|
}
|
|
93
63
|
}));
|
|
64
|
+
const getTaskDetailUrl = (zeniAPI, taskId) => `${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks/${taskId}`;
|
|
65
|
+
const getFetchFailureActions = (taskId, status) => [
|
|
66
|
+
updateEditTaskFetchStatus({ taskId, fetchState: 'Error', error: status }),
|
|
67
|
+
openSnackbar({
|
|
68
|
+
messageSection: 'fetch_task_detail',
|
|
69
|
+
messageText: 'failed',
|
|
70
|
+
type: 'error',
|
|
71
|
+
showStatusIcon: true,
|
|
72
|
+
variables: [
|
|
73
|
+
{
|
|
74
|
+
variableName: '_api-error_',
|
|
75
|
+
variableValue: status.message.includes('delete')
|
|
76
|
+
? 'Cannot retrieve deleted task.'
|
|
77
|
+
: status.message,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
}),
|
|
81
|
+
];
|
|
82
|
+
// An unparseable token is never treated as stale, so the refresh still applies.
|
|
83
|
+
const isOlderSyncToken = (candidate, stored) => {
|
|
84
|
+
const candidateTime = Date.parse(candidate ?? '');
|
|
85
|
+
const storedTime = Date.parse(stored);
|
|
86
|
+
return (!Number.isNaN(candidateTime) &&
|
|
87
|
+
!Number.isNaN(storedTime) &&
|
|
88
|
+
candidateTime < storedTime);
|
|
89
|
+
};
|
|
90
|
+
const isSaveInProgress = (state, taskId) => state.taskDetailState.editTaskStateById[taskId]?.saveStatus.fetchState ===
|
|
91
|
+
'In-Progress';
|
|
92
|
+
// Live refresh of an open page: no reset or loading state, unsaved edits kept,
|
|
93
|
+
// skipped during the user's own save, stale responses dropped.
|
|
94
|
+
const refreshLoadedTaskDetail = (taskId, state$, zeniAPI) => {
|
|
95
|
+
if (isSaveInProgress(state$.value, taskId)) {
|
|
96
|
+
return EMPTY;
|
|
97
|
+
}
|
|
98
|
+
return zeniAPI
|
|
99
|
+
.getJSON(getTaskDetailUrl(zeniAPI, taskId))
|
|
100
|
+
.pipe(mergeMap((response) => {
|
|
101
|
+
if (!isSuccessResponse(response) || response.data == null) {
|
|
102
|
+
return from(getFetchFailureActions(taskId, response.status));
|
|
103
|
+
}
|
|
104
|
+
const state = state$.value;
|
|
105
|
+
const taskPayload = response.data.tasks.find((task) => task.task_id === taskId);
|
|
106
|
+
const storedTask = getTaskById(state.taskState, taskId);
|
|
107
|
+
const taskDetail = state.taskDetailState.editTaskStateById[taskId];
|
|
108
|
+
if (taskPayload == null ||
|
|
109
|
+
storedTask == null ||
|
|
110
|
+
taskDetail == null ||
|
|
111
|
+
isSaveInProgress(state, taskId) ||
|
|
112
|
+
isOlderSyncToken(taskPayload.sync_token, storedTask.syncToken)) {
|
|
113
|
+
return EMPTY;
|
|
114
|
+
}
|
|
115
|
+
const refreshedTask = mapTaskPayloadToTask(taskPayload, storedTask);
|
|
116
|
+
const actions = [
|
|
117
|
+
updateTasks(response.data.tasks),
|
|
118
|
+
updateTags(response.data.tasks.map((task) => task.tags).flat()),
|
|
119
|
+
saveTaskUpdatesToLocalStore({
|
|
120
|
+
taskId,
|
|
121
|
+
taskDetailLocalData: rebaseTaskDetailLocalData(taskDetail.taskDetailLocalData, buildTaskDetailLocalData(storedTask), buildTaskDetailLocalData(refreshedTask)),
|
|
122
|
+
}),
|
|
123
|
+
];
|
|
124
|
+
if (refreshedTask.fileIds.length > 0 &&
|
|
125
|
+
!isEqual(refreshedTask.fileIds, storedTask.fileIds)) {
|
|
126
|
+
actions.push(fetchFileList(refreshedTask.fileIds));
|
|
127
|
+
}
|
|
128
|
+
return from(actions);
|
|
129
|
+
}), catchError((error) => {
|
|
130
|
+
// Keep the page on screen; the next update or reload recovers.
|
|
131
|
+
console.error(`Task detail refresh errored out ${error}`);
|
|
132
|
+
return EMPTY;
|
|
133
|
+
}));
|
|
134
|
+
};
|
|
@@ -2,12 +2,20 @@ import { concat, from } from 'rxjs';
|
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { fetchClassList } from '../../../classList/classListReducer';
|
|
4
4
|
import { fetchUserListByType } from '../../../userListView/userListViewReducer';
|
|
5
|
+
import { isLoadedTaskDetail } from '../taskDetailLocalData';
|
|
5
6
|
import { fetchTaskDetail, fetchTaskDetailPage, fetchTaskHistory, } from '../taskDetailReducer';
|
|
6
7
|
export const fetchTaskDetailPageEpic = (actions$, state$) => actions$.pipe(filter(fetchTaskDetailPage.match), mergeMap((action) => {
|
|
7
8
|
const { cacheOverride, taskId } = action.payload;
|
|
8
9
|
const taskDetailActions = [];
|
|
9
10
|
const state = state$.value;
|
|
10
|
-
if (cacheOverride === true
|
|
11
|
+
if (cacheOverride === true &&
|
|
12
|
+
taskId != null &&
|
|
13
|
+
isLoadedTaskDetail(state.taskDetailState.editTaskStateById[taskId])) {
|
|
14
|
+
// Only the task changes on a live refresh; the list refetches would show the skeleton.
|
|
15
|
+
taskDetailActions.push(fetchTaskDetail(taskId, true));
|
|
16
|
+
taskDetailActions.push(fetchTaskHistory(taskId, true));
|
|
17
|
+
}
|
|
18
|
+
else if (cacheOverride === true) {
|
|
11
19
|
taskDetailActions.push(fetchUserListByType('taskManagerCandidate', true));
|
|
12
20
|
taskDetailActions.push(fetchClassList());
|
|
13
21
|
if (taskId != null) {
|
|
@@ -1,35 +1,14 @@
|
|
|
1
1
|
import recordGet from 'lodash/get';
|
|
2
2
|
import { from } from 'rxjs';
|
|
3
3
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
4
|
+
import { buildTaskDetailLocalData } from '../taskDetailLocalData';
|
|
4
5
|
import { initializeTaskToLocalStore, saveTaskUpdatesToLocalStore, updateEditTaskFetchStatus, } from '../taskDetailReducer';
|
|
5
6
|
export const initializeTaskToLocalStoreEpic = (actions$, state$) => actions$.pipe(filter(initializeTaskToLocalStore.match), mergeMap((action) => {
|
|
6
7
|
const { taskId } = action.payload;
|
|
7
8
|
const state = state$.value;
|
|
8
9
|
const task = recordGet(state.taskState.taskByID, taskId, undefined);
|
|
9
10
|
if (task != null) {
|
|
10
|
-
const taskDetailLocalData =
|
|
11
|
-
name: task.name,
|
|
12
|
-
description: task.description,
|
|
13
|
-
status: task.status.code,
|
|
14
|
-
assignee: task.assignees,
|
|
15
|
-
groupAssignees: task.groupAssignees,
|
|
16
|
-
dueDate: task.dueDate,
|
|
17
|
-
priority: task.priority.code,
|
|
18
|
-
tagIds: task.tagIds,
|
|
19
|
-
fileIds: task.fileIds,
|
|
20
|
-
type: task.type,
|
|
21
|
-
recurringFrequency: task.recurringFrequency,
|
|
22
|
-
recurringDaysOfWeek: task.recurringDaysOfWeek,
|
|
23
|
-
recurringEndDateLocalData: {
|
|
24
|
-
recurringDatePickerOptionsType: 'date', // by default, date would be selected
|
|
25
|
-
recurringEndDate: task.recurringEndDate,
|
|
26
|
-
},
|
|
27
|
-
savedRecurringEndDate: task.recurringEndDate,
|
|
28
|
-
recurringStartDate: task.recurringStartDate,
|
|
29
|
-
timeSpent: task.timeSpent,
|
|
30
|
-
isPrivate: task.isPrivate,
|
|
31
|
-
taskGroupId: task.taskGroupIds[0],
|
|
32
|
-
};
|
|
11
|
+
const taskDetailLocalData = buildTaskDetailLocalData(task);
|
|
33
12
|
const actions = [
|
|
34
13
|
saveTaskUpdatesToLocalStore({ taskDetailLocalData, taskId }),
|
|
35
14
|
updateEditTaskFetchStatus({ taskId, fetchState: 'Completed' }),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import isEqual from 'lodash/isEqual';
|
|
2
|
+
export const buildTaskDetailLocalData = (task) => ({
|
|
3
|
+
name: task.name,
|
|
4
|
+
description: task.description,
|
|
5
|
+
status: task.status.code,
|
|
6
|
+
assignee: task.assignees,
|
|
7
|
+
groupAssignees: task.groupAssignees,
|
|
8
|
+
dueDate: task.dueDate,
|
|
9
|
+
priority: task.priority.code,
|
|
10
|
+
tagIds: task.tagIds,
|
|
11
|
+
fileIds: task.fileIds,
|
|
12
|
+
type: task.type,
|
|
13
|
+
recurringFrequency: task.recurringFrequency,
|
|
14
|
+
recurringDaysOfWeek: task.recurringDaysOfWeek,
|
|
15
|
+
recurringEndDateLocalData: {
|
|
16
|
+
recurringDatePickerOptionsType: 'date', // by default, date would be selected
|
|
17
|
+
recurringEndDate: task.recurringEndDate,
|
|
18
|
+
},
|
|
19
|
+
savedRecurringEndDate: task.recurringEndDate,
|
|
20
|
+
recurringStartDate: task.recurringStartDate,
|
|
21
|
+
timeSpent: task.timeSpent,
|
|
22
|
+
isPrivate: task.isPrivate,
|
|
23
|
+
taskGroupId: task.taskGroupIds[0],
|
|
24
|
+
});
|
|
25
|
+
// Untouched fields (still equal to `baseline`) take the server value; edited ones keep the user's.
|
|
26
|
+
export const rebaseTaskDetailLocalData = (local, baseline, fresh) => {
|
|
27
|
+
const rebased = { ...fresh };
|
|
28
|
+
const keys = Object.keys({ ...local, ...fresh });
|
|
29
|
+
keys.forEach((key) => {
|
|
30
|
+
if (!isEqual(local[key], baseline[key])) {
|
|
31
|
+
Object.assign(rebased, { [key]: local[key] });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return rebased;
|
|
35
|
+
};
|
|
36
|
+
export const isLoadedTaskDetail = (taskDetail) => taskDetail?.fetchTaskStatus.fetchState === 'Completed';
|
|
@@ -4,6 +4,7 @@ import { clearAll } from '../../../rootActions';
|
|
|
4
4
|
import { initialCommonHistory } from '../../spendManagement/commonHistoryView/commonHistory';
|
|
5
5
|
import { toHistoricEvent, } from '../../spendManagement/commonHistoryView/commonHistoryPayload';
|
|
6
6
|
import { initialTaskDetail, } from './taskDetail';
|
|
7
|
+
import { isLoadedTaskDetail } from './taskDetailLocalData';
|
|
7
8
|
export const initialState = {
|
|
8
9
|
newTaskState: initialTaskDetail,
|
|
9
10
|
editTaskStateById: {},
|
|
@@ -23,9 +24,11 @@ const taskDetailView = createSlice({
|
|
|
23
24
|
const { taskId } = action.payload;
|
|
24
25
|
if (taskId != null) {
|
|
25
26
|
const detail = recordGet(draft.editTaskStateById, taskId, undefined);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const isRefresh = action.payload.cacheOverride === true && isLoadedTaskDetail(detail);
|
|
28
|
+
if (!isRefresh &&
|
|
29
|
+
(action.payload.cacheOverride === true ||
|
|
30
|
+
detail == null ||
|
|
31
|
+
detail.fetchTaskStatus.fetchState !== 'In-Progress')) {
|
|
29
32
|
draft.editTaskStateById[taskId] = {
|
|
30
33
|
...initialTaskDetail,
|
|
31
34
|
};
|
|
@@ -350,6 +353,11 @@ const taskDetailView = createSlice({
|
|
|
350
353
|
fetchSubTasks: {
|
|
351
354
|
reducer(draft, action) {
|
|
352
355
|
const { parentTaskId } = action.payload;
|
|
356
|
+
// Only the first load reports In-Progress; it gates the detail page skeleton.
|
|
357
|
+
if (draft.subTaskListFetchStatusByParentId[parentTaskId]?.fetchState ===
|
|
358
|
+
'Completed') {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
353
361
|
draft.subTaskListFetchStatusByParentId[parentTaskId] = {
|
|
354
362
|
fetchState: 'In-Progress',
|
|
355
363
|
error: undefined,
|
|
@@ -10,11 +10,14 @@ export declare const fetchInvoicingCouponList: import("@reduxjs/toolkit").Action
|
|
|
10
10
|
filters?: InvoicingCouponFilters;
|
|
11
11
|
keepExistingListItems?: boolean;
|
|
12
12
|
sort?: InvoicingListSort;
|
|
13
|
+
/** The tab cache to fill; defaults to the active tab's. */
|
|
14
|
+
tabId?: string;
|
|
13
15
|
}], {
|
|
14
16
|
filters: InvoicingCouponFilters;
|
|
15
17
|
cursor: string | undefined;
|
|
16
18
|
keepExistingListItems: boolean;
|
|
17
19
|
sort: InvoicingListSort | undefined;
|
|
20
|
+
tabId: string | undefined;
|
|
18
21
|
}, "invoicingCouponView/fetchInvoicingCouponList", never, never>, fetchInvoicingCouponListPage: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: {
|
|
19
22
|
cacheOverride?: boolean;
|
|
20
23
|
filters?: InvoicingCouponFilters;
|
|
@@ -27,7 +30,11 @@ export declare const fetchInvoicingCouponList: import("@reduxjs/toolkit").Action
|
|
|
27
30
|
ids: ID[];
|
|
28
31
|
keepExistingListItems: boolean;
|
|
29
32
|
nextCursor: string | null;
|
|
30
|
-
|
|
33
|
+
tabId: string;
|
|
34
|
+
}, "invoicingCouponView/updateInvoicingCouponList">, updateInvoicingCouponListFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
35
|
+
status: ZeniAPIStatus;
|
|
36
|
+
tabId: string;
|
|
37
|
+
}, "invoicingCouponView/updateInvoicingCouponListFailure">, fetchInvoicingCouponCounts: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"invoicingCouponView/fetchInvoicingCouponCounts">, updateInvoicingCouponCounts: import("@reduxjs/toolkit").ActionCreatorWithPayload<InvoicingListCounts, "invoicingCouponView/updateInvoicingCouponCounts">, updateInvoicingCouponCountsFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "invoicingCouponView/updateInvoicingCouponCountsFailure">, updateInvoicingCouponFilters: import("@reduxjs/toolkit").ActionCreatorWithPayload<InvoicingCouponFilters, "invoicingCouponView/updateInvoicingCouponFilters">, setInvoicingCouponActiveTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<"active" | "inactive" | "all", "invoicingCouponView/setInvoicingCouponActiveTab">, addInvoicingCouponToList: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
31
38
|
id: ID;
|
|
32
39
|
status?: InvoicingCouponStatus;
|
|
33
40
|
}, "invoicingCouponView/addInvoicingCouponToList">;
|
|
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.addInvoicingCouponToList = exports.setInvoicingCouponActiveTab = exports.updateInvoicingCouponFilters = exports.updateInvoicingCouponCountsFailure = exports.updateInvoicingCouponCounts = exports.fetchInvoicingCouponCounts = exports.updateInvoicingCouponListFailure = exports.updateInvoicingCouponList = exports.fetchInvoicingCouponListPage = exports.fetchInvoicingCouponList = exports.initialState = void 0;
|
|
5
5
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
6
|
const rootActions_1 = require("../../../rootActions");
|
|
7
|
+
const invoicingListTabCache_1 = require("../invoicingListTabCache");
|
|
8
|
+
const couponViewState_1 = require("./couponViewState");
|
|
7
9
|
// The discounts-list tabs look up counts by tab id ('active' / 'inactive'),
|
|
8
10
|
// while a coupon's status is 'active' | 'expired' | 'archived'. Collapse the
|
|
9
11
|
// non-active statuses onto the 'inactive' tab; a newly created coupon with no
|
|
@@ -12,17 +14,11 @@ function couponStatusToTabCountKey(status) {
|
|
|
12
14
|
return status == null || status === 'active' ? 'active' : 'inactive';
|
|
13
15
|
}
|
|
14
16
|
exports.initialState = {
|
|
15
|
-
fetchState: 'Not-Started',
|
|
16
|
-
error: undefined,
|
|
17
17
|
activeTab: 'all',
|
|
18
|
-
|
|
19
|
-
nextCursor: null,
|
|
18
|
+
byTab: {},
|
|
20
19
|
filters: {},
|
|
21
20
|
counts: { all: 0 },
|
|
22
21
|
countsFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
23
|
-
hasValidState() {
|
|
24
|
-
return this.fetchState === 'Completed';
|
|
25
|
-
},
|
|
26
22
|
};
|
|
27
23
|
const couponView = (0, toolkit_1.createSlice)({
|
|
28
24
|
name: 'invoicingCouponView',
|
|
@@ -30,14 +26,9 @@ const couponView = (0, toolkit_1.createSlice)({
|
|
|
30
26
|
reducers: {
|
|
31
27
|
fetchInvoicingCouponList: {
|
|
32
28
|
reducer(draft, action) {
|
|
33
|
-
|
|
34
|
-
draft.ids = [];
|
|
35
|
-
draft.nextCursor = null;
|
|
36
|
-
}
|
|
29
|
+
(0, invoicingListTabCache_1.startInvoicingListTabFetch)(draft.byTab, (0, invoicingListTabCache_1.invoicingListFetchTab)(draft.activeTab, action.payload.tabId, couponViewState_1.INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS), action.payload.keepExistingListItems);
|
|
37
30
|
draft.filters = action.payload.filters;
|
|
38
31
|
draft.sort = action.payload.sort;
|
|
39
|
-
draft.fetchState = 'In-Progress';
|
|
40
|
-
draft.error = undefined;
|
|
41
32
|
},
|
|
42
33
|
prepare(payload) {
|
|
43
34
|
return {
|
|
@@ -46,12 +37,17 @@ const couponView = (0, toolkit_1.createSlice)({
|
|
|
46
37
|
cursor: payload.cursor,
|
|
47
38
|
keepExistingListItems: payload.keepExistingListItems ?? false,
|
|
48
39
|
sort: payload.sort,
|
|
40
|
+
tabId: payload.tabId,
|
|
49
41
|
},
|
|
50
42
|
};
|
|
51
43
|
},
|
|
52
44
|
},
|
|
53
45
|
fetchInvoicingCouponListPage: {
|
|
54
46
|
reducer(draft, action) {
|
|
47
|
+
// Another search or sort makes every cached tab stale.
|
|
48
|
+
if (!(0, invoicingListTabCache_1.isSameInvoicingListQuery)(draft, action.payload)) {
|
|
49
|
+
draft.byTab = {};
|
|
50
|
+
}
|
|
55
51
|
draft.filters = action.payload.filters;
|
|
56
52
|
draft.sort = action.payload.sort;
|
|
57
53
|
},
|
|
@@ -66,16 +62,10 @@ const couponView = (0, toolkit_1.createSlice)({
|
|
|
66
62
|
},
|
|
67
63
|
},
|
|
68
64
|
updateInvoicingCouponList(draft, action) {
|
|
69
|
-
draft.
|
|
70
|
-
? [...new Set([...draft.ids, ...action.payload.ids])]
|
|
71
|
-
: action.payload.ids;
|
|
72
|
-
draft.nextCursor = action.payload.nextCursor;
|
|
73
|
-
draft.fetchState = 'Completed';
|
|
74
|
-
draft.error = undefined;
|
|
65
|
+
(0, invoicingListTabCache_1.completeInvoicingListTabFetch)(draft.byTab, action.payload.tabId, action.payload);
|
|
75
66
|
},
|
|
76
67
|
updateInvoicingCouponListFailure(draft, action) {
|
|
77
|
-
draft.
|
|
78
|
-
draft.error = action.payload;
|
|
68
|
+
(0, invoicingListTabCache_1.failInvoicingListTabFetch)(draft.byTab, action.payload.tabId, action.payload.status);
|
|
79
69
|
},
|
|
80
70
|
fetchInvoicingCouponCounts(draft) {
|
|
81
71
|
draft.countsFetchState.fetchState = 'In-Progress';
|
|
@@ -93,8 +83,8 @@ const couponView = (0, toolkit_1.createSlice)({
|
|
|
93
83
|
updateInvoicingCouponFilters(draft, action) {
|
|
94
84
|
draft.filters = action.payload;
|
|
95
85
|
},
|
|
96
|
-
// Select a status tab. The
|
|
97
|
-
//
|
|
86
|
+
// Select a status tab. The view dispatches the page fetch after, which loads
|
|
87
|
+
// the tab from the server only when its cache is empty or failed.
|
|
98
88
|
setInvoicingCouponActiveTab(draft, action) {
|
|
99
89
|
draft.activeTab = action.payload;
|
|
100
90
|
},
|
|
@@ -103,10 +93,9 @@ const couponView = (0, toolkit_1.createSlice)({
|
|
|
103
93
|
// the same id/counts, so only creates dispatch this.
|
|
104
94
|
addInvoicingCouponToList(draft, action) {
|
|
105
95
|
const { id, status } = action.payload;
|
|
106
|
-
if (draft.
|
|
96
|
+
if (!(0, invoicingListTabCache_1.addToInvoicingListTabs)(draft.byTab, id, couponStatusToTabCountKey(status))) {
|
|
107
97
|
return;
|
|
108
98
|
}
|
|
109
|
-
draft.ids = [id, ...draft.ids];
|
|
110
99
|
draft.counts.all += 1;
|
|
111
100
|
const tabCountKey = couponStatusToTabCountKey(status);
|
|
112
101
|
draft.counts[tabCountKey] = (draft.counts[tabCountKey] ?? 0) + 1;
|