@zeniai/client-epic-state 5.3.10 → 5.3.12-beta0DI
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/entity/task/taskPayload.d.ts +1 -0
- package/lib/entity/task/taskPayload.js +1 -0
- package/lib/entity/task/taskReducer.d.ts +4 -1
- package/lib/entity/task/taskReducer.js +19 -2
- package/lib/entity/task/taskState.d.ts +3 -0
- package/lib/entity/timeEntry/timeEntryPayload.d.ts +26 -0
- package/lib/entity/timeEntry/timeEntryPayload.js +2 -0
- package/lib/entity/timeEntry/timeEntryReducer.d.ts +8 -0
- package/lib/entity/timeEntry/timeEntryReducer.js +76 -0
- package/lib/entity/timeEntry/timeEntrySelector.d.ts +10 -0
- package/lib/entity/timeEntry/timeEntrySelector.js +32 -0
- package/lib/entity/timeEntry/timeEntryState.d.ts +43 -0
- package/lib/entity/timeEntry/timeEntryState.js +13 -0
- package/lib/epic.d.ts +4 -1
- package/lib/epic.js +9 -0
- package/lib/esm/entity/task/taskPayload.js +1 -0
- package/lib/esm/entity/task/taskReducer.js +18 -1
- package/lib/esm/entity/timeEntry/timeEntryPayload.js +1 -0
- package/lib/esm/entity/timeEntry/timeEntryReducer.js +70 -0
- package/lib/esm/entity/timeEntry/timeEntrySelector.js +23 -0
- package/lib/esm/entity/timeEntry/timeEntryState.js +8 -0
- package/lib/esm/epic.js +9 -0
- package/lib/esm/index.js +4 -1
- package/lib/esm/reducer.js +6 -0
- package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +3 -0
- package/lib/esm/view/taskManager/taskListView/taskList.js +6 -0
- package/lib/esm/view/taskManager/taskListView/taskListFilterHelpers.js +7 -0
- package/lib/esm/view/taskManager/taskListView/taskListSelector.js +27 -11
- package/lib/esm/view/taskManager/taskListView/taskListViewHelpers.js +37 -0
- package/lib/esm/view/taskManager/timeEntryView/epic/fetchTaskCategoriesEpic.js +24 -0
- package/lib/esm/view/taskManager/timeEntryView/epic/fetchTimeEntriesEpic.js +73 -0
- package/lib/esm/view/taskManager/timeEntryView/epic/writeTimeEntryEpic.js +77 -0
- package/lib/esm/view/taskManager/timeEntryView/timeEntryViewPayload.js +1 -0
- package/lib/esm/view/taskManager/timeEntryView/timeEntryViewReducer.js +160 -0
- package/lib/esm/view/taskManager/timeEntryView/timeEntryViewSelector.js +41 -0
- package/lib/esm/view/taskManager/timeEntryView/timeEntryViewState.js +1 -0
- package/lib/index.d.ts +5 -1
- package/lib/index.js +31 -16
- package/lib/reducer.d.ts +2 -0
- package/lib/reducer.js +6 -0
- package/lib/rootStateTypes.d.ts +4 -0
- package/lib/view/taskManager/taskDetailView/taskDetailSelector.d.ts +4 -0
- package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +3 -0
- package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
- package/lib/view/taskManager/taskListView/taskList.js +6 -0
- package/lib/view/taskManager/taskListView/taskListFilterHelpers.d.ts +4 -0
- package/lib/view/taskManager/taskListView/taskListFilterHelpers.js +9 -1
- package/lib/view/taskManager/taskListView/taskListSelector.d.ts +6 -3
- package/lib/view/taskManager/taskListView/taskListSelector.js +26 -10
- package/lib/view/taskManager/taskListView/taskListViewHelpers.d.ts +1 -1
- package/lib/view/taskManager/taskListView/taskListViewHelpers.js +37 -0
- package/lib/view/taskManager/timeEntryView/epic/fetchTaskCategoriesEpic.d.ts +18 -0
- package/lib/view/taskManager/timeEntryView/epic/fetchTaskCategoriesEpic.js +28 -0
- package/lib/view/taskManager/timeEntryView/epic/fetchTimeEntriesEpic.d.ts +23 -0
- package/lib/view/taskManager/timeEntryView/epic/fetchTimeEntriesEpic.js +78 -0
- package/lib/view/taskManager/timeEntryView/epic/writeTimeEntryEpic.d.ts +23 -0
- package/lib/view/taskManager/timeEntryView/epic/writeTimeEntryEpic.js +83 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewPayload.d.ts +27 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewPayload.js +2 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewReducer.d.ts +48 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewReducer.js +164 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewSelector.d.ts +24 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewSelector.js +46 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewState.d.ts +21 -0
- package/lib/view/taskManager/timeEntryView/timeEntryViewState.js +5 -0
- package/package.json +1 -1
package/lib/reducer.js
CHANGED
|
@@ -109,6 +109,7 @@ const taskGroupReducer_1 = __importStar(require("./entity/taskGroup/taskGroupRed
|
|
|
109
109
|
const taskGroupTemplateReducer_1 = __importStar(require("./entity/taskGroupTemplate/taskGroupTemplateReducer"));
|
|
110
110
|
const taskSummaryReducer_1 = __importStar(require("./entity/taskSummary/taskSummaryReducer"));
|
|
111
111
|
const tenantReducer_1 = __importStar(require("./entity/tenant/tenantReducer"));
|
|
112
|
+
const timeEntryReducer_1 = __importStar(require("./entity/timeEntry/timeEntryReducer"));
|
|
112
113
|
const toastNotificationReducer_1 = __importStar(require("./entity/toastNotification/toastNotificationReducer"));
|
|
113
114
|
const transactionReducer_1 = __importStar(require("./entity/transaction/transactionReducer"));
|
|
114
115
|
const transactionActivityLogReducer_1 = __importStar(require("./entity/transactionActivityLog/transactionActivityLogReducer"));
|
|
@@ -295,6 +296,7 @@ const taskDetailReducer_1 = __importStar(require("./view/taskManager/taskDetailV
|
|
|
295
296
|
const taskGroupTemplateViewReducer_1 = __importStar(require("./view/taskManager/taskGroupTemplateView/taskGroupTemplateViewReducer"));
|
|
296
297
|
const taskGroupViewReducer_1 = __importStar(require("./view/taskManager/taskGroupView/taskGroupViewReducer"));
|
|
297
298
|
const taskListReducer_1 = __importStar(require("./view/taskManager/taskListView/taskListReducer"));
|
|
299
|
+
const timeEntryViewReducer_1 = __importStar(require("./view/taskManager/timeEntryView/timeEntryViewReducer"));
|
|
298
300
|
const tasksCardReducer_1 = __importStar(require("./view/tasksCard/tasksCardReducer"));
|
|
299
301
|
const topExReducer_1 = __importStar(require("./view/topEx/topExReducer"));
|
|
300
302
|
const transactionActivityLogViewReducer_1 = __importStar(require("./view/transactionActivityLogView/transactionActivityLogViewReducer"));
|
|
@@ -383,6 +385,7 @@ const initialEntitiesState = {
|
|
|
383
385
|
subscriptionState: subscriptionReducer_1.initialState,
|
|
384
386
|
subscriptionSummaryState: subscriptionSummaryReducer_1.initialState,
|
|
385
387
|
tagState: tagReducer_1.initialState,
|
|
388
|
+
timeEntryState: timeEntryReducer_1.initialState,
|
|
386
389
|
taskGroupState: taskGroupReducer_1.initialState,
|
|
387
390
|
taskGroupTemplateState: taskGroupTemplateReducer_1.initialState,
|
|
388
391
|
taskState: taskReducer_1.initialState,
|
|
@@ -565,6 +568,7 @@ const initialViewsState = {
|
|
|
565
568
|
taskGroupTemplateViewState: taskGroupTemplateViewReducer_1.initialState,
|
|
566
569
|
taskGroupViewState: taskGroupViewReducer_1.initialState,
|
|
567
570
|
taskListState: taskListReducer_1.initialState,
|
|
571
|
+
timeEntryViewState: timeEntryViewReducer_1.initialState,
|
|
568
572
|
tasksCardState: tasksCardReducer_1.initialState,
|
|
569
573
|
topExState: topExReducer_1.initialState,
|
|
570
574
|
transactionActivityLogViewState: transactionActivityLogViewReducer_1.initialState,
|
|
@@ -673,6 +677,7 @@ const entityReducers = {
|
|
|
673
677
|
subscriptionState: subscriptionReducer_1.default,
|
|
674
678
|
subscriptionSummaryState: subscriptionSummaryReducer_1.default,
|
|
675
679
|
tagState: tagReducer_1.default,
|
|
680
|
+
timeEntryState: timeEntryReducer_1.default,
|
|
676
681
|
taskGroupState: taskGroupReducer_1.default,
|
|
677
682
|
taskGroupTemplateState: taskGroupTemplateReducer_1.default,
|
|
678
683
|
taskState: taskReducer_1.default,
|
|
@@ -854,6 +859,7 @@ const viewReducers = {
|
|
|
854
859
|
taskGroupTemplateViewState: taskGroupTemplateViewReducer_1.default,
|
|
855
860
|
taskGroupViewState: taskGroupViewReducer_1.default,
|
|
856
861
|
taskListState: taskListReducer_1.default,
|
|
862
|
+
timeEntryViewState: timeEntryViewReducer_1.default,
|
|
857
863
|
tagViewState: tagViewReducer_1.default,
|
|
858
864
|
tasksCardState: tasksCardReducer_1.default,
|
|
859
865
|
topExState: topExReducer_1.default,
|
package/lib/rootStateTypes.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ import { TaskGroupState } from './entity/taskGroup/taskGroupState';
|
|
|
69
69
|
import { TaskGroupTemplateState } from './entity/taskGroupTemplate/taskGroupTemplateState';
|
|
70
70
|
import { TaskSummaryState } from './entity/taskSummary/taskSummaryState';
|
|
71
71
|
import { TenantState } from './entity/tenant/tenantState';
|
|
72
|
+
import { TimeEntryState } from './entity/timeEntry/timeEntryState';
|
|
72
73
|
import { ToastNotificationState } from './entity/toastNotification/toastNotificationState';
|
|
73
74
|
import { TransactionState } from './entity/transaction/transactionState';
|
|
74
75
|
import { TransactionActivityLogState } from './entity/transactionActivityLog/transactionActivityLogState';
|
|
@@ -253,6 +254,7 @@ import { TaskDetailViewState } from './view/taskManager/taskDetailView/taskDetai
|
|
|
253
254
|
import { TaskGroupTemplateViewState } from './view/taskManager/taskGroupTemplateView/taskGroupTemplateViewState';
|
|
254
255
|
import { TaskGroupViewState } from './view/taskManager/taskGroupView/taskGroupViewState';
|
|
255
256
|
import { TaskListState } from './view/taskManager/taskListView/taskList';
|
|
257
|
+
import { TimeEntryViewState } from './view/taskManager/timeEntryView/timeEntryViewState';
|
|
256
258
|
import { TasksCardState } from './view/tasksCard/tasksCardState';
|
|
257
259
|
import { TopExState } from './view/topEx/topExState';
|
|
258
260
|
import { TransactionActivityLogViewState } from './view/transactionActivityLogView/transactionActivityLogViewState';
|
|
@@ -345,6 +347,7 @@ export type EntitiesState = {
|
|
|
345
347
|
taskState: TaskState;
|
|
346
348
|
taskSummaryState: TaskSummaryState;
|
|
347
349
|
tenantState: TenantState;
|
|
350
|
+
timeEntryState: TimeEntryState;
|
|
348
351
|
toastNotificationState: ToastNotificationState;
|
|
349
352
|
transactionActivityLogState: TransactionActivityLogState;
|
|
350
353
|
transactionState: TransactionState;
|
|
@@ -523,6 +526,7 @@ export type ViewsState = {
|
|
|
523
526
|
taskGroupViewState: TaskGroupViewState;
|
|
524
527
|
taskListState: TaskListState;
|
|
525
528
|
tasksCardState: TasksCardState;
|
|
529
|
+
timeEntryViewState: TimeEntryViewState;
|
|
526
530
|
topExState: TopExState;
|
|
527
531
|
transactionActivityLogViewState: TransactionActivityLogViewState;
|
|
528
532
|
transactionDetailState: TransactionDetailState;
|
|
@@ -25,6 +25,10 @@ export interface TaskDetailSelectorView extends SelectorView {
|
|
|
25
25
|
isDeleted?: boolean;
|
|
26
26
|
recurringSourceTaskId?: ID;
|
|
27
27
|
snoozedUntil?: ZeniDate | null;
|
|
28
|
+
/** Drives the Category control in the For Internal Use block. Read from
|
|
29
|
+
* the task entity rather than mirrored into local form data: it is not
|
|
30
|
+
* editable inline, and a mirror would need its own discard path. */
|
|
31
|
+
taskCategoryId?: ID;
|
|
28
32
|
taskGroupId?: ID;
|
|
29
33
|
}
|
|
30
34
|
export declare const getTaskDetail: (state: RootState, taskId?: ID) => TaskDetailSelectorView;
|
|
@@ -32,6 +32,7 @@ const getTaskDetail = (state, taskId) => {
|
|
|
32
32
|
let isDeleted = undefined;
|
|
33
33
|
let depth = undefined;
|
|
34
34
|
let recurringSourceTaskId = undefined;
|
|
35
|
+
let taskCategoryId = undefined;
|
|
35
36
|
let snoozedUntil = undefined;
|
|
36
37
|
let taskGroupId = undefined;
|
|
37
38
|
let subtasks = [];
|
|
@@ -45,6 +46,7 @@ const getTaskDetail = (state, taskId) => {
|
|
|
45
46
|
if (taskEntity != null) {
|
|
46
47
|
createdByUser = (0, userAndRole_1.getUserAndUserRole)(userState, userRoleState, addressState, taskEntity.createdBy);
|
|
47
48
|
depth = taskEntity.depth;
|
|
49
|
+
taskCategoryId = taskEntity.taskCategoryId;
|
|
48
50
|
isArchived = taskEntity.isArchived;
|
|
49
51
|
isDeleted = taskEntity.isDeleted;
|
|
50
52
|
recurringSourceTaskId = taskEntity.recurringSourceTaskId;
|
|
@@ -109,6 +111,7 @@ const getTaskDetail = (state, taskId) => {
|
|
|
109
111
|
version: 0,
|
|
110
112
|
createdByUser,
|
|
111
113
|
depth,
|
|
114
|
+
taskCategoryId,
|
|
112
115
|
isArchived,
|
|
113
116
|
isDeleted,
|
|
114
117
|
recurringSourceTaskId,
|
|
@@ -48,7 +48,7 @@ export interface TaskFilterCategory {
|
|
|
48
48
|
values: (string | ZeniDate)[];
|
|
49
49
|
valuesCombinationOperator: 'ANY';
|
|
50
50
|
}
|
|
51
|
-
export type TaskListFilterCategoryField = 'status' | 'dueDate' | 'assignee' | 'creationDate' | 'priority' | 'tag';
|
|
51
|
+
export type TaskListFilterCategoryField = 'status' | 'dueDate' | 'assignee' | 'createdBy' | 'creationDate' | 'priority' | 'tag' | 'billing';
|
|
52
52
|
export declare const TASK_LIST_GROUP_BY_KEYS: readonly ["groupName", "status", "dueDate", "assignee", "priority", "tag"];
|
|
53
53
|
export declare const toTaskListGroupByKeyType: (v: string) => "status" | "dueDate" | "priority" | "assignee" | "tag" | "groupName";
|
|
54
54
|
export declare const toTaskListGroupByKeyTypeStrict: (v: string) => "status" | "dueDate" | "priority" | "assignee" | "tag" | "groupName" | undefined;
|
|
@@ -53,7 +53,13 @@ exports.TASK_LIST_FILTER_CATEGORIES = [
|
|
|
53
53
|
type: 'user',
|
|
54
54
|
multiple: true,
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
value: 'createdBy',
|
|
58
|
+
type: 'user',
|
|
59
|
+
multiple: true,
|
|
60
|
+
},
|
|
56
61
|
{ value: 'creationDate', type: 'dateRange', multiple: true },
|
|
62
|
+
{ value: 'billing', type: 'dropdown', multiple: true },
|
|
57
63
|
];
|
|
58
64
|
exports.TASK_LIST_GROUP_BY_CATEGORIES = [
|
|
59
65
|
{ value: 'status' },
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { ID } from '../../../commonStateTypes/common';
|
|
1
2
|
import { Task } from '../../../entity/task/taskState';
|
|
3
|
+
import { TaskCategory } from '../../../entity/timeEntry/timeEntryState';
|
|
2
4
|
import { FilterCategoryValueType } from '../../spendManagement/spendManagementFilterHelpers';
|
|
5
|
+
/** Billing lives on the task's category, not the task, so it needs the catalog. */
|
|
6
|
+
export declare const buildCategoryValueGetter: (taskCategoriesById: Record<ID, TaskCategory>) => (key: string, task: Task) => FilterCategoryValueType | (string | undefined)[] | undefined;
|
|
3
7
|
export declare const getCategoryValueForTaskList: (key: string, task: Task) => FilterCategoryValueType | (string | undefined)[] | undefined;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCategoryValueForTaskList = void 0;
|
|
3
|
+
exports.getCategoryValueForTaskList = exports.buildCategoryValueGetter = void 0;
|
|
4
|
+
/** Billing lives on the task's category, not the task, so it needs the catalog. */
|
|
5
|
+
const buildCategoryValueGetter = (taskCategoriesById) => (key, task) => key === 'billing'
|
|
6
|
+
? (taskCategoriesById[task.taskCategoryId ?? '']?.billingType ??
|
|
7
|
+
'non_billable')
|
|
8
|
+
: (0, exports.getCategoryValueForTaskList)(key, task);
|
|
9
|
+
exports.buildCategoryValueGetter = buildCategoryValueGetter;
|
|
4
10
|
const getCategoryValueForTaskList = (key, task) => {
|
|
5
11
|
switch (key) {
|
|
6
12
|
case 'dueDate':
|
|
@@ -15,6 +21,8 @@ const getCategoryValueForTaskList = (key, task) => {
|
|
|
15
21
|
return task.createTime;
|
|
16
22
|
case 'assignee':
|
|
17
23
|
return task.assignees;
|
|
24
|
+
case 'createdBy':
|
|
25
|
+
return task.createdBy;
|
|
18
26
|
default:
|
|
19
27
|
return undefined;
|
|
20
28
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { FetchStateAndError } from '../../../commonStateTypes/common';
|
|
1
|
+
import { FetchStateAndError, ID } from '../../../commonStateTypes/common';
|
|
2
2
|
import { SelectorView } from '../../../commonStateTypes/viewAndReport/viewAndReport';
|
|
3
3
|
import { AddressState } from '../../../entity/address/addressState';
|
|
4
4
|
import { TagState } from '../../../entity/tag/tagState';
|
|
5
5
|
import { Task, TaskState } from '../../../entity/task/taskState';
|
|
6
6
|
import { TaskGroupTemplate } from '../../../entity/taskGroupTemplate/taskGroupTemplateState';
|
|
7
|
+
import { TaskCategory } from '../../../entity/timeEntry/timeEntryState';
|
|
7
8
|
import { UserState } from '../../../entity/user/userState';
|
|
8
9
|
import { UserGroupsState } from '../../../entity/userGroups/userGroupsState';
|
|
9
10
|
import { UserRoleState } from '../../../entity/userRole/userRoleState';
|
|
@@ -48,9 +49,10 @@ export interface TaskListSelectorView extends SelectorView {
|
|
|
48
49
|
* slice. `TaskListState` still satisfies this, so callers are unaffected.
|
|
49
50
|
*/
|
|
50
51
|
type TaskListGroupingInputs = Pick<TaskListState, 'filters' | 'taskIdsByAssignees' | 'taskIdsByDueDate' | 'taskIdsByGroupsIds' | 'taskIdsByPriority' | 'taskIdsByStatus' | 'taskIdsByTags' | 'uiState'>;
|
|
51
|
-
export declare const getTaskListGroupedByAssignees: ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }: {
|
|
52
|
+
export declare const getTaskListGroupedByAssignees: ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }: {
|
|
52
53
|
addressState: AddressState;
|
|
53
54
|
tagState: TagState;
|
|
55
|
+
taskCategoriesById: Record<ID, TaskCategory>;
|
|
54
56
|
taskListState: TaskListGroupingInputs;
|
|
55
57
|
taskState: TaskState;
|
|
56
58
|
userGroupsState: UserGroupsState;
|
|
@@ -62,9 +64,10 @@ export declare const getTaskListGroupedByAssignees: ({ taskListState, taskState,
|
|
|
62
64
|
isDefault: boolean;
|
|
63
65
|
tasks: TaskWithUserDetails[];
|
|
64
66
|
}[];
|
|
65
|
-
export declare const getTaskListGroupedByPriority: ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }: {
|
|
67
|
+
export declare const getTaskListGroupedByPriority: ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }: {
|
|
66
68
|
addressState: AddressState;
|
|
67
69
|
tagState: TagState;
|
|
70
|
+
taskCategoriesById: Record<ID, TaskCategory>;
|
|
68
71
|
taskListState: TaskListGroupingInputs;
|
|
69
72
|
taskState: TaskState;
|
|
70
73
|
userGroupsState: UserGroupsState;
|
|
@@ -53,7 +53,7 @@ const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
|
|
|
53
53
|
}, sortOrder);
|
|
54
54
|
return tasksInOrder;
|
|
55
55
|
};
|
|
56
|
-
const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRoleState, addressState, taskIds, tagState, userGroupsState, }) => {
|
|
56
|
+
const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRoleState, addressState, taskIds, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
57
57
|
const { sortKey, searchText } = taskListState.uiState;
|
|
58
58
|
const sortOrder = (0, sortOrderPayload_1.getSortOrder)(taskListState.uiState.sortOrder);
|
|
59
59
|
const tasks = taskIds
|
|
@@ -76,11 +76,11 @@ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRole
|
|
|
76
76
|
return undefined;
|
|
77
77
|
})
|
|
78
78
|
.filter((value) => value?.name.toLowerCase().includes(searchText.toLowerCase()) === true);
|
|
79
|
-
const filteredTaskList = (0, spendManagementFilterHelpers_1.applyAdvancedFiltersOnList)(taskListFilterHelpers_1.
|
|
79
|
+
const filteredTaskList = (0, spendManagementFilterHelpers_1.applyAdvancedFiltersOnList)((0, taskListFilterHelpers_1.buildCategoryValueGetter)(taskCategoriesById), tasks, taskListState.filters);
|
|
80
80
|
const sortedTaskList = sortTasksList(tagState, filteredTaskList, sortKey, sortOrder);
|
|
81
81
|
return sortedTaskList;
|
|
82
82
|
};
|
|
83
|
-
const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState, taskGroupsIds, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
83
|
+
const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState, taskGroupsIds, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
84
84
|
const taskGroupsWithTasksList = taskGroupsIds
|
|
85
85
|
.map((taskGroupId) => {
|
|
86
86
|
const taskGroup = (0, taskGroupSelector_1.getTaskGroupById)(taskGroupState, taskGroupId);
|
|
@@ -94,6 +94,7 @@ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupStat
|
|
|
94
94
|
taskIds,
|
|
95
95
|
tagState,
|
|
96
96
|
userGroupsState,
|
|
97
|
+
taskCategoriesById,
|
|
97
98
|
});
|
|
98
99
|
if (taskGroup != null) {
|
|
99
100
|
return {
|
|
@@ -125,7 +126,7 @@ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupStat
|
|
|
125
126
|
// Also hides deleted groups, which are empty as a side effect.
|
|
126
127
|
return sortedTaskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
127
128
|
};
|
|
128
|
-
const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
129
|
+
const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
129
130
|
const assigneeGroups = Object.keys(taskListState.taskIdsByAssignees);
|
|
130
131
|
const taskGroupsWithTasksList = assigneeGroups.map((groupKey) => {
|
|
131
132
|
const taskIds = taskListState.taskIdsByAssignees[groupKey];
|
|
@@ -138,6 +139,7 @@ const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, us
|
|
|
138
139
|
taskIds,
|
|
139
140
|
tagState,
|
|
140
141
|
userGroupsState,
|
|
142
|
+
taskCategoriesById,
|
|
141
143
|
});
|
|
142
144
|
const assigneeIds = groupKey.split(',');
|
|
143
145
|
const groupName = (0, userSelector_1.getUsersByUserIds)(userState, assigneeIds)
|
|
@@ -156,7 +158,7 @@ const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, us
|
|
|
156
158
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
157
159
|
};
|
|
158
160
|
exports.getTaskListGroupedByAssignees = getTaskListGroupedByAssignees;
|
|
159
|
-
const getTaskListGroupedByPriority = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
161
|
+
const getTaskListGroupedByPriority = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
160
162
|
const taskGroupsWithTasksList = taskState_1.ALL_PRIORITY_CODE.map((groupKey) => {
|
|
161
163
|
const taskIds = taskListState.taskIdsByPriority[groupKey] ?? [];
|
|
162
164
|
const tasksWithUserDetails = getTasksWithUserDetails({
|
|
@@ -168,6 +170,7 @@ const getTaskListGroupedByPriority = ({ taskListState, taskState, userState, use
|
|
|
168
170
|
taskIds,
|
|
169
171
|
tagState,
|
|
170
172
|
userGroupsState,
|
|
173
|
+
taskCategoriesById,
|
|
171
174
|
});
|
|
172
175
|
return {
|
|
173
176
|
groupName: taskDetailSelector_1.allTaskPriority.find((priority) => priority.code === groupKey)?.name ??
|
|
@@ -180,7 +183,7 @@ const getTaskListGroupedByPriority = ({ taskListState, taskState, userState, use
|
|
|
180
183
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
181
184
|
};
|
|
182
185
|
exports.getTaskListGroupedByPriority = getTaskListGroupedByPriority;
|
|
183
|
-
const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
186
|
+
const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
184
187
|
const taskGroupsWithTasksList = taskState_1.ALL_TASK_STATUS_CODE.map((groupKey) => {
|
|
185
188
|
const taskIds = taskListState.taskIdsByStatus[groupKey] ?? [];
|
|
186
189
|
const tasksWithUserDetails = getTasksWithUserDetails({
|
|
@@ -192,6 +195,7 @@ const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userR
|
|
|
192
195
|
taskIds,
|
|
193
196
|
tagState,
|
|
194
197
|
userGroupsState,
|
|
198
|
+
taskCategoriesById,
|
|
195
199
|
});
|
|
196
200
|
return {
|
|
197
201
|
groupName: taskDetailSelector_1.allTaskStatus.find((taskStatus) => taskStatus.code === groupKey)
|
|
@@ -203,7 +207,7 @@ const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userR
|
|
|
203
207
|
});
|
|
204
208
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
205
209
|
};
|
|
206
|
-
const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
210
|
+
const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
207
211
|
const tagGroups = Object.keys(taskListState.taskIdsByTags);
|
|
208
212
|
const taskGroupsWithTasksList = tagGroups.map((groupKey) => {
|
|
209
213
|
const taskIds = taskListState.taskIdsByTags[groupKey];
|
|
@@ -216,6 +220,7 @@ const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRol
|
|
|
216
220
|
taskIds,
|
|
217
221
|
tagState,
|
|
218
222
|
userGroupsState,
|
|
223
|
+
taskCategoriesById,
|
|
219
224
|
});
|
|
220
225
|
const tagIds = groupKey.split(',');
|
|
221
226
|
const groupName = (0, tagSelector_1.getTagsByIds)(tagState, tagIds)
|
|
@@ -232,7 +237,7 @@ const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRol
|
|
|
232
237
|
});
|
|
233
238
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
234
239
|
};
|
|
235
|
-
const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
|
|
240
|
+
const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
236
241
|
const taskGroupsWithTasksList = taskList_1.DUE_DATE_GROUP_KEYS.map((groupKey) => {
|
|
237
242
|
const taskIds = taskListState.taskIdsByDueDate[groupKey];
|
|
238
243
|
const tasksWithUserDetails = getTasksWithUserDetails({
|
|
@@ -244,6 +249,7 @@ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, user
|
|
|
244
249
|
taskIds,
|
|
245
250
|
tagState,
|
|
246
251
|
userGroupsState,
|
|
252
|
+
taskCategoriesById,
|
|
247
253
|
});
|
|
248
254
|
return {
|
|
249
255
|
groupName: groupKey,
|
|
@@ -254,7 +260,7 @@ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, user
|
|
|
254
260
|
});
|
|
255
261
|
return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
|
|
256
262
|
};
|
|
257
|
-
const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState, }) => {
|
|
263
|
+
const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState, taskCategoriesById, }) => {
|
|
258
264
|
const { groupByKey } = taskListState.uiState;
|
|
259
265
|
if (groupByKey === 'groupName') {
|
|
260
266
|
return getTaskListGroupedByGroupName({
|
|
@@ -267,6 +273,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
267
273
|
taskGroupsIds,
|
|
268
274
|
tagState,
|
|
269
275
|
userGroupsState,
|
|
276
|
+
taskCategoriesById,
|
|
270
277
|
});
|
|
271
278
|
}
|
|
272
279
|
if (groupByKey === 'assignee') {
|
|
@@ -278,6 +285,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
278
285
|
addressState,
|
|
279
286
|
tagState,
|
|
280
287
|
userGroupsState,
|
|
288
|
+
taskCategoriesById,
|
|
281
289
|
});
|
|
282
290
|
}
|
|
283
291
|
if (groupByKey === 'priority') {
|
|
@@ -289,6 +297,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
289
297
|
addressState,
|
|
290
298
|
tagState,
|
|
291
299
|
userGroupsState,
|
|
300
|
+
taskCategoriesById,
|
|
292
301
|
});
|
|
293
302
|
}
|
|
294
303
|
if (groupByKey === 'status') {
|
|
@@ -300,6 +309,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
300
309
|
addressState,
|
|
301
310
|
tagState,
|
|
302
311
|
userGroupsState,
|
|
312
|
+
taskCategoriesById,
|
|
303
313
|
});
|
|
304
314
|
}
|
|
305
315
|
if (groupByKey === 'tag') {
|
|
@@ -311,6 +321,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
311
321
|
addressState,
|
|
312
322
|
tagState,
|
|
313
323
|
userGroupsState,
|
|
324
|
+
taskCategoriesById,
|
|
314
325
|
});
|
|
315
326
|
}
|
|
316
327
|
return getTaskListGroupedByDueDate({
|
|
@@ -321,6 +332,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
|
|
|
321
332
|
addressState,
|
|
322
333
|
tagState,
|
|
323
334
|
userGroupsState,
|
|
335
|
+
taskCategoriesById,
|
|
324
336
|
});
|
|
325
337
|
};
|
|
326
338
|
/**
|
|
@@ -345,7 +357,10 @@ const getTaskGroupsWithTaskListMemoized = (0, toolkit_1.createSelector)((state)
|
|
|
345
357
|
// invalidate this memo and rebuild a board the user is already using.
|
|
346
358
|
(state) => tagsAreStructural(state.taskListState.uiState)
|
|
347
359
|
? state.tagState
|
|
348
|
-
: EMPTY_TAG_STATE, (state) => state.userGroupsState,
|
|
360
|
+
: EMPTY_TAG_STATE, (state) => state.userGroupsState,
|
|
361
|
+
// Only the catalog, not the whole timeEntry slice: billing is read off
|
|
362
|
+
// the task's category, and a timer tick must not rebuild the board.
|
|
363
|
+
(state) => state.timeEntryState.taskCategoriesById, (uiState, filters, taskIdsByGroupsIds, taskIdsByAssignees, taskIdsByPriority, taskIdsByStatus, taskIdsByTags, taskIdsByDueDate, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupsIds, tagState, userGroupsState, taskCategoriesById) => getTaskGroupsWithTaskList({
|
|
349
364
|
taskListState: {
|
|
350
365
|
uiState,
|
|
351
366
|
filters,
|
|
@@ -364,6 +379,7 @@ const getTaskGroupsWithTaskListMemoized = (0, toolkit_1.createSelector)((state)
|
|
|
364
379
|
taskGroupsIds,
|
|
365
380
|
tagState,
|
|
366
381
|
userGroupsState,
|
|
382
|
+
taskCategoriesById,
|
|
367
383
|
}));
|
|
368
384
|
exports.getAllTasks = (0, toolkit_1.createSelector)(
|
|
369
385
|
// `taskState`, `taskGroupState`, `addressState` and `tagState` are gone from
|
|
@@ -3,7 +3,7 @@ import { Task } from '../../../entity/task/taskState';
|
|
|
3
3
|
import { User } from '../../../entity/user/userState';
|
|
4
4
|
import { ZeniDate } from '../../../zeniDayJS';
|
|
5
5
|
import { TaskGroupKey } from './taskList';
|
|
6
|
-
export type SubtaskSortKey = 'name' | 'status' | 'assignee' | 'tag';
|
|
6
|
+
export type SubtaskSortKey = 'name' | 'status' | 'assignee' | 'tag' | 'priority' | 'dueDate' | 'timeSpent';
|
|
7
7
|
export type SubtaskSortOrder = 'asc' | 'desc';
|
|
8
8
|
export declare const getDueDateValueFromDueDateGroupId: (groupId: string) => ZeniDate;
|
|
9
9
|
export declare const getTaskUpdates: (groupByKey: TaskGroupKey, groupId: string) => Partial<Task>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sortSubtasks = exports.getTaskUpdates = exports.getDueDateValueFromDueDateGroupId = void 0;
|
|
4
|
+
const formatMinutesToFromHHMM_1 = require("../../../commonStateTypes/fiscalYearHelpers/formatMinutesToFromHHMM");
|
|
4
5
|
const zeniDayJS_1 = require("../../../zeniDayJS");
|
|
5
6
|
const taskDetailSelector_1 = require("../taskDetailView/taskDetailSelector");
|
|
6
7
|
const taskList_1 = require("./taskList");
|
|
@@ -68,6 +69,11 @@ const sortSubtasks = (subtasks, sortKey, sortOrder, userList, tagList) => {
|
|
|
68
69
|
return acc;
|
|
69
70
|
}, {});
|
|
70
71
|
const unknownStatusRank = taskDetailSelector_1.allTaskStatus.length;
|
|
72
|
+
const priorityOrder = taskDetailSelector_1.allTaskPriority.reduce((acc, priority, index) => {
|
|
73
|
+
acc[priority.code] = index;
|
|
74
|
+
return acc;
|
|
75
|
+
}, {});
|
|
76
|
+
const unknownPriorityRank = taskDetailSelector_1.allTaskPriority.length;
|
|
71
77
|
const cmpStr = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' });
|
|
72
78
|
const keyFn = (task) => {
|
|
73
79
|
if (sortKey === 'name') {
|
|
@@ -94,6 +100,37 @@ const sortSubtasks = (subtasks, sortKey, sortOrder, userList, tagList) => {
|
|
|
94
100
|
const tag = tagList.find((candidate) => candidate.tagId === firstTagId);
|
|
95
101
|
return tag?.name ?? '';
|
|
96
102
|
}
|
|
103
|
+
if (sortKey === 'priority') {
|
|
104
|
+
// Rank by the canonical order, not the code string: alphabetical would
|
|
105
|
+
// sort "high" before "urgent" and read as wrong to anyone scanning it.
|
|
106
|
+
return priorityOrder[task.priority?.code] ?? unknownPriorityRank;
|
|
107
|
+
}
|
|
108
|
+
if (sortKey === 'dueDate') {
|
|
109
|
+
// A task with no date is not "earliest", so undated rows belong at the
|
|
110
|
+
// bottom in BOTH directions. The comparator negates for 'desc', so one
|
|
111
|
+
// fixed sentinel would flip them to the top — the sentinel has to flip
|
|
112
|
+
// with the order to survive that negation.
|
|
113
|
+
// `dueDate` is already a ZeniDate (dayjs), so valueOf() is the epoch.
|
|
114
|
+
// An unparseable date yields NaN, and a NaN comparator leaves the array
|
|
115
|
+
// untouched -- the same silent failure the timeSpent branch guards.
|
|
116
|
+
// Treat it as undated rather than as a date.
|
|
117
|
+
const due = task.dueDate;
|
|
118
|
+
const epoch = due?.valueOf();
|
|
119
|
+
if (epoch == null || Number.isNaN(epoch)) {
|
|
120
|
+
return sortOrder === 'asc'
|
|
121
|
+
? Number.MAX_SAFE_INTEGER
|
|
122
|
+
: Number.MIN_SAFE_INTEGER;
|
|
123
|
+
}
|
|
124
|
+
return epoch;
|
|
125
|
+
}
|
|
126
|
+
if (sortKey === 'timeSpent') {
|
|
127
|
+
// `timeSpent` is an "HHMM" STRING (no colon). Comparing it as text puts
|
|
128
|
+
// "1000" before "0900" only by luck of padding, and any malformed value
|
|
129
|
+
// makes the converter return NaN -- which a comparator turns into "no
|
|
130
|
+
// sort at all", silently. Coerce that to 0.
|
|
131
|
+
const minutes = (0, formatMinutesToFromHHMM_1.convertHHMMStrToMinutes)(task.timeSpent ?? '');
|
|
132
|
+
return Number.isFinite(minutes) ? minutes : 0;
|
|
133
|
+
}
|
|
97
134
|
return '';
|
|
98
135
|
};
|
|
99
136
|
const copy = [...subtasks];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { updateTaskCategories } from '../../../../entity/timeEntry/timeEntryReducer';
|
|
4
|
+
import { RootState } from '../../../../rootStateTypes';
|
|
5
|
+
import { ZeniAPI } from '../../../../zeniAPI';
|
|
6
|
+
import { fetchTaskCategories, fetchTaskCategoriesFailure, fetchTaskCategoriesSuccess } from '../timeEntryViewReducer';
|
|
7
|
+
export type ActionType = ReturnType<typeof fetchTaskCategories> | ReturnType<typeof fetchTaskCategoriesSuccess> | ReturnType<typeof fetchTaskCategoriesFailure> | ReturnType<typeof updateTaskCategories>;
|
|
8
|
+
/**
|
|
9
|
+
* The 156-category catalog that fills the Category dropdown.
|
|
10
|
+
*
|
|
11
|
+
* `switchMap` here, unlike the entry epics: there is one catalog, so a second
|
|
12
|
+
* request supersedes the first rather than racing it.
|
|
13
|
+
*
|
|
14
|
+
* Zeni-only on the server (the billing marker is commercially sensitive), so
|
|
15
|
+
* a customer user gets a 401 — surfaced as Error rather than an empty list,
|
|
16
|
+
* because an empty catalog looks like "no categories exist".
|
|
17
|
+
*/
|
|
18
|
+
export declare const fetchTaskCategoriesEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchTaskCategoriesEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const operators_2 = require("rxjs/operators");
|
|
7
|
+
const timeEntryReducer_1 = require("../../../../entity/timeEntry/timeEntryReducer");
|
|
8
|
+
const responsePayload_1 = require("../../../../responsePayload");
|
|
9
|
+
const timeEntryViewReducer_1 = require("../timeEntryViewReducer");
|
|
10
|
+
/**
|
|
11
|
+
* The 156-category catalog that fills the Category dropdown.
|
|
12
|
+
*
|
|
13
|
+
* `switchMap` here, unlike the entry epics: there is one catalog, so a second
|
|
14
|
+
* request supersedes the first rather than racing it.
|
|
15
|
+
*
|
|
16
|
+
* Zeni-only on the server (the billing marker is commercially sensitive), so
|
|
17
|
+
* a customer user gets a 401 — surfaced as Error rather than an empty list,
|
|
18
|
+
* because an empty catalog looks like "no categories exist".
|
|
19
|
+
*/
|
|
20
|
+
const fetchTaskCategoriesEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(timeEntryViewReducer_1.fetchTaskCategories.match), (0, operators_1.switchMap)(() => zeniAPI
|
|
21
|
+
.getJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/task-categories`)
|
|
22
|
+
.pipe((0, operators_2.mergeMap)((response) => {
|
|
23
|
+
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
24
|
+
return (0, rxjs_1.of)((0, timeEntryReducer_1.updateTaskCategories)(response.data.task_categories ?? []), (0, timeEntryViewReducer_1.fetchTaskCategoriesSuccess)());
|
|
25
|
+
}
|
|
26
|
+
return (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTaskCategoriesFailure)(response.status));
|
|
27
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTaskCategoriesFailure)((0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch task categories errored out' + JSON.stringify(error))))))));
|
|
28
|
+
exports.fetchTaskCategoriesEpic = fetchTaskCategoriesEpic;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { updateTimeEntries } from '../../../../entity/timeEntry/timeEntryReducer';
|
|
4
|
+
import { RootState } from '../../../../rootStateTypes';
|
|
5
|
+
import { ZeniAPI } from '../../../../zeniAPI';
|
|
6
|
+
import { fetchTimeEntriesForRange, fetchTimeEntriesForRangeFailure, fetchTimeEntriesForRangeSuccess, fetchTimeEntriesForTask, fetchTimeEntriesForTaskFailure, fetchTimeEntriesForTaskSuccess } from '../timeEntryViewReducer';
|
|
7
|
+
export type ActionType = ReturnType<typeof fetchTimeEntriesForTask> | ReturnType<typeof fetchTimeEntriesForTaskSuccess> | ReturnType<typeof fetchTimeEntriesForTaskFailure> | ReturnType<typeof fetchTimeEntriesForRange> | ReturnType<typeof fetchTimeEntriesForRangeSuccess> | ReturnType<typeof fetchTimeEntriesForRangeFailure> | ReturnType<typeof updateTimeEntries>;
|
|
8
|
+
/**
|
|
9
|
+
* Every entry on one task, all users — the task detail breakdown.
|
|
10
|
+
*
|
|
11
|
+
* `mergeMap`, not `switchMap`: a detail page and an expanded board row can
|
|
12
|
+
* ask for two different tasks at once, and cancelling the first would leave
|
|
13
|
+
* its breakdown stuck In-Progress forever.
|
|
14
|
+
*/
|
|
15
|
+
export declare const fetchTimeEntriesForTaskEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
|
16
|
+
/**
|
|
17
|
+
* One user's entries in an inclusive date range — the weekly timesheet.
|
|
18
|
+
*
|
|
19
|
+
* Needs the `is_deleted + user_id + entry_date` composite index (api #593).
|
|
20
|
+
* Without it the server returns 500, not an empty list, so the failure is
|
|
21
|
+
* surfaced rather than rendered as a blank week.
|
|
22
|
+
*/
|
|
23
|
+
export declare const fetchTimeEntriesForRangeEpic: (actions$: ActionsObservable<ActionType>, _state$: StateObservable<RootState>, zeniAPI: ZeniAPI) => Observable<ActionType>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchTimeEntriesForRangeEpic = exports.fetchTimeEntriesForTaskEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const timeEntryReducer_1 = require("../../../../entity/timeEntry/timeEntryReducer");
|
|
7
|
+
const responsePayload_1 = require("../../../../responsePayload");
|
|
8
|
+
const timeEntryViewReducer_1 = require("../timeEntryViewReducer");
|
|
9
|
+
const timeEntryViewState_1 = require("../timeEntryViewState");
|
|
10
|
+
/**
|
|
11
|
+
* Every entry on one task, all users — the task detail breakdown.
|
|
12
|
+
*
|
|
13
|
+
* `mergeMap`, not `switchMap`: a detail page and an expanded board row can
|
|
14
|
+
* ask for two different tasks at once, and cancelling the first would leave
|
|
15
|
+
* its breakdown stuck In-Progress forever.
|
|
16
|
+
*/
|
|
17
|
+
const fetchTimeEntriesForTaskEpic = (actions$, state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(timeEntryViewReducer_1.fetchTimeEntriesForTask.match), (0, operators_1.mergeMap)((action) => {
|
|
18
|
+
const { taskId, cacheOverride } = action.payload;
|
|
19
|
+
const cached = state$.value.timeEntryViewState.timeEntryIdsByTaskId[taskId];
|
|
20
|
+
const status = state$.value.timeEntryViewState.fetchStateByTaskId[taskId];
|
|
21
|
+
// The reducer has already flipped this request to In-Progress, so the
|
|
22
|
+
// guard reads the COMPLETED cache, never the fetch state -- a fetch
|
|
23
|
+
// state is not an in-flight signal.
|
|
24
|
+
if (cacheOverride !== true &&
|
|
25
|
+
cached != null &&
|
|
26
|
+
status?.fetchState === 'In-Progress') {
|
|
27
|
+
return (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTimeEntriesForTaskSuccess)({ taskId, timeEntryIds: cached }));
|
|
28
|
+
}
|
|
29
|
+
return zeniAPI
|
|
30
|
+
.getJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks/${taskId}/time-entries`)
|
|
31
|
+
.pipe((0, operators_1.mergeMap)((response) => {
|
|
32
|
+
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
33
|
+
const entries = response.data.time_entries ?? [];
|
|
34
|
+
return (0, rxjs_1.of)((0, timeEntryReducer_1.updateTimeEntries)(entries), (0, timeEntryViewReducer_1.fetchTimeEntriesForTaskSuccess)({
|
|
35
|
+
taskId,
|
|
36
|
+
timeEntryIds: entries.map((entry) => entry.time_entry_id),
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
return (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTimeEntriesForTaskFailure)({ taskId, status: response.status }));
|
|
40
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTimeEntriesForTaskFailure)({
|
|
41
|
+
taskId,
|
|
42
|
+
status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch time entries for task errored out' +
|
|
43
|
+
JSON.stringify(error)),
|
|
44
|
+
}))));
|
|
45
|
+
}));
|
|
46
|
+
exports.fetchTimeEntriesForTaskEpic = fetchTimeEntriesForTaskEpic;
|
|
47
|
+
/**
|
|
48
|
+
* One user's entries in an inclusive date range — the weekly timesheet.
|
|
49
|
+
*
|
|
50
|
+
* Needs the `is_deleted + user_id + entry_date` composite index (api #593).
|
|
51
|
+
* Without it the server returns 500, not an empty list, so the failure is
|
|
52
|
+
* surfaced rather than rendered as a blank week.
|
|
53
|
+
*/
|
|
54
|
+
const fetchTimeEntriesForRangeEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0, operators_1.filter)(timeEntryViewReducer_1.fetchTimeEntriesForRange.match), (0, operators_1.mergeMap)((action) => {
|
|
55
|
+
const { userId, startDate, endDate } = action.payload;
|
|
56
|
+
const rangeKey = (0, timeEntryViewState_1.toTimeEntryRangeKey)(userId, startDate, endDate);
|
|
57
|
+
const query = `user_id=${encodeURIComponent(userId)}&start_date=${encodeURIComponent(startDate)}&end_date=${encodeURIComponent(endDate)}`;
|
|
58
|
+
return zeniAPI
|
|
59
|
+
.getJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/time-entries?${query}`)
|
|
60
|
+
.pipe((0, operators_1.mergeMap)((response) => {
|
|
61
|
+
if ((0, responsePayload_1.isSuccessResponse)(response) && response.data != null) {
|
|
62
|
+
const entries = response.data.time_entries ?? [];
|
|
63
|
+
return (0, rxjs_1.of)((0, timeEntryReducer_1.updateTimeEntries)(entries), (0, timeEntryViewReducer_1.fetchTimeEntriesForRangeSuccess)({
|
|
64
|
+
rangeKey,
|
|
65
|
+
timeEntryIds: entries.map((entry) => entry.time_entry_id),
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
return (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTimeEntriesForRangeFailure)({
|
|
69
|
+
rangeKey,
|
|
70
|
+
status: response.status,
|
|
71
|
+
}));
|
|
72
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, timeEntryViewReducer_1.fetchTimeEntriesForRangeFailure)({
|
|
73
|
+
rangeKey,
|
|
74
|
+
status: (0, responsePayload_1.createZeniAPIStatus)('Unexpected Error', 'Fetch time entries for range errored out' +
|
|
75
|
+
JSON.stringify(error)),
|
|
76
|
+
}))));
|
|
77
|
+
}));
|
|
78
|
+
exports.fetchTimeEntriesForRangeEpic = fetchTimeEntriesForRangeEpic;
|