@zeniai/client-epic-state 5.2.78 → 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/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/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
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_COUPON_CLIENT_FILTERED_TAB_IDS, } from './couponViewState';
|
|
3
5
|
// The discounts-list tabs look up counts by tab id ('active' / 'inactive'),
|
|
4
6
|
// while a coupon's status is 'active' | 'expired' | 'archived'. Collapse the
|
|
5
7
|
// non-active statuses onto the 'inactive' tab; a newly created coupon with no
|
|
@@ -8,17 +10,11 @@ function couponStatusToTabCountKey(status) {
|
|
|
8
10
|
return status == null || status === 'active' ? 'active' : 'inactive';
|
|
9
11
|
}
|
|
10
12
|
export const initialState = {
|
|
11
|
-
fetchState: 'Not-Started',
|
|
12
|
-
error: undefined,
|
|
13
13
|
activeTab: 'all',
|
|
14
|
-
|
|
15
|
-
nextCursor: null,
|
|
14
|
+
byTab: {},
|
|
16
15
|
filters: {},
|
|
17
16
|
counts: { all: 0 },
|
|
18
17
|
countsFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
19
|
-
hasValidState() {
|
|
20
|
-
return this.fetchState === 'Completed';
|
|
21
|
-
},
|
|
22
18
|
};
|
|
23
19
|
const couponView = createSlice({
|
|
24
20
|
name: 'invoicingCouponView',
|
|
@@ -26,14 +22,9 @@ const couponView = createSlice({
|
|
|
26
22
|
reducers: {
|
|
27
23
|
fetchInvoicingCouponList: {
|
|
28
24
|
reducer(draft, action) {
|
|
29
|
-
|
|
30
|
-
draft.ids = [];
|
|
31
|
-
draft.nextCursor = null;
|
|
32
|
-
}
|
|
25
|
+
startInvoicingListTabFetch(draft.byTab, invoicingListFetchTab(draft.activeTab, action.payload.tabId, INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS), action.payload.keepExistingListItems);
|
|
33
26
|
draft.filters = action.payload.filters;
|
|
34
27
|
draft.sort = action.payload.sort;
|
|
35
|
-
draft.fetchState = 'In-Progress';
|
|
36
|
-
draft.error = undefined;
|
|
37
28
|
},
|
|
38
29
|
prepare(payload) {
|
|
39
30
|
return {
|
|
@@ -42,12 +33,17 @@ const couponView = createSlice({
|
|
|
42
33
|
cursor: payload.cursor,
|
|
43
34
|
keepExistingListItems: payload.keepExistingListItems ?? false,
|
|
44
35
|
sort: payload.sort,
|
|
36
|
+
tabId: payload.tabId,
|
|
45
37
|
},
|
|
46
38
|
};
|
|
47
39
|
},
|
|
48
40
|
},
|
|
49
41
|
fetchInvoicingCouponListPage: {
|
|
50
42
|
reducer(draft, action) {
|
|
43
|
+
// Another search or sort makes every cached tab stale.
|
|
44
|
+
if (!isSameInvoicingListQuery(draft, action.payload)) {
|
|
45
|
+
draft.byTab = {};
|
|
46
|
+
}
|
|
51
47
|
draft.filters = action.payload.filters;
|
|
52
48
|
draft.sort = action.payload.sort;
|
|
53
49
|
},
|
|
@@ -62,16 +58,10 @@ const couponView = createSlice({
|
|
|
62
58
|
},
|
|
63
59
|
},
|
|
64
60
|
updateInvoicingCouponList(draft, action) {
|
|
65
|
-
draft.
|
|
66
|
-
? [...new Set([...draft.ids, ...action.payload.ids])]
|
|
67
|
-
: action.payload.ids;
|
|
68
|
-
draft.nextCursor = action.payload.nextCursor;
|
|
69
|
-
draft.fetchState = 'Completed';
|
|
70
|
-
draft.error = undefined;
|
|
61
|
+
completeInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload);
|
|
71
62
|
},
|
|
72
63
|
updateInvoicingCouponListFailure(draft, action) {
|
|
73
|
-
draft.
|
|
74
|
-
draft.error = action.payload;
|
|
64
|
+
failInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload.status);
|
|
75
65
|
},
|
|
76
66
|
fetchInvoicingCouponCounts(draft) {
|
|
77
67
|
draft.countsFetchState.fetchState = 'In-Progress';
|
|
@@ -89,8 +79,8 @@ const couponView = createSlice({
|
|
|
89
79
|
updateInvoicingCouponFilters(draft, action) {
|
|
90
80
|
draft.filters = action.payload;
|
|
91
81
|
},
|
|
92
|
-
// Select a status tab. The
|
|
93
|
-
//
|
|
82
|
+
// Select a status tab. The view dispatches the page fetch after, which loads
|
|
83
|
+
// the tab from the server only when its cache is empty or failed.
|
|
94
84
|
setInvoicingCouponActiveTab(draft, action) {
|
|
95
85
|
draft.activeTab = action.payload;
|
|
96
86
|
},
|
|
@@ -99,10 +89,9 @@ const couponView = createSlice({
|
|
|
99
89
|
// the same id/counts, so only creates dispatch this.
|
|
100
90
|
addInvoicingCouponToList(draft, action) {
|
|
101
91
|
const { id, status } = action.payload;
|
|
102
|
-
if (draft.
|
|
92
|
+
if (!addToInvoicingListTabs(draft.byTab, id, couponStatusToTabCountKey(status))) {
|
|
103
93
|
return;
|
|
104
94
|
}
|
|
105
|
-
draft.ids = [id, ...draft.ids];
|
|
106
95
|
draft.counts.all += 1;
|
|
107
96
|
const tabCountKey = couponStatusToTabCountKey(status);
|
|
108
97
|
draft.counts[tabCountKey] = (draft.counts[tabCountKey] ?? 0) + 1;
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
|
+
import { INVOICING_LIST_ALL_TAB, getInvoicingListTab, invoicingListSourceTab, } from '../invoicingListTabCache';
|
|
2
3
|
import { invoicingTabIdsFromConfig } from '../invoicingTabHelper';
|
|
3
4
|
import { runningInvoicingActionId } from '../runningInvoicingActionId';
|
|
4
|
-
import { ALL_INVOICING_COUPON_TAB_IDS, INVOICING_COUPON_COLUMNS, } from './couponViewState';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { ALL_INVOICING_COUPON_TAB_IDS, INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS, INVOICING_COUPON_COLUMNS, } from './couponViewState';
|
|
6
|
+
// These read the unfiltered `all` cache, so a picker never narrows to
|
|
7
|
+
// whichever tab the discounts list was left on.
|
|
8
|
+
const allCouponsTab = (state) => getInvoicingListTab(state.invoicingCouponViewState.byTab, INVOICING_LIST_ALL_TAB);
|
|
9
|
+
export const getInvoicingCouponListItems = (state) => allCouponsTab(state)
|
|
10
|
+
.ids.map((id) => state.invoicingCouponState.couponByID[id])
|
|
7
11
|
.filter((item) => item != null);
|
|
8
12
|
export const getInvoicingCouponListFetchState = (state) => ({
|
|
9
|
-
fetchState: state.
|
|
10
|
-
error: state.
|
|
13
|
+
fetchState: allCouponsTab(state).fetchState,
|
|
14
|
+
error: allCouponsTab(state).error,
|
|
11
15
|
});
|
|
12
|
-
export const getInvoicingCouponListNextCursor = (state) => state.
|
|
13
|
-
export const getInvoicingCouponListHasMore = (state) => state.
|
|
16
|
+
export const getInvoicingCouponListNextCursor = (state) => allCouponsTab(state).nextCursor;
|
|
17
|
+
export const getInvoicingCouponListHasMore = (state) => allCouponsTab(state).nextCursor != null;
|
|
14
18
|
export const getInvoicingCouponListFilters = (state) => state.invoicingCouponViewState.filters;
|
|
15
19
|
export const getInvoicingCouponCounts = (state) => state.invoicingCouponViewState.counts;
|
|
16
20
|
/**
|
|
@@ -21,20 +25,15 @@ export const getInvoicingCouponCounts = (state) => state.invoicingCouponViewStat
|
|
|
21
25
|
*/
|
|
22
26
|
export const getInvoicingCouponListView = createSelector((state) => state.invoicingCouponState, (state) => state.invoicingCouponViewState, (state) => state.invoicingConfigViewState.discountStatusOptions, (state) => state.invoicingConfigViewState.discountLifecycleActionOptions, (state) => state.editInvoicingCouponDetailViewState.actionStateById, (couponState, listViewState, statusOptions, discountLifecycleActionOptions, actionStateById) => {
|
|
23
27
|
const { activeTab } = listViewState;
|
|
24
|
-
const
|
|
28
|
+
const tab = getInvoicingListTab(listViewState.byTab, invoicingListSourceTab(activeTab, INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS));
|
|
29
|
+
const tabRows = tab.ids
|
|
25
30
|
.map((id) => couponState.couponByID[id])
|
|
26
31
|
.filter((item) => item != null);
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// the whole non-active set.
|
|
33
|
-
const coupons = activeTab === 'all'
|
|
34
|
-
? allCoupons
|
|
35
|
-
: activeTab === 'inactive'
|
|
36
|
-
? allCoupons.filter((coupon) => coupon.status !== 'active')
|
|
37
|
-
: allCoupons.filter((coupon) => coupon.status === activeTab);
|
|
32
|
+
// `inactive` groups every non-active status, which the list API cannot
|
|
33
|
+
// filter on, so it is the `all` rows filtered here.
|
|
34
|
+
const coupons = activeTab === 'inactive'
|
|
35
|
+
? tabRows.filter((coupon) => coupon.status !== 'active')
|
|
36
|
+
: tabRows;
|
|
38
37
|
return {
|
|
39
38
|
activeTab,
|
|
40
39
|
columns: INVOICING_COUPON_COLUMNS,
|
|
@@ -42,7 +41,7 @@ export const getInvoicingCouponListView = createSelector((state) => state.invoic
|
|
|
42
41
|
count: listViewState.counts,
|
|
43
42
|
countsFetchState: listViewState.countsFetchState,
|
|
44
43
|
filters: listViewState.filters,
|
|
45
|
-
nextCursor:
|
|
44
|
+
nextCursor: tab.nextCursor,
|
|
46
45
|
discountLifecycleActionOptions,
|
|
47
46
|
statusOptions,
|
|
48
47
|
tabIds: invoicingTabIdsFromConfig({
|
|
@@ -51,8 +50,8 @@ export const getInvoicingCouponListView = createSelector((state) => state.invoic
|
|
|
51
50
|
statusOptions,
|
|
52
51
|
}),
|
|
53
52
|
runningActionId: runningInvoicingActionId(actionStateById),
|
|
54
|
-
fetchState:
|
|
55
|
-
error:
|
|
53
|
+
fetchState: tab.fetchState,
|
|
54
|
+
error: tab.error,
|
|
56
55
|
sort: listViewState.sort,
|
|
57
56
|
version: 1.0,
|
|
58
57
|
};
|
|
@@ -28,3 +28,5 @@ export const ALL_INVOICING_COUPON_TAB_IDS = [
|
|
|
28
28
|
'inactive',
|
|
29
29
|
];
|
|
30
30
|
export const toInvoicingCouponTabId = (v) => stringToUnion(v, ALL_INVOICING_COUPON_TAB_IDS);
|
|
31
|
+
/** Tabs the list API cannot filter on: they read the `all` rows, filtered here. */
|
|
32
|
+
export const INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS = ['inactive'];
|
|
@@ -1,15 +1,20 @@
|
|
|
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 { updateInvoicingCoupons } from '../../../entity/invoicing/coupon/couponReducer';
|
|
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 { fetchInvoicingCouponList, updateInvoicingCouponList, updateInvoicingCouponListFailure, } from './couponViewReducer';
|
|
8
|
+
import { INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS, } from './couponViewState';
|
|
7
9
|
/**
|
|
8
10
|
* Fetch unless the endpoint is already loaded or in flight — or always when the
|
|
9
11
|
* caller forces a refresh via `cacheOverride` (e.g. on a filter/sort change).
|
|
10
12
|
*/
|
|
11
|
-
const buildQuery = (filters, cursor, sort) => {
|
|
13
|
+
const buildQuery = (filters, cursor, sort, status) => {
|
|
12
14
|
const params = new URLSearchParams();
|
|
15
|
+
if (status != null) {
|
|
16
|
+
params.set('status', status);
|
|
17
|
+
}
|
|
13
18
|
if (filters.search != null && filters.search.length > 0) {
|
|
14
19
|
params.set('search', filters.search);
|
|
15
20
|
}
|
|
@@ -20,9 +25,15 @@ const buildQuery = (filters, cursor, sort) => {
|
|
|
20
25
|
applyListSortParams(params, sort);
|
|
21
26
|
return params;
|
|
22
27
|
};
|
|
23
|
-
export const fetchInvoicingCouponListEpic = (actions$,
|
|
28
|
+
export const fetchInvoicingCouponListEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingCouponList.match), map((action) => ({
|
|
29
|
+
action,
|
|
30
|
+
tabId: invoicingListFetchTab(state$.value.invoicingCouponViewState.activeTab, action.payload.tabId, INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS),
|
|
31
|
+
})),
|
|
32
|
+
// Per tab: a newer fetch of the same tab replaces its in-flight one, while
|
|
33
|
+
// another tab's request keeps running and lands in its own cache.
|
|
34
|
+
groupBy(({ tabId }) => tabId), mergeMap((tabFetches$) => tabFetches$.pipe(switchMap(({ action, tabId }) => {
|
|
24
35
|
const { filters, cursor, keepExistingListItems, sort } = action.payload;
|
|
25
|
-
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/discounts`, buildQuery(filters, cursor, sort));
|
|
36
|
+
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/discounts`, buildQuery(filters, cursor, sort, invoicingListStatusParam(tabId)));
|
|
26
37
|
return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
|
|
27
38
|
if (isSuccessResponse(response)) {
|
|
28
39
|
const items = response.data?.items ?? [];
|
|
@@ -32,10 +43,17 @@ export const fetchInvoicingCouponListEpic = (actions$, _state$, zeniAPI) => acti
|
|
|
32
43
|
ids: items.map((item) => item.id),
|
|
33
44
|
nextCursor: response.data?.next_cursor ?? null,
|
|
34
45
|
keepExistingListItems,
|
|
46
|
+
tabId,
|
|
35
47
|
}),
|
|
36
48
|
];
|
|
37
49
|
}
|
|
38
|
-
return of(updateInvoicingCouponListFailure(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}));
|
|
50
|
+
return of(updateInvoicingCouponListFailure({
|
|
51
|
+
status: response.status,
|
|
52
|
+
tabId,
|
|
53
|
+
}));
|
|
54
|
+
}), catchError((error) => of(updateInvoicingCouponListFailure({
|
|
55
|
+
status: createZeniAPIStatus('Unexpected Error', 'Fetch discount list REST API call errored out ' +
|
|
56
|
+
JSON.stringify(error)),
|
|
57
|
+
tabId,
|
|
58
|
+
}))));
|
|
59
|
+
}))));
|
|
@@ -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 { fetchInvoicingCouponCounts, fetchInvoicingCouponList, fetchInvoicingCouponListPage, } from './couponViewReducer';
|
|
5
|
+
import { INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS } from './couponViewState';
|
|
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).
|
|
@@ -12,8 +14,20 @@ export const fetchInvoicingCouponListPageEpic = (actions$, state$) => actions$.p
|
|
|
12
14
|
const { cacheOverride, filters, sort } = action.payload;
|
|
13
15
|
const state = state$.value.invoicingCouponViewState;
|
|
14
16
|
const pageActions = [];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
const tabId = invoicingListSourceTab(state.activeTab, INVOICING_COUPON_CLIENT_FILTERED_TAB_IDS);
|
|
18
|
+
const tabFetch = planInvoicingListTabFetch(getInvoicingListTab(state.byTab, tabId), cacheOverride);
|
|
19
|
+
if (tabFetch.kind === 'firstPage') {
|
|
20
|
+
pageActions.push(fetchInvoicingCouponList({ filters, sort, tabId }));
|
|
21
|
+
}
|
|
22
|
+
else if (tabFetch.kind === 'nextPage') {
|
|
23
|
+
// Less than a page cached and the server has more: top it up.
|
|
24
|
+
pageActions.push(fetchInvoicingCouponList({
|
|
25
|
+
cursor: tabFetch.cursor,
|
|
26
|
+
filters,
|
|
27
|
+
keepExistingListItems: true,
|
|
28
|
+
sort,
|
|
29
|
+
tabId,
|
|
30
|
+
}));
|
|
17
31
|
}
|
|
18
32
|
if (shouldFetch(state.countsFetchState, cacheOverride)) {
|
|
19
33
|
pageActions.push(fetchInvoicingCouponCounts());
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
1
2
|
import { getAddressByAddressId } from '../../../entity/address/addressSelector';
|
|
2
3
|
import { invoicingPricePointDisplayAmount } from '../../../entity/invoicing/invoicingCatalog/invoicingCatalogSelector';
|
|
3
4
|
import { getInvoicingCatalogListView, getInvoicingCatalogPrices, } from '../invoicingCatalogListView/invoicingCatalogListViewSelector';
|
|
4
5
|
import { invoicingFieldHelpForGroup } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
5
|
-
import {
|
|
6
|
+
import { getInvoicingAllCustomers } from '../invoicingCustomerListView/invoicingCustomerListViewSelector';
|
|
6
7
|
import { getInvoicingSettings } from '../settingsView/settingsViewSelector';
|
|
7
8
|
/**
|
|
8
9
|
* Pricing models whose single-unit amount is a true per-unit rate — the only
|
|
@@ -30,6 +31,25 @@ const hasPerUnitRate = (pricingModel) => pricingModel == null ||
|
|
|
30
31
|
const planNamePrefillPrice = (pricePoint) => hasPerUnitRate(pricePoint.pricingModel)
|
|
31
32
|
? invoicingPricePointDisplayAmount(pricePoint)?.amount
|
|
32
33
|
: undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Plans, add-ons and one-time charges, one option per price point. Memoized on
|
|
36
|
+
* the catalog list view so the options keep their identity while the form draft
|
|
37
|
+
* changes on every keystroke.
|
|
38
|
+
*/
|
|
39
|
+
const getCreateInvoicePlanNames = createSelector(getInvoicingCatalogListView, (catalogListView) => [...catalogListView.plans, ...catalogListView.products].flatMap((item) => (item.subFamilies ?? [])
|
|
40
|
+
.filter((subFamily) => (subFamily.name ?? '') !== '')
|
|
41
|
+
.map((subFamily) => ({
|
|
42
|
+
// A price point with no flag is taxable, as subscription billing reads it.
|
|
43
|
+
catalogLine: {
|
|
44
|
+
entityId: subFamily.id,
|
|
45
|
+
// The item's kind is `type`; `itemType` is set on its price points.
|
|
46
|
+
entityType: item.type ?? subFamily.itemType ?? 'plan',
|
|
47
|
+
isTaxed: subFamily.isTaxable ?? true,
|
|
48
|
+
},
|
|
49
|
+
planName: item.name ?? '',
|
|
50
|
+
price: planNamePrefillPrice(subFamily),
|
|
51
|
+
subFamilyName: subFamily.name ?? '',
|
|
52
|
+
}))));
|
|
33
53
|
/**
|
|
34
54
|
* Display name for a customer option: company, else the full name, else the
|
|
35
55
|
* email, else the raw id.
|
|
@@ -61,7 +81,7 @@ export const getCreateInvoiceFormView = (state) => {
|
|
|
61
81
|
const catalogListView = getInvoicingCatalogListView(state);
|
|
62
82
|
return {
|
|
63
83
|
fieldHelp: invoicingFieldHelpForGroup(state, 'invoice'),
|
|
64
|
-
customers:
|
|
84
|
+
customers: getInvoicingAllCustomers(state).map((customer) => ({
|
|
65
85
|
billingAddress: customer.billingAddressId != null
|
|
66
86
|
? getAddressByAddressId(state.addressState, customer.billingAddressId)
|
|
67
87
|
: undefined,
|
|
@@ -70,19 +90,7 @@ export const getCreateInvoiceFormView = (state) => {
|
|
|
70
90
|
name: customerDisplayName(customer),
|
|
71
91
|
})),
|
|
72
92
|
localData: createInvoiceState.formDraft,
|
|
73
|
-
planNames:
|
|
74
|
-
.filter((subFamily) => (subFamily.name ?? '') !== '')
|
|
75
|
-
.map((subFamily) => ({
|
|
76
|
-
// A price point with no flag is taxable, as subscription billing reads it.
|
|
77
|
-
catalogLine: {
|
|
78
|
-
entityId: subFamily.id,
|
|
79
|
-
entityType: plan.itemType ?? 'plan',
|
|
80
|
-
isTaxed: subFamily.isTaxable ?? true,
|
|
81
|
-
},
|
|
82
|
-
planName: plan.name ?? '',
|
|
83
|
-
price: planNamePrefillPrice(subFamily),
|
|
84
|
-
subFamilyName: subFamily.name ?? '',
|
|
85
|
-
}))),
|
|
93
|
+
planNames: getCreateInvoicePlanNames(state),
|
|
86
94
|
products: getInvoicingCatalogPrices(catalogListView.products),
|
|
87
95
|
savedInvoiceId: createInvoiceState.createdInvoiceId,
|
|
88
96
|
settings: getInvoicingSettings(state),
|
|
@@ -2,6 +2,7 @@ import { from } from 'rxjs';
|
|
|
2
2
|
import { filter, mergeMap } from 'rxjs/operators';
|
|
3
3
|
import { fetchInvoicingCatalogPlanList, fetchInvoicingCatalogProductList, } from '../../invoicingCatalogListView/invoicingCatalogListViewReducer';
|
|
4
4
|
import { fetchInvoicingCustomerList } from '../../invoicingCustomerListView/invoicingCustomerListViewReducer';
|
|
5
|
+
import { INVOICING_LIST_ALL_TAB, getInvoicingListTab, } from '../../invoicingListTabCache';
|
|
5
6
|
import { fetchInvoicingSettings } from '../../settingsView/settingsViewReducer';
|
|
6
7
|
import { fetchCreateInvoiceFormPage } from '../createInvoiceReducer';
|
|
7
8
|
/**
|
|
@@ -21,14 +22,16 @@ const shouldFetch = (fetchStateAndError, cacheOverride) => cacheOverride ||
|
|
|
21
22
|
export const fetchCreateInvoiceFormPageEpic = (actions$, state$) => actions$.pipe(filter(fetchCreateInvoiceFormPage.match), mergeMap((action) => {
|
|
22
23
|
const { cacheOverride } = action.payload;
|
|
23
24
|
const state = state$.value;
|
|
24
|
-
|
|
25
|
+
// The customer picker reads the unfiltered `all` cache.
|
|
26
|
+
const customerTab = getInvoicingListTab(state.invoicingCustomerListViewState.byTab, INVOICING_LIST_ALL_TAB);
|
|
25
27
|
const catalogState = state.invoicingCatalogListViewState;
|
|
26
28
|
const settingsState = state.invoicingSettingsViewState;
|
|
27
29
|
const pageActions = [];
|
|
28
|
-
if (shouldFetch({ fetchState:
|
|
30
|
+
if (shouldFetch({ fetchState: customerTab.fetchState, error: customerTab.error }, cacheOverride)) {
|
|
29
31
|
pageActions.push(fetchInvoicingCustomerList({
|
|
30
32
|
filters: {},
|
|
31
33
|
keepExistingListItems: false,
|
|
34
|
+
tabId: INVOICING_LIST_ALL_TAB,
|
|
32
35
|
}));
|
|
33
36
|
}
|
|
34
37
|
if (shouldFetch({ fetchState: catalogState.fetchState, error: catalogState.error }, cacheOverride)) {
|
|
@@ -5,7 +5,7 @@ import { getFormattedAddress } from '../../addressView/addressViewSelector';
|
|
|
5
5
|
import { getInvoicingCouponListItems } from '../couponView/couponViewSelector';
|
|
6
6
|
import { getInvoicingCatalogListView, getInvoicingCatalogPrices, } from '../invoicingCatalogListView/invoicingCatalogListViewSelector';
|
|
7
7
|
import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
8
|
-
import {
|
|
8
|
+
import { getInvoicingAllCustomers } from '../invoicingCustomerListView/invoicingCustomerListViewSelector';
|
|
9
9
|
import { getInvoicingFormDraftLocalData, isInvoicingFormDraftPending, } from '../invoicingFormDraft';
|
|
10
10
|
import { initialInvoicingSubscriptionFormLocalData } from './invoicingSubscriptionFormConfig';
|
|
11
11
|
import { INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE } from './invoicingSubscriptionFormConfig';
|
|
@@ -280,7 +280,7 @@ export const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID)
|
|
|
280
280
|
addons: catalogItemOptionsOfType('addon'),
|
|
281
281
|
charges: catalogItemOptionsOfType('charge'),
|
|
282
282
|
coupons: getInvoicingCouponListItems(state).map(toInvoicingCouponOption),
|
|
283
|
-
customers:
|
|
283
|
+
customers: getInvoicingAllCustomers(state).map(toInvoicingCustomerOption),
|
|
284
284
|
localData,
|
|
285
285
|
mode,
|
|
286
286
|
plans: planPrices.map(toInvoicingPlanOption),
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { EMPTY, from } from 'rxjs';
|
|
2
2
|
import { filter, mergeMap, withLatestFrom } from 'rxjs/operators';
|
|
3
3
|
import { fetchInvoicingCouponList } from '../couponView/couponViewReducer';
|
|
4
|
+
import { getInvoicingCouponListFetchState } from '../couponView/couponViewSelector';
|
|
4
5
|
import { fetchInvoicingCatalogPlanList } from '../invoicingCatalogListView/invoicingCatalogListViewReducer';
|
|
5
6
|
import { fetchInvoicingConfig } from '../invoicingConfigView/invoicingConfigViewReducer';
|
|
7
|
+
import { INVOICING_LIST_ALL_TAB } from '../invoicingListTabCache';
|
|
6
8
|
import { fetchInvoicingSubscriptionDetail, fetchInvoicingSubscriptionDetailPage, } from './editInvoicingSubscriptionDetailViewReducer';
|
|
7
9
|
/**
|
|
8
10
|
* Fetch unless the endpoint is already loaded or in flight — or always when the
|
|
@@ -47,12 +49,13 @@ export const fetchInvoicingSubscriptionDetailPageEpic = (actions$, state$) => ac
|
|
|
47
49
|
keepExistingListItems: false,
|
|
48
50
|
}));
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
if (shouldFetch({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
// The coupon picker reads the unfiltered `all` cache.
|
|
53
|
+
if (shouldFetch(getInvoicingCouponListFetchState(state), cacheOverride)) {
|
|
54
|
+
pageActions.push(fetchInvoicingCouponList({
|
|
55
|
+
filters: {},
|
|
56
|
+
keepExistingListItems: false,
|
|
57
|
+
tabId: INVOICING_LIST_ALL_TAB,
|
|
58
|
+
}));
|
|
56
59
|
}
|
|
57
60
|
return pageActions.length > 0 ? from(pageActions) : EMPTY;
|
|
58
61
|
}));
|
package/lib/esm/view/invoicing/invoicingCreditNoteListView/epics/fetchInvoicingCreditNoteListEpic.js
CHANGED
|
@@ -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 { updateInvoicingCreditNotes } from '../../../../entity/invoicing/creditNote/creditNoteReducer';
|
|
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 { fetchInvoicingCreditNoteList, updateInvoicingCreditNoteList, updateInvoicingCreditNoteListFailure, } from '../invoicingCreditNoteListViewReducer';
|
|
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.customerId != null) {
|
|
10
14
|
params.set('customer_id', filters.customerId);
|
|
11
15
|
}
|
|
@@ -19,9 +23,15 @@ const buildQuery = (filters, cursor, sort) => {
|
|
|
19
23
|
applyListSortParams(params, sort);
|
|
20
24
|
return params;
|
|
21
25
|
};
|
|
22
|
-
export const fetchInvoicingCreditNoteListEpic = (actions$,
|
|
26
|
+
export const fetchInvoicingCreditNoteListEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingCreditNoteList.match), map((action) => ({
|
|
27
|
+
action,
|
|
28
|
+
tabId: invoicingListFetchTab(state$.value.invoicingCreditNoteListViewState.activeTab, action.payload.tabId),
|
|
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 }) => {
|
|
23
33
|
const { filters, cursor, keepExistingListItems, sort } = action.payload;
|
|
24
|
-
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/credits`, buildQuery(filters, cursor, sort));
|
|
34
|
+
const apiUrl = withQuery(`${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/credits`, buildQuery(filters, cursor, sort, invoicingListStatusParam(tabId)));
|
|
25
35
|
return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
|
|
26
36
|
if (isSuccessResponse(response)) {
|
|
27
37
|
const items = response.data?.items ?? [];
|
|
@@ -31,10 +41,17 @@ export const fetchInvoicingCreditNoteListEpic = (actions$, _state$, zeniAPI) =>
|
|
|
31
41
|
ids: items.map((item) => item.id),
|
|
32
42
|
nextCursor: response.data?.next_cursor ?? null,
|
|
33
43
|
keepExistingListItems,
|
|
44
|
+
tabId,
|
|
34
45
|
}),
|
|
35
46
|
];
|
|
36
47
|
}
|
|
37
|
-
return of(updateInvoicingCreditNoteListFailure(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}));
|
|
48
|
+
return of(updateInvoicingCreditNoteListFailure({
|
|
49
|
+
status: response.status,
|
|
50
|
+
tabId,
|
|
51
|
+
}));
|
|
52
|
+
}), catchError((error) => of(updateInvoicingCreditNoteListFailure({
|
|
53
|
+
status: createZeniAPIStatus('Unexpected Error', 'Fetch credit note list REST API call errored out ' +
|
|
54
|
+
JSON.stringify(error)),
|
|
55
|
+
tabId,
|
|
56
|
+
}))));
|
|
57
|
+
}))));
|
|
@@ -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 { fetchInvoicingCreditNoteCounts, fetchInvoicingCreditNoteList, fetchInvoicingCreditNoteListPage, } from '../invoicingCreditNoteListViewReducer';
|
|
4
5
|
/**
|
|
5
6
|
* Fetch unless the endpoint is already loaded or in flight — or always when the
|
|
@@ -17,8 +18,20 @@ export const fetchInvoicingCreditNoteListPageEpic = (actions$, state$) => action
|
|
|
17
18
|
const { cacheOverride, filters, sort } = action.payload;
|
|
18
19
|
const state = state$.value.invoicingCreditNoteListViewState;
|
|
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(fetchInvoicingCreditNoteList({ 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(fetchInvoicingCreditNoteList({
|
|
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(fetchInvoicingCreditNoteCounts());
|
package/lib/esm/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewReducer.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 invoicingCreditNoteListView = createSlice({
|
|
17
12
|
name: 'invoicingCreditNoteListView',
|
|
@@ -19,14 +14,9 @@ const invoicingCreditNoteListView = createSlice({
|
|
|
19
14
|
reducers: {
|
|
20
15
|
fetchInvoicingCreditNoteList: {
|
|
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 invoicingCreditNoteListView = 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
|
fetchInvoicingCreditNoteListPage: {
|
|
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 invoicingCreditNoteListView = createSlice({
|
|
|
55
50
|
},
|
|
56
51
|
},
|
|
57
52
|
updateInvoicingCreditNoteList(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
|
updateInvoicingCreditNoteListFailure(draft, action) {
|
|
66
|
-
draft.
|
|
67
|
-
draft.error = action.payload;
|
|
56
|
+
failInvoicingListTabFetch(draft.byTab, action.payload.tabId, action.payload.status);
|
|
68
57
|
},
|
|
69
58
|
fetchInvoicingCreditNoteCounts(draft) {
|
|
70
59
|
draft.countsFetchState.fetchState = 'In-Progress';
|
|
@@ -82,8 +71,8 @@ const invoicingCreditNoteListView = createSlice({
|
|
|
82
71
|
updateInvoicingCreditNoteFilters(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
|
setInvoicingCreditNoteActiveTab(draft, action) {
|
|
88
77
|
draft.activeTab = action.payload;
|
|
89
78
|
},
|
package/lib/esm/view/invoicing/invoicingCreditNoteListView/invoicingCreditNoteListViewSelector.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import { getInvoicingCreditNotesByIds } from '../../../entity/invoicing/creditNote/creditNoteSelector';
|
|
3
|
+
import { getInvoicingListTab, invoicingListSourceTab, } from '../invoicingListTabCache';
|
|
3
4
|
import { invoicingTabIdsFromConfig } from '../invoicingTabHelper';
|
|
4
5
|
import { ALL_INVOICING_CREDIT_NOTE_TAB_IDS, INVOICING_CREDIT_NOTE_COLUMNS, } from './invoicingCreditNoteListViewState';
|
|
5
6
|
/**
|
|
@@ -9,12 +10,8 @@ import { ALL_INVOICING_CREDIT_NOTE_TAB_IDS, INVOICING_CREDIT_NOTE_COLUMNS, } fro
|
|
|
9
10
|
*/
|
|
10
11
|
export const getInvoicingCreditNoteListView = createSelector((state) => state.invoicingCreditNoteState, (state) => state.invoicingCreditNoteListViewState, (state) => state.invoicingConfigViewState.creditNoteStatusOptions, (state) => state.invoicingConfigViewState.creditNoteTypeOptions, (creditNoteState, listViewState, statusOptions, typeOptions) => {
|
|
11
12
|
const { activeTab } = listViewState;
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
// active tab client-side ('all' shows everything).
|
|
15
|
-
const creditNotes = activeTab === 'all'
|
|
16
|
-
? allCreditNotes
|
|
17
|
-
: allCreditNotes.filter((creditNote) => creditNote.status === activeTab);
|
|
13
|
+
const tab = getInvoicingListTab(listViewState.byTab, invoicingListSourceTab(activeTab));
|
|
14
|
+
const creditNotes = getInvoicingCreditNotesByIds(creditNoteState, tab.ids);
|
|
18
15
|
return {
|
|
19
16
|
activeTab,
|
|
20
17
|
columns: INVOICING_CREDIT_NOTE_COLUMNS,
|
|
@@ -22,15 +19,15 @@ export const getInvoicingCreditNoteListView = createSelector((state) => state.in
|
|
|
22
19
|
count: listViewState.counts,
|
|
23
20
|
countsFetchState: listViewState.countsFetchState,
|
|
24
21
|
filters: listViewState.filters,
|
|
25
|
-
nextCursor:
|
|
22
|
+
nextCursor: tab.nextCursor,
|
|
26
23
|
statusOptions,
|
|
27
24
|
typeOptions,
|
|
28
25
|
tabIds: invoicingTabIdsFromConfig({
|
|
29
26
|
allTabIds: ALL_INVOICING_CREDIT_NOTE_TAB_IDS,
|
|
30
27
|
statusOptions,
|
|
31
28
|
}),
|
|
32
|
-
fetchState:
|
|
33
|
-
error:
|
|
29
|
+
fetchState: tab.fetchState,
|
|
30
|
+
error: tab.error,
|
|
34
31
|
sort: listViewState.sort,
|
|
35
32
|
version: 1.0,
|
|
36
33
|
};
|