@zeniai/client-epic-state 4.20.2-beta30ND → 4.20.2-beta31RR

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 (65) hide show
  1. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  2. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  3. package/lib/entity/task/taskPayload.d.ts +3 -0
  4. package/lib/entity/task/taskPayload.js +5 -0
  5. package/lib/entity/task/taskState.d.ts +3 -0
  6. package/lib/epic.d.ts +3 -1
  7. package/lib/epic.js +3 -1
  8. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  9. package/lib/esm/entity/task/taskPayload.js +5 -0
  10. package/lib/esm/epic.js +3 -1
  11. package/lib/esm/index.js +4 -4
  12. package/lib/esm/view/companyView/types/cockpitTypes.js +1 -0
  13. package/lib/esm/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +1 -15
  14. package/lib/esm/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +14 -17
  15. package/lib/esm/view/taskManager/taskDetailView/epics/archiveTaskEpic.js +2 -0
  16. package/lib/esm/view/taskManager/taskDetailView/epics/deleteTaskEpic.js +4 -0
  17. package/lib/esm/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +1 -0
  18. package/lib/esm/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +1 -0
  19. package/lib/esm/view/taskManager/taskDetailView/epics/snoozeTaskEpic.js +64 -0
  20. package/lib/esm/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +54 -0
  21. package/lib/esm/view/taskManager/taskDetailView/taskDetail.js +4 -0
  22. package/lib/esm/view/taskManager/taskDetailView/taskDetailReducer.js +49 -1
  23. package/lib/esm/view/taskManager/taskDetailView/taskDetailSelector.js +3 -0
  24. package/lib/esm/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.js +49 -2
  25. package/lib/esm/view/taskManager/taskListView/epics/fetchTaskListEpic.js +13 -3
  26. package/lib/esm/view/taskManager/taskListView/taskList.js +8 -0
  27. package/lib/esm/view/taskManager/taskListView/taskListReducer.js +107 -11
  28. package/lib/esm/view/taskManager/taskListView/taskListSelector.js +56 -17
  29. package/lib/index.d.ts +4 -4
  30. package/lib/index.js +8 -4
  31. package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
  32. package/lib/view/companyTaskManagerView/companyTaskManagerViewReducer.d.ts +2 -2
  33. package/lib/view/companyView/types/cockpitTypes.d.ts +1 -1
  34. package/lib/view/companyView/types/cockpitTypes.js +1 -0
  35. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  36. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.d.ts +0 -2
  37. package/lib/view/profitAndLossClassesView/profitAndLossClassesByClassHorizontalSelector.js +2 -16
  38. package/lib/view/profitAndLossClassesView/profitAndLossHorizontalEnrichment.js +14 -17
  39. package/lib/view/taskManager/taskDetailView/epics/archiveTaskEpic.d.ts +2 -1
  40. package/lib/view/taskManager/taskDetailView/epics/archiveTaskEpic.js +2 -0
  41. package/lib/view/taskManager/taskDetailView/epics/deleteTaskEpic.d.ts +2 -1
  42. package/lib/view/taskManager/taskDetailView/epics/deleteTaskEpic.js +4 -0
  43. package/lib/view/taskManager/taskDetailView/epics/initializeTaskToLocalStoreEpic.js +1 -0
  44. package/lib/view/taskManager/taskDetailView/epics/saveTaskDetailEpic.js +1 -0
  45. package/lib/view/taskManager/taskDetailView/epics/snoozeTaskEpic.d.ts +9 -0
  46. package/lib/view/taskManager/taskDetailView/epics/snoozeTaskEpic.js +68 -0
  47. package/lib/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.d.ts +8 -0
  48. package/lib/view/taskManager/taskDetailView/epics/unsnoozeTaskEpic.js +58 -0
  49. package/lib/view/taskManager/taskDetailView/taskDetail.d.ts +2 -0
  50. package/lib/view/taskManager/taskDetailView/taskDetail.js +4 -0
  51. package/lib/view/taskManager/taskDetailView/taskDetailReducer.d.ts +10 -1
  52. package/lib/view/taskManager/taskDetailView/taskDetailReducer.js +50 -2
  53. package/lib/view/taskManager/taskDetailView/taskDetailSelector.d.ts +2 -0
  54. package/lib/view/taskManager/taskDetailView/taskDetailSelector.js +3 -0
  55. package/lib/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.d.ts +6 -0
  56. package/lib/view/taskManager/taskListView/epics/bulkUpdateTaskListEpic.js +49 -2
  57. package/lib/view/taskManager/taskListView/epics/fetchTaskListEpic.js +13 -3
  58. package/lib/view/taskManager/taskListView/taskList.d.ts +15 -1
  59. package/lib/view/taskManager/taskListView/taskList.js +10 -1
  60. package/lib/view/taskManager/taskListView/taskListPayload.d.ts +3 -0
  61. package/lib/view/taskManager/taskListView/taskListReducer.d.ts +7 -4
  62. package/lib/view/taskManager/taskListView/taskListReducer.js +108 -12
  63. package/lib/view/taskManager/taskListView/taskListSelector.d.ts +8 -3
  64. package/lib/view/taskManager/taskListView/taskListSelector.js +56 -17
  65. package/package.json +1 -1
@@ -72,22 +72,19 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
72
72
  return getEmptyHorizontalSectionBalancesSlice(report.firstMonthOfFY, timeframe);
73
73
  }
74
74
  const { startDate, endDate } = report.selectedPeriod;
75
- const balances = report.classColumns.map((col, columnIndex) => {
76
- const amount = classAmounts[columnIndex] ?? 0;
77
- return {
78
- balance: {
79
- amount,
80
- currencyCode,
81
- currencySymbol,
82
- },
83
- balanceTimeFrame: timeframe,
84
- startDate,
85
- endDate,
86
- type: amount === 0 ? 'default_with_null_balance' : 'default',
87
- id: col.classId,
88
- label: col.className,
89
- };
90
- });
75
+ const balances = report.classColumns.map((col, columnIndex) => ({
76
+ balance: {
77
+ amount: classAmounts[columnIndex] ?? 0,
78
+ currencyCode,
79
+ currencySymbol,
80
+ },
81
+ balanceTimeFrame: timeframe,
82
+ startDate,
83
+ endDate,
84
+ type: 'default',
85
+ id: col.classId,
86
+ label: col.className,
87
+ }));
91
88
  balances.push({
92
89
  balance: {
93
90
  amount: totalAmount,
@@ -97,7 +94,7 @@ function buildHorizontalClassColumnBalancesSlice(report, classAmounts, totalAmou
97
94
  balanceTimeFrame: timeframe,
98
95
  startDate,
99
96
  endDate,
100
- type: totalAmount === 0 ? 'default_with_null_balance' : 'default',
97
+ type: 'default',
101
98
  id: HORIZONTAL_CLASS_TOTAL_BALANCE_ID,
102
99
  label: 'Total',
103
100
  });
@@ -4,6 +4,7 @@ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
4
  import { updateTasks } from '../../../../entity/task/taskReducer';
5
5
  import { getTaskById } from '../../../../entity/task/taskSelector';
6
6
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
7
+ import { removeTaskFromList } from '../../taskListView/taskListReducer';
7
8
  import { archiveTask, archiveTaskSuccessOrFailure } from '../taskDetailReducer';
8
9
  // Epic function to handle archiving of tasks
9
10
  export const archiveTaskEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(archiveTask.match), mergeMap((action) => handleArchiveTask(action.payload.taskId, state$, zeniAPI)));
@@ -25,6 +26,7 @@ const handleApiResponse = (response, taskId) => {
25
26
  response.data.tasks.length > 0) {
26
27
  const fetchActions = [
27
28
  updateTasks(response.data.tasks),
29
+ removeTaskFromList(taskId),
28
30
  archiveTaskSuccessOrFailure({
29
31
  fetchState: 'Completed',
30
32
  taskId,
@@ -2,6 +2,7 @@ import { from, of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
4
  import { createZeniAPIStatus, isSuccessStatus, } from '../../../../responsePayload';
5
+ import { removeTaskFromList } from '../../taskListView/taskListReducer';
5
6
  import { deleteTask, deleteTaskSuccessOrFailure } from '../taskDetailReducer';
6
7
  export const deleteTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(deleteTask.match), mergeMap((action) => {
7
8
  const { taskId } = action.payload;
@@ -10,6 +11,9 @@ export const deleteTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filt
10
11
  .pipe(mergeMap((response) => {
11
12
  if (isSuccessStatus(response)) {
12
13
  const actions = [];
14
+ if (taskId != null) {
15
+ actions.push(removeTaskFromList(taskId));
16
+ }
13
17
  actions.push(deleteTaskSuccessOrFailure({ fetchState: 'Completed', taskId }));
14
18
  return from(actions);
15
19
  }
@@ -25,6 +25,7 @@ export const initializeTaskToLocalStoreEpic = (actions$, state$) => actions$.pip
25
25
  savedRecurringEndDate: task.recurringEndDate,
26
26
  recurringStartDate: task.recurringStartDate,
27
27
  timeSpent: task.timeSpent,
28
+ isPrivate: task.isPrivate,
28
29
  };
29
30
  const actions = [
30
31
  saveTaskUpdatesToLocalStore({ taskDetailLocalData, taskId }),
@@ -94,5 +94,6 @@ const prepareTaskPayload = (state, taskId, taskGroupId) => {
94
94
  sync_token: syncToken,
95
95
  task_group_ids: taskGroupId != null ? [taskGroupId] : [],
96
96
  time_spent: convertHHMMStrToMinutes(localData.timeSpent),
97
+ ...(taskId == null ? { is_private: localData.isPrivate ?? false } : {}),
97
98
  };
98
99
  };
@@ -0,0 +1,64 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
+ import { updateTasks } from '../../../../entity/task/taskReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
+ import { date } from '../../../../zeniDayJS';
7
+ import { removeTaskFromList } from '../../taskListView/taskListReducer';
8
+ import { snoozeTask, snoozeTaskSuccessOrFailure } from '../taskDetailReducer';
9
+ export const snoozeTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(snoozeTask.match), mergeMap((action) => handleSnoozeTask(action.payload.taskId, action.payload.snoozedUntil, zeniAPI)));
10
+ const formatSnoozedUntilForDisplay = (snoozedUntil) => date(snoozedUntil).format('MMM D, h:mm A');
11
+ const handleSnoozeTask = (taskId, snoozedUntil, zeniAPI) => {
12
+ const snoozeTaskApi$ = zeniAPI.postAndGetJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks/${taskId}/snooze`, { snoozed_until: snoozedUntil });
13
+ return snoozeTaskApi$.pipe(mergeMap((response) => handleApiResponse(response, taskId, snoozedUntil)), catchError((error) => of(snoozeTaskSuccessOrFailure({
14
+ fetchState: 'Error',
15
+ taskId,
16
+ error: createZeniAPIStatus('Unexpected Error', 'Snooze Task REST API call errored out: ' + JSON.stringify(error)),
17
+ }))));
18
+ };
19
+ const handleApiResponse = (response, taskId, snoozedUntil) => {
20
+ if (isSuccessResponse(response) &&
21
+ response.data != null &&
22
+ response.data.tasks.length > 0) {
23
+ const fetchActions = [
24
+ updateTasks(response.data.tasks),
25
+ removeTaskFromList(taskId),
26
+ snoozeTaskSuccessOrFailure({
27
+ fetchState: 'Completed',
28
+ taskId,
29
+ }),
30
+ openSnackbar({
31
+ messageSection: 'snooze_task',
32
+ messageText: 'success',
33
+ type: 'success',
34
+ variables: [
35
+ {
36
+ variableName: '_snoozed_until_',
37
+ variableValue: formatSnoozedUntilForDisplay(snoozedUntil),
38
+ },
39
+ ],
40
+ }),
41
+ ];
42
+ return from(fetchActions);
43
+ }
44
+ else {
45
+ return from([
46
+ snoozeTaskSuccessOrFailure({
47
+ fetchState: 'Error',
48
+ taskId,
49
+ error: response.status,
50
+ }),
51
+ openSnackbar({
52
+ messageSection: 'snooze_task',
53
+ messageText: 'failed',
54
+ type: 'error',
55
+ variables: [
56
+ {
57
+ variableName: '_api-error_',
58
+ variableValue: response.status.message,
59
+ },
60
+ ],
61
+ }),
62
+ ]);
63
+ }
64
+ };
@@ -0,0 +1,54 @@
1
+ import { from, of } from 'rxjs';
2
+ import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { openSnackbar } from '../../../../entity/snackbar/snackbarReducer';
4
+ import { updateTasks } from '../../../../entity/task/taskReducer';
5
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
6
+ import { snoozeTaskSuccessOrFailure, unsnoozeTask, } from '../taskDetailReducer';
7
+ export const unsnoozeTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(unsnoozeTask.match), mergeMap((action) => handleUnsnoozeTask(action.payload.taskId, zeniAPI)));
8
+ const handleUnsnoozeTask = (taskId, zeniAPI) => {
9
+ const unsnoozeTaskApi$ = zeniAPI.deleteAndGetJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks/${taskId}/snooze`);
10
+ return unsnoozeTaskApi$.pipe(mergeMap((response) => handleApiResponse(response, taskId)), catchError((error) => of(snoozeTaskSuccessOrFailure({
11
+ fetchState: 'Error',
12
+ taskId,
13
+ error: createZeniAPIStatus('Unexpected Error', 'Unsnooze Task REST API call errored out: ' + JSON.stringify(error)),
14
+ }))));
15
+ };
16
+ const handleApiResponse = (response, taskId) => {
17
+ if (isSuccessResponse(response) &&
18
+ response.data != null &&
19
+ response.data.tasks.length > 0) {
20
+ const fetchActions = [
21
+ updateTasks(response.data.tasks),
22
+ snoozeTaskSuccessOrFailure({
23
+ fetchState: 'Completed',
24
+ taskId,
25
+ }),
26
+ openSnackbar({
27
+ messageSection: 'unsnooze_task',
28
+ messageText: 'success',
29
+ type: 'success',
30
+ }),
31
+ ];
32
+ return from(fetchActions);
33
+ }
34
+ else {
35
+ return from([
36
+ snoozeTaskSuccessOrFailure({
37
+ fetchState: 'Error',
38
+ taskId,
39
+ error: response.status,
40
+ }),
41
+ openSnackbar({
42
+ messageSection: 'unsnooze_task',
43
+ messageText: 'failed',
44
+ type: 'error',
45
+ variables: [
46
+ {
47
+ variableName: '_api-error_',
48
+ variableValue: response.status.message,
49
+ },
50
+ ],
51
+ }),
52
+ ]);
53
+ }
54
+ };
@@ -31,5 +31,9 @@ export const initialTaskDetail = {
31
31
  fetchState: 'Not-Started',
32
32
  error: undefined,
33
33
  },
34
+ snoozeStatus: {
35
+ fetchState: 'Not-Started',
36
+ error: undefined,
37
+ },
34
38
  taskDetailLocalData: initialTaskDetailLocalData,
35
39
  };
@@ -185,6 +185,54 @@ const taskDetailView = createSlice({
185
185
  draft.editTaskStateById[taskId].archiveStatus.fetchState = fetchState;
186
186
  draft.editTaskStateById[taskId].archiveStatus.error = error;
187
187
  },
188
+ snoozeTask: {
189
+ reducer(draft, action) {
190
+ const { taskId } = action.payload;
191
+ draft.editTaskStateById[taskId] = {
192
+ ...(draft.editTaskStateById[taskId] != null
193
+ ? draft.editTaskStateById[taskId]
194
+ : initialTaskDetail),
195
+ snoozeStatus: {
196
+ fetchState: 'In-Progress',
197
+ error: undefined,
198
+ },
199
+ };
200
+ },
201
+ prepare(taskId, snoozedUntil) {
202
+ return {
203
+ payload: {
204
+ taskId,
205
+ snoozedUntil,
206
+ },
207
+ };
208
+ },
209
+ },
210
+ snoozeTaskSuccessOrFailure(draft, action) {
211
+ const { taskId, fetchState, error } = action.payload;
212
+ draft.editTaskStateById[taskId].snoozeStatus.fetchState = fetchState;
213
+ draft.editTaskStateById[taskId].snoozeStatus.error = error;
214
+ },
215
+ unsnoozeTask: {
216
+ reducer(draft, action) {
217
+ const { taskId } = action.payload;
218
+ draft.editTaskStateById[taskId] = {
219
+ ...(draft.editTaskStateById[taskId] != null
220
+ ? draft.editTaskStateById[taskId]
221
+ : initialTaskDetail),
222
+ snoozeStatus: {
223
+ fetchState: 'In-Progress',
224
+ error: undefined,
225
+ },
226
+ };
227
+ },
228
+ prepare(taskId) {
229
+ return {
230
+ payload: {
231
+ taskId,
232
+ },
233
+ };
234
+ },
235
+ },
188
236
  deleteTask: {
189
237
  reducer(draft, action) {
190
238
  const { taskId } = action.payload;
@@ -270,5 +318,5 @@ const taskDetailView = createSlice({
270
318
  },
271
319
  },
272
320
  });
273
- export const { fetchTaskDetailPage, fetchTaskDetail, initializeTaskToLocalStore, updateEditTaskFetchStatus, saveTaskUpdatesToLocalStore, discardTaskUpdatesInLocalStore, saveTaskDetail, archiveTask, archiveTaskSuccessOrFailure, saveTaskSuccessOrFailure, deleteTask, removeTaskDetail, deleteTaskSuccessOrFailure, fetchTaskHistory, updateTaskHistory, updateTaskHistoryFetchStatus, clearTaskDetail, updateCreatedTagToLocalStore, updateDeletedTagToLocalStore, } = taskDetailView.actions;
321
+ export const { fetchTaskDetailPage, fetchTaskDetail, initializeTaskToLocalStore, updateEditTaskFetchStatus, saveTaskUpdatesToLocalStore, discardTaskUpdatesInLocalStore, saveTaskDetail, archiveTask, archiveTaskSuccessOrFailure, snoozeTask, snoozeTaskSuccessOrFailure, unsnoozeTask, saveTaskSuccessOrFailure, deleteTask, removeTaskDetail, deleteTaskSuccessOrFailure, fetchTaskHistory, updateTaskHistory, updateTaskHistoryFetchStatus, clearTaskDetail, updateCreatedTagToLocalStore, updateDeletedTagToLocalStore, } = taskDetailView.actions;
274
322
  export default taskDetailView.reducer;
@@ -21,11 +21,13 @@ export const getTaskDetail = (state, taskId) => {
21
21
  };
22
22
  let taskHistory = [];
23
23
  let createdByUser = undefined;
24
+ let snoozedUntil = undefined;
24
25
  if (taskId != null && sourceTaskDetail != null) {
25
26
  const taskEntity = getTaskById(taskState, taskId);
26
27
  const fileIdsInEntity = taskEntity?.fileIds ?? [];
27
28
  if (taskEntity != null) {
28
29
  createdByUser = getUserAndUserRole(userState, userRoleState, addressState, taskEntity.createdBy);
30
+ snoozedUntil = taskEntity.snoozedUntil;
29
31
  }
30
32
  if (fileIdsInEntity.length > 0) {
31
33
  fetchStatus = reduceFetchState([
@@ -74,6 +76,7 @@ export const getTaskDetail = (state, taskId) => {
74
76
  version: 0,
75
77
  createdByUser,
76
78
  showTaskDetailFormFooter,
79
+ snoozedUntil,
77
80
  };
78
81
  };
79
82
  export const allTaskStatus = [
@@ -10,10 +10,11 @@ export const bulkUpdateTaskListEpic = (actions$, state$, zeniAPI) => actions$.pi
10
10
  const state = state$.value;
11
11
  const { taskIds, updates, groupId } = action.payload;
12
12
  const payload = {
13
- tasks: getTasksListPayload(state, taskIds, updates),
13
+ task_ids: taskIds,
14
+ updates: toBulkUpdatesPayload(state, updates, groupId),
14
15
  };
15
16
  return zeniAPI
16
- .putAndGetJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks`, payload)
17
+ .putAndGetJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks/bulk-update`, payload)
17
18
  .pipe(mergeMap((response) => {
18
19
  if (isSuccessResponse(response) && response.data != null) {
19
20
  return of(updateTasks(response.data.tasks), updateTasksListOnBulkUpdateTasksSuccess({
@@ -33,6 +34,46 @@ export const bulkUpdateTaskListEpic = (actions$, state$, zeniAPI) => actions$.pi
33
34
  }));
34
35
  }));
35
36
  }));
37
+ const toBulkUpdatesPayload = (_state, updates, groupId) => {
38
+ const payload = {};
39
+ if (updates.status != null) {
40
+ payload.status = updates.status.code;
41
+ }
42
+ if (updates.priority != null) {
43
+ payload.priority = updates.priority.code;
44
+ }
45
+ if (updates.assignees != null) {
46
+ payload.assignees = updates.assignees;
47
+ }
48
+ if (updates.groupAssignees != null) {
49
+ payload.group_assignees = updates.groupAssignees;
50
+ }
51
+ if (updates.dueDate != null) {
52
+ payload.due_date = updates.dueDate.format(DEFAULT_DATE_FORMAT);
53
+ }
54
+ if (updates.tagIds != null) {
55
+ payload.tags = updates.tagIds;
56
+ }
57
+ if (updates.timeSpent != null) {
58
+ payload.time_spent = convertHHMMStrToMinutes(updates.timeSpent);
59
+ }
60
+ if (updates.isArchived != null) {
61
+ payload.is_archived = updates.isArchived;
62
+ }
63
+ if (updates.isDeleted != null) {
64
+ payload.is_deleted = updates.isDeleted;
65
+ }
66
+ if ('snoozedUntil' in updates) {
67
+ payload.snoozed_until = null;
68
+ payload.snoozed_by = null;
69
+ payload.snoozed_at = null;
70
+ payload.is_snooze_notification_sent = false;
71
+ }
72
+ if (groupId != null) {
73
+ payload.task_group_ids = [groupId];
74
+ }
75
+ return payload;
76
+ };
36
77
  export const getTasksListPayload = (state, taskIds, updates) => {
37
78
  const { taskState } = state;
38
79
  const tasksList = taskIds
@@ -71,6 +112,12 @@ const toTaskPayload = (state, task) => {
71
112
  assignees: task.assignees,
72
113
  description: task.description,
73
114
  group_assignees: task.groupAssignees,
115
+ is_archived: task.isArchived,
116
+ is_deleted: task.isDeleted,
117
+ is_snooze_notification_sent: false,
118
+ snoozed_at: null,
119
+ snoozed_by: null,
120
+ snoozed_until: task.snoozedUntil != null ? task.snoozedUntil.toISOString() : null,
74
121
  due_date: task.dueDate != null ? task.dueDate.format(DEFAULT_DATE_FORMAT) : null,
75
122
  priority: task.priority.code,
76
123
  status: task.status.code,
@@ -9,10 +9,20 @@ export const fetchTaskListEpic = (actions$, _state$, zeniAPI) => actions$.pipe(f
9
9
  .getJSON(`${zeniAPI.apiEndPoints.taskMicroServiceBaseUrl}/1.0/task-manager/tasks?query=${encodeURIComponent(`{"task_type": "all"}`)}`)
10
10
  .pipe(mergeMap((response) => {
11
11
  if (isSuccessResponse(response) && response.data != null) {
12
- return of(updateTasks(response.data.tasks), updateTaskList({
13
- data: response.data.tasks,
12
+ const { tasks, deleted, archived, snoozed } = response.data;
13
+ const allTasks = [
14
+ ...tasks,
15
+ ...(deleted ?? []),
16
+ ...(archived ?? []),
17
+ ...(snoozed ?? []),
18
+ ];
19
+ return of(updateTasks(allTasks), updateTaskList({
20
+ data: tasks,
21
+ deleted: deleted ?? [],
22
+ archived: archived ?? [],
23
+ snoozed: snoozed ?? [],
14
24
  updateType: 'replace',
15
- }), updateTags(response.data.tasks.map((task) => task.tags).flat()));
25
+ }), updateTags(allTasks.map((task) => task.tags).flat()));
16
26
  }
17
27
  else {
18
28
  return of(updateTaskListFetchStatus({
@@ -1,4 +1,11 @@
1
1
  import { stringToUnion, stringToUnionStrict, } from '../../../commonStateTypes/stringToUnion';
2
+ export const ALL_TASK_LIST_TABS = [
3
+ 'live',
4
+ 'archived',
5
+ 'deleted',
6
+ 'snoozed',
7
+ ];
8
+ export const toTaskListTabType = (v) => stringToUnion(v, ALL_TASK_LIST_TABS);
2
9
  export const DUE_DATE_GROUP_KEYS = [
3
10
  'overdue',
4
11
  'today',
@@ -26,6 +33,7 @@ const ALL_TASK_LIST_SORT_KEYS = [
26
33
  'status',
27
34
  'priority',
28
35
  'timeSpent',
36
+ 'visibility',
29
37
  ];
30
38
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
39
  const toTaskListSortKeyType = (v) => stringToUnion(v, ALL_TASK_LIST_SORT_KEYS);
@@ -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;
@@ -187,7 +216,12 @@ const taskList = createSlice({
187
216
  const { taskIds, groupId } = action.payload;
188
217
  draft.updateTasksFetchState.fetchState = 'Completed';
189
218
  draft.updateTasksFetchState.error = undefined;
190
- if (groupId != null) {
219
+ if (groupId == null) {
220
+ // Tab-changing operations (archive/delete/unarchive/undo-delete/unsnooze):
221
+ // Remove affected tasks from the current tab's top-level indices
222
+ removeTaskIdsFromTopLevel(draft, taskIds);
223
+ }
224
+ else if (groupId != null) {
191
225
  const { groupByKey } = draft.uiState;
192
226
  if (groupByKey === 'groupName') {
193
227
  Object.keys(draft.taskIdsByGroupsIds).forEach((gId) => {
@@ -404,12 +438,34 @@ const taskList = createSlice({
404
438
  draft.updateTasksFetchState.fetchState = 'Error';
405
439
  draft.updateTasksFetchState.error = action.payload.error;
406
440
  },
441
+ removeTaskFromList(draft, action) {
442
+ const taskIdToRemove = action.payload;
443
+ draft.taskIds = draft.taskIds.filter((id) => id !== taskIdToRemove);
444
+ Object.keys(draft.taskIdsByGroupsIds).forEach((groupId) => {
445
+ draft.taskIdsByGroupsIds[groupId] = draft.taskIdsByGroupsIds[groupId].filter((id) => id !== taskIdToRemove);
446
+ });
447
+ Object.keys(draft.taskIdsByPriority).forEach((key) => {
448
+ draft.taskIdsByPriority[key] = draft.taskIdsByPriority[key].filter((id) => id !== taskIdToRemove);
449
+ });
450
+ Object.keys(draft.taskIdsByStatus).forEach((key) => {
451
+ draft.taskIdsByStatus[key] = draft.taskIdsByStatus[key].filter((id) => id !== taskIdToRemove);
452
+ });
453
+ Object.keys(draft.taskIdsByDueDate).forEach((key) => {
454
+ draft.taskIdsByDueDate[key] = draft.taskIdsByDueDate[key].filter((id) => id !== taskIdToRemove);
455
+ });
456
+ Object.keys(draft.taskIdsByAssignees).forEach((key) => {
457
+ draft.taskIdsByAssignees[key] = draft.taskIdsByAssignees[key].filter((id) => id !== taskIdToRemove);
458
+ });
459
+ Object.keys(draft.taskIdsByTags).forEach((key) => {
460
+ draft.taskIdsByTags[key] = draft.taskIdsByTags[key].filter((id) => id !== taskIdToRemove);
461
+ });
462
+ },
407
463
  clearTaskList(draft) {
408
464
  Object.assign(draft, initialState);
409
465
  },
410
466
  },
411
467
  });
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;
468
+ 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
469
  export default taskList.reducer;
414
470
  /**
415
471
  * helpers
@@ -488,6 +544,46 @@ const getTagsGroupKey = (tags) => {
488
544
  tagIds.sort();
489
545
  return tagIds.join(',');
490
546
  };
547
+ const removeTaskIdsFromTopLevel = (draft, taskIds) => {
548
+ const isRemoved = (id) => taskIds.includes(id);
549
+ draft.taskIds = draft.taskIds.filter((id) => !isRemoved(id));
550
+ Object.keys(draft.taskIdsByGroupsIds).forEach((gId) => {
551
+ draft.taskIdsByGroupsIds[gId] = draft.taskIdsByGroupsIds[gId].filter((id) => !isRemoved(id));
552
+ });
553
+ Object.keys(draft.taskIdsByPriority).forEach((key) => {
554
+ draft.taskIdsByPriority[key] = draft.taskIdsByPriority[key].filter((id) => !isRemoved(id));
555
+ });
556
+ Object.keys(draft.taskIdsByStatus).forEach((key) => {
557
+ draft.taskIdsByStatus[key] = draft.taskIdsByStatus[key].filter((id) => !isRemoved(id));
558
+ });
559
+ Object.keys(draft.taskIdsByDueDate).forEach((key) => {
560
+ draft.taskIdsByDueDate[key] = draft.taskIdsByDueDate[key].filter((id) => !isRemoved(id));
561
+ });
562
+ Object.keys(draft.taskIdsByAssignees).forEach((key) => {
563
+ draft.taskIdsByAssignees[key] = draft.taskIdsByAssignees[key].filter((id) => !isRemoved(id));
564
+ });
565
+ Object.keys(draft.taskIdsByTags).forEach((key) => {
566
+ draft.taskIdsByTags[key] = draft.taskIdsByTags[key].filter((id) => !isRemoved(id));
567
+ });
568
+ };
569
+ const toTabData = (grouped) => ({
570
+ taskIds: grouped.taskIds,
571
+ taskIdsByAssignees: grouped.taskIdsByAssignees,
572
+ taskIdsByDueDate: grouped.taskIdsByDueDate,
573
+ taskIdsByGroupsIds: grouped.taskIdsByGroupIds,
574
+ taskIdsByPriority: grouped.taskIdsGroupedByPriority,
575
+ taskIdsByStatus: grouped.taskIdsGroupedByStatus,
576
+ taskIdsByTags: grouped.taskIdsByTags,
577
+ });
578
+ const copyTabDataToTopLevel = (draft, tabData) => {
579
+ draft.taskIds = tabData.taskIds;
580
+ draft.taskIdsByGroupsIds = tabData.taskIdsByGroupsIds;
581
+ draft.taskIdsByPriority = tabData.taskIdsByPriority;
582
+ draft.taskIdsByStatus = tabData.taskIdsByStatus;
583
+ draft.taskIdsByAssignees = tabData.taskIdsByAssignees;
584
+ draft.taskIdsByDueDate = tabData.taskIdsByDueDate;
585
+ draft.taskIdsByTags = tabData.taskIdsByTags;
586
+ };
491
587
  const getGroupedTaskIds = (tasks) => {
492
588
  const taskIds = [];
493
589
  const taskIdsByGroupIds = {};