@janiscommerce/app-tracking-shift 1.0.1
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/README.md +1 -0
- package/lib/Formatter.js +214 -0
- package/lib/OfflineData.js +91 -0
- package/lib/Shift.js +538 -0
- package/lib/ShiftWorklogs.js +92 -0
- package/lib/StaffApiServices.js +151 -0
- package/lib/TrackerRecords.js +103 -0
- package/lib/components/WithShiftTracking/index.js +36 -0
- package/lib/constant/index.js +33 -0
- package/lib/context/ShiftTrackingContext.js +7 -0
- package/lib/db/StorageService.js +5 -0
- package/lib/db/TimeTrackerService.js +5 -0
- package/lib/hooks/useMMKVObject/index.js +19 -0
- package/lib/index.js +7 -0
- package/lib/provider/ShiftTrackingProvider.js +174 -0
- package/lib/utils/crashlytics/index.js +5 -0
- package/lib/utils/helpers/index.js +20 -0
- package/lib/utils/provider/downloadWorkLogTypes/index.js +30 -0
- package/lib/utils/provider/getShiftWorkLogsFromJanis/index.js +18 -0
- package/lib/utils/provider/index.js +5 -0
- package/lib/utils/provider/isAuthorizedToUseStaffMS/index.js +39 -0
- package/lib/utils/provider/openShift/index.js +69 -0
- package/lib/utils/provider/saveWorkLogTimesInDB/index.js +38 -0
- package/lib/utils/request/index.js +15 -0
- package/lib/utils/storage/getShiftData/index.js +33 -0
- package/lib/utils/storage/getStaffAuthorizationData/index.js +24 -0
- package/lib/utils/storage/getWorkLogTypesData/index.js +37 -0
- package/lib/utils/storage/index.js +19 -0
- package/lib/utils/userInfo/getUserId/index.js +12 -0
- package/package.json +92 -0
package/lib/Shift.js
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import TimeTracker from './db/TimeTrackerService';
|
|
2
|
+
import StaffService from './StaffApiServices';
|
|
3
|
+
import Storage from './db/StorageService';
|
|
4
|
+
import Crashlytics from './utils/crashlytics';
|
|
5
|
+
import ShiftWorklogs from './ShiftWorklogs';
|
|
6
|
+
import {generateRandomId, isEmptyArray, isEmptyObject, isObject} from './utils/helpers';
|
|
7
|
+
import {getObject, getShiftData, setObject} from './utils/storage';
|
|
8
|
+
import {
|
|
9
|
+
SHIFT_ID,
|
|
10
|
+
SHIFT_STATUS,
|
|
11
|
+
SHIFT_DATA,
|
|
12
|
+
CURRENT_WORKLOG_DATA,
|
|
13
|
+
CURRENT_WORKLOG_ID,
|
|
14
|
+
EXCLUDED_WORKLOG_TYPES,
|
|
15
|
+
ONE_HOUR_EXTENSION,
|
|
16
|
+
} from './constant';
|
|
17
|
+
import TrackerRecords from './TrackerRecords';
|
|
18
|
+
import Formatter from './Formatter';
|
|
19
|
+
import OfflineData from './OfflineData';
|
|
20
|
+
/**
|
|
21
|
+
* Class to manage work shifts
|
|
22
|
+
* @class Shift
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
class Shift {
|
|
26
|
+
/**
|
|
27
|
+
* Open a work shift in the staff MS and record this event in the time tracking database.
|
|
28
|
+
* @param {Object} params
|
|
29
|
+
* @throws {Error} error
|
|
30
|
+
* @returns {Promise<string>} shiftId => ID related to the shift that has just been opened for the user
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
async checkStaffMSAuthorization() {
|
|
34
|
+
try {
|
|
35
|
+
const {result: setting} = await StaffService.getSetting('global');
|
|
36
|
+
|
|
37
|
+
const {enabledShiftAndWorkLog = false} = setting || {};
|
|
38
|
+
|
|
39
|
+
return enabledShiftAndWorkLog;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
Crashlytics.recordError(error, 'Error checking staff MS authorization');
|
|
42
|
+
return Promise.reject(error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async open(params = {}) {
|
|
47
|
+
try {
|
|
48
|
+
Crashlytics.log('user open shift');
|
|
49
|
+
const {date} = params;
|
|
50
|
+
const {result: shift} = await StaffService.openShift();
|
|
51
|
+
const {id: shiftId = ''} = shift || {};
|
|
52
|
+
|
|
53
|
+
const openShift = await this.getUserOpenShift({id: shiftId});
|
|
54
|
+
|
|
55
|
+
await this._startTracking({
|
|
56
|
+
id: shiftId,
|
|
57
|
+
date: date || openShift.startDate,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
Storage.set(SHIFT_ID, shiftId);
|
|
61
|
+
Storage.set(SHIFT_STATUS, 'opened');
|
|
62
|
+
Storage.set(SHIFT_DATA, JSON.stringify(openShift));
|
|
63
|
+
|
|
64
|
+
return shiftId;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
Crashlytics.recordError(error, 'Error opening shift in staff service');
|
|
67
|
+
return Promise.reject(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Finish a work shift in the staff MS and record this event in the time tracking database.
|
|
73
|
+
* @param {Object} params
|
|
74
|
+
* @throws {Error} error
|
|
75
|
+
* @returns {Promise<string>} shiftId => ID related to the shift that has just been closed for the user
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
async finish(params = {}) {
|
|
79
|
+
try {
|
|
80
|
+
Crashlytics.log('user close shift');
|
|
81
|
+
const shiftIsExpired = this.isDateToCloseExceeded();
|
|
82
|
+
|
|
83
|
+
if (shiftIsExpired) {
|
|
84
|
+
await this.reOpen();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (OfflineData.hasData) {
|
|
88
|
+
await this.sendPendingWorkLogs();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const {date} = params;
|
|
92
|
+
const {result: shift} = await StaffService.closeShift();
|
|
93
|
+
const {id: shiftId = ''} = shift || {};
|
|
94
|
+
const endDate = date || new Date().toISOString();
|
|
95
|
+
const shiftData = getShiftData();
|
|
96
|
+
|
|
97
|
+
await this._finishTracking({id: shiftId, date});
|
|
98
|
+
|
|
99
|
+
const updatedShiftData = {
|
|
100
|
+
...shiftData,
|
|
101
|
+
endDate,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
Storage.set(SHIFT_STATUS, 'closed');
|
|
105
|
+
Storage.set(SHIFT_DATA, JSON.stringify(updatedShiftData));
|
|
106
|
+
|
|
107
|
+
return shiftId;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
Crashlytics.recordError(error, 'Error closing shift in staff service');
|
|
110
|
+
return Promise.reject(error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Open a work log in the staff MS and record this event in the time tracking database.
|
|
116
|
+
* @param {Object} workLog
|
|
117
|
+
* @param {string} workLog.referenceId => Reference ID related to the work log
|
|
118
|
+
* @param {string} workLog.name => Name related to the work log
|
|
119
|
+
* @param {string} workLog.type => Type related to the work log
|
|
120
|
+
* @param {number} workLog.suggestedTime => Suggested time related to the work log
|
|
121
|
+
* @throws {Error} error
|
|
122
|
+
* @returns {Promise<string>} workLogId => ID related to the work log that has just been opened for the user
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
async openWorkLog(workLog = {}) {
|
|
126
|
+
try {
|
|
127
|
+
Crashlytics.log('user open shift worklog', workLog);
|
|
128
|
+
|
|
129
|
+
if (!isObject(workLog) || isEmptyObject(workLog)) return null;
|
|
130
|
+
|
|
131
|
+
const {referenceId, name, type, suggestedTime = 0} = workLog;
|
|
132
|
+
const shiftId = Storage.getString(SHIFT_ID);
|
|
133
|
+
const startTime = new Date().toISOString();
|
|
134
|
+
const randomId = generateRandomId();
|
|
135
|
+
|
|
136
|
+
const workLogId = Formatter.formatWorkLogId(referenceId, randomId);
|
|
137
|
+
|
|
138
|
+
// TODO: uncomment this when resolve how to handle offline workLogs
|
|
139
|
+
// await ShiftWorklogs.open({
|
|
140
|
+
// referenceId,
|
|
141
|
+
// startDate: startTime,
|
|
142
|
+
// });
|
|
143
|
+
|
|
144
|
+
const dataForTimeTracker = {
|
|
145
|
+
type,
|
|
146
|
+
name,
|
|
147
|
+
shiftId,
|
|
148
|
+
referenceId,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
OfflineData.save(workLogId, {
|
|
152
|
+
referenceId,
|
|
153
|
+
startDate: startTime,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await this._startTracking({
|
|
157
|
+
id: workLogId,
|
|
158
|
+
date: startTime,
|
|
159
|
+
payload: dataForTimeTracker,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const suggestedFinishDate = new Date(startTime).getTime() + suggestedTime * 60 * 1000;
|
|
163
|
+
|
|
164
|
+
const dataForStorage = {
|
|
165
|
+
...dataForTimeTracker,
|
|
166
|
+
suggestedFinishDate: new Date(suggestedFinishDate).toISOString(),
|
|
167
|
+
suggestedTime,
|
|
168
|
+
startDate: startTime,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
if (!EXCLUDED_WORKLOG_TYPES.includes(referenceId)) {
|
|
172
|
+
Storage.set(SHIFT_STATUS, 'paused');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
Storage.set(CURRENT_WORKLOG_ID, workLogId);
|
|
176
|
+
Storage.set(CURRENT_WORKLOG_DATA, JSON.stringify(dataForStorage));
|
|
177
|
+
|
|
178
|
+
return workLogId;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
Crashlytics.recordError(error, 'Error opening shift worklog');
|
|
181
|
+
return Promise.reject(error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Finish a work log in the staff MS and record this event in the time tracking database.
|
|
187
|
+
* @param {Object} workLog
|
|
188
|
+
* @param {string} workLog.referenceId => Reference ID related to the work log
|
|
189
|
+
* @param {string} workLog.name => Name related to the work log
|
|
190
|
+
* @param {string} workLog.type => Type related to the work log
|
|
191
|
+
* @throws {Error} error
|
|
192
|
+
* @returns {Promise<string>} workLogId => ID related to the work log that has just been closed for the user
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
async finishWorkLog(workLog = {}) {
|
|
196
|
+
try {
|
|
197
|
+
Crashlytics.log('user close shift worklog', workLog);
|
|
198
|
+
if (!isObject(workLog) || isEmptyObject(workLog)) return null;
|
|
199
|
+
|
|
200
|
+
const {referenceId, name, type} = workLog;
|
|
201
|
+
const shiftId = Storage.getString(SHIFT_ID);
|
|
202
|
+
const shiftStatus = Storage.getString(SHIFT_STATUS);
|
|
203
|
+
const endTime = new Date().toISOString();
|
|
204
|
+
const workLogId = Storage.getString(CURRENT_WORKLOG_ID);
|
|
205
|
+
|
|
206
|
+
// TODO: uncomment this when resolve how to handle offline workLogs
|
|
207
|
+
// await ShiftWorklogs.finish({
|
|
208
|
+
// referenceId,
|
|
209
|
+
// endDate: endTime,
|
|
210
|
+
// });
|
|
211
|
+
|
|
212
|
+
const dataForTimeTracker = {
|
|
213
|
+
type,
|
|
214
|
+
name,
|
|
215
|
+
shiftId,
|
|
216
|
+
referenceId,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
OfflineData.save(workLogId, {
|
|
220
|
+
referenceId,
|
|
221
|
+
endDate: endTime,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await this._finishTracking({
|
|
225
|
+
id: workLogId,
|
|
226
|
+
date: endTime,
|
|
227
|
+
payload: dataForTimeTracker,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
if (shiftStatus === 'paused') {
|
|
231
|
+
Storage.set(SHIFT_STATUS, 'opened');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this._deleteCurrentWorkLogData();
|
|
235
|
+
|
|
236
|
+
return workLogId;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
Crashlytics.recordError(error, 'Error closing shift worklog');
|
|
239
|
+
return Promise.reject(error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async sendPendingWorkLogs() {
|
|
244
|
+
try {
|
|
245
|
+
const storageData = OfflineData.get();
|
|
246
|
+
const formatedWorkLogs = Formatter.formatOfflineWorkLog(storageData);
|
|
247
|
+
|
|
248
|
+
if (isEmptyArray(formatedWorkLogs)) return null;
|
|
249
|
+
|
|
250
|
+
const shiftIsExpired = this.isDateToCloseExceeded();
|
|
251
|
+
|
|
252
|
+
if (shiftIsExpired) {
|
|
253
|
+
await this.reOpen();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await ShiftWorklogs.postPendingBatch(formatedWorkLogs);
|
|
257
|
+
OfflineData.deleteAll();
|
|
258
|
+
return null;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
Crashlytics.recordError(error, 'Error posting pending work logs');
|
|
261
|
+
return Promise.reject(error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get the open shift for a user
|
|
267
|
+
* @param {Object} params
|
|
268
|
+
* @param {string} params.userId => ID related to the user
|
|
269
|
+
* @param {string} params.id => ID related to the shift
|
|
270
|
+
* @throws {Error} error
|
|
271
|
+
* @returns {Promise<Object>} shift => The open shift for the user
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
async getUserOpenShift(params = {}) {
|
|
275
|
+
try {
|
|
276
|
+
Crashlytics.log('user get open shift', params);
|
|
277
|
+
const {userId, id, ...rest} = params;
|
|
278
|
+
const {result: shifts} = await StaffService.getShiftsList({
|
|
279
|
+
filters: {
|
|
280
|
+
userId,
|
|
281
|
+
status: 'opened',
|
|
282
|
+
...(!!id && {id}),
|
|
283
|
+
...rest,
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const [openShift] = shifts || [];
|
|
287
|
+
|
|
288
|
+
return openShift || {};
|
|
289
|
+
} catch (error) {
|
|
290
|
+
Crashlytics.recordError(error, 'Error getting open shift in staff service');
|
|
291
|
+
return Promise.reject(error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get the work logs for a shift
|
|
297
|
+
* @param {string} shiftId => ID related to the shift
|
|
298
|
+
* @throws {Error} error
|
|
299
|
+
* @returns {Promise<Array>} workLogs => Array of work logs
|
|
300
|
+
*/
|
|
301
|
+
|
|
302
|
+
async getWorkLogs(shiftId) {
|
|
303
|
+
try {
|
|
304
|
+
Crashlytics.log('user get work logs');
|
|
305
|
+
const userShiftId = shiftId || Storage.getString(SHIFT_ID);
|
|
306
|
+
|
|
307
|
+
if (!userShiftId) throw new Error('Shift ID not found');
|
|
308
|
+
|
|
309
|
+
const workLogs = await ShiftWorklogs.getList(userShiftId);
|
|
310
|
+
|
|
311
|
+
return Formatter.formatWorkLogsFromJanis(workLogs);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
Crashlytics.recordError(error, 'Error getting work logs in staff service');
|
|
314
|
+
return Promise.reject(error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Re open current shift in the staff MS and extend storage shift closing date.
|
|
320
|
+
* @throws {Error} error
|
|
321
|
+
* @returns {Promise<null>} null
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
async reOpen() {
|
|
325
|
+
try {
|
|
326
|
+
Crashlytics.log('user re open shift');
|
|
327
|
+
const shiftIsExpired = this.isDateMaxToCloseExceeded();
|
|
328
|
+
|
|
329
|
+
if (shiftIsExpired) {
|
|
330
|
+
throw new Error('The deadline for ending the shift has been exceeded');
|
|
331
|
+
}
|
|
332
|
+
await StaffService.openShift();
|
|
333
|
+
this._extendShiftClosingDate();
|
|
334
|
+
|
|
335
|
+
return null;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
Crashlytics.recordError(error, 'Error re opening shift');
|
|
338
|
+
return Promise.reject(error);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
isDateToCloseExceeded() {
|
|
343
|
+
const shiftData = getObject(SHIFT_DATA);
|
|
344
|
+
const {dateToClose} = shiftData;
|
|
345
|
+
const currentDate = new Date();
|
|
346
|
+
|
|
347
|
+
return new Date(dateToClose).getTime() < currentDate.getTime();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
isDateMaxToCloseExceeded() {
|
|
351
|
+
const shiftData = getObject(SHIFT_DATA);
|
|
352
|
+
const {dateMaxToClose} = shiftData;
|
|
353
|
+
const currentDate = new Date();
|
|
354
|
+
|
|
355
|
+
return new Date(dateMaxToClose).getTime() < currentDate.getTime();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Gets a shift report based on the shift ID and its registered events.
|
|
360
|
+
*
|
|
361
|
+
* The report includes information about performed activities, start and end times,
|
|
362
|
+
* total elapsed time, work time and pause time.
|
|
363
|
+
*
|
|
364
|
+
* @async
|
|
365
|
+
* @function getReport
|
|
366
|
+
* @throws {Error} If the `shiftId` is not found in storage.
|
|
367
|
+
* @returns {Promise<{
|
|
368
|
+
* activities: Array<{
|
|
369
|
+
* id: string,
|
|
370
|
+
* name: string,
|
|
371
|
+
* type: string,
|
|
372
|
+
* startDate: string,
|
|
373
|
+
* endDate: string,
|
|
374
|
+
* duration: number
|
|
375
|
+
* }>,
|
|
376
|
+
* startDate: string,
|
|
377
|
+
* endDate: string,
|
|
378
|
+
* elapsedTime: number,
|
|
379
|
+
* workTime: number,
|
|
380
|
+
* pauseTime: number,
|
|
381
|
+
* isComplete: boolean,
|
|
382
|
+
* error: string | null
|
|
383
|
+
* }>} Object with shift details.
|
|
384
|
+
*
|
|
385
|
+
* @property {Array} activities - List of activities registered during the shift.
|
|
386
|
+
* @property {string} activities[].id - Unique identifier of the activity.
|
|
387
|
+
* @property {string} activities[].name - Name or description of the activity.
|
|
388
|
+
* @property {string} activities[].startDate - Start date and time of the activity.
|
|
389
|
+
* @property {string} activities[].endDate - End date and time of the activity.
|
|
390
|
+
* @property {number} activities[].duration - Duration of the activity in milliseconds.
|
|
391
|
+
* @property {string} startDate - Start date and time of the shift.
|
|
392
|
+
* @property {string} endDate - End date and time of the shift (can be empty if not finished).
|
|
393
|
+
* @property {number} elapsedTime - Total elapsed time between shift start and end.
|
|
394
|
+
* @property {number} workTime - Effective work time (total time minus pauses).
|
|
395
|
+
* @property {number} pauseTime - Total time of registered pauses.
|
|
396
|
+
* @property {boolean} isComplete - Indicates if the report was obtained completely without errors.
|
|
397
|
+
* @property {string|null} error - Error message if the report is not complete, or null if there were no errors.
|
|
398
|
+
*/
|
|
399
|
+
|
|
400
|
+
async getReport() {
|
|
401
|
+
try {
|
|
402
|
+
Crashlytics.log('user get shift report');
|
|
403
|
+
const shiftId = Storage.getString(SHIFT_ID);
|
|
404
|
+
|
|
405
|
+
if (!shiftId) throw new Error('Shift ID is required');
|
|
406
|
+
|
|
407
|
+
const startDate = await TrackerRecords.getStartDateById(shiftId);
|
|
408
|
+
const endDate = await TrackerRecords.getEndDateById(shiftId);
|
|
409
|
+
const workLogs = await ShiftWorklogs.getShiftTrackedWorkLogs(shiftId);
|
|
410
|
+
|
|
411
|
+
const activities = Formatter.formatShiftActivities(workLogs);
|
|
412
|
+
const elapsedTime = TimeTracker.getElapsedTime({
|
|
413
|
+
startTime: startDate,
|
|
414
|
+
...(!!endDate && {endTime: endDate}),
|
|
415
|
+
format: false,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const pauseTime = activities.reduce((acc, activity) => acc + (activity?.duration || 0), 0);
|
|
419
|
+
const workTime = elapsedTime - pauseTime;
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
activities,
|
|
423
|
+
startDate,
|
|
424
|
+
endDate,
|
|
425
|
+
elapsedTime,
|
|
426
|
+
workTime,
|
|
427
|
+
pauseTime,
|
|
428
|
+
};
|
|
429
|
+
} catch (reason) {
|
|
430
|
+
Crashlytics.recordError(reason, 'Error getting shift report');
|
|
431
|
+
return Promise.reject(reason);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Fetch the work log types from the staff MS and prepare them for register an activity.
|
|
437
|
+
* @throws {Error} error
|
|
438
|
+
* @returns {Promise<Array>} workLogTypes => Array of work log types
|
|
439
|
+
*/
|
|
440
|
+
|
|
441
|
+
async fetchWorklogTypes() {
|
|
442
|
+
try {
|
|
443
|
+
Crashlytics.log('user fetch worklog types');
|
|
444
|
+
const {result: workLogTypes = []} = await StaffService.getWorkLogTypes();
|
|
445
|
+
|
|
446
|
+
return Formatter.formatWorkLogTypes(workLogTypes);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
Crashlytics.recordError(error, 'Error fetching worklog types in staff service');
|
|
449
|
+
return Promise.reject(error);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Delete all registers related to the shift in the time tracking database.
|
|
455
|
+
* @throws {Error} error
|
|
456
|
+
* @returns {Promise<boolean>} true => if the registers were deleted successfully
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
async deleteShiftRegisters() {
|
|
460
|
+
try {
|
|
461
|
+
Crashlytics.log('user delete shift registers');
|
|
462
|
+
this._deleteShiftData();
|
|
463
|
+
this._deleteCurrentWorkLogData();
|
|
464
|
+
OfflineData.deleteAll();
|
|
465
|
+
return await TimeTracker.deleteAllEvents();
|
|
466
|
+
} catch (error) {
|
|
467
|
+
Crashlytics.recordError(error, 'Error deleting registers from shift tracking database');
|
|
468
|
+
return Promise.reject(error);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
_deleteShiftData() {
|
|
473
|
+
Storage.delete(SHIFT_ID);
|
|
474
|
+
Storage.delete(SHIFT_STATUS);
|
|
475
|
+
Storage.delete(SHIFT_DATA);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
_deleteCurrentWorkLogData() {
|
|
479
|
+
Storage.delete(CURRENT_WORKLOG_ID);
|
|
480
|
+
Storage.delete(CURRENT_WORKLOG_DATA);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* @private
|
|
485
|
+
* Start id tracking in the time tracking database.
|
|
486
|
+
* @param {Object} params
|
|
487
|
+
* @param {string} params.id => ID related to the shift
|
|
488
|
+
* @param {string} params.date => Date related to the shift
|
|
489
|
+
* @param {Object} params.payload => Payload related to the shift
|
|
490
|
+
*/
|
|
491
|
+
|
|
492
|
+
async _startTracking(params) {
|
|
493
|
+
const {id, date, payload} = params;
|
|
494
|
+
await TimeTracker.addEvent({
|
|
495
|
+
id,
|
|
496
|
+
time: date || new Date().toISOString(),
|
|
497
|
+
type: 'start',
|
|
498
|
+
payload,
|
|
499
|
+
}).catch(() => null);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* @private
|
|
504
|
+
* Finish id tracking in the time tracking database.
|
|
505
|
+
* @param {Object} params
|
|
506
|
+
* @param {string} params.id => ID related to the shift
|
|
507
|
+
* @param {string} params.date => Date related to the shift
|
|
508
|
+
* @param {Object} params.payload => Payload related to the shift
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
async _finishTracking(params) {
|
|
512
|
+
const {id, date, payload} = params;
|
|
513
|
+
await TimeTracker.addEvent({
|
|
514
|
+
id,
|
|
515
|
+
time: date || new Date().toISOString(),
|
|
516
|
+
type: 'finish',
|
|
517
|
+
payload,
|
|
518
|
+
}).catch(() => null);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* @private
|
|
523
|
+
* Extend the shift closing date.
|
|
524
|
+
* @throws {Error} error
|
|
525
|
+
* @returns {Promise<null>} null
|
|
526
|
+
*/
|
|
527
|
+
|
|
528
|
+
_extendShiftClosingDate() {
|
|
529
|
+
const shiftData = getObject(SHIFT_DATA);
|
|
530
|
+
const {dateToClose} = shiftData;
|
|
531
|
+
const updatedClosingDate = new Date(dateToClose).getTime() + ONE_HOUR_EXTENSION;
|
|
532
|
+
|
|
533
|
+
shiftData.dateToClose = new Date(updatedClosingDate).toISOString();
|
|
534
|
+
|
|
535
|
+
setObject(SHIFT_DATA, shiftData);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
export default new Shift();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import StaffApiServices from './StaffApiServices';
|
|
2
|
+
import TrackerRecords from './TrackerRecords';
|
|
3
|
+
import {isArray, isEmptyArray} from './utils/helpers';
|
|
4
|
+
|
|
5
|
+
class ShiftWorklogs {
|
|
6
|
+
async open(params) {
|
|
7
|
+
try {
|
|
8
|
+
const {referenceId = '', startDate = ''} = params || {};
|
|
9
|
+
const {result} = await StaffApiServices.postWorklog({
|
|
10
|
+
workLogTypeRefId: referenceId,
|
|
11
|
+
startDate,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const {itemsCreated = [], itemsUpdated} = result || {};
|
|
15
|
+
|
|
16
|
+
if (itemsCreated?.length) {
|
|
17
|
+
const [createdWorklog = {}] = itemsCreated;
|
|
18
|
+
return createdWorklog?.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const [updatedWorklog] = itemsUpdated || [];
|
|
22
|
+
|
|
23
|
+
return updatedWorklog?.id;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return Promise.reject(error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async finish(params) {
|
|
30
|
+
try {
|
|
31
|
+
const {referenceId = '', endDate = ''} = params || {};
|
|
32
|
+
const {result} = await StaffApiServices.postWorklog({
|
|
33
|
+
workLogTypeRefId: referenceId,
|
|
34
|
+
endDate,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const {itemsCreated = [], itemsUpdated} = result;
|
|
38
|
+
if (itemsCreated?.length) {
|
|
39
|
+
const [createdWorklog = {}] = itemsCreated;
|
|
40
|
+
return createdWorklog?.id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const [updatedWorklog] = itemsUpdated || [];
|
|
44
|
+
|
|
45
|
+
return updatedWorklog?.id;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return Promise.reject(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getList(shiftId) {
|
|
52
|
+
try {
|
|
53
|
+
const {result: workLogs} = await StaffApiServices.getWorkLogsList({
|
|
54
|
+
filters: {
|
|
55
|
+
shiftId,
|
|
56
|
+
status: ['inProgress', 'finished'],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return workLogs || [];
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return Promise.reject(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getShiftTrackedWorkLogs(shiftId) {
|
|
67
|
+
try {
|
|
68
|
+
if (!shiftId) throw new Error(`Shift ID is required, but got ${shiftId}`);
|
|
69
|
+
|
|
70
|
+
const workLogEvents = await TrackerRecords.getClientShiftActivities(shiftId);
|
|
71
|
+
const shiftWorkLogs = workLogEvents.filter((e) => e?.payload?.shiftId === shiftId);
|
|
72
|
+
shiftWorkLogs.sort((a, b) => new Date(a?.time) - new Date(b?.time));
|
|
73
|
+
|
|
74
|
+
return shiftWorkLogs;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return Promise.reject(error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async postPendingBatch(pendingWorkLogs = []) {
|
|
81
|
+
try {
|
|
82
|
+
if (!isArray(pendingWorkLogs) || isEmptyArray(pendingWorkLogs)) return null;
|
|
83
|
+
|
|
84
|
+
await StaffApiServices.postWorklog(pendingWorkLogs);
|
|
85
|
+
return null;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return Promise.reject(error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default new ShiftWorklogs();
|