@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
package/lib/esm/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListEpic.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
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 { extractInvoicingCustomerAddresses } from '../../../../entity/invoicing/invoicingCustomer/invoicingCustomerPayload';
|
|
5
5
|
import { updateInvoicingCustomers } from '../../../../entity/invoicing/invoicingCustomer/invoicingCustomerReducer';
|
|
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 { fetchInvoicingCustomerList, updateInvoicingCustomerList, updateInvoicingCustomerListFailure, } from '../invoicingCustomerListViewReducer';
|
|
9
|
-
|
|
10
|
+
import { INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS, } from '../invoicingCustomerListViewState';
|
|
11
|
+
const buildQuery = (filters, cursor, sort, status) => {
|
|
10
12
|
const params = new URLSearchParams();
|
|
13
|
+
if (status != null) {
|
|
14
|
+
params.set('status', status);
|
|
15
|
+
}
|
|
11
16
|
if (filters.search != null && filters.search.length > 0) {
|
|
12
17
|
params.set('search', filters.search);
|
|
13
18
|
}
|
|
@@ -18,9 +23,15 @@ const buildQuery = (filters, cursor, sort) => {
|
|
|
18
23
|
applyListSortParams(params, sort);
|
|
19
24
|
return params;
|
|
20
25
|
};
|
|
21
|
-
export const fetchInvoicingCustomerListEpic = (actions$,
|
|
26
|
+
export const fetchInvoicingCustomerListEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingCustomerList.match), map((action) => ({
|
|
27
|
+
action,
|
|
28
|
+
tabId: invoicingListFetchTab(state$.value.invoicingCustomerListViewState.activeTab, action.payload.tabId, INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS),
|
|
29
|
+
})),
|
|
30
|
+
// Per tab: a newer fetch of the same tab replaces its in-flight one, while
|
|
31
|
+
// another tab's request keeps running and lands in its own cache.
|
|
32
|
+
groupBy(({ tabId }) => tabId), mergeMap((tabFetches$) => tabFetches$.pipe(switchMap(({ action, tabId }) => {
|
|
22
33
|
const { filters, cursor, keepExistingListItems, sort } = action.payload;
|
|
23
|
-
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/customers`, buildQuery(filters, cursor, sort));
|
|
34
|
+
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/customers`, buildQuery(filters, cursor, sort, invoicingListStatusParam(tabId)));
|
|
24
35
|
return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
|
|
25
36
|
if (isSuccessResponse(response)) {
|
|
26
37
|
const items = response.data?.items ?? [];
|
|
@@ -31,10 +42,17 @@ export const fetchInvoicingCustomerListEpic = (actions$, _state$, zeniAPI) => ac
|
|
|
31
42
|
ids: items.map((item) => item.id),
|
|
32
43
|
nextCursor: response.data?.next_cursor ?? null,
|
|
33
44
|
keepExistingListItems,
|
|
45
|
+
tabId,
|
|
34
46
|
}),
|
|
35
47
|
];
|
|
36
48
|
}
|
|
37
|
-
return of(updateInvoicingCustomerListFailure(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}));
|
|
49
|
+
return of(updateInvoicingCustomerListFailure({
|
|
50
|
+
status: response.status,
|
|
51
|
+
tabId,
|
|
52
|
+
}));
|
|
53
|
+
}), catchError((error) => of(updateInvoicingCustomerListFailure({
|
|
54
|
+
status: createZeniAPIStatus('Unexpected Error', 'Fetch customer list REST API call errored out ' +
|
|
55
|
+
JSON.stringify(error)),
|
|
56
|
+
tabId,
|
|
57
|
+
}))));
|
|
58
|
+
}))));
|
package/lib/esm/view/invoicing/invoicingCustomerListView/epics/fetchInvoicingCustomerListPageEpic.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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 { fetchInvoicingCustomerCounts, fetchInvoicingCustomerList, fetchInvoicingCustomerListPage, } from '../invoicingCustomerListViewReducer';
|
|
5
|
+
import { INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS } from '../invoicingCustomerListViewState';
|
|
4
6
|
/**
|
|
5
7
|
* Fetch unless the endpoint is already loaded or in flight — or always when the
|
|
6
8
|
* caller forces a refresh via `cacheOverride` (e.g. on a filter/sort change).
|
|
@@ -17,8 +19,20 @@ export const fetchInvoicingCustomerListPageEpic = (actions$, state$) => actions$
|
|
|
17
19
|
const { cacheOverride, filters, sort } = action.payload;
|
|
18
20
|
const state = state$.value.invoicingCustomerListViewState;
|
|
19
21
|
const pageActions = [];
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const tabId = invoicingListSourceTab(state.activeTab, INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS);
|
|
23
|
+
const tabFetch = planInvoicingListTabFetch(getInvoicingListTab(state.byTab, tabId), cacheOverride);
|
|
24
|
+
if (tabFetch.kind === 'firstPage') {
|
|
25
|
+
pageActions.push(fetchInvoicingCustomerList({ filters, sort, tabId }));
|
|
26
|
+
}
|
|
27
|
+
else if (tabFetch.kind === 'nextPage') {
|
|
28
|
+
// Less than a page cached and the server has more: top it up.
|
|
29
|
+
pageActions.push(fetchInvoicingCustomerList({
|
|
30
|
+
cursor: tabFetch.cursor,
|
|
31
|
+
filters,
|
|
32
|
+
keepExistingListItems: true,
|
|
33
|
+
sort,
|
|
34
|
+
tabId,
|
|
35
|
+
}));
|
|
22
36
|
}
|
|
23
37
|
if (shouldFetch(state.countsFetchState, cacheOverride)) {
|
|
24
38
|
pageActions.push(fetchInvoicingCustomerCounts());
|
package/lib/esm/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewReducer.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { clearAll } from '../../../rootActions';
|
|
3
|
+
import { addToInvoicingListTabs, completeInvoicingListTabFetch, failInvoicingListTabFetch, invoicingListFetchTab, isSameInvoicingListQuery, startInvoicingListTabFetch, } from '../invoicingListTabCache';
|
|
4
|
+
import { INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS, } from './invoicingCustomerListViewState';
|
|
3
5
|
export const initialState = {
|
|
4
|
-
fetchState: 'Not-Started',
|
|
5
|
-
error: undefined,
|
|
6
6
|
activeTab: 'all',
|
|
7
|
-
|
|
8
|
-
nextCursor: null,
|
|
7
|
+
byTab: {},
|
|
9
8
|
filters: {},
|
|
10
9
|
counts: { all: 0 },
|
|
11
10
|
countsFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
12
|
-
hasValidState() {
|
|
13
|
-
return this.fetchState === 'Completed';
|
|
14
|
-
},
|
|
15
11
|
};
|
|
16
12
|
const invoicingCustomerListView = createSlice({
|
|
17
13
|
name: 'invoicingCustomerListView',
|
|
@@ -19,14 +15,9 @@ const invoicingCustomerListView = createSlice({
|
|
|
19
15
|
reducers: {
|
|
20
16
|
fetchInvoicingCustomerList: {
|
|
21
17
|
reducer(draft, action) {
|
|
22
|
-
|
|
23
|
-
draft.ids = [];
|
|
24
|
-
draft.nextCursor = null;
|
|
25
|
-
}
|
|
18
|
+
startInvoicingListTabFetch(draft.byTab, invoicingListFetchTab(draft.activeTab, action.payload.tabId, INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS), action.payload.keepExistingListItems);
|
|
26
19
|
draft.filters = action.payload.filters;
|
|
27
20
|
draft.sort = action.payload.sort;
|
|
28
|
-
draft.fetchState = 'In-Progress';
|
|
29
|
-
draft.error = undefined;
|
|
30
21
|
},
|
|
31
22
|
prepare(payload) {
|
|
32
23
|
return {
|
|
@@ -35,12 +26,17 @@ const invoicingCustomerListView = createSlice({
|
|
|
35
26
|
cursor: payload.cursor,
|
|
36
27
|
keepExistingListItems: payload.keepExistingListItems ?? false,
|
|
37
28
|
sort: payload.sort,
|
|
29
|
+
tabId: payload.tabId,
|
|
38
30
|
},
|
|
39
31
|
};
|
|
40
32
|
},
|
|
41
33
|
},
|
|
42
34
|
fetchInvoicingCustomerListPage: {
|
|
43
35
|
reducer(draft, action) {
|
|
36
|
+
// Another search or sort makes every cached tab stale.
|
|
37
|
+
if (!isSameInvoicingListQuery(draft, action.payload)) {
|
|
38
|
+
draft.byTab = {};
|
|
39
|
+
}
|
|
44
40
|
draft.filters = action.payload.filters;
|
|
45
41
|
draft.sort = action.payload.sort;
|
|
46
42
|
},
|
|
@@ -55,16 +51,10 @@ const invoicingCustomerListView = createSlice({
|
|
|
55
51
|
},
|
|
56
52
|
},
|
|
57
53
|
updateInvoicingCustomerList(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;
|
|
54
|
+
completeInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload);
|
|
64
55
|
},
|
|
65
56
|
updateInvoicingCustomerListFailure(draft, action) {
|
|
66
|
-
draft.
|
|
67
|
-
draft.error = action.payload;
|
|
57
|
+
failInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload.status);
|
|
68
58
|
},
|
|
69
59
|
fetchInvoicingCustomerCounts(draft) {
|
|
70
60
|
draft.countsFetchState.fetchState = 'In-Progress';
|
|
@@ -82,8 +72,8 @@ const invoicingCustomerListView = createSlice({
|
|
|
82
72
|
updateInvoicingCustomerFilters(draft, action) {
|
|
83
73
|
draft.filters = action.payload;
|
|
84
74
|
},
|
|
85
|
-
// Select a status tab. The
|
|
86
|
-
//
|
|
75
|
+
// Select a status tab. The view dispatches the page fetch after, which loads
|
|
76
|
+
// the tab from the server only when its cache is empty or failed.
|
|
87
77
|
setInvoicingCustomerActiveTab(draft, action) {
|
|
88
78
|
draft.activeTab = action.payload;
|
|
89
79
|
},
|
|
@@ -94,10 +84,9 @@ const invoicingCustomerListView = createSlice({
|
|
|
94
84
|
// dispatch time, so bump only `counts.all` when `status` is absent.
|
|
95
85
|
addInvoicingCustomerToList(draft, action) {
|
|
96
86
|
const { id, status } = action.payload;
|
|
97
|
-
if (draft.
|
|
87
|
+
if (!addToInvoicingListTabs(draft.byTab, id, status)) {
|
|
98
88
|
return;
|
|
99
89
|
}
|
|
100
|
-
draft.ids = [id, ...draft.ids];
|
|
101
90
|
draft.counts.all += 1;
|
|
102
91
|
if (status != null) {
|
|
103
92
|
draft.counts[status] = (draft.counts[status] ?? 0) + 1;
|
package/lib/esm/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewSelector.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { getInvoicingCustomersByIds } from '../../../entity/invoicing/invoicingCustomer/invoicingCustomerSelector';
|
|
3
|
+
import { INVOICING_LIST_ALL_TAB, getInvoicingListTab, invoicingListSourceTab, } from '../invoicingListTabCache';
|
|
3
4
|
import { invoicingTabIdsFromConfig } from '../invoicingTabHelper';
|
|
4
|
-
import { ALL_INVOICING_CUSTOMER_TAB_IDS, INVOICING_CUSTOMER_COLUMNS, } from './invoicingCustomerListViewState';
|
|
5
|
+
import { ALL_INVOICING_CUSTOMER_TAB_IDS, INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS, INVOICING_CUSTOMER_COLUMNS, } from './invoicingCustomerListViewState';
|
|
5
6
|
/**
|
|
6
7
|
* Memoized so the denormalized `customers` array (and the wrapping view object)
|
|
7
8
|
* keep referential identity until the customer entity slice or the list-view
|
|
@@ -10,22 +11,19 @@ import { ALL_INVOICING_CUSTOMER_TAB_IDS, INVOICING_CUSTOMER_COLUMNS, } from './i
|
|
|
10
11
|
*/
|
|
11
12
|
export const getInvoicingCustomerListView = createSelector((state) => state.invoicingCustomerState, (state) => state.invoicingCustomerListViewState, (state) => state.invoicingConfigViewState.customerStatusOptions, (customerState, listViewState, statusOptions) => {
|
|
12
13
|
const { activeTab } = listViewState;
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
: activeTab === 'at_risk'
|
|
20
|
-
? allCustomers.filter((customer) => customer.isAtRisk === true)
|
|
21
|
-
: allCustomers.filter((customer) => customer.status === activeTab);
|
|
14
|
+
const tab = getInvoicingListTab(listViewState.byTab, invoicingListSourceTab(activeTab, INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS));
|
|
15
|
+
const tabRows = getInvoicingCustomersByIds(customerState, tab.ids);
|
|
16
|
+
// `at_risk` is not a stored status, so it is the `all` rows filtered here.
|
|
17
|
+
const customers = activeTab === 'at_risk'
|
|
18
|
+
? tabRows.filter((customer) => customer.isAtRisk === true)
|
|
19
|
+
: tabRows;
|
|
22
20
|
return {
|
|
23
21
|
activeTab,
|
|
24
22
|
columns: INVOICING_CUSTOMER_COLUMNS,
|
|
25
23
|
customers,
|
|
26
|
-
fetchState:
|
|
27
|
-
error:
|
|
28
|
-
nextCursor:
|
|
24
|
+
fetchState: tab.fetchState,
|
|
25
|
+
error: tab.error,
|
|
26
|
+
nextCursor: tab.nextCursor,
|
|
29
27
|
filters: listViewState.filters,
|
|
30
28
|
count: listViewState.counts,
|
|
31
29
|
countsFetchState: listViewState.countsFetchState,
|
|
@@ -38,3 +36,8 @@ export const getInvoicingCustomerListView = createSelector((state) => state.invo
|
|
|
38
36
|
sort: listViewState.sort,
|
|
39
37
|
};
|
|
40
38
|
});
|
|
39
|
+
/**
|
|
40
|
+
* Every loaded customer, unfiltered: the pickers read this rather than the list
|
|
41
|
+
* view so they never narrow to whichever tab the customers list was left on.
|
|
42
|
+
*/
|
|
43
|
+
export const getInvoicingAllCustomers = (state) => getInvoicingCustomersByIds(state.invoicingCustomerState, getInvoicingListTab(state.invoicingCustomerListViewState.byTab, INVOICING_LIST_ALL_TAB).ids);
|
|
@@ -23,3 +23,5 @@ export const ALL_INVOICING_CUSTOMER_TAB_IDS = [
|
|
|
23
23
|
'at_risk',
|
|
24
24
|
];
|
|
25
25
|
export const toInvoicingCustomerTabId = (v) => stringToUnion(v, ALL_INVOICING_CUSTOMER_TAB_IDS);
|
|
26
|
+
/** Tabs the list API cannot filter on: they read the `all` rows, filtered here. */
|
|
27
|
+
export const INVOICING_CUSTOMER_CLIENT_FILTERED_TAB_IDS = ['at_risk'];
|
|
@@ -1,11 +1,15 @@
|
|
|
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 { updateInvoicingDunningCases } from '../../../../entity/invoicing/dunningCase/dunningCaseReducer';
|
|
4
4
|
import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
|
|
5
5
|
import { INVOICING_LIST_PAGE_SIZE, applyListSortParams, withQuery, } from '../../invoicingApiHelper';
|
|
6
|
+
import { invoicingListFetchTab, invoicingListStatusParam, } from '../../invoicingListTabCache';
|
|
6
7
|
import { fetchInvoicingDunningCaseList, updateInvoicingDunningCaseList, updateInvoicingDunningCaseListFailure, } from '../invoicingDunningCaseListViewReducer';
|
|
7
|
-
const buildQuery = (filters, cursor, sort) => {
|
|
8
|
+
const buildQuery = (filters, cursor, sort, status) => {
|
|
8
9
|
const params = new URLSearchParams();
|
|
10
|
+
if (status != null) {
|
|
11
|
+
params.set('status', status);
|
|
12
|
+
}
|
|
9
13
|
if (filters.search != null && filters.search.length > 0) {
|
|
10
14
|
params.set('search', filters.search);
|
|
11
15
|
}
|
|
@@ -16,9 +20,15 @@ const buildQuery = (filters, cursor, sort) => {
|
|
|
16
20
|
applyListSortParams(params, sort);
|
|
17
21
|
return params;
|
|
18
22
|
};
|
|
19
|
-
export const fetchInvoicingDunningCaseListEpic = (actions$,
|
|
23
|
+
export const fetchInvoicingDunningCaseListEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingDunningCaseList.match), map((action) => ({
|
|
24
|
+
action,
|
|
25
|
+
tabId: invoicingListFetchTab(state$.value.invoicingDunningCaseListViewState.activeTab, action.payload.tabId),
|
|
26
|
+
})),
|
|
27
|
+
// Per tab: a newer fetch of the same tab replaces its in-flight one, while
|
|
28
|
+
// another tab's request keeps running and lands in its own cache.
|
|
29
|
+
groupBy(({ tabId }) => tabId), mergeMap((tabFetches$) => tabFetches$.pipe(switchMap(({ action, tabId }) => {
|
|
20
30
|
const { filters, cursor, keepExistingListItems, sort } = action.payload;
|
|
21
|
-
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/dunning`, buildQuery(filters, cursor, sort));
|
|
31
|
+
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/dunning`, buildQuery(filters, cursor, sort, invoicingListStatusParam(tabId)));
|
|
22
32
|
return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
|
|
23
33
|
if (isSuccessResponse(response)) {
|
|
24
34
|
const items = response.data?.items ?? [];
|
|
@@ -28,10 +38,17 @@ export const fetchInvoicingDunningCaseListEpic = (actions$, _state$, zeniAPI) =>
|
|
|
28
38
|
ids: items.map((item) => item.id),
|
|
29
39
|
nextCursor: response.data?.next_cursor ?? null,
|
|
30
40
|
keepExistingListItems,
|
|
41
|
+
tabId,
|
|
31
42
|
}),
|
|
32
43
|
];
|
|
33
44
|
}
|
|
34
|
-
return of(updateInvoicingDunningCaseListFailure(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}));
|
|
45
|
+
return of(updateInvoicingDunningCaseListFailure({
|
|
46
|
+
status: response.status,
|
|
47
|
+
tabId,
|
|
48
|
+
}));
|
|
49
|
+
}), catchError((error) => of(updateInvoicingDunningCaseListFailure({
|
|
50
|
+
status: createZeniAPIStatus('Unexpected Error', 'Fetch dunning case list REST API call errored out ' +
|
|
51
|
+
JSON.stringify(error)),
|
|
52
|
+
tabId,
|
|
53
|
+
}))));
|
|
54
|
+
}))));
|
|
@@ -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 { fetchInvoicingDunningCaseCounts, fetchInvoicingDunningCaseList, fetchInvoicingDunningCaseListPage, } from '../invoicingDunningCaseListViewReducer';
|
|
4
5
|
/**
|
|
5
6
|
* Fetch unless the endpoint is already loaded or in flight — or always when the
|
|
@@ -17,8 +18,20 @@ export const fetchInvoicingDunningCaseListPageEpic = (actions$, state$) => actio
|
|
|
17
18
|
const { cacheOverride, filters, sort } = action.payload;
|
|
18
19
|
const state = state$.value.invoicingDunningCaseListViewState;
|
|
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(fetchInvoicingDunningCaseList({ 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(fetchInvoicingDunningCaseList({
|
|
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(fetchInvoicingDunningCaseCounts());
|
package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { createSlice } from '@reduxjs/toolkit';
|
|
2
2
|
import { clearAll } from '../../../rootActions';
|
|
3
|
+
import { 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 invoicingDunningCaseListView = createSlice({
|
|
17
12
|
name: 'invoicingDunningCaseListView',
|
|
@@ -19,14 +14,9 @@ const invoicingDunningCaseListView = createSlice({
|
|
|
19
14
|
reducers: {
|
|
20
15
|
fetchInvoicingDunningCaseList: {
|
|
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 invoicingDunningCaseListView = 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
|
fetchInvoicingDunningCaseListPage: {
|
|
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 invoicingDunningCaseListView = createSlice({
|
|
|
55
50
|
},
|
|
56
51
|
},
|
|
57
52
|
updateInvoicingDunningCaseList(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
|
updateInvoicingDunningCaseListFailure(draft, action) {
|
|
66
|
-
draft.
|
|
67
|
-
draft.error = action.payload;
|
|
56
|
+
failInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload.status);
|
|
68
57
|
},
|
|
69
58
|
fetchInvoicingDunningCaseCounts(draft) {
|
|
70
59
|
draft.countsFetchState.fetchState = 'In-Progress';
|
|
@@ -82,8 +71,8 @@ const invoicingDunningCaseListView = createSlice({
|
|
|
82
71
|
updateInvoicingDunningCaseFilters(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
|
setInvoicingDunningCaseActiveTab(draft, action) {
|
|
88
77
|
draft.activeTab = action.payload;
|
|
89
78
|
},
|
package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewSelector.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { getInvoicingDunningCasesByIds } from '../../../entity/invoicing/dunningCase/dunningCaseSelector';
|
|
3
|
+
import { getInvoicingListTab, invoicingListSourceTab, } from '../invoicingListTabCache';
|
|
3
4
|
import { invoicingTabIdsFromConfig } from '../invoicingTabHelper';
|
|
4
5
|
import { ALL_INVOICING_DUNNING_CASE_TAB_IDS, INVOICING_DUNNING_CASE_COLUMNS, } from './invoicingDunningCaseListViewState';
|
|
5
6
|
/**
|
|
@@ -9,12 +10,8 @@ import { ALL_INVOICING_DUNNING_CASE_TAB_IDS, INVOICING_DUNNING_CASE_COLUMNS, } f
|
|
|
9
10
|
*/
|
|
10
11
|
export const getInvoicingDunningCaseListView = createSelector((state) => state.invoicingDunningCaseState, (state) => state.invoicingDunningCaseListViewState, (state) => state.invoicingConfigViewState.dunningCaseStatusOptions, (dunningCaseState, listViewState, statusOptions) => {
|
|
11
12
|
const { activeTab } = listViewState;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
// active tab client-side ('all' shows everything).
|
|
15
|
-
const dunningCases = activeTab === 'all'
|
|
16
|
-
? allDunningCases
|
|
17
|
-
: allDunningCases.filter((dunningCase) => dunningCase.status === activeTab);
|
|
13
|
+
const tab = getInvoicingListTab(listViewState.byTab, invoicingListSourceTab(activeTab));
|
|
14
|
+
const dunningCases = getInvoicingDunningCasesByIds(dunningCaseState, tab.ids);
|
|
18
15
|
return {
|
|
19
16
|
activeTab,
|
|
20
17
|
columns: INVOICING_DUNNING_CASE_COLUMNS,
|
|
@@ -22,9 +19,9 @@ export const getInvoicingDunningCaseListView = createSelector((state) => state.i
|
|
|
22
19
|
count: listViewState.counts,
|
|
23
20
|
countsFetchState: listViewState.countsFetchState,
|
|
24
21
|
filters: listViewState.filters,
|
|
25
|
-
nextCursor:
|
|
26
|
-
fetchState:
|
|
27
|
-
error:
|
|
22
|
+
nextCursor: tab.nextCursor,
|
|
23
|
+
fetchState: tab.fetchState,
|
|
24
|
+
error: tab.error,
|
|
28
25
|
statusOptions,
|
|
29
26
|
tabIds: invoicingTabIdsFromConfig({
|
|
30
27
|
allTabIds: ALL_INVOICING_DUNNING_CASE_TAB_IDS,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { INVOICING_LIST_PAGE_SIZE, } from './invoicingApiHelper';
|
|
2
|
+
export const INVOICING_LIST_ALL_TAB = 'all';
|
|
3
|
+
const NOT_STARTED_TAB = Object.freeze({
|
|
4
|
+
ids: [],
|
|
5
|
+
nextCursor: null,
|
|
6
|
+
fetchState: 'Not-Started',
|
|
7
|
+
error: undefined,
|
|
8
|
+
});
|
|
9
|
+
/** A tab's cache, or a not-started one when it was never fetched. */
|
|
10
|
+
export const getInvoicingListTab = (byTab, tabId) => byTab[tabId] ?? NOT_STARTED_TAB;
|
|
11
|
+
/**
|
|
12
|
+
* The cache a tab reads and fetches. A tab the list API cannot filter on (a
|
|
13
|
+
* derived grouping such as the customers' `at_risk`, not a stored status) is
|
|
14
|
+
* the `all` rows, filtered by the selector.
|
|
15
|
+
*/
|
|
16
|
+
export const invoicingListSourceTab = (tabId, clientFilteredTabIds = []) => clientFilteredTabIds.includes(tabId) ? INVOICING_LIST_ALL_TAB : tabId;
|
|
17
|
+
/**
|
|
18
|
+
* The cache a list fetch fills: the tab the caller named, else the active tab's
|
|
19
|
+
* source. The reducer and the epic both resolve it, so they agree on the tab.
|
|
20
|
+
*/
|
|
21
|
+
export const invoicingListFetchTab = (activeTab, requestedTab, clientFilteredTabIds = []) => requestedTab ?? invoicingListSourceTab(activeTab, clientFilteredTabIds);
|
|
22
|
+
/** The `status` query param for a source tab; `all` sends none. */
|
|
23
|
+
export const invoicingListStatusParam = (sourceTab) => sourceTab === INVOICING_LIST_ALL_TAB ? undefined : sourceTab;
|
|
24
|
+
/**
|
|
25
|
+
* Whether two list queries match, so their per-tab caches stay valid. A filter
|
|
26
|
+
* left unset and one cleared to `''` are the same query.
|
|
27
|
+
*/
|
|
28
|
+
export const isSameInvoicingListQuery = (a, b) => {
|
|
29
|
+
const aFilters = a.filters;
|
|
30
|
+
const bFilters = b.filters;
|
|
31
|
+
const keys = new Set([...Object.keys(aFilters), ...Object.keys(bFilters)]);
|
|
32
|
+
return ([...keys].every((key) => (aFilters[key] ?? '') === (bFilters[key] ?? '')) &&
|
|
33
|
+
a.sort?.field === b.sort?.field &&
|
|
34
|
+
a.sort?.dir === b.sort?.dir);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Fetch a tab that is empty or failed; top up one that holds less than a page
|
|
38
|
+
* while the server says there is more; leave a full or complete one cached.
|
|
39
|
+
* Runs on page/tab visits only, so a server returning short pages cannot loop.
|
|
40
|
+
*/
|
|
41
|
+
export function planInvoicingListTabFetch(tab, cacheOverride) {
|
|
42
|
+
if (cacheOverride) {
|
|
43
|
+
return { kind: 'firstPage' };
|
|
44
|
+
}
|
|
45
|
+
if (tab.fetchState === 'In-Progress') {
|
|
46
|
+
return { kind: 'none' };
|
|
47
|
+
}
|
|
48
|
+
if (tab.fetchState !== 'Completed') {
|
|
49
|
+
return { kind: 'firstPage' };
|
|
50
|
+
}
|
|
51
|
+
if (tab.ids.length < INVOICING_LIST_PAGE_SIZE && tab.nextCursor != null) {
|
|
52
|
+
return { cursor: tab.nextCursor, kind: 'nextPage' };
|
|
53
|
+
}
|
|
54
|
+
return { kind: 'none' };
|
|
55
|
+
}
|
|
56
|
+
const tabFor = (byTab, tabId) => {
|
|
57
|
+
byTab[tabId] ?? (byTab[tabId] = { ...NOT_STARTED_TAB, ids: [] });
|
|
58
|
+
return byTab[tabId];
|
|
59
|
+
};
|
|
60
|
+
export function startInvoicingListTabFetch(byTab, tabId, keepExistingListItems) {
|
|
61
|
+
const tab = tabFor(byTab, tabId);
|
|
62
|
+
if (!keepExistingListItems) {
|
|
63
|
+
tab.ids = [];
|
|
64
|
+
tab.nextCursor = null;
|
|
65
|
+
}
|
|
66
|
+
tab.fetchState = 'In-Progress';
|
|
67
|
+
tab.error = undefined;
|
|
68
|
+
}
|
|
69
|
+
export function completeInvoicingListTabFetch(byTab, tabId, page) {
|
|
70
|
+
const tab = tabFor(byTab, tabId);
|
|
71
|
+
tab.ids = page.keepExistingListItems
|
|
72
|
+
? [...new Set([...tab.ids, ...page.ids])]
|
|
73
|
+
: page.ids;
|
|
74
|
+
tab.nextCursor = page.nextCursor;
|
|
75
|
+
tab.fetchState = 'Completed';
|
|
76
|
+
tab.error = undefined;
|
|
77
|
+
}
|
|
78
|
+
export function failInvoicingListTabFetch(byTab, tabId, error) {
|
|
79
|
+
const tab = tabFor(byTab, tabId);
|
|
80
|
+
tab.fetchState = 'Error';
|
|
81
|
+
tab.error = error;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A created record leads `all` and, when its status is known, that status's
|
|
85
|
+
* tab if already loaded. Returns false when `all` already had it.
|
|
86
|
+
*/
|
|
87
|
+
export function addToInvoicingListTabs(byTab, id, status) {
|
|
88
|
+
const all = tabFor(byTab, INVOICING_LIST_ALL_TAB);
|
|
89
|
+
if (all.ids.includes(id)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
all.ids = [id, ...all.ids];
|
|
93
|
+
const statusTab = status != null ? byTab[status] : undefined;
|
|
94
|
+
if (statusTab != null && !statusTab.ids.includes(id)) {
|
|
95
|
+
statusTab.ids = [id, ...statusTab.ids];
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { concat, from } from 'rxjs';
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
|
+
import { INVOICING_LIST_ALL_TAB, getInvoicingListTab, } from '../../invoicingListTabCache';
|
|
3
4
|
import { fetchInvoicingSubscriptionList } from '../../invoicingSubscriptionListView/invoicingSubscriptionListViewReducer';
|
|
4
|
-
import { getInvoicingSubscriptionListView } from '../../invoicingSubscriptionListView/invoicingSubscriptionListViewSelector';
|
|
5
5
|
import { fetchInvoicingSettings } from '../../settingsView/settingsViewReducer';
|
|
6
6
|
import { getInvoicingSettingsFetchState } from '../../settingsView/settingsViewSelector';
|
|
7
7
|
import { fetchInvoicingOverview, fetchInvoicingOverviewChurn, fetchInvoicingOverviewDashboardSummary, fetchInvoicingOverviewKPIs, fetchInvoicingOverviewMRR, fetchInvoicingOverviewPlanRevenue, fetchInvoicingOverviewRevenue, } from '../invoicingOverviewReducer';
|
|
@@ -44,12 +44,9 @@ export const fetchInvoicingOverviewEpic = (actions$, state$) => actions$.pipe(fi
|
|
|
44
44
|
if (shouldFetch(getInvoicingSettingsFetchState(rootState), cacheOverride)) {
|
|
45
45
|
overviewActions.push(fetchInvoicingSettings());
|
|
46
46
|
}
|
|
47
|
-
const
|
|
48
|
-
if (shouldFetch({
|
|
49
|
-
|
|
50
|
-
error: subscriptionListView.error,
|
|
51
|
-
}, cacheOverride)) {
|
|
52
|
-
overviewActions.push(fetchInvoicingSubscriptionList({}));
|
|
47
|
+
const subscriptionsTab = getInvoicingListTab(rootState.invoicingSubscriptionListViewState.byTab, INVOICING_LIST_ALL_TAB);
|
|
48
|
+
if (shouldFetch(subscriptionsTab, cacheOverride)) {
|
|
49
|
+
overviewActions.push(fetchInvoicingSubscriptionList({ tabId: INVOICING_LIST_ALL_TAB }));
|
|
53
50
|
}
|
|
54
51
|
return concat(from(overviewActions));
|
|
55
52
|
}));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { reduceAllFetchState } from '../../../commonStateTypes/reduceFetchState';
|
|
2
|
-
import {
|
|
2
|
+
import { getInvoicingSubscriptionsByIds } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionSelector';
|
|
3
|
+
import { INVOICING_LIST_ALL_TAB, getInvoicingListTab, } from '../invoicingListTabCache';
|
|
3
4
|
import { getInvoicingSettings, getInvoicingSettingsFetchState, } from '../settingsView/settingsViewSelector';
|
|
4
5
|
const PERIOD_OPTION_MONTHS = 12;
|
|
5
6
|
function daysInMonth(year, month) {
|
|
@@ -25,10 +26,12 @@ export function buildPeriodOptions() {
|
|
|
25
26
|
export const getInvoicingOverview = (state) => {
|
|
26
27
|
const overview = state.invoicingOverviewState;
|
|
27
28
|
const settingsFetchState = getInvoicingSettingsFetchState(state);
|
|
28
|
-
|
|
29
|
+
// The overview waits on the unfiltered `all` subscriptions, not the tab the
|
|
30
|
+
// subscriptions list was left on.
|
|
31
|
+
const subscriptionsTab = getInvoicingListTab(state.invoicingSubscriptionListViewState.byTab, INVOICING_LIST_ALL_TAB);
|
|
29
32
|
const subscriptionListFetchState = {
|
|
30
|
-
fetchState:
|
|
31
|
-
error:
|
|
33
|
+
fetchState: subscriptionsTab.fetchState,
|
|
34
|
+
error: subscriptionsTab.error,
|
|
32
35
|
};
|
|
33
36
|
const reducedFetchState = reduceAllFetchState([
|
|
34
37
|
overview.kpisFetchState,
|
|
@@ -56,7 +59,7 @@ export const getInvoicingOverview = (state) => {
|
|
|
56
59
|
fetchState: settingsFetchState,
|
|
57
60
|
},
|
|
58
61
|
subscriptionList: {
|
|
59
|
-
data:
|
|
62
|
+
data: getInvoicingSubscriptionsByIds(state.invoicingSubscriptionState, subscriptionsTab.ids),
|
|
60
63
|
fetchState: subscriptionListFetchState,
|
|
61
64
|
},
|
|
62
65
|
...reducedFetchState,
|