@zeniai/client-epic-state 5.0.32 → 5.0.33-betaRR0

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.
Files changed (112) hide show
  1. package/lib/commonStateTypes/fiscalYearHelpers/formatZeniDateFY.d.ts +1 -0
  2. package/lib/commonStateTypes/fiscalYearHelpers/formatZeniDateFY.js +2 -1
  3. package/lib/entity/file/fileState.d.ts +4 -4
  4. package/lib/entity/file/fileState.js +4 -0
  5. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  6. package/lib/entity/snackbar/snackbarTypes.js +4 -0
  7. package/lib/entity/task/taskPayload.d.ts +3 -0
  8. package/lib/entity/task/taskPayload.js +5 -0
  9. package/lib/entity/task/taskState.d.ts +3 -0
  10. package/lib/entity/tenant/clearAllEpic.d.ts +4 -2
  11. package/lib/entity/tenant/clearAllEpic.js +4 -0
  12. package/lib/entity/transaction/stateTypes/attachment.d.ts +2 -2
  13. package/lib/epic.d.ts +7 -1
  14. package/lib/epic.js +7 -1
  15. package/lib/esm/commonStateTypes/fiscalYearHelpers/formatZeniDateFY.js +1 -0
  16. package/lib/esm/entity/file/fileState.js +4 -0
  17. package/lib/esm/entity/snackbar/snackbarTypes.js +4 -0
  18. package/lib/esm/entity/task/taskPayload.js +5 -0
  19. package/lib/esm/entity/tenant/clearAllEpic.js +4 -0
  20. package/lib/esm/epic.js +7 -1
  21. package/lib/esm/index.js +10 -4
  22. package/lib/esm/init.js +24 -4
  23. package/lib/esm/reducer.js +6 -0
  24. package/lib/esm/view/taskManager/cannedResponsesView/cannedResponses.js +1 -0
  25. package/lib/esm/view/taskManager/cannedResponsesView/cannedResponsesPayload.js +12 -0
  26. package/lib/esm/view/taskManager/cannedResponsesView/cannedResponsesReducer.js +64 -0
  27. package/lib/esm/view/taskManager/cannedResponsesView/cannedResponsesSelector.js +3 -0
  28. package/lib/esm/view/taskManager/cannedResponsesView/epics/deleteCannedResponseEpic.js +17 -0
  29. package/lib/esm/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.js +21 -0
  30. package/lib/esm/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.js +29 -0
  31. package/lib/esm/view/taskManager/taskDetailView/epics/archiveTaskEpic.js +3 -0
  32. package/lib/esm/view/taskManager/taskDetailView/epics/deleteTaskEpic.js +5 -0
  33. package/lib/esm/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +1 -0
  34. package/lib/esm/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +1 -0
  35. package/lib/esm/view/taskManager/taskDetailView/epics/snoozeTaskEpic.js +66 -0
  36. package/lib/esm/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +57 -0
  37. package/lib/esm/view/taskManager/taskDetailView/taskDetail.js +4 -0
  38. package/lib/esm/view/taskManager/taskDetailView/taskDetailReducer.js +60 -7
  39. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +12 -0
  40. package/lib/esm/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.js +56 -21
  41. package/lib/esm/view/taskManager/taskListView/epics/dragNDropTasksEpic.js +1 -0
  42. package/lib/esm/view/taskManager/taskListView/epics/fetchTaskListEpic.js +13 -4
  43. package/lib/esm/view/taskManager/taskListView/epics/updateTaskFromListViewEpic.js +3 -2
  44. package/lib/esm/view/taskManager/taskListView/taskList.js +7 -0
  45. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +106 -12
  46. package/lib/esm/view/taskManager/taskListView/taskListSelector.js +48 -39
  47. package/lib/esm/view/zeniOAuthView/epics/approveOAuthConsentEpic.js +64 -0
  48. package/lib/esm/view/zeniOAuthView/zeniOAuthParamsParser.js +41 -0
  49. package/lib/esm/view/zeniOAuthView/zeniOAuthReducer.js +49 -0
  50. package/lib/esm/view/zeniOAuthView/zeniOAuthSelector.js +3 -0
  51. package/lib/esm/view/zeniOAuthView/zeniOAuthState.js +1 -0
  52. package/lib/index.d.ts +12 -4
  53. package/lib/index.js +28 -5
  54. package/lib/init.d.ts +5 -3
  55. package/lib/init.js +24 -4
  56. package/lib/reducer.d.ts +6 -0
  57. package/lib/reducer.js +6 -0
  58. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  59. package/lib/view/taskManager/cannedResponsesView/cannedResponses.d.ts +19 -0
  60. package/lib/view/taskManager/cannedResponsesView/cannedResponses.js +2 -0
  61. package/lib/view/taskManager/cannedResponsesView/cannedResponsesPayload.d.ts +21 -0
  62. package/lib/view/taskManager/cannedResponsesView/cannedResponsesPayload.js +15 -0
  63. package/lib/view/taskManager/cannedResponsesView/cannedResponsesReducer.d.ts +30 -0
  64. package/lib/view/taskManager/cannedResponsesView/cannedResponsesReducer.js +68 -0
  65. package/lib/view/taskManager/cannedResponsesView/cannedResponsesSelector.d.ts +3 -0
  66. package/lib/view/taskManager/cannedResponsesView/cannedResponsesSelector.js +6 -0
  67. package/lib/view/taskManager/cannedResponsesView/epics/deleteCannedResponseEpic.d.ts +7 -0
  68. package/lib/view/taskManager/cannedResponsesView/epics/deleteCannedResponseEpic.js +21 -0
  69. package/lib/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.d.ts +7 -0
  70. package/lib/view/taskManager/cannedResponsesView/epics/fetchCannedResponsesEpic.js +25 -0
  71. package/lib/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.d.ts +7 -0
  72. package/lib/view/taskManager/cannedResponsesView/epics/saveCannedResponseEpic.js +33 -0
  73. package/lib/view/taskManager/taskDetailView/epics/archiveTaskEpic.d.ts +2 -1
  74. package/lib/view/taskManager/taskDetailView/epics/archiveTaskEpic.js +3 -0
  75. package/lib/view/taskManager/taskDetailView/epics/deleteTaskEpic.d.ts +2 -1
  76. package/lib/view/taskManager/taskDetailView/epics/deleteTaskEpic.js +5 -0
  77. package/lib/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +1 -0
  78. package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +1 -0
  79. package/lib/view/taskManager/taskDetailView/epics/snoozeTaskEpic.d.ts +9 -0
  80. package/lib/view/taskManager/taskDetailView/epics/snoozeTaskEpic.js +70 -0
  81. package/lib/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.d.ts +9 -0
  82. package/lib/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +61 -0
  83. package/lib/view/taskManager/taskDetailView/taskDetail.d.ts +2 -0
  84. package/lib/view/taskManager/taskDetailView/taskDetail.js +4 -0
  85. package/lib/view/taskManager/taskDetailView/taskDetailReducer.d.ts +10 -1
  86. package/lib/view/taskManager/taskDetailView/taskDetailReducer.js +61 -8
  87. package/lib/view/taskManager/taskDetailView/taskDetailSelector.d.ts +5 -0
  88. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +12 -0
  89. package/lib/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.d.ts +6 -7
  90. package/lib/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.js +56 -22
  91. package/lib/view/taskManager/taskListView/epics/dragNDropTasksEpic.js +1 -0
  92. package/lib/view/taskManager/taskListView/epics/fetchTaskListEpic.js +13 -4
  93. package/lib/view/taskManager/taskListView/epics/updateTaskFromListViewEpic.js +3 -2
  94. package/lib/view/taskManager/taskListView/taskList.d.ts +14 -1
  95. package/lib/view/taskManager/taskListView/taskList.js +8 -1
  96. package/lib/view/taskManager/taskListView/taskListPayload.d.ts +3 -0
  97. package/lib/view/taskManager/taskListView/taskListReducer.d.ts +9 -6
  98. package/lib/view/taskManager/taskListView/taskListReducer.js +107 -13
  99. package/lib/view/taskManager/taskListView/taskListSelector.d.ts +9 -3
  100. package/lib/view/taskManager/taskListView/taskListSelector.js +48 -39
  101. package/lib/view/zeniOAuthView/epics/approveOAuthConsentEpic.d.ts +27 -0
  102. package/lib/view/zeniOAuthView/epics/approveOAuthConsentEpic.js +68 -0
  103. package/lib/view/zeniOAuthView/zeniOAuthParamsParser.d.ts +16 -0
  104. package/lib/view/zeniOAuthView/zeniOAuthParamsParser.js +44 -0
  105. package/lib/view/zeniOAuthView/zeniOAuthReducer.d.ts +29 -0
  106. package/lib/view/zeniOAuthView/zeniOAuthReducer.js +53 -0
  107. package/lib/view/zeniOAuthView/zeniOAuthSelector.d.ts +5 -0
  108. package/lib/view/zeniOAuthView/zeniOAuthSelector.js +9 -0
  109. package/lib/view/zeniOAuthView/zeniOAuthState.d.ts +4 -0
  110. package/lib/view/zeniOAuthView/zeniOAuthState.js +2 -0
  111. package/package.json +1 -1
  112. package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
@@ -29,7 +29,23 @@ const initialTaskIdsByDueDate = {
29
29
  today: [],
30
30
  upcoming: [],
31
31
  };
32
+ const initialTabData = {
33
+ taskIds: [],
34
+ taskIdsByAssignees: {},
35
+ taskIdsByDueDate: { ...initialTaskIdsByDueDate },
36
+ taskIdsByGroupsIds: {},
37
+ taskIdsByPriority: { ...initialTaskIdsByPriority },
38
+ taskIdsByStatus: { ...initialTaskIdsByStatus },
39
+ taskIdsByTags: {},
40
+ };
32
41
  export const initialState = {
42
+ currentTab: 'live',
43
+ byTab: {
44
+ live: { ...initialTabData },
45
+ archived: { ...initialTabData },
46
+ deleted: { ...initialTabData },
47
+ snoozed: { ...initialTabData },
48
+ },
33
49
  taskIds: [],
34
50
  taskIdsByGroupsIds: {},
35
51
  taskIdsByPriority: initialTaskIdsByPriority,
@@ -86,17 +102,30 @@ const taskList = createSlice({
86
102
  draft.error = undefined;
87
103
  },
88
104
  updateTaskList(draft, action) {
89
- const { data } = action.payload;
105
+ const { data, archived, deleted, snoozed } = action.payload;
90
106
  draft.fetchState = 'Completed';
91
107
  draft.error = undefined;
92
- const { taskIds, taskIdsGroupedByPriority, taskIdsByGroupIds, taskIdsGroupedByStatus, taskIdsByAssignees, taskIdsByDueDate, taskIdsByTags, } = getGroupedTaskIds(data);
93
- draft.taskIds = taskIds;
94
- draft.taskIdsByGroupsIds = taskIdsByGroupIds;
95
- draft.taskIdsByPriority = taskIdsGroupedByPriority;
96
- draft.taskIdsByStatus = taskIdsGroupedByStatus;
97
- draft.taskIdsByAssignees = taskIdsByAssignees;
98
- draft.taskIdsByDueDate = taskIdsByDueDate;
99
- draft.taskIdsByTags = taskIdsByTags;
108
+ // Group live tasks
109
+ const liveGrouped = getGroupedTaskIds(data);
110
+ draft.byTab.live = toTabData(liveGrouped);
111
+ // Group other tabs if provided
112
+ if (archived != null) {
113
+ draft.byTab.archived = toTabData(getGroupedTaskIds(archived));
114
+ }
115
+ if (deleted != null) {
116
+ draft.byTab.deleted = toTabData(getGroupedTaskIds(deleted));
117
+ }
118
+ if (snoozed != null) {
119
+ draft.byTab.snoozed = toTabData(getGroupedTaskIds(snoozed));
120
+ }
121
+ // Copy active tab data to top-level fields
122
+ const activeTabData = draft.byTab[draft.currentTab];
123
+ copyTabDataToTopLevel(draft, activeTabData);
124
+ },
125
+ updateTaskListTab(draft, action) {
126
+ draft.currentTab = action.payload;
127
+ const activeTabData = draft.byTab[action.payload];
128
+ copyTabDataToTopLevel(draft, activeTabData);
100
129
  },
101
130
  initiateTaskListLocalData(draft, action) {
102
131
  const { taskGroupById } = action.payload;
@@ -184,10 +213,13 @@ const taskList = createSlice({
184
213
  },
185
214
  },
186
215
  updateTasksListOnBulkUpdateTasksSuccess(draft, action) {
187
- const { taskIds, groupId } = action.payload;
216
+ const { taskIds, groupId, removeFromList } = action.payload;
188
217
  draft.updateTasksFetchState.fetchState = 'Completed';
189
218
  draft.updateTasksFetchState.error = undefined;
190
- if (groupId != null) {
219
+ if (removeFromList) {
220
+ removeTaskIds(draft, taskIds);
221
+ }
222
+ else if (groupId != null) {
191
223
  const { groupByKey } = draft.uiState;
192
224
  if (groupByKey === 'groupName') {
193
225
  Object.keys(draft.taskIdsByGroupsIds).forEach((gId) => {
@@ -279,6 +311,7 @@ const taskList = createSlice({
279
311
  newTaskGroupName: '',
280
312
  selectedTaskIds: [],
281
313
  };
314
+ syncTopLevelToActiveTab(draft);
282
315
  },
283
316
  updateTasksListOnBulkUpdateTasksFailure(draft, action) {
284
317
  draft.updateTasksFetchState.fetchState = 'Error';
@@ -298,6 +331,7 @@ const taskList = createSlice({
298
331
  }
299
332
  delete draft.taskIdsByGroupsIds[taskGroupId];
300
333
  delete draft.localData.existingTaskGroupNamesByGroupId[taskGroupId];
334
+ syncTopLevelToActiveTab(draft);
301
335
  },
302
336
  updateTaskListOnNewTaskCreationSuccess(draft, action) {
303
337
  const { task, taskGroupId, currentTaskState } = action.payload;
@@ -332,6 +366,7 @@ const taskList = createSlice({
332
366
  doUpdateTaskList(draft, task, taskGroupId);
333
367
  }
334
368
  });
369
+ syncTopLevelToActiveTab(draft);
335
370
  },
336
371
  updateTaskListOnTaskGroupNameUpdate(draft, action) {
337
372
  const { taskGroupName, taskGroupId } = action.payload;
@@ -395,6 +430,7 @@ const taskList = createSlice({
395
430
  };
396
431
  }
397
432
  }
433
+ syncTopLevelToActiveTab(draft);
398
434
  },
399
435
  updateTasksListOnUpdateTaskSuccess(draft) {
400
436
  draft.updateTasksFetchState.fetchState = 'Completed';
@@ -404,12 +440,15 @@ const taskList = createSlice({
404
440
  draft.updateTasksFetchState.fetchState = 'Error';
405
441
  draft.updateTasksFetchState.error = action.payload.error;
406
442
  },
443
+ removeTaskFromList(draft, action) {
444
+ removeTaskIds(draft, [action.payload]);
445
+ },
407
446
  clearTaskList(draft) {
408
447
  Object.assign(draft, initialState);
409
448
  },
410
449
  },
411
450
  });
412
- export const { fetchTaskListPage, fetchTaskList, updateTaskList, updateTaskListFetchStatus, updateTaskListSearchText, updateTaskListUIState, updateTaskFilters, updateTaskListOnNewGroupCreationSuccess, bulkUpdateTaskList, updateTasksListOnBulkUpdateTasksSuccess, updateTasksListOnBulkUpdateTasksFailure, initiateTaskListLocalData, updateTaskListLocalData, updateTaskListOnTaskGroupDeletion, dragNDropTasks, updateTaskListOnNewTaskCreationSuccess, updateTaskListOnTaskGroupNameUpdate, updateTaskFromListView, updateTasksListOnUpdateTaskSuccess, updateTasksListOnUpdateTaskFailure, updateTaskListOnCreateTaskFromTemplateSuccess, clearTaskList, } = taskList.actions;
451
+ export const { fetchTaskListPage, fetchTaskList, updateTaskList, updateTaskListTab, updateTaskListFetchStatus, updateTaskListSearchText, updateTaskListUIState, updateTaskFilters, updateTaskListOnNewGroupCreationSuccess, bulkUpdateTaskList, updateTasksListOnBulkUpdateTasksSuccess, updateTasksListOnBulkUpdateTasksFailure, initiateTaskListLocalData, updateTaskListLocalData, updateTaskListOnTaskGroupDeletion, dragNDropTasks, updateTaskListOnNewTaskCreationSuccess, updateTaskListOnTaskGroupNameUpdate, updateTaskFromListView, updateTasksListOnUpdateTaskSuccess, updateTasksListOnUpdateTaskFailure, updateTaskListOnCreateTaskFromTemplateSuccess, removeTaskFromList, clearTaskList, } = taskList.actions;
413
452
  export default taskList.reducer;
414
453
  /**
415
454
  * helpers
@@ -488,6 +527,61 @@ const getTagsGroupKey = (tags) => {
488
527
  tagIds.sort();
489
528
  return tagIds.join(',');
490
529
  };
530
+ const toTabData = (grouped) => ({
531
+ taskIds: grouped.taskIds,
532
+ taskIdsByAssignees: grouped.taskIdsByAssignees,
533
+ taskIdsByDueDate: grouped.taskIdsByDueDate,
534
+ taskIdsByGroupsIds: grouped.taskIdsByGroupIds,
535
+ taskIdsByPriority: grouped.taskIdsGroupedByPriority,
536
+ taskIdsByStatus: grouped.taskIdsGroupedByStatus,
537
+ taskIdsByTags: grouped.taskIdsByTags,
538
+ });
539
+ const syncTopLevelToActiveTab = (draft) => {
540
+ const tabData = draft.byTab[draft.currentTab];
541
+ tabData.taskIds = draft.taskIds;
542
+ tabData.taskIdsByGroupsIds = draft.taskIdsByGroupsIds;
543
+ tabData.taskIdsByPriority = draft.taskIdsByPriority;
544
+ tabData.taskIdsByStatus = draft.taskIdsByStatus;
545
+ tabData.taskIdsByAssignees = draft.taskIdsByAssignees;
546
+ tabData.taskIdsByDueDate = draft.taskIdsByDueDate;
547
+ tabData.taskIdsByTags = draft.taskIdsByTags;
548
+ };
549
+ const copyTabDataToTopLevel = (draft, tabData) => {
550
+ draft.taskIds = tabData.taskIds;
551
+ draft.taskIdsByGroupsIds = tabData.taskIdsByGroupsIds;
552
+ draft.taskIdsByPriority = tabData.taskIdsByPriority;
553
+ draft.taskIdsByStatus = tabData.taskIdsByStatus;
554
+ draft.taskIdsByAssignees = tabData.taskIdsByAssignees;
555
+ draft.taskIdsByDueDate = tabData.taskIdsByDueDate;
556
+ draft.taskIdsByTags = tabData.taskIdsByTags;
557
+ };
558
+ const removeIdsFromTabData = (tabData, taskIds) => {
559
+ const removedSet = new Set(taskIds);
560
+ const isRemoved = (id) => removedSet.has(id);
561
+ tabData.taskIds = tabData.taskIds.filter((id) => !isRemoved(id));
562
+ Object.keys(tabData.taskIdsByGroupsIds).forEach((gId) => {
563
+ tabData.taskIdsByGroupsIds[gId] = tabData.taskIdsByGroupsIds[gId].filter((id) => !isRemoved(id));
564
+ });
565
+ Object.keys(tabData.taskIdsByPriority).forEach((key) => {
566
+ tabData.taskIdsByPriority[key] = tabData.taskIdsByPriority[key].filter((id) => !isRemoved(id));
567
+ });
568
+ Object.keys(tabData.taskIdsByStatus).forEach((key) => {
569
+ tabData.taskIdsByStatus[key] = tabData.taskIdsByStatus[key].filter((id) => !isRemoved(id));
570
+ });
571
+ Object.keys(tabData.taskIdsByDueDate).forEach((key) => {
572
+ tabData.taskIdsByDueDate[key] = tabData.taskIdsByDueDate[key].filter((id) => !isRemoved(id));
573
+ });
574
+ Object.keys(tabData.taskIdsByAssignees).forEach((key) => {
575
+ tabData.taskIdsByAssignees[key] = tabData.taskIdsByAssignees[key].filter((id) => !isRemoved(id));
576
+ });
577
+ Object.keys(tabData.taskIdsByTags).forEach((key) => {
578
+ tabData.taskIdsByTags[key] = tabData.taskIdsByTags[key].filter((id) => !isRemoved(id));
579
+ });
580
+ };
581
+ const removeTaskIds = (draft, taskIds) => {
582
+ removeIdsFromTabData(draft, taskIds);
583
+ removeIdsFromTabData(draft.byTab[draft.currentTab], taskIds);
584
+ };
491
585
  const getGroupedTaskIds = (tasks) => {
492
586
  const taskIds = [];
493
587
  const taskIdsByGroupIds = {};
@@ -1,5 +1,4 @@
1
1
  import { createSelector } from '@reduxjs/toolkit';
2
- import isEqual from 'lodash/isEqual';
3
2
  import orderBy from 'lodash/orderBy';
4
3
  import { getSortOrder, } from '../../../commonPayloadTypes/sortOrderPayload';
5
4
  import { reduceAnyFetchState } from '../../../commonStateTypes/reduceFetchState';
@@ -15,11 +14,6 @@ import { getUserGroupList } from '../../userGroupListView/userGroupListViewSelec
15
14
  import { getUserList, } from '../../userListView/userListViewSelector';
16
15
  import { allTaskPriority, allTaskStatus, } from '../taskDetailView/taskDetailSelector';
17
16
  import { DUE_DATE_GROUP_KEYS, } from './taskList';
18
- import { initialState } from './taskListReducer';
19
- const isDefaultFilters = (taskListState) => {
20
- return (isEqual(taskListState.filters, initialState.filters) &&
21
- initialState.uiState.searchText === taskListState.uiState.searchText);
22
- };
23
17
  const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
24
18
  const tasksInOrder = orderBy(tasksList, (task) => {
25
19
  const tagsByIds = getTagsByIds(tagState, task.tagIds);
@@ -34,8 +28,12 @@ const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
34
28
  return task.status.name;
35
29
  case 'priority':
36
30
  return ALL_PRIORITY_CODE.indexOf(task.priority.code);
31
+ case 'assignee':
32
+ return task.firstAssigneeSortName;
37
33
  case 'timeSpent':
38
34
  return task.timeSpent;
35
+ case 'visibility':
36
+ return task.isPrivate === true ? 1 : 0;
39
37
  case 'name':
40
38
  default:
41
39
  return task.name.toLowerCase();
@@ -43,7 +41,7 @@ const sortTasksList = (tagState, tasksList, sortKey, sortOrder) => {
43
41
  }, sortOrder);
44
42
  return tasksInOrder;
45
43
  };
46
- const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRoleState, addressState, taskIds, tagState, }) => {
44
+ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRoleState, addressState, taskIds, tagState, userGroupsState, }) => {
47
45
  const { sortKey, searchText } = taskListState.uiState;
48
46
  const sortOrder = getSortOrder(taskListState.uiState.sortOrder);
49
47
  const tasks = taskIds
@@ -53,7 +51,15 @@ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRole
53
51
  const assigneeUsers = task.assignees
54
52
  .map((userId) => getUserAndUserRole(userState, userRoleState, addressState, userId))
55
53
  .filter((value) => value != null);
56
- return { ...task, assigneeUsers };
54
+ const firstUser = assigneeUsers[0]?.user;
55
+ const firstUserName = (firstUser != null ? getUserName(firstUser) : '') ||
56
+ firstUser?.email ||
57
+ '';
58
+ const firstGroupName = task.groupAssignees
59
+ .map((gId) => userGroupsState.userGroupByUserGroupId[gId]?.name)
60
+ .find((name) => name != null && name !== '');
61
+ const firstAssigneeSortName = (firstUserName !== '' ? firstUserName : (firstGroupName ?? '')).toLowerCase();
62
+ return { ...task, assigneeUsers, firstAssigneeSortName };
57
63
  }
58
64
  return undefined;
59
65
  })
@@ -62,7 +68,7 @@ const getTasksWithUserDetails = ({ taskListState, taskState, userState, userRole
62
68
  const sortedTaskList = sortTasksList(tagState, filteredTaskList, sortKey, sortOrder);
63
69
  return sortedTaskList;
64
70
  };
65
- const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState, taskGroupViewState, userState, userRoleState, addressState, tagState, }) => {
71
+ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupState, taskGroupViewState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
66
72
  const { taskGroupsIds } = taskGroupViewState;
67
73
  const taskGroupsWithTasksList = taskGroupsIds
68
74
  .map((taskGroupId) => {
@@ -76,6 +82,7 @@ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupStat
76
82
  addressState,
77
83
  taskIds,
78
84
  tagState,
85
+ userGroupsState,
79
86
  });
80
87
  if (taskGroup != null) {
81
88
  return {
@@ -104,12 +111,9 @@ const getTaskListGroupedByGroupName = ({ taskListState, taskState, taskGroupStat
104
111
  if (defaultTaskGroup != null) {
105
112
  sortedTaskGroupsWithTasksList.push(defaultTaskGroup);
106
113
  }
107
- if (isDefaultFilters(taskListState) === false) {
108
- return sortedTaskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
109
- }
110
- return sortedTaskGroupsWithTasksList;
114
+ return sortedTaskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
111
115
  };
112
- export const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, }) => {
116
+ export const getTaskListGroupedByAssignees = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
113
117
  const assigneeGroups = Object.keys(taskListState.taskIdsByAssignees);
114
118
  const taskGroupsWithTasksList = assigneeGroups.map((groupKey) => {
115
119
  const taskIds = taskListState.taskIdsByAssignees[groupKey];
@@ -121,6 +125,7 @@ export const getTaskListGroupedByAssignees = ({ taskListState, taskState, userSt
121
125
  addressState,
122
126
  taskIds,
123
127
  tagState,
128
+ userGroupsState,
124
129
  });
125
130
  const assigneeIds = groupKey.split(',');
126
131
  const groupName = getUsersByUserIds(userState, assigneeIds)
@@ -136,12 +141,9 @@ export const getTaskListGroupedByAssignees = ({ taskListState, taskState, userSt
136
141
  tasks: tasksWithUserDetails,
137
142
  };
138
143
  });
139
- if (isDefaultFilters(taskListState) === false) {
140
- return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
141
- }
142
- return taskGroupsWithTasksList;
144
+ return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
143
145
  };
144
- export const getTaskListGroupedByPriority = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, }) => {
146
+ export const getTaskListGroupedByPriority = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
145
147
  const taskGroupsWithTasksList = ALL_PRIORITY_CODE.map((groupKey) => {
146
148
  const taskIds = taskListState.taskIdsByPriority[groupKey] ?? [];
147
149
  const tasksWithUserDetails = getTasksWithUserDetails({
@@ -152,6 +154,7 @@ export const getTaskListGroupedByPriority = ({ taskListState, taskState, userSta
152
154
  addressState,
153
155
  taskIds,
154
156
  tagState,
157
+ userGroupsState,
155
158
  });
156
159
  return {
157
160
  groupName: allTaskPriority.find((priority) => priority.code === groupKey)?.name ??
@@ -161,12 +164,9 @@ export const getTaskListGroupedByPriority = ({ taskListState, taskState, userSta
161
164
  tasks: tasksWithUserDetails,
162
165
  };
163
166
  });
164
- if (isDefaultFilters(taskListState) === false) {
165
- return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
166
- }
167
- return taskGroupsWithTasksList;
167
+ return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
168
168
  };
169
- const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, }) => {
169
+ const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
170
170
  const taskGroupsWithTasksList = ALL_TASK_STATUS_CODE.map((groupKey) => {
171
171
  const taskIds = taskListState.taskIdsByStatus[groupKey] ?? [];
172
172
  const tasksWithUserDetails = getTasksWithUserDetails({
@@ -177,6 +177,7 @@ const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userR
177
177
  addressState,
178
178
  taskIds,
179
179
  tagState,
180
+ userGroupsState,
180
181
  });
181
182
  return {
182
183
  groupName: allTaskStatus.find((taskStatus) => taskStatus.code === groupKey)
@@ -186,12 +187,9 @@ const getTaskListGroupedByStatus = ({ taskListState, taskState, userState, userR
186
187
  tasks: tasksWithUserDetails,
187
188
  };
188
189
  });
189
- if (isDefaultFilters(taskListState) === false) {
190
- return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
191
- }
192
- return taskGroupsWithTasksList;
190
+ return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
193
191
  };
194
- const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, }) => {
192
+ const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
195
193
  const tagGroups = Object.keys(taskListState.taskIdsByTags);
196
194
  const taskGroupsWithTasksList = tagGroups.map((groupKey) => {
197
195
  const taskIds = taskListState.taskIdsByTags[groupKey];
@@ -203,6 +201,7 @@ const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRol
203
201
  addressState,
204
202
  taskIds,
205
203
  tagState,
204
+ userGroupsState,
206
205
  });
207
206
  const tagIds = groupKey.split(',');
208
207
  const groupName = getTagsByIds(tagState, tagIds)
@@ -217,12 +216,9 @@ const getTaskListGroupedByTags = ({ taskListState, taskState, userState, userRol
217
216
  tasks: tasksWithUserDetails,
218
217
  };
219
218
  });
220
- if (isDefaultFilters(taskListState) === false) {
221
- return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
222
- }
223
- return taskGroupsWithTasksList;
219
+ return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
224
220
  };
225
- const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, }) => {
221
+ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, userRoleState, addressState, tagState, userGroupsState, }) => {
226
222
  const taskGroupsWithTasksList = DUE_DATE_GROUP_KEYS.map((groupKey) => {
227
223
  const taskIds = taskListState.taskIdsByDueDate[groupKey];
228
224
  const tasksWithUserDetails = getTasksWithUserDetails({
@@ -233,6 +229,7 @@ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, user
233
229
  addressState,
234
230
  taskIds,
235
231
  tagState,
232
+ userGroupsState,
236
233
  });
237
234
  return {
238
235
  groupName: groupKey,
@@ -241,12 +238,9 @@ const getTaskListGroupedByDueDate = ({ taskListState, taskState, userState, user
241
238
  tasks: tasksWithUserDetails,
242
239
  };
243
240
  });
244
- if (isDefaultFilters(taskListState) === false) {
245
- return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
246
- }
247
- return taskGroupsWithTasksList;
241
+ return taskGroupsWithTasksList.filter((group) => group.tasks.length > 0);
248
242
  };
249
- const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupViewState, tagState, }) => {
243
+ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRoleState, addressState, taskGroupState, taskGroupViewState, tagState, userGroupsState, }) => {
250
244
  const { groupByKey } = taskListState.uiState;
251
245
  if (groupByKey === 'groupName') {
252
246
  return getTaskListGroupedByGroupName({
@@ -258,6 +252,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
258
252
  taskGroupState,
259
253
  taskGroupViewState,
260
254
  tagState,
255
+ userGroupsState,
261
256
  });
262
257
  }
263
258
  if (groupByKey === 'assignee') {
@@ -268,6 +263,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
268
263
  userRoleState,
269
264
  addressState,
270
265
  tagState,
266
+ userGroupsState,
271
267
  });
272
268
  }
273
269
  if (groupByKey === 'priority') {
@@ -278,6 +274,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
278
274
  userRoleState,
279
275
  addressState,
280
276
  tagState,
277
+ userGroupsState,
281
278
  });
282
279
  }
283
280
  if (groupByKey === 'status') {
@@ -288,6 +285,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
288
285
  userRoleState,
289
286
  addressState,
290
287
  tagState,
288
+ userGroupsState,
291
289
  });
292
290
  }
293
291
  if (groupByKey === 'tag') {
@@ -298,6 +296,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
298
296
  userRoleState,
299
297
  addressState,
300
298
  tagState,
299
+ userGroupsState,
301
300
  });
302
301
  }
303
302
  return getTaskListGroupedByDueDate({
@@ -307,6 +306,7 @@ const getTaskGroupsWithTaskList = ({ taskListState, taskState, userState, userRo
307
306
  userRoleState,
308
307
  addressState,
309
308
  tagState,
309
+ userGroupsState,
310
310
  });
311
311
  };
312
312
  export const getAllTasks = createSelector((state) => state.taskListState, (state) => state.taskState, (state) => state.userState, (state) => state.userRoleState, (state) => state.userListViewState, (state) => state.addressState, (state) => state.taskGroupState, (state) => state.taskGroupViewState, (state) => state.tagViewState, (state) => state.tagState, (state) => state.taskGroupTemplateState, (state) => state.taskGroupTemplateViewState, (state) => state.userGroupsState, (state) => state.userGroupListViewState, (_state, isTaskTemplatesEnabled) => isTaskTemplatesEnabled, (taskListState, taskState, userState, userRoleState, userListViewState, addressState, taskGroupState, taskGroupViewState, tagViewState, tagState, taskGroupTemplateState, taskGroupTemplateViewState, userGroupsState, userGroupListViewState, isTaskTemplatesEnabled) => {
@@ -320,6 +320,7 @@ export const getAllTasks = createSelector((state) => state.taskListState, (state
320
320
  taskGroupState,
321
321
  taskGroupViewState,
322
322
  tagState,
323
+ userGroupsState,
323
324
  });
324
325
  const fetchStateVariables = [
325
326
  taskListState,
@@ -331,7 +332,15 @@ export const getAllTasks = createSelector((state) => state.taskListState, (state
331
332
  }
332
333
  const { fetchState, error } = reduceAnyFetchState(fetchStateVariables);
333
334
  const taskGroupTemplates = getTaskGroupTemplateByIds(taskGroupTemplateState, taskGroupTemplateViewState.taskGroupTemplateIds);
335
+ const tabCounts = {
336
+ live: taskListState.byTab.live.taskIds.length,
337
+ archived: taskListState.byTab.archived.taskIds.length,
338
+ deleted: taskListState.byTab.deleted.taskIds.length,
339
+ snoozed: taskListState.byTab.snoozed.taskIds.length,
340
+ };
334
341
  return {
342
+ currentTab: taskListState.currentTab,
343
+ tabCounts,
335
344
  uiState: taskListState.uiState,
336
345
  filters: taskListState.filters,
337
346
  taskGroupTemplates,
@@ -0,0 +1,64 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, concatMap, filter, mergeMap, switchMap, } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
4
+ import { createZeniAPIStatus } from '../../../responsePayload';
5
+ import { approveOAuthConsent, approveOAuthConsentFailure, approveOAuthConsentSuccess, } from '../zeniOAuthReducer';
6
+ const failureSnackbar = (reason) => openSnackbar({
7
+ messageSection: 'oauth_consent_approve',
8
+ messageText: 'failed',
9
+ type: 'error',
10
+ variables: [{ variableName: '__reason__', variableValue: reason }],
11
+ });
12
+ export const approveOAuthConsentEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(approveOAuthConsent.match), switchMap((action) => {
13
+ if (state$.value.zeniOAuthViewState.fetchState === 'In-Progress') {
14
+ return from([]);
15
+ }
16
+ const { userId, zeniSessionId, tenantId, clientId, redirectUri, codeChallenge, codeChallengeMethod, state, responseType, } = action.payload;
17
+ const authHeaders = {
18
+ 'zeni-user-id': userId,
19
+ 'zeni-tenant-id': tenantId,
20
+ };
21
+ if (zeniSessionId != null) {
22
+ authHeaders['zeni-session-id'] = zeniSessionId;
23
+ }
24
+ return zeniAPI
25
+ .postJSON(`${zeniAPI.apiEndPoints.authMicroServiceBaseUrl}/oauth/authorize/approve`, {
26
+ client_id: clientId,
27
+ redirect_uri: redirectUri,
28
+ code_challenge: codeChallenge,
29
+ code_challenge_method: codeChallengeMethod,
30
+ state,
31
+ response_type: responseType,
32
+ tenant_id: tenantId,
33
+ }, authHeaders)
34
+ .pipe(concatMap((response) => from(response.json()).pipe(mergeMap((responseJson) => {
35
+ if (!response.ok) {
36
+ const errBody = responseJson;
37
+ const errorMessage = errBody?.error ??
38
+ errBody?.message ??
39
+ errBody?.status?.message ??
40
+ `Authorization failed (${response.status})`;
41
+ return from([
42
+ approveOAuthConsentFailure(createZeniAPIStatus(errorMessage)),
43
+ failureSnackbar(errorMessage),
44
+ ]);
45
+ }
46
+ const body = responseJson;
47
+ const redirectUrl = body?.redirect_url ?? body?.data?.redirect_url;
48
+ if (redirectUrl == null || redirectUrl === '') {
49
+ const noRedirectMessage = 'No redirect URL returned from authorization server';
50
+ return from([
51
+ approveOAuthConsentFailure(createZeniAPIStatus(noRedirectMessage)),
52
+ failureSnackbar(noRedirectMessage),
53
+ ]);
54
+ }
55
+ return of(approveOAuthConsentSuccess({ redirectUrl }));
56
+ }))), catchError((error) => {
57
+ const errorDetail = error instanceof Error ? error.message : JSON.stringify(error);
58
+ const networkErrorMessage = 'OAuth approve request failed: ' + errorDetail;
59
+ return from([
60
+ approveOAuthConsentFailure(createZeniAPIStatus(networkErrorMessage)),
61
+ failureSnackbar(networkErrorMessage),
62
+ ]);
63
+ }));
64
+ }));
@@ -0,0 +1,41 @@
1
+ export function parseOAuthParams(search) {
2
+ const searchParams = new URLSearchParams(search);
3
+ const clientId = searchParams.get('client_id');
4
+ const redirectUri = searchParams.get('redirect_uri');
5
+ const codeChallenge = searchParams.get('code_challenge');
6
+ const codeChallengeMethod = searchParams.get('code_challenge_method') ?? 'S256';
7
+ const state = searchParams.get('state');
8
+ const responseType = searchParams.get('response_type') ?? 'code';
9
+ if (clientId == null || clientId === '') {
10
+ return { params: null, error: 'Missing client_id parameter' };
11
+ }
12
+ if (redirectUri == null || redirectUri === '') {
13
+ return { params: null, error: 'Missing redirect_uri parameter' };
14
+ }
15
+ try {
16
+ void new URL(redirectUri);
17
+ }
18
+ catch {
19
+ return { params: null, error: 'Invalid redirect_uri parameter' };
20
+ }
21
+ if (codeChallenge == null || codeChallenge === '') {
22
+ return {
23
+ params: null,
24
+ error: 'Missing code_challenge parameter (PKCE required)',
25
+ };
26
+ }
27
+ if (state == null || state === '') {
28
+ return { params: null, error: 'Missing state parameter' };
29
+ }
30
+ return {
31
+ params: {
32
+ clientId,
33
+ redirectUri,
34
+ codeChallenge,
35
+ codeChallengeMethod,
36
+ state,
37
+ responseType,
38
+ },
39
+ error: null,
40
+ };
41
+ }
@@ -0,0 +1,49 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ export const initialState = {
3
+ fetchState: 'Not-Started',
4
+ error: undefined,
5
+ approveRedirectUrl: undefined,
6
+ };
7
+ const zeniOAuthView = createSlice({
8
+ name: 'zeniOAuthView',
9
+ initialState,
10
+ reducers: {
11
+ approveOAuthConsent: {
12
+ prepare(userId, zeniSessionId, tenantId, clientId, redirectUri, codeChallenge, codeChallengeMethod, state, responseType) {
13
+ return {
14
+ payload: {
15
+ userId,
16
+ zeniSessionId,
17
+ tenantId,
18
+ clientId,
19
+ redirectUri,
20
+ codeChallenge,
21
+ codeChallengeMethod,
22
+ state,
23
+ responseType,
24
+ },
25
+ };
26
+ },
27
+ reducer(draft) {
28
+ draft.fetchState = 'In-Progress';
29
+ draft.error = undefined;
30
+ draft.approveRedirectUrl = undefined;
31
+ },
32
+ },
33
+ approveOAuthConsentSuccess(draft, action) {
34
+ draft.fetchState = 'Completed';
35
+ draft.error = undefined;
36
+ draft.approveRedirectUrl = action.payload.redirectUrl;
37
+ },
38
+ approveOAuthConsentFailure(draft, action) {
39
+ draft.fetchState = 'Error';
40
+ draft.error = action.payload;
41
+ draft.approveRedirectUrl = undefined;
42
+ },
43
+ clearZeniOAuthView() {
44
+ return initialState;
45
+ },
46
+ },
47
+ });
48
+ export const { approveOAuthConsent, approveOAuthConsentSuccess, approveOAuthConsentFailure, clearZeniOAuthView, } = zeniOAuthView.actions;
49
+ export default zeniOAuthView.reducer;
@@ -0,0 +1,3 @@
1
+ export const getZeniOAuthApproveFetchState = (state) => state.zeniOAuthViewState.fetchState;
2
+ export const getZeniOAuthApproveRedirectUrl = (state) => state.zeniOAuthViewState.approveRedirectUrl;
3
+ export const getZeniOAuthApproveError = (state) => state.zeniOAuthViewState.error?.message ?? null;
@@ -0,0 +1 @@
1
+ export {};