@zeniai/client-epic-state 5.3.7-betaRD0 → 5.3.8-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/tenant/epic/fetchAllTenantsEpic.js +38 -45
- 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/tenant/epic/fetchAllTenantsEpic.js +39 -46
- 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 +6 -3
- package/lib/esm/init.js +3 -14
- 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 +5 -0
- package/lib/esm/view/taskManager/taskListView/taskListFilterHelpers.js +2 -0
- 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 +7 -4
- package/lib/index.js +66 -53
- package/lib/init.d.ts +0 -9
- package/lib/init.js +4 -17
- 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 +5 -0
- package/lib/view/taskManager/taskListView/taskListFilterHelpers.js +2 -0
- 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/lib/zeniAPI.d.ts +2 -2
- package/package.json +1 -26
- package/lib/esm/sessionEndpointsLoader.js +0 -7
- package/lib/sessionEndpointsLoader.d.ts +0 -9
- package/lib/sessionEndpointsLoader.js +0 -11
- package/lib/tsconfig.mocks.tsbuildinfo +0 -1
- package/lib/tsconfig.typecheck.tsbuildinfo +0 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ID } from '../../../commonStateTypes/common';
|
|
2
|
+
import { ZeniAPIStatus } from '../../../responsePayload';
|
|
3
|
+
import { TimeEntryRangeKey, TimeEntryViewState } from './timeEntryViewState';
|
|
4
|
+
export declare const initialState: TimeEntryViewState;
|
|
5
|
+
export declare const fetchTimeEntriesForTask: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[taskId: string, cacheOverride?: any], {
|
|
6
|
+
taskId: string;
|
|
7
|
+
cacheOverride: any;
|
|
8
|
+
}, "timeEntryView/fetchTimeEntriesForTask", never, never>, fetchTimeEntriesForTaskSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
9
|
+
taskId: ID;
|
|
10
|
+
timeEntryIds: ID[];
|
|
11
|
+
}, "timeEntryView/fetchTimeEntriesForTaskSuccess">, fetchTimeEntriesForTaskFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
12
|
+
status: ZeniAPIStatus;
|
|
13
|
+
taskId: ID;
|
|
14
|
+
}, "timeEntryView/fetchTimeEntriesForTaskFailure">, fetchTimeEntriesForRange: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[userId: string, startDate: string, endDate: string, cacheOverride?: any], {
|
|
15
|
+
userId: string;
|
|
16
|
+
startDate: string;
|
|
17
|
+
endDate: string;
|
|
18
|
+
cacheOverride: any;
|
|
19
|
+
}, "timeEntryView/fetchTimeEntriesForRange", never, never>, fetchTimeEntriesForRangeSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
20
|
+
rangeKey: TimeEntryRangeKey;
|
|
21
|
+
timeEntryIds: ID[];
|
|
22
|
+
}, "timeEntryView/fetchTimeEntriesForRangeSuccess">, fetchTimeEntriesForRangeFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
23
|
+
rangeKey: TimeEntryRangeKey;
|
|
24
|
+
status: ZeniAPIStatus;
|
|
25
|
+
}, "timeEntryView/fetchTimeEntriesForRangeFailure">, logTime: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[taskId: string, minutes: number, entryDate: string, source?: "manual" | "timer" | undefined, identifier?: string | undefined], {
|
|
26
|
+
taskId: string;
|
|
27
|
+
minutes: number;
|
|
28
|
+
entryDate: string;
|
|
29
|
+
source: "manual" | "timer";
|
|
30
|
+
identifier: string | undefined;
|
|
31
|
+
}, "timeEntryView/logTime", never, never>, logTimeSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
32
|
+
taskId: ID;
|
|
33
|
+
timeEntryId: ID;
|
|
34
|
+
}, "timeEntryView/logTimeSuccess">, logTimeFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
35
|
+
status: ZeniAPIStatus;
|
|
36
|
+
taskId: ID;
|
|
37
|
+
}, "timeEntryView/logTimeFailure">, updateTimeEntryMinutes: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[timeEntryId: string, minutes: number], {
|
|
38
|
+
timeEntryId: string;
|
|
39
|
+
minutes: number;
|
|
40
|
+
}, "timeEntryView/updateTimeEntryMinutes", never, never>, deleteTimeEntry: import("@reduxjs/toolkit").ActionCreatorWithPayload<string, "timeEntryView/deleteTimeEntry">, timeEntryWriteSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
41
|
+
timeEntryId: ID;
|
|
42
|
+
removedFromTaskId?: ID;
|
|
43
|
+
}, "timeEntryView/timeEntryWriteSuccess">, timeEntryWriteFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
44
|
+
status: ZeniAPIStatus;
|
|
45
|
+
timeEntryId: ID;
|
|
46
|
+
}, "timeEntryView/timeEntryWriteFailure">, fetchTaskCategories: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"timeEntryView/fetchTaskCategories">, fetchTaskCategoriesSuccess: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"timeEntryView/fetchTaskCategoriesSuccess">, fetchTaskCategoriesFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "timeEntryView/fetchTaskCategoriesFailure">;
|
|
47
|
+
declare const _default: import("redux").Reducer<TimeEntryViewState>;
|
|
48
|
+
export default _default;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.fetchTaskCategoriesFailure = exports.fetchTaskCategoriesSuccess = exports.fetchTaskCategories = exports.timeEntryWriteFailure = exports.timeEntryWriteSuccess = exports.deleteTimeEntry = exports.updateTimeEntryMinutes = exports.logTimeFailure = exports.logTimeSuccess = exports.logTime = exports.fetchTimeEntriesForRangeFailure = exports.fetchTimeEntriesForRangeSuccess = exports.fetchTimeEntriesForRange = exports.fetchTimeEntriesForTaskFailure = exports.fetchTimeEntriesForTaskSuccess = exports.fetchTimeEntriesForTask = exports.initialState = void 0;
|
|
5
|
+
const toolkit_1 = require("@reduxjs/toolkit");
|
|
6
|
+
const rootActions_1 = require("../../../rootActions");
|
|
7
|
+
const NOT_STARTED = { fetchState: 'Not-Started', error: undefined };
|
|
8
|
+
exports.initialState = {
|
|
9
|
+
timeEntryIdsByTaskId: {},
|
|
10
|
+
fetchStateByTaskId: {},
|
|
11
|
+
timeEntryIdsByRangeKey: {},
|
|
12
|
+
fetchStateByRangeKey: {},
|
|
13
|
+
saveStateByTaskId: {},
|
|
14
|
+
saveStateByTimeEntryId: {},
|
|
15
|
+
taskCategoryFetchState: NOT_STARTED,
|
|
16
|
+
};
|
|
17
|
+
const timeEntryView = (0, toolkit_1.createSlice)({
|
|
18
|
+
name: 'timeEntryView',
|
|
19
|
+
initialState: exports.initialState,
|
|
20
|
+
reducers: {
|
|
21
|
+
fetchTimeEntriesForTask: {
|
|
22
|
+
prepare(taskId, cacheOverride = false) {
|
|
23
|
+
return { payload: { taskId, cacheOverride } };
|
|
24
|
+
},
|
|
25
|
+
reducer(draft, action) {
|
|
26
|
+
draft.fetchStateByTaskId[action.payload.taskId] = {
|
|
27
|
+
fetchState: 'In-Progress',
|
|
28
|
+
error: undefined,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
fetchTimeEntriesForTaskSuccess(draft, action) {
|
|
33
|
+
const { taskId, timeEntryIds } = action.payload;
|
|
34
|
+
// Replaced, not merged: the server is authoritative about which entries
|
|
35
|
+
// a task has, and a merge would resurrect one that was just deleted.
|
|
36
|
+
draft.timeEntryIdsByTaskId[taskId] = timeEntryIds;
|
|
37
|
+
draft.fetchStateByTaskId[taskId] = {
|
|
38
|
+
fetchState: 'Completed',
|
|
39
|
+
error: undefined,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
fetchTimeEntriesForTaskFailure(draft, action) {
|
|
43
|
+
draft.fetchStateByTaskId[action.payload.taskId] = {
|
|
44
|
+
fetchState: 'Error',
|
|
45
|
+
error: action.payload.status,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
fetchTimeEntriesForRange: {
|
|
49
|
+
prepare(userId, startDate, endDate, cacheOverride = false) {
|
|
50
|
+
return { payload: { userId, startDate, endDate, cacheOverride } };
|
|
51
|
+
},
|
|
52
|
+
reducer(draft, action) {
|
|
53
|
+
const { userId, startDate, endDate } = action.payload;
|
|
54
|
+
draft.fetchStateByRangeKey[`${userId}_${startDate}_${endDate}`] = {
|
|
55
|
+
fetchState: 'In-Progress',
|
|
56
|
+
error: undefined,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
fetchTimeEntriesForRangeSuccess(draft, action) {
|
|
61
|
+
const { rangeKey, timeEntryIds } = action.payload;
|
|
62
|
+
draft.timeEntryIdsByRangeKey[rangeKey] = timeEntryIds;
|
|
63
|
+
draft.fetchStateByRangeKey[rangeKey] = {
|
|
64
|
+
fetchState: 'Completed',
|
|
65
|
+
error: undefined,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
fetchTimeEntriesForRangeFailure(draft, action) {
|
|
69
|
+
draft.fetchStateByRangeKey[action.payload.rangeKey] = {
|
|
70
|
+
fetchState: 'Error',
|
|
71
|
+
error: action.payload.status,
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
logTime: {
|
|
75
|
+
prepare(taskId, minutes, entryDate, source = 'manual', identifier) {
|
|
76
|
+
return { payload: { taskId, minutes, entryDate, source, identifier } };
|
|
77
|
+
},
|
|
78
|
+
reducer(draft, action) {
|
|
79
|
+
draft.saveStateByTaskId[action.payload.taskId] = {
|
|
80
|
+
fetchState: 'In-Progress',
|
|
81
|
+
error: undefined,
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
logTimeSuccess(draft, action) {
|
|
86
|
+
const { taskId, timeEntryId } = action.payload;
|
|
87
|
+
draft.saveStateByTaskId[taskId] = {
|
|
88
|
+
fetchState: 'Completed',
|
|
89
|
+
error: undefined,
|
|
90
|
+
};
|
|
91
|
+
const existing = draft.timeEntryIdsByTaskId[taskId] ?? [];
|
|
92
|
+
// Guarded against a duplicate id: a retried write with the same
|
|
93
|
+
// deterministic identifier returns the SAME entry, and pushing it twice
|
|
94
|
+
// would double it in the breakdown even though the server has one row.
|
|
95
|
+
if (!existing.includes(timeEntryId)) {
|
|
96
|
+
draft.timeEntryIdsByTaskId[taskId] = [...existing, timeEntryId];
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
logTimeFailure(draft, action) {
|
|
100
|
+
draft.saveStateByTaskId[action.payload.taskId] = {
|
|
101
|
+
fetchState: 'Error',
|
|
102
|
+
error: action.payload.status,
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
updateTimeEntryMinutes: {
|
|
106
|
+
prepare(timeEntryId, minutes) {
|
|
107
|
+
return { payload: { timeEntryId, minutes } };
|
|
108
|
+
},
|
|
109
|
+
reducer(draft, action) {
|
|
110
|
+
draft.saveStateByTimeEntryId[action.payload.timeEntryId] = {
|
|
111
|
+
fetchState: 'In-Progress',
|
|
112
|
+
error: undefined,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
deleteTimeEntry(draft, action) {
|
|
117
|
+
draft.saveStateByTimeEntryId[action.payload] = {
|
|
118
|
+
fetchState: 'In-Progress',
|
|
119
|
+
error: undefined,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
timeEntryWriteSuccess(draft, action) {
|
|
123
|
+
const { timeEntryId, removedFromTaskId } = action.payload;
|
|
124
|
+
draft.saveStateByTimeEntryId[timeEntryId] = {
|
|
125
|
+
fetchState: 'Completed',
|
|
126
|
+
error: undefined,
|
|
127
|
+
};
|
|
128
|
+
if (removedFromTaskId != null) {
|
|
129
|
+
draft.timeEntryIdsByTaskId[removedFromTaskId] = (draft.timeEntryIdsByTaskId[removedFromTaskId] ?? []).filter((candidate) => candidate !== timeEntryId);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
timeEntryWriteFailure(draft, action) {
|
|
133
|
+
draft.saveStateByTimeEntryId[action.payload.timeEntryId] = {
|
|
134
|
+
fetchState: 'Error',
|
|
135
|
+
error: action.payload.status,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
fetchTaskCategories(draft) {
|
|
139
|
+
draft.taskCategoryFetchState = {
|
|
140
|
+
fetchState: 'In-Progress',
|
|
141
|
+
error: undefined,
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
fetchTaskCategoriesSuccess(draft) {
|
|
145
|
+
draft.taskCategoryFetchState = {
|
|
146
|
+
fetchState: 'Completed',
|
|
147
|
+
error: undefined,
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
fetchTaskCategoriesFailure(draft, action) {
|
|
151
|
+
draft.taskCategoryFetchState = {
|
|
152
|
+
fetchState: 'Error',
|
|
153
|
+
error: action.payload,
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
extraReducers: (builder) => {
|
|
158
|
+
builder.addCase(rootActions_1.clearAll, (draft) => {
|
|
159
|
+
Object.assign(draft, exports.initialState);
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
_a = timeEntryView.actions, exports.fetchTimeEntriesForTask = _a.fetchTimeEntriesForTask, exports.fetchTimeEntriesForTaskSuccess = _a.fetchTimeEntriesForTaskSuccess, exports.fetchTimeEntriesForTaskFailure = _a.fetchTimeEntriesForTaskFailure, exports.fetchTimeEntriesForRange = _a.fetchTimeEntriesForRange, exports.fetchTimeEntriesForRangeSuccess = _a.fetchTimeEntriesForRangeSuccess, exports.fetchTimeEntriesForRangeFailure = _a.fetchTimeEntriesForRangeFailure, exports.logTime = _a.logTime, exports.logTimeSuccess = _a.logTimeSuccess, exports.logTimeFailure = _a.logTimeFailure, exports.updateTimeEntryMinutes = _a.updateTimeEntryMinutes, exports.deleteTimeEntry = _a.deleteTimeEntry, exports.timeEntryWriteSuccess = _a.timeEntryWriteSuccess, exports.timeEntryWriteFailure = _a.timeEntryWriteFailure, exports.fetchTaskCategories = _a.fetchTaskCategories, exports.fetchTaskCategoriesSuccess = _a.fetchTaskCategoriesSuccess, exports.fetchTaskCategoriesFailure = _a.fetchTaskCategoriesFailure;
|
|
164
|
+
exports.default = timeEntryView.reducer;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FetchState, ID } from '../../../commonStateTypes/common';
|
|
2
|
+
import { TimeEntry } from '../../../entity/timeEntry/timeEntryState';
|
|
3
|
+
import { ZeniAPIStatus } from '../../../responsePayload';
|
|
4
|
+
import { RootState } from '../../../rootStateTypes';
|
|
5
|
+
import { TimeEntryRangeKey } from './timeEntryViewState';
|
|
6
|
+
export interface TimeEntryListView {
|
|
7
|
+
fetchState: FetchState;
|
|
8
|
+
timeEntries: TimeEntry[];
|
|
9
|
+
totalMinutes: number;
|
|
10
|
+
error?: ZeniAPIStatus;
|
|
11
|
+
}
|
|
12
|
+
/** Every entry on one task, all users — the task detail breakdown. */
|
|
13
|
+
export declare function getTimeEntriesForTask(state: RootState, taskId: ID): TimeEntryListView;
|
|
14
|
+
export interface TimesheetWeekView extends TimeEntryListView {
|
|
15
|
+
/** Minutes per `YYYY-MM-DD`. The weekly grid is a column per day, so the
|
|
16
|
+
* grouping belongs here rather than in every consumer. */
|
|
17
|
+
minutesByDate: Record<string, number>;
|
|
18
|
+
rangeKey: TimeEntryRangeKey;
|
|
19
|
+
}
|
|
20
|
+
/** One user's week — the timesheet grid. */
|
|
21
|
+
export declare function getTimeEntriesForRange(state: RootState, userId: ID, startDate: string, endDate: string): TimesheetWeekView;
|
|
22
|
+
/** Save state for the row the user is editing, so two rows saving at once do
|
|
23
|
+
* not share one spinner. */
|
|
24
|
+
export declare function getTimeEntrySaveState(state: RootState, taskId: ID): FetchState;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTimeEntriesForTask = getTimeEntriesForTask;
|
|
4
|
+
exports.getTimeEntriesForRange = getTimeEntriesForRange;
|
|
5
|
+
exports.getTimeEntrySaveState = getTimeEntrySaveState;
|
|
6
|
+
const timeEntrySelector_1 = require("../../../entity/timeEntry/timeEntrySelector");
|
|
7
|
+
const timeEntryViewState_1 = require("./timeEntryViewState");
|
|
8
|
+
const byEntryDateThenCreate = (left, right) => left.entryDate.valueOf() - right.entryDate.valueOf() ||
|
|
9
|
+
left.createTime.valueOf() - right.createTime.valueOf();
|
|
10
|
+
const sumMinutes = (entries) => entries.reduce((total, entry) => total + (entry.minutes || 0), 0);
|
|
11
|
+
/** Every entry on one task, all users — the task detail breakdown. */
|
|
12
|
+
function getTimeEntriesForTask(state, taskId) {
|
|
13
|
+
const { timeEntryIdsByTaskId, fetchStateByTaskId } = state.timeEntryViewState;
|
|
14
|
+
const timeEntries = (0, timeEntrySelector_1.getTimeEntriesByIds)(state.timeEntryState, timeEntryIdsByTaskId[taskId] ?? []).sort(byEntryDateThenCreate);
|
|
15
|
+
return {
|
|
16
|
+
timeEntries,
|
|
17
|
+
totalMinutes: sumMinutes(timeEntries),
|
|
18
|
+
fetchState: fetchStateByTaskId[taskId]?.fetchState ?? 'Not-Started',
|
|
19
|
+
error: fetchStateByTaskId[taskId]?.error,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** One user's week — the timesheet grid. */
|
|
23
|
+
function getTimeEntriesForRange(state, userId, startDate, endDate) {
|
|
24
|
+
const rangeKey = (0, timeEntryViewState_1.toTimeEntryRangeKey)(userId, startDate, endDate);
|
|
25
|
+
const { timeEntryIdsByRangeKey, fetchStateByRangeKey } = state.timeEntryViewState;
|
|
26
|
+
const timeEntries = (0, timeEntrySelector_1.getTimeEntriesByIds)(state.timeEntryState, timeEntryIdsByRangeKey[rangeKey] ?? []).sort(byEntryDateThenCreate);
|
|
27
|
+
const minutesByDate = {};
|
|
28
|
+
timeEntries.forEach((entry) => {
|
|
29
|
+
const key = entry.entryDate.format('YYYY-MM-DD');
|
|
30
|
+
minutesByDate[key] = (minutesByDate[key] ?? 0) + (entry.minutes || 0);
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
rangeKey,
|
|
34
|
+
timeEntries,
|
|
35
|
+
minutesByDate,
|
|
36
|
+
totalMinutes: sumMinutes(timeEntries),
|
|
37
|
+
fetchState: fetchStateByRangeKey[rangeKey]?.fetchState ?? 'Not-Started',
|
|
38
|
+
error: fetchStateByRangeKey[rangeKey]?.error,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Save state for the row the user is editing, so two rows saving at once do
|
|
42
|
+
* not share one spinner. */
|
|
43
|
+
function getTimeEntrySaveState(state, taskId) {
|
|
44
|
+
return (state.timeEntryViewState.saveStateByTaskId[taskId]?.fetchState ??
|
|
45
|
+
'Not-Started');
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FetchStateAndError, ID } from '../../../commonStateTypes/common';
|
|
2
|
+
/** Mon-Fri is quoted to people inclusively, so the key is both ends. */
|
|
3
|
+
export type TimeEntryRangeKey = `${string}_${string}_${string}`;
|
|
4
|
+
export declare const toTimeEntryRangeKey: (userId: ID, startDate: string, endDate: string) => TimeEntryRangeKey;
|
|
5
|
+
export interface TimeEntryViewState {
|
|
6
|
+
fetchStateByRangeKey: Record<TimeEntryRangeKey, FetchStateAndError>;
|
|
7
|
+
fetchStateByTaskId: Record<ID, FetchStateAndError>;
|
|
8
|
+
/** Per task, because two rows can be saving at once on the board and a
|
|
9
|
+
* single flag would make one row's spinner answer for the other. */
|
|
10
|
+
saveStateByTaskId: Record<ID, FetchStateAndError>;
|
|
11
|
+
/** Per entry, for edits and deletes addressed by entry id. */
|
|
12
|
+
saveStateByTimeEntryId: Record<ID, FetchStateAndError>;
|
|
13
|
+
taskCategoryFetchState: FetchStateAndError;
|
|
14
|
+
/** Ids per (user, start, end). Keyed by the window rather than by user so
|
|
15
|
+
* two weeks open at once do not overwrite each other. */
|
|
16
|
+
timeEntryIdsByRangeKey: Record<TimeEntryRangeKey, ID[]>;
|
|
17
|
+
/** Ids per task, so the detail breakdown does not scan every entry in the
|
|
18
|
+
* store. Replaced wholesale on fetch — the server is authoritative about
|
|
19
|
+
* which entries a task has. */
|
|
20
|
+
timeEntryIdsByTaskId: Record<ID, ID[]>;
|
|
21
|
+
}
|
package/lib/zeniAPI.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface RESTAPIEndpoints {
|
|
|
25
25
|
readonly tenantMicroServiceBaseUrl: string;
|
|
26
26
|
}
|
|
27
27
|
export interface ZeniAPI<ResponseType = FetchResponse> {
|
|
28
|
-
apiEndPoints: RESTAPIEndpoints;
|
|
28
|
+
readonly apiEndPoints: RESTAPIEndpoints;
|
|
29
29
|
readonly sessionId?: string;
|
|
30
30
|
readonly tenantId?: string;
|
|
31
31
|
readonly userId?: string;
|
|
@@ -40,7 +40,7 @@ export interface ZeniAPI<ResponseType = FetchResponse> {
|
|
|
40
40
|
update(userId?: string, sessionId?: string, tenantId?: string): void;
|
|
41
41
|
}
|
|
42
42
|
export declare class ZeniAPIClient implements ZeniAPI {
|
|
43
|
-
apiEndPoints: RESTAPIEndpoints;
|
|
43
|
+
readonly apiEndPoints: RESTAPIEndpoints;
|
|
44
44
|
userId?: string | undefined;
|
|
45
45
|
sessionId?: string | undefined;
|
|
46
46
|
tenantId?: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeniai/client-epic-state",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.8-beta0DI",
|
|
4
4
|
"description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -53,31 +53,6 @@
|
|
|
53
53
|
"url": "https://github.com/zeni-ai/client-epic-state/issues"
|
|
54
54
|
},
|
|
55
55
|
"homepage": "https://github.com/zeni-ai/client-epic-state#readme",
|
|
56
|
-
"devDependencies": {
|
|
57
|
-
"@eslint/js": "^9.24.0",
|
|
58
|
-
"@liveblocks/client": "^2.23.0",
|
|
59
|
-
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
60
|
-
"@types/big.js": "^6.2.2",
|
|
61
|
-
"@types/lodash": "^4.17.23",
|
|
62
|
-
"@types/node": "^20.19.0",
|
|
63
|
-
"@types/url-parse": "^1.4.11",
|
|
64
|
-
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
65
|
-
"@typescript-eslint/parser": "^8.56.0",
|
|
66
|
-
"concurrently": "^9.1.2",
|
|
67
|
-
"eslint": "^9.24.0",
|
|
68
|
-
"eslint-config-prettier": "^10.0.2",
|
|
69
|
-
"eslint-plugin-perfectionist": "^4.15.0",
|
|
70
|
-
"eslint-plugin-unused-imports": "^4.1.4",
|
|
71
|
-
"globals": "^16.0.0",
|
|
72
|
-
"knip": "^6.21.0",
|
|
73
|
-
"madge": "^8.0.0",
|
|
74
|
-
"prettier": "^3.5.3",
|
|
75
|
-
"rimraf": "^6.0.1",
|
|
76
|
-
"semver": "^7.7.1",
|
|
77
|
-
"typescript": "^5.7.3",
|
|
78
|
-
"vite": "^7.3.5",
|
|
79
|
-
"vitest": "^4.1.11"
|
|
80
|
-
},
|
|
81
56
|
"dependencies": {
|
|
82
57
|
"@redux-devtools/extension": "^3.3.0",
|
|
83
58
|
"@reduxjs/toolkit": "^1.6.2",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Called after sign-in, before the user's tenants are fetched. The app uses it
|
|
3
|
-
* to fetch the endpoints that are only served to a signed-in session and to
|
|
4
|
-
* hand them to `updateZeniAPIEndpoints`. Without a loader, tenants are fetched
|
|
5
|
-
* straight away with the endpoints given to `initialize`.
|
|
6
|
-
*/
|
|
7
|
-
export type SessionEndpointsLoader = (userId: string, sessionId?: string) => Promise<void>;
|
|
8
|
-
export declare function setSessionEndpointsLoader(loader: SessionEndpointsLoader | undefined): void;
|
|
9
|
-
export declare function getSessionEndpointsLoader(): SessionEndpointsLoader | undefined;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setSessionEndpointsLoader = setSessionEndpointsLoader;
|
|
4
|
-
exports.getSessionEndpointsLoader = getSessionEndpointsLoader;
|
|
5
|
-
let sessionEndpointsLoader;
|
|
6
|
-
function setSessionEndpointsLoader(loader) {
|
|
7
|
-
sessionEndpointsLoader = loader;
|
|
8
|
-
}
|
|
9
|
-
function getSessionEndpointsLoader() {
|
|
10
|
-
return sessionEndpointsLoader;
|
|
11
|
-
}
|