@zeniai/client-epic-state 5.2.74 → 5.2.76
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/commonStateTypes/common.d.ts +3 -0
- package/lib/commonStateTypes/common.js +10 -1
- package/lib/entity/invoicing/coupon/couponSelector.d.ts +1 -0
- package/lib/entity/invoicing/coupon/couponSelector.js +5 -0
- package/lib/epic.d.ts +2 -1
- package/lib/epic.js +2 -0
- package/lib/esm/commonStateTypes/common.js +8 -0
- package/lib/esm/entity/invoicing/coupon/couponSelector.js +4 -0
- package/lib/esm/epic.js +2 -0
- package/lib/esm/index.js +3 -3
- package/lib/esm/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +140 -9
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.js +55 -0
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +35 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +53 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionEstimateEpic.js +49 -0
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +18 -13
- package/lib/esm/view/taskManager/taskListView/taskListSelector.js +70 -19
- package/lib/index.d.ts +5 -5
- package/lib/index.js +18 -15
- package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.d.ts +1 -1
- package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +140 -9
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.d.ts +46 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.js +57 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.d.ts +10 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +36 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.d.ts +20 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +55 -2
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +58 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionEstimateEpic.d.ts +7 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionEstimateEpic.js +53 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.d.ts +8 -0
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +21 -14
- package/lib/view/taskManager/taskListView/taskListSelector.d.ts +13 -2
- package/lib/view/taskManager/taskListView/taskListSelector.js +70 -19
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchInvoicingSubscriptionEstimateEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const couponSelector_1 = require("../../../entity/invoicing/coupon/couponSelector");
|
|
7
|
+
const responsePayload_1 = require("../../../responsePayload");
|
|
8
|
+
const editInvoicingSubscriptionDetailViewPayload_1 = require("./editInvoicingSubscriptionDetailViewPayload");
|
|
9
|
+
const editInvoicingSubscriptionDetailViewReducer_1 = require("./editInvoicingSubscriptionDetailViewReducer");
|
|
10
|
+
const saveInvoicingSubscriptionEpic_1 = require("./saveInvoicingSubscriptionEpic");
|
|
11
|
+
// Posts Save's exact body; a changed coupon rides along as `coupons` (Save syncs it after the PUT).
|
|
12
|
+
const fetchInvoicingSubscriptionEstimateEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(editInvoicingSubscriptionDetailViewReducer_1.fetchInvoicingSubscriptionEstimate.match), (0, operators_1.switchMap)((action) => {
|
|
13
|
+
const { id } = action.payload;
|
|
14
|
+
const { data, localData, previousCouponCode } = (0, saveInvoicingSubscriptionEpic_1.invoicingSubscriptionSaveRequest)(state$.value, id);
|
|
15
|
+
const couponCode = localData.couponId;
|
|
16
|
+
const coupon = (0, couponSelector_1.getInvoicingCouponByCode)(state$.value.invoicingCouponState, couponCode);
|
|
17
|
+
const body = id != null &&
|
|
18
|
+
(0, saveInvoicingSubscriptionEpic_1.isInvoicingSubscriptionCouponChanged)(previousCouponCode, couponCode)
|
|
19
|
+
? {
|
|
20
|
+
...data,
|
|
21
|
+
coupons: couponCode !== ''
|
|
22
|
+
? [
|
|
23
|
+
{
|
|
24
|
+
applied_count: 0,
|
|
25
|
+
apply_till: null,
|
|
26
|
+
coupon_code: couponCode,
|
|
27
|
+
coupon_id: coupon?.id ?? couponCode,
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
: [],
|
|
31
|
+
}
|
|
32
|
+
: data;
|
|
33
|
+
const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/subscriptions`;
|
|
34
|
+
const url = id != null
|
|
35
|
+
? `${baseUrl}/${encodeURIComponent(id)}/estimate`
|
|
36
|
+
: `${baseUrl}/estimate`;
|
|
37
|
+
return zeniAPI
|
|
38
|
+
.postAndGetJSON(url, body)
|
|
39
|
+
.pipe((0, operators_1.map)((response) => (0, responsePayload_1.isSuccessResponse)(response) && response.data != null
|
|
40
|
+
? (0, editInvoicingSubscriptionDetailViewReducer_1.updateInvoicingSubscriptionEstimate)({
|
|
41
|
+
estimate: (0, editInvoicingSubscriptionDetailViewPayload_1.mapInvoicingSubscriptionEstimatePayloadToState)(response.data),
|
|
42
|
+
id,
|
|
43
|
+
})
|
|
44
|
+
: (0, editInvoicingSubscriptionDetailViewReducer_1.updateInvoicingSubscriptionEstimateFailure)({
|
|
45
|
+
id,
|
|
46
|
+
status: response.status,
|
|
47
|
+
})), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, editInvoicingSubscriptionDetailViewReducer_1.updateInvoicingSubscriptionEstimateFailure)({
|
|
48
|
+
id,
|
|
49
|
+
status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Subscription estimate REST API call errored out ' +
|
|
50
|
+
JSON.stringify(error)),
|
|
51
|
+
}))));
|
|
52
|
+
}));
|
|
53
|
+
exports.fetchInvoicingSubscriptionEstimateEpic = fetchInvoicingSubscriptionEstimateEpic;
|
package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
+
import { ID } from '../../../commonStateTypes/common';
|
|
3
4
|
import { updateAddresses } from '../../../entity/address/addressReducer';
|
|
4
5
|
import { updateInvoicingSubscriptions } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionReducer';
|
|
5
6
|
import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
|
|
@@ -10,5 +11,12 @@ import { invalidateInvoicingCustomerDetail } from '../editInvoicingCustomerDetai
|
|
|
10
11
|
import { invalidateInvoiceListForCustomer } from '../invoiceList/invoiceListReducer';
|
|
11
12
|
import { addInvoicingSubscriptionToList } from '../invoicingSubscriptionListView/invoicingSubscriptionListViewReducer';
|
|
12
13
|
import { submitInvoicingSubscriptionForm, submitInvoicingSubscriptionFormFailure, submitInvoicingSubscriptionFormSuccess, syncInvoicingSubscriptionCoupons } from './editInvoicingSubscriptionDetailViewReducer';
|
|
14
|
+
import { InvoicingSubscriptionFormLocalData } from './editInvoicingSubscriptionDetailViewState';
|
|
13
15
|
export type ActionType = ReturnType<typeof submitInvoicingSubscriptionForm> | ReturnType<typeof submitInvoicingSubscriptionFormSuccess> | ReturnType<typeof submitInvoicingSubscriptionFormFailure> | ReturnType<typeof syncInvoicingSubscriptionCoupons> | ReturnType<typeof resetNewAddressDataInLocalStore> | ReturnType<typeof updateAddresses> | ReturnType<typeof updateInvoicingSubscriptions> | ReturnType<typeof addInvoicingSubscriptionToList> | ReturnType<typeof invalidateInvoicingCustomerDetail> | ReturnType<typeof invalidateInvoiceListForCustomer> | ReturnType<typeof openSnackbar>;
|
|
16
|
+
export declare const isInvoicingSubscriptionCouponChanged: (previousCouponCode: string, nextCouponCode: string) => boolean;
|
|
17
|
+
export declare const invoicingSubscriptionSaveRequest: (rootState: RootState, id?: ID) => {
|
|
18
|
+
data: Record<string, unknown>;
|
|
19
|
+
localData: InvoicingSubscriptionFormLocalData;
|
|
20
|
+
previousCouponCode: string;
|
|
21
|
+
};
|
|
14
22
|
export declare const saveInvoicingSubscriptionEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.saveInvoicingSubscriptionEpic = void 0;
|
|
3
|
+
exports.saveInvoicingSubscriptionEpic = exports.invoicingSubscriptionSaveRequest = exports.isInvoicingSubscriptionCouponChanged = void 0;
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
6
|
const addressReducer_1 = require("../../../entity/address/addressReducer");
|
|
@@ -17,9 +17,11 @@ const editInvoicingSubscriptionDetailViewPayload_1 = require("./editInvoicingSub
|
|
|
17
17
|
const editInvoicingSubscriptionDetailViewReducer_1 = require("./editInvoicingSubscriptionDetailViewReducer");
|
|
18
18
|
const invoicingSubscriptionFormConfig_1 = require("./invoicingSubscriptionFormConfig");
|
|
19
19
|
const normalizeCouponCode = (value) => (value ?? '').trim().toUpperCase();
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const isInvoicingSubscriptionCouponChanged = (previousCouponCode, nextCouponCode) => normalizeCouponCode(previousCouponCode) !==
|
|
21
|
+
normalizeCouponCode(nextCouponCode);
|
|
22
|
+
exports.isInvoicingSubscriptionCouponChanged = isInvoicingSubscriptionCouponChanged;
|
|
23
|
+
// The draft a save reads and the body it sends; the estimate previews the same body.
|
|
24
|
+
const invoicingSubscriptionSaveRequest = (rootState, id) => {
|
|
23
25
|
const viewState = rootState.editInvoicingSubscriptionDetailViewState;
|
|
24
26
|
const localData = (id != null
|
|
25
27
|
? viewState.editDraftById[id]?.localData
|
|
@@ -28,18 +30,24 @@ const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => actions$.pi
|
|
|
28
30
|
// object in the payload (same pattern as the customer save flow).
|
|
29
31
|
const shippingAddress = rootState.addressViewState.newAddressState?.[invoicingSubscriptionFormConfig_1.INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE]?.addressToCreate;
|
|
30
32
|
const data = (0, editInvoicingSubscriptionDetailViewPayload_1.localDataToSaveInvoicingSubscriptionPayload)(localData, shippingAddress);
|
|
31
|
-
// Coupons are synced via apply/remove-coupon after PUT (not via coupon_ids).
|
|
32
33
|
if (id != null) {
|
|
33
34
|
delete data.coupon_ids;
|
|
34
35
|
}
|
|
36
|
+
const savedCoupon = id != null
|
|
37
|
+
? rootState.invoicingSubscriptionState.invoicingSubscriptionByID[id]
|
|
38
|
+
?.coupons?.[0]
|
|
39
|
+
: undefined;
|
|
40
|
+
return {
|
|
41
|
+
data,
|
|
42
|
+
localData,
|
|
43
|
+
previousCouponCode: savedCoupon?.couponCode ?? savedCoupon?.couponID ?? '',
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
exports.invoicingSubscriptionSaveRequest = invoicingSubscriptionSaveRequest;
|
|
47
|
+
const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(editInvoicingSubscriptionDetailViewReducer_1.submitInvoicingSubscriptionForm.match), (0, operators_1.mergeMap)((action) => {
|
|
48
|
+
const { id } = action.payload;
|
|
49
|
+
const { data, localData, previousCouponCode } = (0, exports.invoicingSubscriptionSaveRequest)(state$.value, id);
|
|
35
50
|
const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0`;
|
|
36
|
-
const previousCouponCode = id != null
|
|
37
|
-
? (rootState.invoicingSubscriptionState.invoicingSubscriptionByID[id]
|
|
38
|
-
?.coupons?.[0]?.couponCode ??
|
|
39
|
-
rootState.invoicingSubscriptionState.invoicingSubscriptionByID[id]
|
|
40
|
-
?.coupons?.[0]?.couponID ??
|
|
41
|
-
'')
|
|
42
|
-
: '';
|
|
43
51
|
const request$ = id != null
|
|
44
52
|
? zeniAPI.putAndGetJSON(`${baseUrl}/subscriptions/${encodeURIComponent(id)}`, { ...data })
|
|
45
53
|
: zeniAPI.postAndGetJSON(`${baseUrl}/subscriptions`, { ...data });
|
|
@@ -57,8 +65,7 @@ const saveInvoicingSubscriptionEpic = (actions$, state$, zeniAPI) => actions$.pi
|
|
|
57
65
|
}
|
|
58
66
|
const saved = response.data;
|
|
59
67
|
const couponChanged = id != null &&
|
|
60
|
-
|
|
61
|
-
normalizeCouponCode(localData.couponId);
|
|
68
|
+
(0, exports.isInvoicingSubscriptionCouponChanged)(previousCouponCode, localData.couponId);
|
|
62
69
|
// Edit + coupon change: merge the saved subscription, then hand the
|
|
63
70
|
// coupon attach/detach to syncInvoicingSubscriptionCouponsEpic, which
|
|
64
71
|
// finishes the flow (form success on both calls ok, form failure
|
|
@@ -38,10 +38,20 @@ export interface TaskListSelectorView extends SelectorView {
|
|
|
38
38
|
userGroupList: UserGroupListSelectorView;
|
|
39
39
|
userList: UserListSelectorView;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Exactly the parts of `TaskListState` the grouping pipeline reads.
|
|
43
|
+
*
|
|
44
|
+
* `TaskListState` also carries `localData` — the current selection and the
|
|
45
|
+
* group name being typed — which changes on every checkbox click and every
|
|
46
|
+
* keystroke. None of the work below looks at it, so naming the real inputs is
|
|
47
|
+
* what lets the pipeline be memoised against them instead of against the whole
|
|
48
|
+
* slice. `TaskListState` still satisfies this, so callers are unaffected.
|
|
49
|
+
*/
|
|
50
|
+
type TaskListGroupingInputs = Pick<TaskListState, 'filters' | 'taskIdsByAssignees' | 'taskIdsByDueDate' | 'taskIdsByGroupsIds' | 'taskIdsByPriority' | 'taskIdsByStatus' | 'taskIdsByTags' | 'uiState'>;
|
|
41
51
|
export declare const getTaskListGroupedByAssignees: ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }: {
|
|
42
52
|
addressState: AddressState;
|
|
43
53
|
tagState: TagState;
|
|
44
|
-
taskListState:
|
|
54
|
+
taskListState: TaskListGroupingInputs;
|
|
45
55
|
taskState: TaskState;
|
|
46
56
|
userGroupsState: UserGroupsState;
|
|
47
57
|
userRoleState: UserRoleState;
|
|
@@ -55,7 +65,7 @@ export declare const getTaskListGroupedByAssignees: ({ taskListState, taskState,
|
|
|
55
65
|
export declare const getTaskListGroupedByPriority: ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }: {
|
|
56
66
|
addressState: AddressState;
|
|
57
67
|
tagState: TagState;
|
|
58
|
-
taskListState:
|
|
68
|
+
taskListState: TaskListGroupingInputs;
|
|
59
69
|
taskState: TaskState;
|
|
60
70
|
userGroupsState: UserGroupsState;
|
|
61
71
|
userRoleState: UserRoleState;
|
|
@@ -67,3 +77,4 @@ export declare const getTaskListGroupedByPriority: ({ taskListState, taskState,
|
|
|
67
77
|
tasks: TaskWithUserDetails[];
|
|
68
78
|
}[];
|
|
69
79
|
export declare const getAllTasks: (state: RootState, isTaskTemplatesEnabled: boolean) => TaskListSelectorView;
|
|
80
|
+
export {};
|
|
@@ -7,6 +7,7 @@ exports.getAllTasks = exports.getTaskListGroupedByPriority = exports.getTaskList
|
|
|
7
7
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
8
8
|
const orderBy_1 = __importDefault(require("lodash/orderBy"));
|
|
9
9
|
const sortOrderPayload_1 = require("../../../commonPayloadTypes/sortOrderPayload");
|
|
10
|
+
const common_1 = require("../../../commonStateTypes/common");
|
|
10
11
|
const reduceFetchState_1 = require("../../../commonStateTypes/reduceFetchState");
|
|
11
12
|
const tagSelector_1 = require("../../../entity/tag/tagSelector");
|
|
12
13
|
const taskSelector_1 = require("../../../entity/task/taskSelector");
|
|
@@ -23,14 +24,18 @@ const taskList_1 = require("./taskList");
|
|
|
23
24
|
const taskListFilterHelpers_1 = require("./taskListFilterHelpers");
|
|
24
25
|
const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
|
|
25
26
|
const tasksInOrder = (0, orderBy_1.default)(tasksList, (task) => {
|
|
26
|
-
const tagsByIds = (0, tagSelector_1.getTagsByIds)(tagState, task.tagIds);
|
|
27
27
|
switch (sortKey) {
|
|
28
28
|
case 'dueDate':
|
|
29
29
|
return task.dueDate;
|
|
30
30
|
case 'creationDate':
|
|
31
31
|
return task.createTime;
|
|
32
32
|
case 'tag':
|
|
33
|
-
|
|
33
|
+
// Resolved HERE, not above the switch. Hoisted, it allocated a fresh
|
|
34
|
+
// array per task on every sort key, which made `tagState` a real
|
|
35
|
+
// dependency of the whole pipeline: tags arriving after first paint
|
|
36
|
+
// then rebuilt every group and re-rendered every row, moving that
|
|
37
|
+
// work out from behind the skeleton and into the user's hands.
|
|
38
|
+
return (0, tagSelector_1.getTagsByIds)(tagState, task.tagIds)[0]?.name ?? ''; // only one tag is supported
|
|
34
39
|
case 'status':
|
|
35
40
|
return task.status.name;
|
|
36
41
|
case 'priority':
|
|
@@ -75,8 +80,7 @@ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRole
|
|
|
75
80
|
const sortedTaskList = sortTasksList(tagState, filteredTaskList, sortKey, sortOrder);
|
|
76
81
|
return sortedTaskList;
|
|
77
82
|
};
|
|
78
|
-
const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState,
|
|
79
|
-
const { taskGroupsIds } = taskGroupViewState;
|
|
83
|
+
const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState, taskGroupsIds, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
80
84
|
const taskGroupsWithTasksList = taskGroupsIds
|
|
81
85
|
.map((taskGroupId) => {
|
|
82
86
|
const taskGroup = (0, taskGroupSelector_1.getTaskGroupById)(taskGroupState, taskGroupId);
|
|
@@ -118,6 +122,7 @@ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupStat
|
|
|
118
122
|
if (defaultTaskGroup != null) {
|
|
119
123
|
sortedTaskGroupsWithTasksList.push(defaultTaskGroup);
|
|
120
124
|
}
|
|
125
|
+
// Also hides deleted groups, which are empty as a side effect.
|
|
121
126
|
return sortedTaskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
122
127
|
};
|
|
123
128
|
const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
@@ -249,7 +254,7 @@ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, user
|
|
|
249
254
|
});
|
|
250
255
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
251
256
|
};
|
|
252
|
-
const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState,
|
|
257
|
+
const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState, }) => {
|
|
253
258
|
const { groupByKey } = taskListState.uiState;
|
|
254
259
|
if (groupByKey === 'groupName') {
|
|
255
260
|
return getTaskListGroupedByGroupName({
|
|
@@ -259,7 +264,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
259
264
|
userRoleState,
|
|
260
265
|
addressState,
|
|
261
266
|
taskGroupState,
|
|
262
|
-
|
|
267
|
+
taskGroupsIds,
|
|
263
268
|
tagState,
|
|
264
269
|
userGroupsState,
|
|
265
270
|
});
|
|
@@ -318,24 +323,70 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
318
323
|
userGroupsState,
|
|
319
324
|
});
|
|
320
325
|
};
|
|
321
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Tags are STRUCTURAL only when they name a group or order the rows. Anywhere
|
|
328
|
+
* else they are decoration on a row that has already rendered.
|
|
329
|
+
*/
|
|
330
|
+
const tagsAreStructural = (uiState) => uiState.groupByKey === 'tag' || uiState.sortKey === 'tag';
|
|
331
|
+
/** One frozen instance, so the "tags are not structural" input never changes. */
|
|
332
|
+
const EMPTY_TAG_STATE = { tagByID: {} };
|
|
333
|
+
/** Inputs are listed field by field, not as the slice: immer keeps each one
|
|
334
|
+
* referentially stable when `localData` changes, so the memo holds. */
|
|
335
|
+
const getTaskGroupsWithTaskListMemoized = (0, toolkit_1.createSelector)((state) => state.taskListState.uiState, (state) => state.taskListState.filters, (state) => state.taskListState.taskIdsByGroupsIds, (state) => state.taskListState.taskIdsByAssignees, (state) => state.taskListState.taskIdsByPriority, (state) => state.taskListState.taskIdsByStatus, (state) => state.taskListState.taskIdsByTags, (state) => state.taskListState.taskIdsByDueDate, (state) => state.taskState, (state) => state.userState, (state) => state.userRoleState, (state) => state.addressState, (state) => state.taskGroupState,
|
|
336
|
+
// `taskGroupsIds`, not the whole slice. `TaskGroupViewState` also carries
|
|
337
|
+
// taskGroupCreationStatus / DeleteStatus / UpdateStatus and fetchState, and
|
|
338
|
+
// none of them reach the board — but each transition produces a fresh slice
|
|
339
|
+
// reference, so an inline group rename alone rebuilt the entire grouped list
|
|
340
|
+
// twice (In-Progress, then Completed). That is the same needless rebuild this
|
|
341
|
+
// change removes for the other slices.
|
|
342
|
+
(state) => state.taskGroupViewState.taskGroupsIds,
|
|
343
|
+
// Only when tags are STRUCTURAL — they name a group or order the rows.
|
|
344
|
+
// Otherwise a stable empty slice, so tags landing after first paint cannot
|
|
345
|
+
// invalidate this memo and rebuild a board the user is already using.
|
|
346
|
+
(state) => tagsAreStructural(state.taskListState.uiState)
|
|
347
|
+
? state.tagState
|
|
348
|
+
: EMPTY_TAG_STATE, (state) => state.userGroupsState, (uiState, filters, taskIdsByGroupsIds, taskIdsByAssignees, taskIdsByPriority, taskIdsByStatus, taskIdsByTags, taskIdsByDueDate, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState) => getTaskGroupsWithTaskList({
|
|
349
|
+
taskListState: {
|
|
350
|
+
uiState,
|
|
351
|
+
filters,
|
|
352
|
+
taskIdsByGroupsIds,
|
|
353
|
+
taskIdsByAssignees,
|
|
354
|
+
taskIdsByPriority,
|
|
355
|
+
taskIdsByStatus,
|
|
356
|
+
taskIdsByTags,
|
|
357
|
+
taskIdsByDueDate,
|
|
358
|
+
},
|
|
359
|
+
taskState,
|
|
360
|
+
userState,
|
|
361
|
+
userRoleState,
|
|
362
|
+
addressState,
|
|
363
|
+
taskGroupState,
|
|
364
|
+
taskGroupsIds,
|
|
365
|
+
tagState,
|
|
366
|
+
userGroupsState,
|
|
367
|
+
}));
|
|
368
|
+
exports.getAllTasks = (0, toolkit_1.createSelector)(
|
|
369
|
+
// `taskState`, `taskGroupState`, `addressState` and `tagState` are gone from
|
|
370
|
+
// here: the grouped list was their only consumer and it is memoised
|
|
371
|
+
// separately now, so a tag edit or an address arriving no longer rebuilds
|
|
372
|
+
// this view.
|
|
373
|
+
(state) => state.taskListState, (state) => state.userState, (state) => state.userRoleState, (state) => state.userListViewState, (state) => state.taskGroupViewState, (state) => state.tagViewState,
|
|
374
|
+
// A BOOLEAN, not the tag slice: reselect compares primitives by value, so
|
|
375
|
+
// this invalidates only when the map goes empty <-> non-empty.
|
|
376
|
+
(state) => (0, common_1.hasAnyKey)(state.tagState.tagByID), (state) => state.taskGroupTemplateState, (state) => state.taskGroupTemplateViewState, (state) => state.userGroupsState, (state) => state.userGroupListViewState, getTaskGroupsWithTaskListMemoized, (_state, isTaskTemplatesEnabled) => isTaskTemplatesEnabled, (taskListState, userState, userRoleState, userListViewState, taskGroupViewState, tagViewState, hasAnyTag, taskGroupTemplateState, taskGroupTemplateViewState, userGroupsState, userGroupListViewState, taskGroupsWithTasksList, isTaskTemplatesEnabled) => {
|
|
322
377
|
const { localData } = taskListState;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
taskState,
|
|
326
|
-
userState,
|
|
327
|
-
userRoleState,
|
|
328
|
-
addressState,
|
|
329
|
-
taskGroupState,
|
|
330
|
-
taskGroupViewState,
|
|
331
|
-
tagState,
|
|
332
|
-
userGroupsState,
|
|
333
|
-
});
|
|
378
|
+
// Only the data the list is BUILT from. Tags decorate a row that can
|
|
379
|
+
// already be drawn.
|
|
334
380
|
const fetchStateVariables = [
|
|
335
381
|
taskListState,
|
|
336
382
|
taskGroupViewState,
|
|
337
|
-
tagViewState,
|
|
338
383
|
];
|
|
384
|
+
// Structural tags are waited on. A failed fetch is bypassed only when tags
|
|
385
|
+
// are already in the store, because then the headers still render.
|
|
386
|
+
if (tagsAreStructural(taskListState.uiState) &&
|
|
387
|
+
!(hasAnyTag && tagViewState.fetchState === 'Error')) {
|
|
388
|
+
fetchStateVariables.push(tagViewState);
|
|
389
|
+
}
|
|
339
390
|
if (isTaskTemplatesEnabled) {
|
|
340
391
|
fetchStateVariables.push(taskGroupTemplateViewState);
|
|
341
392
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.76",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|