@zeniai/client-epic-state 5.2.75 → 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/esm/commonStateTypes/common.js +8 -0
- package/lib/esm/index.js +2 -2
- package/lib/esm/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +140 -9
- package/lib/esm/view/taskManager/taskListView/taskListSelector.js +70 -19
- package/lib/index.d.ts +2 -2
- package/lib/index.js +16 -15
- package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.d.ts +1 -1
- package/lib/view/companyTaskManagerView/epics/fetchCompanyTaskManagerViewEpic.js +140 -9
- package/lib/view/taskManager/taskListView/taskListSelector.d.ts +13 -2
- package/lib/view/taskManager/taskListView/taskListSelector.js +70 -19
- package/package.json +1 -1
|
@@ -29,3 +29,6 @@ export interface FullName {
|
|
|
29
29
|
lastName: string;
|
|
30
30
|
}
|
|
31
31
|
export declare const getUnderScoreKey: (keys: string[]) => string;
|
|
32
|
+
/** Early-exit emptiness check: `Object.keys(x).length > 0` allocates an array
|
|
33
|
+
* on every call, and this one runs inside selector inputs. */
|
|
34
|
+
export declare const hasAnyKey: (record: object) => boolean;
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getUnderScoreKey = exports.toCompletionStatusType = void 0;
|
|
3
|
+
exports.hasAnyKey = exports.getUnderScoreKey = exports.toCompletionStatusType = void 0;
|
|
4
4
|
const stringToUnion_1 = require("./stringToUnion");
|
|
5
5
|
const COMPLETION_STATUS_TYPES = ['incomplete', 'complete'];
|
|
6
6
|
const toCompletionStatusType = (v) => (0, stringToUnion_1.stringToUnion)(v, COMPLETION_STATUS_TYPES);
|
|
7
7
|
exports.toCompletionStatusType = toCompletionStatusType;
|
|
8
8
|
const getUnderScoreKey = (keys) => keys.join('_');
|
|
9
9
|
exports.getUnderScoreKey = getUnderScoreKey;
|
|
10
|
+
/** Early-exit emptiness check: `Object.keys(x).length > 0` allocates an array
|
|
11
|
+
* on every call, and this one runs inside selector inputs. */
|
|
12
|
+
const hasAnyKey = (record) => {
|
|
13
|
+
for (const _key in record) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
};
|
|
18
|
+
exports.hasAnyKey = hasAnyKey;
|
|
@@ -2,3 +2,11 @@ import { stringToUnion } from './stringToUnion';
|
|
|
2
2
|
const COMPLETION_STATUS_TYPES = ['incomplete', 'complete'];
|
|
3
3
|
export const toCompletionStatusType = (v) => stringToUnion(v, COMPLETION_STATUS_TYPES);
|
|
4
4
|
export const getUnderScoreKey = (keys) => keys.join('_');
|
|
5
|
+
/** Early-exit emptiness check: `Object.keys(x).length > 0` allocates an array
|
|
6
|
+
* on every call, and this one runs inside selector inputs. */
|
|
7
|
+
export const hasAnyKey = (record) => {
|
|
8
|
+
for (const _key in record) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
};
|
package/lib/esm/index.js
CHANGED
|
@@ -416,7 +416,7 @@ import { allTaskPriority, allTaskStatus, getTaskDetail, } from './view/taskManag
|
|
|
416
416
|
import { createTaskFromTaskGroupTemplate } from './view/taskManager/taskGroupTemplateView/taskGroupTemplateViewReducer';
|
|
417
417
|
import { createNewTaskGroup, deleteTaskGroup, fetchAllTaskGroups, updateTaskGroupName, } from './view/taskManager/taskGroupView/taskGroupViewReducer';
|
|
418
418
|
import { ALL_TASK_LIST_TABS, TASK_LIST_FILTER_CATEGORIES, TASK_LIST_GROUP_BY_CATEGORIES, toDueDateGroupKeyType, toTaskListGroupByKeyType, toTaskListGroupByKeyTypeStrict, } from './view/taskManager/taskListView/taskList';
|
|
419
|
-
import { bulkUpdateTaskList, dragNDropTasks, fetchTaskListPage, initiateTaskListLocalData, removeTaskFromList, updateTaskFilters, updateTaskFromListView, updateTaskListLocalData, updateTaskListSearchText, updateTaskListTab, updateTaskListUIState, } from './view/taskManager/taskListView/taskListReducer';
|
|
419
|
+
import { bulkUpdateTaskList, dragNDropTasks, fetchTaskList, fetchTaskListPage, initiateTaskListLocalData, removeTaskFromList, updateTaskFilters, updateTaskFromListView, updateTaskListLocalData, updateTaskListSearchText, updateTaskListTab, updateTaskListUIState, } from './view/taskManager/taskListView/taskListReducer';
|
|
420
420
|
import { getAllTasks, } from './view/taskManager/taskListView/taskListSelector';
|
|
421
421
|
import { getDueDateValueFromDueDateGroupId, getTaskUpdates, sortSubtasks, } from './view/taskManager/taskListView/taskListViewHelpers';
|
|
422
422
|
import { fetchTasksCard } from './view/tasksCard/tasksCardReducer';
|
|
@@ -671,7 +671,7 @@ export { fetchGlobalMerchantRecommendation, createGlobalMerchant, updateCreateGl
|
|
|
671
671
|
export { approveVendorGlobalReview, rejectVendorGlobalReview, fetchVendorGlobalReviewView, updateSelectedGlobalMerchant, updateVendorGlobalReviewViewUIState, getVendorGlobalReviewView, getTenantMerchantByMerchantId, toVendorGlobalReviewColumnSortKeyType, updateVendorGlobalReviewViewLocalData, };
|
|
672
672
|
export { fetchArAging, updateArAgingUIState, fetchArAgingDetail, updateArAgingDetailUIState, getArAgingReport, getArAgingDetailForCustomer, updateArAgingNodeCollapseState, };
|
|
673
673
|
export { toRecurringBillFrequency, toRecurringBillFrequencyStrict, };
|
|
674
|
-
export { getTaskGroupById, getAllTasks, fetchTaskListPage, getTaskDetail, fetchTaskDetailPage, saveTaskUpdatesToLocalStore, saveTaskDetail, createSubTask, resetSubTaskCreateStatus, fetchSubTasks, archiveTask, fetchCannedResponses, saveCannedResponse, deleteCannedResponse, getCannedResponsesView, updateTaskListSearchText, updateTaskListUIState, allTaskStatus, allTaskPriority, updateTaskFilters, TASK_LIST_FILTER_CATEGORIES, TASK_LIST_GROUP_BY_CATEGORIES, deleteTask, discardTaskUpdatesInLocalStore, bulkUpdateTaskList, createNewTaskGroup, initiateTaskListLocalData, deleteTaskGroup, updateTaskListLocalData, dragNDropTasks, updateTaskGroupName, toTaskListGroupByKeyType, toTaskListGroupByKeyTypeStrict, getTaskUpdates, sortSubtasks, fetchAllTaskGroups, initialTaskDetail, getDueDateValueFromDueDateGroupId, toPriorityCodeType, toTaskStatusCodeType, toDueDateGroupKeyType, updateTaskFromListView, updateTaskListTab, removeTaskFromList, snoozeTask, unsnoozeTask, convertHHMMStrToMinutes, initialTaskDetailLocalData, ALL_TASK_LIST_TABS, };
|
|
674
|
+
export { getTaskGroupById, getAllTasks, fetchTaskList, fetchTaskListPage, getTaskDetail, fetchTaskDetailPage, saveTaskUpdatesToLocalStore, saveTaskDetail, createSubTask, resetSubTaskCreateStatus, fetchSubTasks, archiveTask, fetchCannedResponses, saveCannedResponse, deleteCannedResponse, getCannedResponsesView, updateTaskListSearchText, updateTaskListUIState, allTaskStatus, allTaskPriority, updateTaskFilters, TASK_LIST_FILTER_CATEGORIES, TASK_LIST_GROUP_BY_CATEGORIES, deleteTask, discardTaskUpdatesInLocalStore, bulkUpdateTaskList, createNewTaskGroup, initiateTaskListLocalData, deleteTaskGroup, updateTaskListLocalData, dragNDropTasks, updateTaskGroupName, toTaskListGroupByKeyType, toTaskListGroupByKeyTypeStrict, getTaskUpdates, sortSubtasks, fetchAllTaskGroups, initialTaskDetail, getDueDateValueFromDueDateGroupId, toPriorityCodeType, toTaskStatusCodeType, toDueDateGroupKeyType, updateTaskFromListView, updateTaskListTab, removeTaskFromList, snoozeTask, unsnoozeTask, convertHHMMStrToMinutes, initialTaskDetailLocalData, ALL_TASK_LIST_TABS, };
|
|
675
675
|
export { getAllTags, fetchTagList, createTag, deleteTag, };
|
|
676
676
|
export { getAllCardsAndBankPaymentMethods, createCardSetupIntent, confirmCardSetupIntent, addCardPaymentSource, fetchPaymentSources, resetCardPaymentErrorStatuses, clearCardPaymentView, };
|
|
677
677
|
export { getAuditReportGroupViewSelectorView, getAuditRuleGroupViewSelectorView, getUserFromAllUsers, fetchAuditRuleGroupView, fetchAuditReportGroupView, saveReasonForAuditRule, clearAuditReportGroupViewByCompanyId, };
|
|
@@ -4,19 +4,41 @@ import { getSortOrder } from '../../../commonPayloadTypes/sortOrderPayload';
|
|
|
4
4
|
import { updateTags } from '../../../entity/tag/tagReducer';
|
|
5
5
|
import { updateTasks } from '../../../entity/task/taskReducer';
|
|
6
6
|
import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
|
|
7
|
+
import { date as toZeniDate } from '../../../zeniDayJS';
|
|
7
8
|
import { fetchCompanyTaskManagerView, updateCompanyTaskManagerViewOnFailure, updateCompanyTaskManagerViewOnSuccess, } from '../companyTaskManagerViewReducer';
|
|
8
9
|
const DEFAULT_PAGE_SIZE = 100;
|
|
9
|
-
|
|
10
|
+
// Mutates the accumulator rather than spreading it: spreading rebuilds the whole
|
|
11
|
+
// map on every entry, which is O(n^2) over a tenant's tag list.
|
|
12
|
+
const toTagNameByID = (state) => Object.entries(state.tagState.tagByID).reduce((result, [tagId, tag]) => {
|
|
13
|
+
const name = tag?.name;
|
|
14
|
+
if (name != null && name !== '') {
|
|
15
|
+
result[tagId] = name;
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}, {});
|
|
19
|
+
const hasTagCategory = (filters) => (filters?.categories ?? []).some((category) => category.field === 'tag');
|
|
20
|
+
export const fetchCompanyTaskManagerViewEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchCompanyTaskManagerView.match), switchMap((action) => {
|
|
10
21
|
const { from: fromOffset, searchString, sortKey, sortOrder, filters, } = action.payload;
|
|
11
22
|
const queryValue = {
|
|
12
23
|
size: DEFAULT_PAGE_SIZE,
|
|
13
24
|
from: fromOffset,
|
|
14
25
|
global_search: searchString ?? '',
|
|
26
|
+
// TasksEsSearch defaults this to true, which AND-injects an
|
|
27
|
+
// `assignees = <caller>` filter. Cockpit's task list is a cross-tenant
|
|
28
|
+
// view grouped by company (and offers an Assignee filter), so it must
|
|
29
|
+
// NOT be scoped to the operator — otherwise every category filter
|
|
30
|
+
// AND's down to their own tasks and returns nothing. The metrics panel
|
|
31
|
+
// deliberately passes `true`; this list deliberately passes `false`.
|
|
32
|
+
is_filter_by_logged_in_user: false,
|
|
15
33
|
sort_by: toSortKeyPayload(sortKey),
|
|
16
34
|
sort_order: getSortOrder(sortOrder),
|
|
17
35
|
filter_join: filters?.categoryCombinationOperator === 'AND' ? 'and' : 'or',
|
|
18
36
|
...(filters?.categories != null && filters.categories.length > 0
|
|
19
|
-
? {
|
|
37
|
+
? {
|
|
38
|
+
// Only walk the tag map when a tag filter is actually in play;
|
|
39
|
+
// otherwise every dispatch pays for a map nothing reads.
|
|
40
|
+
filter_by: toFilterByPayload(filters, hasTagCategory(filters) ? toTagNameByID(state$.value) : {}),
|
|
41
|
+
}
|
|
20
42
|
: {}),
|
|
21
43
|
task_group_scopes: filters?.taskGroupScopes != null && filters.taskGroupScopes.length > 0
|
|
22
44
|
? filters.taskGroupScopes
|
|
@@ -63,14 +85,123 @@ const toSortKeyPayload = (sortKey) => {
|
|
|
63
85
|
return sortKey;
|
|
64
86
|
}
|
|
65
87
|
};
|
|
66
|
-
|
|
88
|
+
// Cockpit filter-category keys are UI identifiers; the ES index uses its own
|
|
89
|
+
// field names (see TasksEsSearch.get). Anything not listed here already matches
|
|
90
|
+
// the contract (assignees, priority, status, company_id).
|
|
91
|
+
const ES_FIELD_BY_CATEGORY = {
|
|
92
|
+
creationDate: 'create_time',
|
|
93
|
+
dueDate: 'due_date',
|
|
94
|
+
tag: 'tags',
|
|
95
|
+
};
|
|
96
|
+
// ES rejects `is` / `is_not` on date fields — they only accept `between`, whose
|
|
97
|
+
// values are the [start, end] pair the dateRange picker already emits.
|
|
98
|
+
const ES_DATE_FIELDS = new Set(['create_time', 'due_date']);
|
|
99
|
+
// The cockpit date picker hands over ZeniDate objects; once filters have round-
|
|
100
|
+
// tripped through redux they can arrive as ISO or date-only strings instead.
|
|
101
|
+
// Both have to reach ES as instants: `create_time`/`update_time` carry a real
|
|
102
|
+
// timestamp, so a date-only upper bound lands on midnight and drops everything
|
|
103
|
+
// created later that day — a single-day Created filter would return almost
|
|
104
|
+
// nothing, which is the empty state this epic exists to fix.
|
|
105
|
+
// A real check rather than `value as ZeniDate`: the cast would silently lie
|
|
106
|
+
// about a raw Date, a number, or anything else redux hands back, leaving the
|
|
107
|
+
// runtime guard as the only thing standing between us and a crash.
|
|
108
|
+
const isZeniDateLike = (value) => value != null &&
|
|
109
|
+
typeof value.isValid === 'function' &&
|
|
110
|
+
typeof value.format === 'function';
|
|
111
|
+
const CALENDAR_DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
112
|
+
const toDateBound = (value, edge) => {
|
|
113
|
+
// A bare `YYYY-MM-DD` carries no instant to misread — use it verbatim. This
|
|
114
|
+
// is the only genuinely timezone-proof shape, which is why it is worth a
|
|
115
|
+
// fast path of its own.
|
|
116
|
+
if (typeof value === 'string' && CALENDAR_DATE_ONLY.test(value)) {
|
|
117
|
+
return edge === 'start'
|
|
118
|
+
? `${value}T00:00:00.000Z`
|
|
119
|
+
: `${value}T23:59:59.999Z`;
|
|
120
|
+
}
|
|
121
|
+
// Everything else came from the picker, and the picker emits LOCAL midnight:
|
|
122
|
+
// `ZeniDatePicker` hands react-datepicker's local-midnight Date to
|
|
123
|
+
// `zeniDate(date.toISOString())`. So the instant encodes "midnight on the day
|
|
124
|
+
// the user clicked, in the user's zone" — and the day they clicked is
|
|
125
|
+
// recovered by reading it locally, never in UTC.
|
|
126
|
+
//
|
|
127
|
+
// Reading it in UTC subtracts the offset for every UTC+ operator: clicking
|
|
128
|
+
// "Mar 4" in Asia/Kolkata stores 2026-03-03T18:30:00Z, which is the 3rd in
|
|
129
|
+
// UTC. A single-day filter then returns the wrong day's tasks rather than
|
|
130
|
+
// none, and US-based testing never sees it.
|
|
131
|
+
const parsed = typeof value === 'string' ? toZeniDate(value) : value;
|
|
132
|
+
if (!isZeniDateLike(parsed) || !parsed.isValid()) {
|
|
133
|
+
// Drop the row rather than send garbage: ES rejects the whole request on
|
|
134
|
+
// one bad bound, taking the other filters down with it.
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
const ymd = parsed.format('YYYY-MM-DD');
|
|
138
|
+
return edge === 'start' ? `${ymd}T00:00:00.000Z` : `${ymd}T23:59:59.999Z`;
|
|
139
|
+
};
|
|
140
|
+
const toFilterByPayload = (filters, tagNameByID) => {
|
|
67
141
|
if (filters.categories == null) {
|
|
68
142
|
return [];
|
|
69
143
|
}
|
|
70
|
-
return filters.categories.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
144
|
+
return filters.categories.flatMap((category) => {
|
|
145
|
+
const categoryField = category.field ?? '';
|
|
146
|
+
const field = ES_FIELD_BY_CATEGORY[categoryField] ?? categoryField;
|
|
147
|
+
// The dropdown is keyed by tagId, but ES matches `tags` on the tag NAME,
|
|
148
|
+
// so resolve through the tag entity this epic itself populates. Unknown ids
|
|
149
|
+
// fall through unchanged rather than being dropped.
|
|
150
|
+
const isDateField = ES_DATE_FIELDS.has(field);
|
|
151
|
+
// `between` is exactly [from, to]. A half-filled picker (one value) makes ES
|
|
152
|
+
// reject the query outright, and a longer array would quietly bind extras to
|
|
153
|
+
// `end` — both land the user back on an unexplained empty page, which is the
|
|
154
|
+
// failure this epic exists to remove. Drop the row instead.
|
|
155
|
+
if (isDateField && category.values.length !== 2) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
const values = category.values.map((value, index) => {
|
|
159
|
+
if (isDateField) {
|
|
160
|
+
// [from, to] — widen the upper bound to the end of its day.
|
|
161
|
+
return toDateBound(value, index === 0 ? 'start' : 'end');
|
|
162
|
+
}
|
|
163
|
+
const raw = String(value);
|
|
164
|
+
if (field !== 'tags') {
|
|
165
|
+
return raw;
|
|
166
|
+
}
|
|
167
|
+
// An id we cannot resolve would reach ES verbatim and match nothing —
|
|
168
|
+
// another silent empty page. Can happen when a filter is hydrated from the
|
|
169
|
+
// URL before `updateTags` has populated `tagByID`, so keep the value
|
|
170
|
+
// (dropping it would widen the result set) but make it visible in logs.
|
|
171
|
+
const tagName = tagNameByID[raw];
|
|
172
|
+
if (tagName == null) {
|
|
173
|
+
console.debug(`[taskManager] unresolved tagId "${raw}" sent to ES verbatim; expect no matches`);
|
|
174
|
+
}
|
|
175
|
+
return tagName ?? raw;
|
|
176
|
+
});
|
|
177
|
+
// `toDateBound` returns '' for a value it cannot parse. Honour that here:
|
|
178
|
+
// emitting a `between` with an empty bound fails the whole ES query, which
|
|
179
|
+
// takes every OTHER filter in the same request down with it — the silent
|
|
180
|
+
// empty page this epic exists to remove, from one unparseable value that a
|
|
181
|
+
// URL-hydrated filter can supply.
|
|
182
|
+
if (isDateField && values.some((value) => value === '')) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
// ES has no date-exclusion operator: `is`/`is_not` are rejected on date
|
|
186
|
+
// fields, and `between` can only select a range, never exclude one. The
|
|
187
|
+
// operator dropdown still offers "is not" for every category, so an excluded
|
|
188
|
+
// date range would otherwise be sent as a plain `between` — returning exactly
|
|
189
|
+
// the rows the user asked to hide. Drop the row instead: a superset is
|
|
190
|
+
// wrong-but-obvious, an inverted result is wrong-and-invisible.
|
|
191
|
+
if (isDateField && category.matchingOperator !== 'equal') {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
return [
|
|
195
|
+
{
|
|
196
|
+
field,
|
|
197
|
+
operator: isDateField
|
|
198
|
+
? 'between'
|
|
199
|
+
: category.matchingOperator === 'equal'
|
|
200
|
+
? 'is'
|
|
201
|
+
: 'is_not',
|
|
202
|
+
values,
|
|
203
|
+
join: category.valuesCombinationOperator === 'ALL' ? 'and' : 'or',
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
});
|
|
76
207
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import orderBy from 'lodash/orderBy';
|
|
3
3
|
import { getSortOrder, } from '../../../commonPayloadTypes/sortOrderPayload';
|
|
4
|
+
import { hasAnyKey, } from '../../../commonStateTypes/common';
|
|
4
5
|
import { reduceAnyFetchState } from '../../../commonStateTypes/reduceFetchState';
|
|
5
6
|
import { getTagsByIds } from '../../../entity/tag/tagSelector';
|
|
6
7
|
import { getTaskById } from '../../../entity/task/taskSelector';
|
|
@@ -17,14 +18,18 @@ import { DUE_DATE_GROUP_KEYS, } from './taskList';
|
|
|
17
18
|
import { getCategoryValueForTaskList } from './taskListFilterHelpers';
|
|
18
19
|
const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
|
|
19
20
|
const tasksInOrder = orderBy(tasksList, (task) => {
|
|
20
|
-
const tagsByIds = getTagsByIds(tagState, task.tagIds);
|
|
21
21
|
switch (sortKey) {
|
|
22
22
|
case 'dueDate':
|
|
23
23
|
return task.dueDate;
|
|
24
24
|
case 'creationDate':
|
|
25
25
|
return task.createTime;
|
|
26
26
|
case 'tag':
|
|
27
|
-
|
|
27
|
+
// Resolved HERE, not above the switch. Hoisted, it allocated a fresh
|
|
28
|
+
// array per task on every sort key, which made `tagState` a real
|
|
29
|
+
// dependency of the whole pipeline: tags arriving after first paint
|
|
30
|
+
// then rebuilt every group and re-rendered every row, moving that
|
|
31
|
+
// work out from behind the skeleton and into the user's hands.
|
|
32
|
+
return getTagsByIds(tagState, task.tagIds)[0]?.name ?? ''; // only one tag is supported
|
|
28
33
|
case 'status':
|
|
29
34
|
return task.status.name;
|
|
30
35
|
case 'priority':
|
|
@@ -69,8 +74,7 @@ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRole
|
|
|
69
74
|
const sortedTaskList = sortTasksList(tagState, filteredTaskList, sortKey, sortOrder);
|
|
70
75
|
return sortedTaskList;
|
|
71
76
|
};
|
|
72
|
-
const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState,
|
|
73
|
-
const { taskGroupsIds } = taskGroupViewState;
|
|
77
|
+
const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState, taskGroupsIds, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
74
78
|
const taskGroupsWithTasksList = taskGroupsIds
|
|
75
79
|
.map((taskGroupId) => {
|
|
76
80
|
const taskGroup = getTaskGroupById(taskGroupState, taskGroupId);
|
|
@@ -112,6 +116,7 @@ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupStat
|
|
|
112
116
|
if (defaultTaskGroup != null) {
|
|
113
117
|
sortedTaskGroupsWithTasksList.push(defaultTaskGroup);
|
|
114
118
|
}
|
|
119
|
+
// Also hides deleted groups, which are empty as a side effect.
|
|
115
120
|
return sortedTaskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
116
121
|
};
|
|
117
122
|
export const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
@@ -241,7 +246,7 @@ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, user
|
|
|
241
246
|
});
|
|
242
247
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
243
248
|
};
|
|
244
|
-
const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState,
|
|
249
|
+
const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState, }) => {
|
|
245
250
|
const { groupByKey } = taskListState.uiState;
|
|
246
251
|
if (groupByKey === 'groupName') {
|
|
247
252
|
return getTaskListGroupedByGroupName({
|
|
@@ -251,7 +256,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
251
256
|
userRoleState,
|
|
252
257
|
addressState,
|
|
253
258
|
taskGroupState,
|
|
254
|
-
|
|
259
|
+
taskGroupsIds,
|
|
255
260
|
tagState,
|
|
256
261
|
userGroupsState,
|
|
257
262
|
});
|
|
@@ -310,24 +315,70 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
310
315
|
userGroupsState,
|
|
311
316
|
});
|
|
312
317
|
};
|
|
313
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Tags are STRUCTURAL only when they name a group or order the rows. Anywhere
|
|
320
|
+
* else they are decoration on a row that has already rendered.
|
|
321
|
+
*/
|
|
322
|
+
const tagsAreStructural = (uiState) => uiState.groupByKey === 'tag' || uiState.sortKey === 'tag';
|
|
323
|
+
/** One frozen instance, so the "tags are not structural" input never changes. */
|
|
324
|
+
const EMPTY_TAG_STATE = { tagByID: {} };
|
|
325
|
+
/** Inputs are listed field by field, not as the slice: immer keeps each one
|
|
326
|
+
* referentially stable when `localData` changes, so the memo holds. */
|
|
327
|
+
const getTaskGroupsWithTaskListMemoized = 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,
|
|
328
|
+
// `taskGroupsIds`, not the whole slice. `TaskGroupViewState` also carries
|
|
329
|
+
// taskGroupCreationStatus / DeleteStatus / UpdateStatus and fetchState, and
|
|
330
|
+
// none of them reach the board — but each transition produces a fresh slice
|
|
331
|
+
// reference, so an inline group rename alone rebuilt the entire grouped list
|
|
332
|
+
// twice (In-Progress, then Completed). That is the same needless rebuild this
|
|
333
|
+
// change removes for the other slices.
|
|
334
|
+
(state) => state.taskGroupViewState.taskGroupsIds,
|
|
335
|
+
// Only when tags are STRUCTURAL — they name a group or order the rows.
|
|
336
|
+
// Otherwise a stable empty slice, so tags landing after first paint cannot
|
|
337
|
+
// invalidate this memo and rebuild a board the user is already using.
|
|
338
|
+
(state) => tagsAreStructural(state.taskListState.uiState)
|
|
339
|
+
? state.tagState
|
|
340
|
+
: EMPTY_TAG_STATE, (state) => state.userGroupsState, (uiState, filters, taskIdsByGroupsIds, taskIdsByAssignees, taskIdsByPriority, taskIdsByStatus, taskIdsByTags, taskIdsByDueDate, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState) => getTaskGroupsWithTaskList({
|
|
341
|
+
taskListState: {
|
|
342
|
+
uiState,
|
|
343
|
+
filters,
|
|
344
|
+
taskIdsByGroupsIds,
|
|
345
|
+
taskIdsByAssignees,
|
|
346
|
+
taskIdsByPriority,
|
|
347
|
+
taskIdsByStatus,
|
|
348
|
+
taskIdsByTags,
|
|
349
|
+
taskIdsByDueDate,
|
|
350
|
+
},
|
|
351
|
+
taskState,
|
|
352
|
+
userState,
|
|
353
|
+
userRoleState,
|
|
354
|
+
addressState,
|
|
355
|
+
taskGroupState,
|
|
356
|
+
taskGroupsIds,
|
|
357
|
+
tagState,
|
|
358
|
+
userGroupsState,
|
|
359
|
+
}));
|
|
360
|
+
export const getAllTasks = createSelector(
|
|
361
|
+
// `taskState`, `taskGroupState`, `addressState` and `tagState` are gone from
|
|
362
|
+
// here: the grouped list was their only consumer and it is memoised
|
|
363
|
+
// separately now, so a tag edit or an address arriving no longer rebuilds
|
|
364
|
+
// this view.
|
|
365
|
+
(state) => state.taskListState, (state) => state.userState, (state) => state.userRoleState, (state) => state.userListViewState, (state) => state.taskGroupViewState, (state) => state.tagViewState,
|
|
366
|
+
// A BOOLEAN, not the tag slice: reselect compares primitives by value, so
|
|
367
|
+
// this invalidates only when the map goes empty <-> non-empty.
|
|
368
|
+
(state) => 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) => {
|
|
314
369
|
const { localData } = taskListState;
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
taskState,
|
|
318
|
-
userState,
|
|
319
|
-
userRoleState,
|
|
320
|
-
addressState,
|
|
321
|
-
taskGroupState,
|
|
322
|
-
taskGroupViewState,
|
|
323
|
-
tagState,
|
|
324
|
-
userGroupsState,
|
|
325
|
-
});
|
|
370
|
+
// Only the data the list is BUILT from. Tags decorate a row that can
|
|
371
|
+
// already be drawn.
|
|
326
372
|
const fetchStateVariables = [
|
|
327
373
|
taskListState,
|
|
328
374
|
taskGroupViewState,
|
|
329
|
-
tagViewState,
|
|
330
375
|
];
|
|
376
|
+
// Structural tags are waited on. A failed fetch is bypassed only when tags
|
|
377
|
+
// are already in the store, because then the headers still render.
|
|
378
|
+
if (tagsAreStructural(taskListState.uiState) &&
|
|
379
|
+
!(hasAnyTag && tagViewState.fetchState === 'Error')) {
|
|
380
|
+
fetchStateVariables.push(tagViewState);
|
|
381
|
+
}
|
|
331
382
|
if (isTaskTemplatesEnabled) {
|
|
332
383
|
fetchStateVariables.push(taskGroupTemplateViewState);
|
|
333
384
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -629,7 +629,7 @@ import { TaskDetailSelectorView, allTaskPriority, allTaskStatus, getTaskDetail }
|
|
|
629
629
|
import { createTaskFromTaskGroupTemplate } from './view/taskManager/taskGroupTemplateView/taskGroupTemplateViewReducer';
|
|
630
630
|
import { createNewTaskGroup, deleteTaskGroup, fetchAllTaskGroups, updateTaskGroupName } from './view/taskManager/taskGroupView/taskGroupViewReducer';
|
|
631
631
|
import { ALL_TASK_LIST_TABS, DueDateGroupKey, TASK_LIST_FILTER_CATEGORIES, TASK_LIST_GROUP_BY_CATEGORIES, TaskFilterCategory, TaskGroupKey, TaskListFilterCategoryField, TaskListFilters, TaskListLocalData, TaskListTab, TaskListUIState, TaskListViewSortKey, toDueDateGroupKeyType, toTaskListGroupByKeyType, toTaskListGroupByKeyTypeStrict } from './view/taskManager/taskListView/taskList';
|
|
632
|
-
import { bulkUpdateTaskList, dragNDropTasks, fetchTaskListPage, initiateTaskListLocalData, removeTaskFromList, updateTaskFilters, updateTaskFromListView, updateTaskListLocalData, updateTaskListSearchText, updateTaskListTab, updateTaskListUIState } from './view/taskManager/taskListView/taskListReducer';
|
|
632
|
+
import { bulkUpdateTaskList, dragNDropTasks, fetchTaskList, fetchTaskListPage, initiateTaskListLocalData, removeTaskFromList, updateTaskFilters, updateTaskFromListView, updateTaskListLocalData, updateTaskListSearchText, updateTaskListTab, updateTaskListUIState } from './view/taskManager/taskListView/taskListReducer';
|
|
633
633
|
import { TaskGroupWithTasksList, TaskListSelectorView, TaskWithUserDetails, getAllTasks } from './view/taskManager/taskListView/taskListSelector';
|
|
634
634
|
import { SubtaskSortKey, SubtaskSortOrder, getDueDateValueFromDueDateGroupId, getTaskUpdates, sortSubtasks } from './view/taskManager/taskListView/taskListViewHelpers';
|
|
635
635
|
import { fetchTasksCard } from './view/tasksCard/tasksCardReducer';
|
|
@@ -961,7 +961,7 @@ export { fetchGlobalMerchantRecommendation, createGlobalMerchant, updateCreateGl
|
|
|
961
961
|
export { approveVendorGlobalReview, rejectVendorGlobalReview, fetchVendorGlobalReviewView, updateSelectedGlobalMerchant, updateVendorGlobalReviewViewUIState, getVendorGlobalReviewView, getTenantMerchantByMerchantId, toVendorGlobalReviewColumnSortKeyType, VendorGlobalReview, VendorGlobalReviewViewUIState, VendorGlobalReviewViewColumnSortKey, VendorGlobalReviewViewSelectorView, VendorGlobalReviewViewState, VendorGlobalReviewViewLocalData, updateVendorGlobalReviewViewLocalData, GlobalReviewPageCurrentSelectionByColumn, GlobalRecommendationFetchSource, };
|
|
962
962
|
export { fetchArAging, updateArAgingUIState, fetchArAgingDetail, updateArAgingDetailUIState, ArAgingReport, ArAgingDetail, NestedAgingBalancesByCustomer, getArAgingReport, getArAgingDetailForCustomer, updateArAgingNodeCollapseState, };
|
|
963
963
|
export { toRecurringBillFrequency, toRecurringBillFrequencyStrict, RecurringBillFrequency, RecurringBillDetail, RecurringBillConfig, RecurringBillStatus, RecurringBillPaymentStatus, };
|
|
964
|
-
export { Task, TaskPayload, TaskListSelectorView, TaskWithUserDetails, TaskListViewSortKey, TaskListUIState, TaskDetailSelectorView, EditTaskLocalData, TaskStatusCodeType, PriorityCodeType, getTaskGroupById, TaskStatus, TaskPriority, TaskCodeType, getAllTasks, fetchTaskListPage, getTaskDetail, fetchTaskDetailPage, saveTaskUpdatesToLocalStore, saveTaskDetail, createSubTask, resetSubTaskCreateStatus, fetchSubTasks, CreateSubTaskPayload, archiveTask, CannedResponse, CannedResponsesViewState, fetchCannedResponses, saveCannedResponse, deleteCannedResponse, getCannedResponsesView, updateTaskListSearchText, updateTaskListUIState, allTaskStatus, allTaskPriority, TaskListFilters, updateTaskFilters, TASK_LIST_FILTER_CATEGORIES, TASK_LIST_GROUP_BY_CATEGORIES, TaskListFilterCategoryField, deleteTask, discardTaskUpdatesInLocalStore, TaskFilterCategory, TaskType, TaskGroupWithTasksList, TaskListLocalData, bulkUpdateTaskList, createNewTaskGroup, initiateTaskListLocalData, deleteTaskGroup, updateTaskListLocalData, dragNDropTasks, updateTaskGroupName, TaskGroupState, TaskGroupKey, toTaskListGroupByKeyType, toTaskListGroupByKeyTypeStrict, SubtaskSortKey, SubtaskSortOrder, getTaskUpdates, sortSubtasks, fetchAllTaskGroups, initialTaskDetail, getDueDateValueFromDueDateGroupId, toPriorityCodeType, toTaskStatusCodeType, DueDateGroupKey, toDueDateGroupKeyType, updateTaskFromListView, updateTaskListTab, removeTaskFromList, snoozeTask, unsnoozeTask, convertHHMMStrToMinutes, initialTaskDetailLocalData, TaskListTab, ALL_TASK_LIST_TABS, };
|
|
964
|
+
export { Task, TaskPayload, TaskListSelectorView, TaskWithUserDetails, TaskListViewSortKey, TaskListUIState, TaskDetailSelectorView, EditTaskLocalData, TaskStatusCodeType, PriorityCodeType, getTaskGroupById, TaskStatus, TaskPriority, TaskCodeType, getAllTasks, fetchTaskList, fetchTaskListPage, getTaskDetail, fetchTaskDetailPage, saveTaskUpdatesToLocalStore, saveTaskDetail, createSubTask, resetSubTaskCreateStatus, fetchSubTasks, CreateSubTaskPayload, archiveTask, CannedResponse, CannedResponsesViewState, fetchCannedResponses, saveCannedResponse, deleteCannedResponse, getCannedResponsesView, updateTaskListSearchText, updateTaskListUIState, allTaskStatus, allTaskPriority, TaskListFilters, updateTaskFilters, TASK_LIST_FILTER_CATEGORIES, TASK_LIST_GROUP_BY_CATEGORIES, TaskListFilterCategoryField, deleteTask, discardTaskUpdatesInLocalStore, TaskFilterCategory, TaskType, TaskGroupWithTasksList, TaskListLocalData, bulkUpdateTaskList, createNewTaskGroup, initiateTaskListLocalData, deleteTaskGroup, updateTaskListLocalData, dragNDropTasks, updateTaskGroupName, TaskGroupState, TaskGroupKey, toTaskListGroupByKeyType, toTaskListGroupByKeyTypeStrict, SubtaskSortKey, SubtaskSortOrder, getTaskUpdates, sortSubtasks, fetchAllTaskGroups, initialTaskDetail, getDueDateValueFromDueDateGroupId, toPriorityCodeType, toTaskStatusCodeType, DueDateGroupKey, toDueDateGroupKeyType, updateTaskFromListView, updateTaskListTab, removeTaskFromList, snoozeTask, unsnoozeTask, convertHHMMStrToMinutes, initialTaskDetailLocalData, TaskListTab, ALL_TASK_LIST_TABS, };
|
|
965
965
|
export { Tag, TagState, TagViewState, getAllTags, TagViewSelectorView, fetchTagList, createTag, deleteTag, };
|
|
966
966
|
export { CardPayment, CardPaymentViewState, SetupIntentData, CardPaymentSelectorView, getAllCardsAndBankPaymentMethods, createCardSetupIntent, confirmCardSetupIntent, addCardPaymentSource, fetchPaymentSources, resetCardPaymentErrorStatuses, clearCardPaymentView, AddCardPaymentSourceType, };
|
|
967
967
|
export { getAuditReportGroupViewSelectorView, getAuditRuleGroupViewSelectorView, getUserFromAllUsers, AuditReportGroupSelectorView, AuditReportGroupViewSelectorView, AuditRuleGroupViewSelectorView, fetchAuditRuleGroupView, fetchAuditReportGroupView, saveReasonForAuditRule, AuditRuleGroup, AuditRuleGroupReport, AuditReportRule, AuditRuleEntityType, AuditRuleEntityIdType, AuditRuleBypassReason, clearAuditReportGroupViewByCompanyId, };
|
package/lib/index.js
CHANGED
|
@@ -71,21 +71,21 @@ exports.TIME_SERIES_DURATIONS = exports.anyCardOnHold = exports.getChargeCardByI
|
|
|
71
71
|
exports.deleteScheduleAccruedDetail = exports.createNewSchedulesAccrued = exports.updateExpenseAutomationActualMatchingUIState = exports.undoReversalAccruals = exports.searchMatchTransactions = exports.searchMatchAccruals = exports.openMatchNewModal = exports.matchAndReverseAccruals = exports.ignoreActualMatchingSuggestion = exports.fetchExpenseAutomationActualMatchingPage = exports.fetchActualMatchingMatch = exports.fetchExpenseAutomationActualMatching = exports.clearMatchNewModal = exports.closeMatchNewModal = exports.clearExpenseAutomationActualMatchingView = exports.ackCloseMatchAfterUndo = exports.ackCloseMatchAfterIgnore = exports.updateJeScheduleTransactionKeys = exports.updateJeScheduleLocalDataById = exports.updateJeOneTimeAccrualLocalData = exports.removeJeOneTimeAccrualLocalData = exports.updateJeNewScheduleLocalData = exports.updateExpenseAutomationJESchedulesUIState = exports.searchJeTransactionsForNewScheduleSuccess = exports.searchJeTransactionsForNewScheduleFailure = exports.searchJeTransactionsForNewSchedule = exports.saveJeNewScheduleSuccess = exports.saveJeNewScheduleFailure = exports.saveJeNewSchedule = exports.retryExpenseAutomationJESchedule = exports.resetJeNewScheduleLocalData = exports.removeJeScheduleTransactionKey = exports.initializeJeScheduleLocalData = exports.initializeJeNewSchedule = exports.initializeJeAccountSettingsView = exports.ignoreExpenseAutomationJESchedule = exports.fetchExpenseAutomationJESchedulesPage = exports.clearJeSearchTransactionsForNewSchedule = exports.clearExpenseAutomationJESchedulesView = exports.clearExpenseAutomationJEScheduleLocalData = exports.updateApAgingDetailUIState = exports.getApAgingDetailForVendor = exports.fetchApAgingDetail = exports.updateApAgingUIState = exports.getApAgingReport = exports.fetchApAging = exports.aggregatedReportView = exports.fetchAggregatedReport = exports.toTimeSeriesDuration = exports.convertToTimeSeriesSelectionRange = void 0;
|
|
72
72
|
exports.getQBOUrlForLink = exports.updatedSelectedJELinkRowIndex = exports.updateAccruedJEScheduleAccruedByListKey = exports.updateScheduleListDownloadState = exports.updateScheduleDetailsLocalData = exports.createNewSchedules = exports.deleteScheduleDetail = exports.saveScheduleDetails = exports.fetchScheduleDetailsPage = exports.getAccruedScheduleDetailsView = exports.getScheduleDetailsView = exports.fetchScheduleDetails = exports.updateSelectedJEScheduleKey = exports.updateScheduleListSortState = exports.updateScheduleListScrollState = exports.updateScheduleListSearchText = exports.updateScheduleListSubTab = exports.toScheduleSubTabType = exports.toScheduleListTabsFileTypeStrict = exports.toScheduleTypesTypeStrict = exports.toScheduleTypesType = exports.getFetchStateForScheduleAccountList = exports.updateScheduleListLocalData = exports.fetchSchedulesAccount = exports.fetchDownloadSchedules = exports.fetchAccruedScheduleList = exports.fetchScheduleList = exports.getAccruedScheduleListReport = exports.getScheduleListReport = exports.ALL_SCHEDULES_TYPES = exports.ALL_ONE_TIME_ACCRUAL_REVERT_REASONS = exports.ALL_ONE_TIME_ACCRUAL_IGNORE_REASONS = exports.ALL_JE_SCHEDULE_REVERT_REASONS = exports.ALL_JE_SCHEDULE_IGNORE_REASONS = exports.getJEScheduleTransactionKey = exports.resetAccruedDetailNewScheduleState = exports.resetSelectedJEAccruedScheduleKey = exports.updateSelectedJEAccruedScheduleKey = exports.updateScheduleAccruedDetailsLocalData = exports.updateLinkBillExpenseLocalData = exports.clearSelectedJELinkRowIndex = exports.fetchRecommendedTransactionRowIndex = exports.updatedJELinkInLocalDataAccruedExpenses = exports.updatedJEAccruedLinkWithRecommendedLocalData = exports.updateAmountsInScheduleAccruedDetail = exports.saveScheduleAccruedDetails = exports.resetJEAccruedLinkInLocalData = exports.fetchScheduleAccruedDetailsPage = exports.fetchScheduleAccruedDetails = exports.cancelScheduleAccruedJournalEntry = void 0;
|
|
73
73
|
exports.getArAgingDetailForCustomer = exports.getArAgingReport = exports.updateArAgingDetailUIState = exports.fetchArAgingDetail = exports.updateArAgingUIState = exports.fetchArAging = exports.updateVendorGlobalReviewViewLocalData = exports.toVendorGlobalReviewColumnSortKeyType = exports.getTenantMerchantByMerchantId = exports.getVendorGlobalReviewView = exports.updateVendorGlobalReviewViewUIState = exports.updateSelectedGlobalMerchant = exports.fetchVendorGlobalReviewView = exports.rejectVendorGlobalReview = exports.approveVendorGlobalReview = exports.getGlobalMerchantView = exports.clearGlobalMerchantView = exports.updateCreateGlobalMerchantLocalData = exports.createGlobalMerchant = exports.fetchGlobalMerchantRecommendation = exports.getVendorDetailSelectorView = exports.saveVendorDetailsView = exports.updateReviewVendorDetailLocalData = exports.clearGlobalMerchantAutoCompleteResults = exports.fetchGlobalMerchantAutoCompleteView = exports.updateVendorFirstReviewSortUiState = exports.updateVendorFirstReviewViewLocalData = exports.saveVendorFirstReviewView = exports.clearRecentlySavedErroredVendorData = exports.updateVendorFirstReviewViewPageToken = exports.resetVendorFirstReviewLocalData = exports.updateVendorFirstReviewViewScrollYOffset = exports.fetchVendorFirstReviewAttachments = exports.fetchVendorFirstReviewView = exports.getVendorFirstReviewAttachmentView = exports.getVendorFirstReviewView = exports.getGlobalMerchantAutoCompleteResults = exports.toVendorFirstReviewViewColumnKeyType = exports.getVendorTabView = exports.updateVendorTabViewTab = exports.fetchVendorTabView = exports.resetMarkAsCompleteStatus = exports.markAsCompleteScheduleDetail = exports.getDefaultSelectedTimeframeForScheduleType = exports.getFetchStateForScheduleListByType = exports.updatedJELinkWithRecommendedLocalData = exports.resetJELinkInLocalData = exports.updateAmountsInScheduleDetail = exports.updatedJELinkInLocalData = exports.getThirdPartyIDFromQBOURL = void 0;
|
|
74
|
-
exports.
|
|
75
|
-
exports.
|
|
76
|
-
exports.
|
|
77
|
-
exports.
|
|
78
|
-
exports.
|
|
79
|
-
exports.
|
|
80
|
-
exports.
|
|
81
|
-
exports.
|
|
82
|
-
exports.
|
|
83
|
-
exports.
|
|
84
|
-
exports.
|
|
85
|
-
exports.
|
|
86
|
-
exports.
|
|
87
|
-
exports.
|
|
88
|
-
exports.isPendingReviewAccrual = exports.isManuallyReversedAccrual = exports.isAutoReversedAccrual = exports.getExpenseAutomationActualMatchingView = exports.getExpenseAutomationActualMatchingMatchView = exports.parseActualMatchingMatchId = exports.getCloseMatchAfterUndoMatchId = exports.getCloseMatchAfterUndo = exports.getCloseMatchAfterIgnoreMatchId = exports.getActualMatchingMatchModalConfirmStatus = exports.getActualMatchingReversalDate = exports.getActualMatchingAmount = exports.isPendingReviewRow = exports.isRenderableOneTimeAccrual = exports.isOneTimeAccrualRow = exports.isExistingTabRow = exports.isAiAccountantSchedule = exports.isAccruedScheduleRow = exports.getJEScheduleRowType = exports.getExpenseAutomationJEOneTimeAccrualDetailView = exports.getInvoicingOnboardingGateView = exports.getInvoicingConfigView = exports.updateInvoicingConfig = exports.resetInvoicingConfigActionStates = exports.fetchInvoicingConfig = exports.enableInvoicing = exports.acceptInvoicingTerms = exports.toInvoicingQboCustomerMatchingMode = exports.toInvoicingNamedOptionsByKey = exports.getInvoicingQboAccountMappingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.getInvoicingQboView = exports.invoicingAccountsForQboAccountTypes = exports.startInvoicingQboBackfill = exports.retryInvoicingQboSyncTask = exports.fetchInvoicingQboSyncHealth = exports.fetchInvoicingQboConnection = exports.fetchInvoicingAccountingPickers = exports.isStripeConnectedFromSettings = exports.initialInvoicingBrandingFormLocalData = exports.entityToInvoicingBrandingFormLocalData = exports.INVOICING_BUSINESS_ADDRESS_TYPE = exports.invoicingQboSettingsLocalDataToPayload = exports.initialInvoicingQboSettingsLocalData = exports.entityToInvoicingQboSettingsLocalData = exports.getInvoicingSettingsView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = void 0;
|
|
74
|
+
exports.unsnoozeTask = exports.snoozeTask = exports.removeTaskFromList = exports.updateTaskListTab = exports.updateTaskFromListView = exports.toDueDateGroupKeyType = exports.toTaskStatusCodeType = exports.toPriorityCodeType = exports.getDueDateValueFromDueDateGroupId = exports.initialTaskDetail = exports.fetchAllTaskGroups = exports.sortSubtasks = exports.getTaskUpdates = exports.toTaskListGroupByKeyTypeStrict = exports.toTaskListGroupByKeyType = exports.updateTaskGroupName = exports.dragNDropTasks = exports.updateTaskListLocalData = exports.deleteTaskGroup = exports.initiateTaskListLocalData = exports.createNewTaskGroup = exports.bulkUpdateTaskList = exports.discardTaskUpdatesInLocalStore = exports.deleteTask = exports.TASK_LIST_GROUP_BY_CATEGORIES = exports.TASK_LIST_FILTER_CATEGORIES = exports.updateTaskFilters = exports.allTaskPriority = exports.allTaskStatus = exports.updateTaskListUIState = exports.updateTaskListSearchText = exports.getCannedResponsesView = exports.deleteCannedResponse = exports.saveCannedResponse = exports.fetchCannedResponses = exports.archiveTask = exports.fetchSubTasks = exports.resetSubTaskCreateStatus = exports.createSubTask = exports.saveTaskDetail = exports.saveTaskUpdatesToLocalStore = exports.fetchTaskDetailPage = exports.getTaskDetail = exports.fetchTaskListPage = exports.fetchTaskList = exports.getAllTasks = exports.getTaskGroupById = exports.toRecurringBillFrequencyStrict = exports.toRecurringBillFrequency = exports.updateArAgingNodeCollapseState = void 0;
|
|
75
|
+
exports.notifyMeForFeature = exports.fetchRegisteredInterests = exports.getNotificationsForSelectedSubTab = exports.getExternalNotificationsForSelectedSubTab = exports.getNotificationView = exports.updateNotificationViewUIState = exports.updateNotificationViewSubTab = exports.updateNotificationViewCurrentTabAndSubTab = exports.updateNotificationViewTabState = exports.updateNotificationViewNotificationStatus = exports.updateNotificationViewAllNotificationsStatus = exports.fetchNotificationUnreadCountSuccess = exports.fetchNotificationUnreadCount = exports.fetchNotificationView = exports.toNotificationTabTypeStrict = exports.toNotificationSubTabTypeStrict = exports.updateCommentsNotificationsStatuses = exports.updateCommentsNotifications = exports.toNotificationModeStrict = exports.parseOAuthParams = exports.getZeniOAuthApproveRedirectUrl = exports.getZeniOAuthApproveFetchState = exports.getZeniOAuthApproveError = exports.clearZeniOAuthView = exports.approveOAuthConsentSuccess = exports.approveOAuthConsentFailure = exports.approveOAuthConsent = exports.getAuthenticationView = exports.fetchCollaborationAuthToken = exports.clearAuditReportGroupViewByCompanyId = exports.saveReasonForAuditRule = exports.fetchAuditReportGroupView = exports.fetchAuditRuleGroupView = exports.getUserFromAllUsers = exports.getAuditRuleGroupViewSelectorView = exports.getAuditReportGroupViewSelectorView = exports.clearCardPaymentView = exports.resetCardPaymentErrorStatuses = exports.fetchPaymentSources = exports.addCardPaymentSource = exports.confirmCardSetupIntent = exports.createCardSetupIntent = exports.getAllCardsAndBankPaymentMethods = exports.deleteTag = exports.createTag = exports.fetchTagList = exports.getAllTags = exports.ALL_TASK_LIST_TABS = exports.initialTaskDetailLocalData = exports.convertHHMMStrToMinutes = void 0;
|
|
76
|
+
exports.getIntlWireVerificationView = exports.updateVerificationFormLocalData = exports.submitInternationalVerificationForm = exports.fetchInternationalVerificationForm = exports.createTaskFromTaskGroupTemplate = exports.getCompanyTaskManagerView = exports.fetchTaskManagerMetrics = exports.fetchCompanyTaskManagerView = exports.fetchCockpitContext = exports.toRecurringFrequency = exports.toDayOfWeek = exports.getRecurringEndDateFromCount = exports.getMinAllowedEndDate = exports.SEMI_WEEKLY_REQUIRED_DAYS_COUNT = exports.ALL_WEEK_DAYS = exports.updateReferViewed = exports.getRewardsPlanCard = exports.fetchRewardsPlan = exports.resendReferralInvite = exports.updateReferralListSortUiState = exports.saveReferralFormDataInLocalStore = exports.clearReferrals = exports.sendReferralInvite = exports.fetchReferrals = exports.DEFAULT_REFERRER_AMOUNT = exports.AmountStatusTypes = exports.StatusTypes = exports.toReferralListViewSortKeyType = exports.getInviteFormView = exports.getReferralListView = exports.toNotificationFrequency = exports.toNotificationChannel = exports.toggleEventChannel = exports.setGroupFrequency = exports.setGroupChannelMaster = exports.saveNotificationPreferences = exports.clearNotificationPreferencesLocalOverrides = exports.clearAllNotificationPreferencesView = exports.hasUnsavedNotificationPreferences = exports.getNotificationRegistry = exports.getNotificationPreferencesSaveState = exports.getNotificationLocalOverrides = exports.getEffectiveNotificationPreferences = exports.getNotifications = exports.getLastNotificationTime = exports.pushToastNotification = exports.isFeatureInterestRegistered = exports.getRegisteredInterestsByFeature = exports.getRegisteredInterests = exports.getFeatureNotificationView = void 0;
|
|
77
|
+
exports.fetchChatSessionsForUser = exports.fetchChatHistory = exports.deleteSkill = exports.deleteRoutine = exports.deleteChatSession = exports.createSessionAndSubmit = exports.createSession = exports.closeSkillsBrowser = exports.closeRoutinesBrowser = exports.closeRoutineRuns = exports.clearSkillMutation = exports.clearSkillDetail = exports.clearLastContextMessage = exports.clearDeleteChatSessionStatus = exports.clearCurrentSessionId = exports.clearAiCfoSidePanelHostPageContext = exports.applyAiCfoSidePanelHostPageTransition = exports.acceptMasterTOS = exports.getAiAccountantCockpitView = exports.updateAiAccountantUIState = exports.triggerAiAccountantJob = exports.setSelectedTenantIdsForJobTrigger = exports.fetchAiAccountantJobs = exports.fetchAiAccountantCustomers = exports.cancelAiAccountantOnboarding = exports.getAiAccountantJobsByTenantId = exports.getAiAccountantCustomers = exports.updateAiAccountantJobs = exports.updateAiAccountantCustomer = exports.updateAiAccountantCustomers = exports.toAiAccountantJob = exports.toAiAccountantEnrollment = exports.toAiAccountantCustomer = exports.toAiAccountantOperationType = exports.toAiAccountantJobStatus = exports.toAiAccountantEnrollmentStatus = exports.getAllowedOperationsForStatus = exports.getTreasuryFundsMaximumYield = exports.getTreasurySetupViewDetails = exports.updateFundAllocationLocalData = exports.fetchPortfolioAllocation = exports.updatePortfolioAllocation = exports.fetchTreasuryFunds = exports.fetchTreasurySetupView = exports.acceptTreasuryTerms = exports.getExpressPayView = exports.resetExpressPayLocalData = exports.submitExpressPay = exports.updateExpressPayFormLocalData = exports.fetchExpressPayInitialDetails = void 0;
|
|
78
|
+
exports.appendSyntheticAiCfoAnswer = exports.toAiCfoVisualization = exports.toAiCfoExports = exports.clearSession = exports.addQuestionPayload = exports.upsertOrAddQuestionAnswerPayload = exports.upsertAnswerPayload = exports.updateAiCfoAnswerCardPolicyWizardPlan = exports.setSessions = exports.setNewSession = exports.getSuggestedQuestionsForPageContext = exports.getSkillVersions = exports.getSkillVersionBody = exports.getSkillsEnabled = exports.getSkills = exports.getSkillMutation = exports.getSkillDetail = exports.getRoutinesEnabled = exports.getRoutines = exports.getRoutineRuns = exports.getRoutineMutation = exports.getAiCfoView = exports.canCreateRoutine = exports.updateResponseState = exports.updateCurrentInput = exports.updateCotCollapsedState = exports.updateAiCfoViewScrollPosition = exports.submitQuestion = exports.submitFeedback = exports.stopSubmitQuestion = exports.stopSubmit = exports.setSkillVisibility = exports.setSession = exports.setRoutineEnabled = exports.saveSkill = exports.saveRoutine = exports.runRoutine = exports.openSkillsBrowser = exports.openRoutinesBrowser = exports.fetchSuggestedQuestionsSuccess = exports.fetchSuggestedQuestionsFailure = exports.fetchSuggestedQuestions = exports.fetchSkillVersions = exports.fetchSkillVersionBody = exports.fetchSkillsSuccess = exports.fetchSkillsFailure = exports.fetchSkills = exports.fetchSkillDetail = exports.fetchRoutines = exports.fetchRoutineRuns = void 0;
|
|
79
|
+
exports.getCardPolicySuggestedAllowMerchants = exports.getCardPolicySuggestedAllowCategories = exports.getCardPolicyStats = exports.getCardPolicyMccCategoriesFetchState = exports.getCardPolicyMccCategoriesError = exports.getCardPolicyMccCategories = exports.getAllCardPolicyTemplates = exports.updatePolicyRecommendationFromUploadSuccess = exports.updatePolicyRecommendationFromUploadFailure = exports.updatePolicyDocumentExtractionSuccess = exports.updatePolicyDocumentExtractionFailure = exports.updateCreatedCardPolicyTemplate = exports.updateCardPolicyVendorOptionsFailure = exports.updateCardPolicyVendorOptions = exports.updateCardPolicyTemplates = exports.updateCardPolicyStats = exports.updateCardPolicyMccCategoriesFailure = exports.updateCardPolicyMccCategories = exports.removeCardPolicyTemplate = exports.fetchCardPolicyVendorOptions = exports.fetchCardPolicyRecommendationFromUpload = exports.fetchCardPolicyMccCategories = exports.extractPolicyDocument = exports.clearPolicyDocumentExtraction = exports.toMessageType = exports.toMessageSender = exports.ALL_SYNTHETIC_AI_CFO_ANSWER_KINDS = exports.toInteractiveFormTypeStrict = exports.toInteractiveFormType = exports.toAiCfoAnswerResponseTypeStrict = exports.toAiCfoAnswerResponseType = exports.toAiCfoAnswerStateTypeStrict = exports.toAiCfoAnswerStateType = exports.toAiCfoChartTypeStrict = exports.toAiCfoChartType = exports.toAiCfoVisualizationTypeStrict = exports.toAiCfoVisualizationType = exports.ALL_INTERACTIVE_FORM_TYPES = exports.ALL_AI_CFO_ANSWER_RESPONSE_TYPES = exports.ALL_AI_CFO_ANSWER_STATE_TYPES = exports.ALL_AI_CFO_VISUALIZATION_TYPES = exports.ALL_AI_CFO_CHARTS_TYPES = exports.getSyntheticAiCfoAnswerByQuestionAnswerId = exports.getSyntheticAiCfoAnswersForChatSession = exports.getAiCfoSelectorView = exports.getAllQuestionsForChatSession = exports.getQuestionAnswerByIdForChatSession = exports.getAllQuestionAnswersForChatSession = exports.removeSyntheticAiCfoAnswer = exports.updateSyntheticAiCfoAnswer = void 0;
|
|
80
|
+
exports.CARD_POLICY_LIMIT_ROW_ID_TRANSACTION = exports.CARD_POLICY_LIMIT_ROW_ID_REQUIRE_RECEIPT = exports.getManualCardPolicyFormDraft = exports.getLastCreatedCardPolicyTemplateIds = exports.getLastCreatedCardPolicyTemplateId = exports.getLastCreateCardPolicyTemplateErrors = exports.getLastCreateCardPolicySourceChatSessionId = exports.getCreateCardPolicyTemplateRequestState = exports.getAiCardPolicyFormDraft = exports.updateManualCardPolicyFormDraft = exports.updateCreateCardPolicyTemplateRequestState = exports.updateAiCardPolicyFormDraftFromUploadPlan = exports.updateAiCardPolicyFormDraft = exports.seedManualCardPolicyFormDraft = exports.seedAiCardPolicyFormDraft = exports.createCardPolicyTemplates = exports.clearManualCardPolicyFormDraft = exports.clearCreateCardPolicy = exports.clearAiCardPolicyFormDraft = exports.applyExtractedPolicyToManualCardPolicyDraft = exports.applyExtractedPolicyToAiCardPolicyDraft = exports.toUpdateCardPolicyTemplateRequestBody = exports.toExtractedCardPolicyRules = exports.toCreateCardPolicyTemplatesRequestBody = exports.toCreateCardPolicyTemplateRequestBody = exports.toCardPolicyVendorSearchOption = exports.toCardPolicyTemplateList = exports.toCardPolicyTemplate = exports.toCardPolicyStats = exports.toCardPolicyEditFormDraft = exports.toBulkCreateCardPolicyTemplateError = exports.toCardPolicyTemplateStatus = exports.toCardPolicyTemplateMode = exports.ALL_CARD_POLICY_TEMPLATE_STATUSES = exports.ALL_CARD_POLICY_TEMPLATE_MODES = exports.getUploadedPolicyDocumentFileName = exports.getPolicyRecommendationFromUploadFetchState = exports.getPolicyRecommendationFromUploadError = exports.getPolicyRecommendationFromUploadChatSessionId = exports.getPolicyRecommendationFromUploadAnswerId = exports.getPolicyDocumentExtractionFetchState = exports.getPolicyDocumentExtractionError = exports.getExtractedCardPolicyRules = exports.getCardPolicyVendorSearchString = exports.getCardPolicyVendorSearchOptions = exports.getCardPolicyVendorSearchFetchState = exports.getCardPolicyTemplatesByIds = exports.getCardPolicyTemplateById = exports.getCardPolicySuggestedBlockMerchants = exports.getCardPolicySuggestedBlockCategories = void 0;
|
|
81
|
+
exports.deleteAutoTransferRule = exports.updateAutoTransferRule = exports.createAutoTransferRule = exports.fetchAutoTransferRules = exports.getTreasuryTaxLetters = exports.fetchTreasuryTaxLetterList = exports.getTreasuryStatements = exports.fetchTreasuryStatementList = exports.getTreasuryTransferMoney = exports.clearTreasuryTransferMoney = exports.updateTreasuryTransferMoneyLocalData = exports.executeTreasuryTransferMoney = exports.getTreasuryDetail = exports.updateTreasuryTransactionListUIState = exports.fetchTreasuryTransactionList = exports.fetchTreasuryOverviewDetail = exports.getUpdateCardPolicyFetchState = exports.getCardPolicyFormDraft = exports.getCardPolicyDetailView = exports.getCardPolicyDetailFetchState = exports.updateCardPolicyFormDraft = exports.updateCardPolicyFetchStatus = exports.updateCardPolicyDetailFetchStatus = exports.updateCardPolicy = exports.fetchCardPolicyDetail = exports.getCardPolicyTemplateIds = exports.getCardPolicyListView = exports.getCardPolicyListFetchState = exports.getArchiveCardPolicyFetchState = exports.updateCardPolicyListFetchStatus = exports.updateArchiveCardPolicyFetchStatus = exports.prependCardPolicyTemplateIds = exports.fetchCardPolicyList = exports.archiveCardPolicy = exports.toManualCardPolicyTemplateRequestFromDraft = exports.toBulkCardPolicyTemplateRequestsFromDraft = exports.toCardPolicyTemplateRequestFromDraft = exports.applyAiCardPolicyFormDraftUpdate = exports.deriveAiPolicyReviewRowsFromInputs = exports.buildManualCardPolicyFormDraftSeed = exports.buildUploadReviewRows = exports.buildEmptyLimitRows = exports.buildAiCardPolicyFormDraftSeed = exports.toVendorChipFieldValue = exports.toMccCategoryChipFieldValue = exports.buildVendorChipId = exports.buildMccCategoryChipId = exports.VENDOR_CHIP_ID_PREFIX = exports.MCC_CHIP_ID_PREFIX = exports.toMccCategoryLike = void 0;
|
|
82
|
+
exports.fetchCreateInvoiceFormPage = exports.createInvoice = exports.getInvoiceDetail = exports.fetchInvoiceDetailPage = exports.fetchInvoiceDetail = exports.getInvoiceListView = exports.ALL_INVOICING_INVOICE_TAB_IDS = exports.updateInvoiceListFilters = exports.setInvoiceActiveTab = exports.fetchInvoiceListPage = exports.fetchInvoiceList = exports.fetchInvoiceKPIs = exports.fetchInvoiceCounts = exports.getInvoicesByIds = exports.getInvoiceById = exports.updateInvoices = exports.removeInvoice = exports.mapAuditLogEntry = exports.toAuditLogEntityRouteOption = exports.isInvoicingEntityLinkable = exports.buildInvoicingEntityPath = exports.toInvoicingDiscountLifecycleActionOption = exports.toInvoicingDiscountLifecycleAction = exports.toInvoicingCatalogProductLifecycleActionOption = exports.toInvoicingCatalogItemLifecycleActionOption = exports.toInvoicingCatalogProductLifecycleAction = exports.toInvoicingCatalogItemLifecycleAction = exports.toInvoicingSubscriptionLifecycleActionOption = exports.toInvoicingSubscriptionLifecycleAction = exports.toInvoicingDunningAction = exports.INVOICING_DISCOUNT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_PRODUCT_LIFECYCLE_ACTIONS = exports.INVOICING_CATALOG_ITEM_LIFECYCLE_ACTIONS = exports.INVOICING_SUBSCRIPTION_LIFECYCLE_ACTIONS = exports.INVOICING_DUNNING_ACTIONS = exports.toKycProvidedDocumentTypeFromAllowed = exports.toKycProvidedDocumentType = exports.DEFAULT_SESSION_CONFIG = exports.SessionManager = exports.getTransactionActivityLogView = exports.fetchTransactionActivityLog = exports.BULK_UPLOAD_BAR_COMPLETE_HOLD_MS = exports.BULK_UPLOAD_AUTOMATCHING_TIMEOUT_MS = exports.getAutoTransferRuleHistory = exports.getAutoTransferRuleById = exports.getAutoTransferRules = exports.clearRuleUpdateLocalData = exports.updateRuleLocalData = exports.fetchAutoTransferReviewDetail = exports.fetchAutoTransferRuleHistory = void 0;
|
|
83
|
+
exports.INVOICING_CUSTOMER_BILLING_ADDRESS_TYPE = exports.updateInvoicingCustomerFormDraft = exports.submitInvoicingCustomerFormSuccess = exports.submitInvoicingCustomerFormFailure = exports.submitInvoicingCustomerForm = exports.resetEditInvoicingCustomerDetailView = exports.initializeInvoicingCustomerAddress = exports.getRecordPaymentView = exports.getRecordPaymentInvoiceOptions = exports.getRecordPaymentFormView = exports.getCreateInvoiceFormView = exports.getInvoicingAuditView = exports.fetchInvoicingAuditLog = exports.getInvoicingOverview = exports.buildPeriodOptions = exports.fetchInvoicingOverview = exports.clearInvoicingOverview = exports.initialRecordPaymentFormLocalData = exports.localDataToRecordPaymentPayload = exports.localDataToInvoiceRecordPaymentBody = exports.initializeRecordPaymentDraftFromInvoice = exports.getCreateCreditNoteView = exports.getCreateCreditNoteFormView = exports.initialCreateCreditNoteFormLocalData = exports.localDataToCreateCreditNotePayload = exports.updateCreateCreditNoteFormDraft = exports.submitCreateCreditNote = exports.resetCreateCreditNote = exports.createCreditNoteSuccess = exports.createCreditNoteFailure = exports.getIssueCreditNoteView = exports.initialIssueCreditNoteFormLocalData = exports.localDataToIssueCreditNotePayload = exports.initializeIssueCreditNoteDraftFromInvoice = exports.computeIssueCreditNoteTotal = exports.updateIssueCreditNoteFormDraft = exports.submitIssueCreditNote = exports.resetIssueCreditNote = exports.issueCreditNoteSuccess = exports.issueCreditNoteFailure = exports.initializeIssueCreditNoteDraft = exports.updateRecordPaymentFormDraft = exports.submitRecordPayment = exports.initializeRecordPaymentDraft = exports.resetRecordPayment = exports.initialInvoicingCreateInvoiceFormLocalData = exports.createInvoiceFormLocalDataToRequest = exports.blankInvoicingCreateInvoiceLineItem = exports.updateInvoicingCreateInvoiceFormDraft = exports.resetCreateInvoice = void 0;
|
|
84
|
+
exports.runInvoicingCatalogPlanAction = exports.fetchInvoicingCatalogItemDetailPage = exports.shouldDefaultAdjustmentCreditNote = exports.isBackdatedInvoiceDate = exports.buildVoidInvoiceSubmitBody = exports.updateInvoicingInvoice = exports.runInvoicingInvoiceAction = exports.getInvoicingCouponFormView = exports.getEditInvoicingCouponDetailViewState = exports.initialInvoicingCouponFormLocalData = exports.entityToInvoicingCouponFormLocalData = exports.updateInvoicingCouponFormDraft = exports.submitInvoicingCouponForm = exports.saveInvoicingCoupon = exports.resetEditInvoicingCouponDetailView = exports.fetchInvoicingCouponDetailPage = exports.getSavedInvoicingCatalogItemKind = exports.getSavedInvoicingCatalogItemId = exports.getInvoicingCatalogItemFormView = exports.getInvoicingApplicableCatalogItemOptions = exports.isTieredCatalogPricingModel = exports.initialInvoicingCatalogItemFormLocalData = exports.invoicingProductToCatalogItemFormSubFamily = exports.entityToInvoicingCatalogItemFormLocalData = exports.blankInvoicingCatalogTier = exports.blankInvoicingCatalogSubFamily = exports.INVOICING_CATALOG_TIER_TYPE_VALUES = exports.INVOICING_CATALOG_PRORATION_VALUES = exports.INVOICING_CATALOG_PRICING_MODEL_VALUES = exports.INVOICING_CATALOG_APPLICABILITY_VALUES = exports.updateInvoicingCatalogItemFormDraft = exports.submitInvoicingCatalogItemForm = exports.resetEditInvoicingCatalogItemDetailView = exports.getInvoicingSubscriptionFormView = exports.getEditInvoicingSubscriptionDetailViewState = exports.toInvoicingSubscriptionQuantity = exports.invoicingSubscriptionTodayIso = exports.initialInvoicingSubscriptionFormLocalData = exports.entityToInvoicingSubscriptionFormLocalData = exports.INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE = exports.updateInvoicingSubscriptionFormDraft = exports.submitInvoicingSubscriptionForm = exports.resetEditInvoicingSubscriptionDetailView = exports.initializeInvoicingSubscriptionAddress = exports.fetchInvoicingSubscriptionFormPage = exports.fetchInvoicingSubscriptionEstimate = exports.clearInvoicingSubscriptionEstimate = exports.getInvoicingCustomerFormView = exports.initialInvoicingCustomerFormLocalData = exports.entityToInvoicingCustomerFormLocalData = void 0;
|
|
85
|
+
exports.fetchInvoicingSubscriptionListPage = exports.fetchInvoicingSubscriptionList = exports.fetchInvoicingSubscriptionCounts = exports.updateInvoicingSubscriptions = exports.removeInvoicingSubscription = exports.resetPromotionalCreditAction = exports.submitAddPromotionalCredits = exports.getInvoicingSetupIntentFetchState = exports.getInvoicingSetupIntent = exports.getInvoicingSentPaymentLinkMagicUrl = exports.getInvoicingSentPaymentLinkEmail = exports.getInvoicingPlaidLinkToken = exports.getInvoicingPaymentMethodSaveState = exports.getInvoicingPaymentLinkSendState = exports.sendInvoicingPaymentLink = exports.saveInvoicingPaymentMethod = exports.resetInvoicingCustomerPaymentMethod = exports.fetchInvoicingPlaidLinkToken = exports.createInvoicingSetupIntent = exports.fetchInvoicingPaymentLinkEmailPreviewPage = exports.fetchInvoicingCustomerDetailPage = exports.fetchInvoicingCustomerDetail = exports.getInvoicingCustomerListView = exports.INVOICING_CUSTOMER_COLUMNS = exports.ALL_INVOICING_CUSTOMER_TAB_IDS = exports.ALL_INVOICING_CUSTOMER_SORT_KEYS = exports.updateInvoicingCustomerFilters = exports.setInvoicingCustomerActiveTab = exports.fetchInvoicingCustomerListPage = exports.fetchInvoicingCustomerList = exports.fetchInvoicingCustomerCounts = exports.updateInvoicingCustomers = exports.removeInvoicingCustomer = exports.INVOICING_LIST_PAGE_SIZE = exports.uploadInvoicingMigrationFiles = exports.startInvoicingMigration = exports.rollbackInvoicingMigration = exports.retryInvoicingMigration = exports.connectInvoicingChargebee = exports.getInvoicingDataImportView = exports.setInvoicingActiveMigrationSessionId = exports.fetchInvoicingMigrationSessions = exports.fetchInvoicingMigrationSession = exports.fetchInvoicingMigrationDiagnostics = exports.fetchInvoicingDataImportStatus = exports.runInvoicingPaymentAction = exports.runInvoicingDunningAction = exports.runInvoicingSubscriptionAction = exports.runInvoicingDiscountAction = exports.runInvoicingCatalogProductAction = void 0;
|
|
86
|
+
exports.getInvoicingCouponListHasMore = exports.getInvoicingCouponListFilters = exports.getInvoicingCouponListFetchState = exports.getInvoicingCouponCounts = exports.INVOICING_COUPON_COLUMNS = exports.ALL_INVOICING_COUPON_TAB_IDS = exports.ALL_INVOICING_COUPON_SORT_KEYS = exports.updateInvoicingCouponFilters = exports.setInvoicingCouponActiveTab = exports.fetchInvoicingCouponListPage = exports.fetchInvoicingCouponList = exports.fetchInvoicingCouponCounts = exports.updateInvoicingCoupons = exports.removeInvoicingCoupon = exports.getInvoicingCreditNoteDetail = exports.getInvoicingCreditNoteById = exports.fetchInvoicingCreditNoteDetail = exports.getInvoicingCreditNoteListView = exports.INVOICING_CREDIT_NOTE_COLUMNS = exports.ALL_INVOICING_CREDIT_NOTE_TAB_IDS = exports.ALL_INVOICING_CREDIT_NOTE_SORT_KEYS = exports.updateInvoicingCreditNoteFilters = exports.setInvoicingCreditNoteActiveTab = exports.fetchInvoicingCreditNoteListPage = exports.fetchInvoicingCreditNoteList = exports.fetchInvoicingCreditNoteCounts = exports.updateInvoicingCreditNotes = exports.removeInvoicingCreditNote = exports.getInvoicingTransactionDetail = exports.getInvoicingTransactionById = exports.fetchInvoicingTransactionDetail = exports.getInvoicingTransactionListView = exports.INVOICING_TRANSACTION_COLUMNS = exports.ALL_INVOICING_TRANSACTION_TAB_IDS = exports.ALL_INVOICING_TRANSACTION_SORT_KEYS = exports.updateInvoicingTransactionFilters = exports.setInvoicingTransactionActiveTab = exports.fetchInvoicingTransactionListPage = exports.fetchInvoicingTransactionList = exports.updateInvoicingTransactions = exports.removeInvoicingTransaction = exports.getInvoicingSubscriptionById = exports.fetchInvoicingSubscriptionDetailPage = exports.fetchInvoicingSubscriptionDetail = exports.getInvoicingSubscriptionListView = exports.INVOICING_SUBSCRIPTION_COLUMNS = exports.ALL_INVOICING_SUBSCRIPTION_TAB_IDS = exports.ALL_INVOICING_SUBSCRIPTION_SORT_KEYS = exports.updateInvoicingSubscriptionFilters = exports.setInvoicingSubscriptionActiveTab = void 0;
|
|
87
|
+
exports.getInvoicingBrandingFormView = exports.updateInvoicingSettings = exports.updateInvoicingQboSettingsDraft = exports.updateInvoicingBrandingFormDraft = exports.submitInvoicingQboSettings = exports.submitInvoicingBrandingForm = exports.saveInvoicingSettings = exports.resetSaveInvoicingSettings = exports.resetInvoicingQboSettingsDraft = exports.resetInvoicingBrandingFormDraft = exports.fetchInvoicingSettings = exports.disconnectInvoicingStripe = exports.connectInvoicingStripe = exports.getLowestInvoicingCatalogPricePointAmount = exports.getInvoicingCatalogItemById = exports.fetchInvoicingCatalogItemDetail = exports.getInvoicingCatalogListView = exports.updateInvoicingCatalogProductFilters = exports.updateInvoicingCatalogPlanFilters = exports.fetchInvoicingCatalogProductList = exports.fetchInvoicingCatalogPlanList = exports.fetchInvoicingCatalogListPage = exports.fetchInvoicingCatalogCounts = exports.updateInvoicingCatalogItems = exports.removeInvoicingCatalogItem = exports.mergeInvoicingCatalogPricePoint = exports.getInvoicingDunningEmailPreview = exports.getInvoicingDunningCaseDetail = exports.getInvoicingDunningCaseById = exports.invalidateInvoicingDunningEmailPreview = exports.fetchInvoicingDunningEmailPreviewPage = exports.fetchInvoicingDunningCaseDetailPage = exports.fetchInvoicingDunningCaseDetail = exports.clearInvoicingDunningCaseDetailView = exports.getInvoicingDunningCaseListView = exports.INVOICING_DUNNING_CASE_COLUMNS = exports.ALL_INVOICING_DUNNING_CASE_TAB_IDS = exports.ALL_INVOICING_DUNNING_CASE_SORT_KEYS = exports.updateInvoicingDunningCaseFilters = exports.setInvoicingDunningCaseActiveTab = exports.fetchInvoicingDunningCaseListPage = exports.fetchInvoicingDunningCaseList = exports.fetchInvoicingDunningCaseCounts = exports.updateInvoicingDunningCases = exports.removeInvoicingDunningCase = exports.fetchInvoicingCouponDetail = exports.getInvoicingCouponById = exports.getInvoicingCouponListView = exports.getInvoicingCouponListNextCursor = exports.getInvoicingCouponListItems = void 0;
|
|
88
|
+
exports.isPendingReviewAccrual = exports.isManuallyReversedAccrual = exports.isAutoReversedAccrual = exports.getExpenseAutomationActualMatchingView = exports.getExpenseAutomationActualMatchingMatchView = exports.parseActualMatchingMatchId = exports.getCloseMatchAfterUndoMatchId = exports.getCloseMatchAfterUndo = exports.getCloseMatchAfterIgnoreMatchId = exports.getActualMatchingMatchModalConfirmStatus = exports.getActualMatchingReversalDate = exports.getActualMatchingAmount = exports.isPendingReviewRow = exports.isRenderableOneTimeAccrual = exports.isOneTimeAccrualRow = exports.isExistingTabRow = exports.isAiAccountantSchedule = exports.isAccruedScheduleRow = exports.getJEScheduleRowType = exports.getExpenseAutomationJEOneTimeAccrualDetailView = exports.getInvoicingOnboardingGateView = exports.getInvoicingConfigView = exports.updateInvoicingConfig = exports.resetInvoicingConfigActionStates = exports.fetchInvoicingConfig = exports.enableInvoicing = exports.acceptInvoicingTerms = exports.toInvoicingQboCustomerMatchingMode = exports.toInvoicingNamedOptionsByKey = exports.getInvoicingQboAccountMappingId = exports.INVOICING_QBO_CUSTOMER_MATCHING_MODES = exports.getInvoicingQboView = exports.invoicingAccountsForQboAccountTypes = exports.startInvoicingQboBackfill = exports.retryInvoicingQboSyncTask = exports.fetchInvoicingQboSyncHealth = exports.fetchInvoicingQboConnection = exports.fetchInvoicingAccountingPickers = exports.isStripeConnectedFromSettings = exports.initialInvoicingBrandingFormLocalData = exports.entityToInvoicingBrandingFormLocalData = exports.INVOICING_BUSINESS_ADDRESS_TYPE = exports.invoicingQboSettingsLocalDataToPayload = exports.initialInvoicingQboSettingsLocalData = exports.entityToInvoicingQboSettingsLocalData = exports.getInvoicingSettingsView = exports.getInvoicingSettingsHubView = exports.getInvoicingSettingsFetchState = exports.getInvoicingSettings = exports.getInvoicingQboSettingsFormView = void 0;
|
|
89
89
|
const allowedValue_1 = require("./commonStateTypes/allowedValue");
|
|
90
90
|
Object.defineProperty(exports, "isAllowedValueWithCode", { enumerable: true, get: function () { return allowedValue_1.isAllowedValueWithCode; } });
|
|
91
91
|
Object.defineProperty(exports, "isAllowedValueWithID", { enumerable: true, get: function () { return allowedValue_1.isAllowedValueWithID; } });
|
|
@@ -2181,6 +2181,7 @@ Object.defineProperty(exports, "toTaskListGroupByKeyTypeStrict", { enumerable: t
|
|
|
2181
2181
|
const taskListReducer_1 = require("./view/taskManager/taskListView/taskListReducer");
|
|
2182
2182
|
Object.defineProperty(exports, "bulkUpdateTaskList", { enumerable: true, get: function () { return taskListReducer_1.bulkUpdateTaskList; } });
|
|
2183
2183
|
Object.defineProperty(exports, "dragNDropTasks", { enumerable: true, get: function () { return taskListReducer_1.dragNDropTasks; } });
|
|
2184
|
+
Object.defineProperty(exports, "fetchTaskList", { enumerable: true, get: function () { return taskListReducer_1.fetchTaskList; } });
|
|
2184
2185
|
Object.defineProperty(exports, "fetchTaskListPage", { enumerable: true, get: function () { return taskListReducer_1.fetchTaskListPage; } });
|
|
2185
2186
|
Object.defineProperty(exports, "initiateTaskListLocalData", { enumerable: true, get: function () { return taskListReducer_1.initiateTaskListLocalData; } });
|
|
2186
2187
|
Object.defineProperty(exports, "removeTaskFromList", { enumerable: true, get: function () { return taskListReducer_1.removeTaskFromList; } });
|
|
@@ -6,4 +6,4 @@ import { RootState } from '../../../rootStateTypes';
|
|
|
6
6
|
import { ZeniAPI } from '../../../zeniAPI';
|
|
7
7
|
import { fetchCompanyTaskManagerView, updateCompanyTaskManagerViewOnFailure, updateCompanyTaskManagerViewOnSuccess } from '../companyTaskManagerViewReducer';
|
|
8
8
|
export type ActionType = ReturnType<typeof fetchCompanyTaskManagerView> | ReturnType<typeof updateCompanyTaskManagerViewOnSuccess> | ReturnType<typeof updateTasks> | ReturnType<typeof updateTags> | ReturnType<typeof updateCompanyTaskManagerViewOnFailure>;
|
|
9
|
-
export declare const fetchCompanyTaskManagerViewEpic: (actions$: ActionsObservable<ActionType>,
|
|
9
|
+
export declare const fetchCompanyTaskManagerViewEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
|
@@ -7,19 +7,41 @@ const sortOrderPayload_1 = require("../../../commonPayloadTypes/sortOrderPayload
|
|
|
7
7
|
const tagReducer_1 = require("../../../entity/tag/tagReducer");
|
|
8
8
|
const taskReducer_1 = require("../../../entity/task/taskReducer");
|
|
9
9
|
const responsePayload_1 = require("../../../responsePayload");
|
|
10
|
+
const zeniDayJS_1 = require("../../../zeniDayJS");
|
|
10
11
|
const companyTaskManagerViewReducer_1 = require("../companyTaskManagerViewReducer");
|
|
11
12
|
const DEFAULT_PAGE_SIZE = 100;
|
|
12
|
-
|
|
13
|
+
// Mutates the accumulator rather than spreading it: spreading rebuilds the whole
|
|
14
|
+
// map on every entry, which is O(n^2) over a tenant's tag list.
|
|
15
|
+
const toTagNameByID = (state) => Object.entries(state.tagState.tagByID).reduce((result, [tagId, tag]) => {
|
|
16
|
+
const name = tag?.name;
|
|
17
|
+
if (name != null && name !== '') {
|
|
18
|
+
result[tagId] = name;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}, {});
|
|
22
|
+
const hasTagCategory = (filters) => (filters?.categories ?? []).some((category) => category.field === 'tag');
|
|
23
|
+
const fetchCompanyTaskManagerViewEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(companyTaskManagerViewReducer_1.fetchCompanyTaskManagerView.match), (0, operators_1.switchMap)((action) => {
|
|
13
24
|
const { from: fromOffset, searchString, sortKey, sortOrder, filters, } = action.payload;
|
|
14
25
|
const queryValue = {
|
|
15
26
|
size: DEFAULT_PAGE_SIZE,
|
|
16
27
|
from: fromOffset,
|
|
17
28
|
global_search: searchString ?? '',
|
|
29
|
+
// TasksEsSearch defaults this to true, which AND-injects an
|
|
30
|
+
// `assignees = <caller>` filter. Cockpit's task list is a cross-tenant
|
|
31
|
+
// view grouped by company (and offers an Assignee filter), so it must
|
|
32
|
+
// NOT be scoped to the operator — otherwise every category filter
|
|
33
|
+
// AND's down to their own tasks and returns nothing. The metrics panel
|
|
34
|
+
// deliberately passes `true`; this list deliberately passes `false`.
|
|
35
|
+
is_filter_by_logged_in_user: false,
|
|
18
36
|
sort_by: toSortKeyPayload(sortKey),
|
|
19
37
|
sort_order: (0, sortOrderPayload_1.getSortOrder)(sortOrder),
|
|
20
38
|
filter_join: filters?.categoryCombinationOperator === 'AND' ? 'and' : 'or',
|
|
21
39
|
...(filters?.categories != null && filters.categories.length > 0
|
|
22
|
-
? {
|
|
40
|
+
? {
|
|
41
|
+
// Only walk the tag map when a tag filter is actually in play;
|
|
42
|
+
// otherwise every dispatch pays for a map nothing reads.
|
|
43
|
+
filter_by: toFilterByPayload(filters, hasTagCategory(filters) ? toTagNameByID(state$.value) : {}),
|
|
44
|
+
}
|
|
23
45
|
: {}),
|
|
24
46
|
task_group_scopes: filters?.taskGroupScopes != null && filters.taskGroupScopes.length > 0
|
|
25
47
|
? filters.taskGroupScopes
|
|
@@ -67,14 +89,123 @@ const toSortKeyPayload = (sortKey) => {
|
|
|
67
89
|
return sortKey;
|
|
68
90
|
}
|
|
69
91
|
};
|
|
70
|
-
|
|
92
|
+
// Cockpit filter-category keys are UI identifiers; the ES index uses its own
|
|
93
|
+
// field names (see TasksEsSearch.get). Anything not listed here already matches
|
|
94
|
+
// the contract (assignees, priority, status, company_id).
|
|
95
|
+
const ES_FIELD_BY_CATEGORY = {
|
|
96
|
+
creationDate: 'create_time',
|
|
97
|
+
dueDate: 'due_date',
|
|
98
|
+
tag: 'tags',
|
|
99
|
+
};
|
|
100
|
+
// ES rejects `is` / `is_not` on date fields — they only accept `between`, whose
|
|
101
|
+
// values are the [start, end] pair the dateRange picker already emits.
|
|
102
|
+
const ES_DATE_FIELDS = new Set(['create_time', 'due_date']);
|
|
103
|
+
// The cockpit date picker hands over ZeniDate objects; once filters have round-
|
|
104
|
+
// tripped through redux they can arrive as ISO or date-only strings instead.
|
|
105
|
+
// Both have to reach ES as instants: `create_time`/`update_time` carry a real
|
|
106
|
+
// timestamp, so a date-only upper bound lands on midnight and drops everything
|
|
107
|
+
// created later that day — a single-day Created filter would return almost
|
|
108
|
+
// nothing, which is the empty state this epic exists to fix.
|
|
109
|
+
// A real check rather than `value as ZeniDate`: the cast would silently lie
|
|
110
|
+
// about a raw Date, a number, or anything else redux hands back, leaving the
|
|
111
|
+
// runtime guard as the only thing standing between us and a crash.
|
|
112
|
+
const isZeniDateLike = (value) => value != null &&
|
|
113
|
+
typeof value.isValid === 'function' &&
|
|
114
|
+
typeof value.format === 'function';
|
|
115
|
+
const CALENDAR_DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
116
|
+
const toDateBound = (value, edge) => {
|
|
117
|
+
// A bare `YYYY-MM-DD` carries no instant to misread — use it verbatim. This
|
|
118
|
+
// is the only genuinely timezone-proof shape, which is why it is worth a
|
|
119
|
+
// fast path of its own.
|
|
120
|
+
if (typeof value === 'string' && CALENDAR_DATE_ONLY.test(value)) {
|
|
121
|
+
return edge === 'start'
|
|
122
|
+
? `${value}T00:00:00.000Z`
|
|
123
|
+
: `${value}T23:59:59.999Z`;
|
|
124
|
+
}
|
|
125
|
+
// Everything else came from the picker, and the picker emits LOCAL midnight:
|
|
126
|
+
// `ZeniDatePicker` hands react-datepicker's local-midnight Date to
|
|
127
|
+
// `zeniDate(date.toISOString())`. So the instant encodes "midnight on the day
|
|
128
|
+
// the user clicked, in the user's zone" — and the day they clicked is
|
|
129
|
+
// recovered by reading it locally, never in UTC.
|
|
130
|
+
//
|
|
131
|
+
// Reading it in UTC subtracts the offset for every UTC+ operator: clicking
|
|
132
|
+
// "Mar 4" in Asia/Kolkata stores 2026-03-03T18:30:00Z, which is the 3rd in
|
|
133
|
+
// UTC. A single-day filter then returns the wrong day's tasks rather than
|
|
134
|
+
// none, and US-based testing never sees it.
|
|
135
|
+
const parsed = typeof value === 'string' ? (0, zeniDayJS_1.date)(value) : value;
|
|
136
|
+
if (!isZeniDateLike(parsed) || !parsed.isValid()) {
|
|
137
|
+
// Drop the row rather than send garbage: ES rejects the whole request on
|
|
138
|
+
// one bad bound, taking the other filters down with it.
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
const ymd = parsed.format('YYYY-MM-DD');
|
|
142
|
+
return edge === 'start' ? `${ymd}T00:00:00.000Z` : `${ymd}T23:59:59.999Z`;
|
|
143
|
+
};
|
|
144
|
+
const toFilterByPayload = (filters, tagNameByID) => {
|
|
71
145
|
if (filters.categories == null) {
|
|
72
146
|
return [];
|
|
73
147
|
}
|
|
74
|
-
return filters.categories.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
148
|
+
return filters.categories.flatMap((category) => {
|
|
149
|
+
const categoryField = category.field ?? '';
|
|
150
|
+
const field = ES_FIELD_BY_CATEGORY[categoryField] ?? categoryField;
|
|
151
|
+
// The dropdown is keyed by tagId, but ES matches `tags` on the tag NAME,
|
|
152
|
+
// so resolve through the tag entity this epic itself populates. Unknown ids
|
|
153
|
+
// fall through unchanged rather than being dropped.
|
|
154
|
+
const isDateField = ES_DATE_FIELDS.has(field);
|
|
155
|
+
// `between` is exactly [from, to]. A half-filled picker (one value) makes ES
|
|
156
|
+
// reject the query outright, and a longer array would quietly bind extras to
|
|
157
|
+
// `end` — both land the user back on an unexplained empty page, which is the
|
|
158
|
+
// failure this epic exists to remove. Drop the row instead.
|
|
159
|
+
if (isDateField && category.values.length !== 2) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
const values = category.values.map((value, index) => {
|
|
163
|
+
if (isDateField) {
|
|
164
|
+
// [from, to] — widen the upper bound to the end of its day.
|
|
165
|
+
return toDateBound(value, index === 0 ? 'start' : 'end');
|
|
166
|
+
}
|
|
167
|
+
const raw = String(value);
|
|
168
|
+
if (field !== 'tags') {
|
|
169
|
+
return raw;
|
|
170
|
+
}
|
|
171
|
+
// An id we cannot resolve would reach ES verbatim and match nothing —
|
|
172
|
+
// another silent empty page. Can happen when a filter is hydrated from the
|
|
173
|
+
// URL before `updateTags` has populated `tagByID`, so keep the value
|
|
174
|
+
// (dropping it would widen the result set) but make it visible in logs.
|
|
175
|
+
const tagName = tagNameByID[raw];
|
|
176
|
+
if (tagName == null) {
|
|
177
|
+
console.debug(`[taskManager] unresolved tagId "${raw}" sent to ES verbatim; expect no matches`);
|
|
178
|
+
}
|
|
179
|
+
return tagName ?? raw;
|
|
180
|
+
});
|
|
181
|
+
// `toDateBound` returns '' for a value it cannot parse. Honour that here:
|
|
182
|
+
// emitting a `between` with an empty bound fails the whole ES query, which
|
|
183
|
+
// takes every OTHER filter in the same request down with it — the silent
|
|
184
|
+
// empty page this epic exists to remove, from one unparseable value that a
|
|
185
|
+
// URL-hydrated filter can supply.
|
|
186
|
+
if (isDateField && values.some((value) => value === '')) {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
// ES has no date-exclusion operator: `is`/`is_not` are rejected on date
|
|
190
|
+
// fields, and `between` can only select a range, never exclude one. The
|
|
191
|
+
// operator dropdown still offers "is not" for every category, so an excluded
|
|
192
|
+
// date range would otherwise be sent as a plain `between` — returning exactly
|
|
193
|
+
// the rows the user asked to hide. Drop the row instead: a superset is
|
|
194
|
+
// wrong-but-obvious, an inverted result is wrong-and-invisible.
|
|
195
|
+
if (isDateField && category.matchingOperator !== 'equal') {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
field,
|
|
201
|
+
operator: isDateField
|
|
202
|
+
? 'between'
|
|
203
|
+
: category.matchingOperator === 'equal'
|
|
204
|
+
? 'is'
|
|
205
|
+
: 'is_not',
|
|
206
|
+
values,
|
|
207
|
+
join: category.valuesCombinationOperator === 'ALL' ? 'and' : 'or',
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
});
|
|
80
211
|
};
|
|
@@ -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",
|