@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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import Request from './utils/request';
|
|
2
|
+
|
|
3
|
+
class StaffApiServices {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.service = 'staff';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Opens an user's shift
|
|
10
|
+
* @returns {Promise<Object>} - Response from the API
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
async openShift() {
|
|
14
|
+
try {
|
|
15
|
+
return await Request.post({
|
|
16
|
+
service: this.service,
|
|
17
|
+
namespace: 'shift-open',
|
|
18
|
+
});
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return Promise.reject(error);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Closes current user's shift
|
|
26
|
+
* @returns {Promise<Object>} - Response from the API
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
async closeShift() {
|
|
30
|
+
try {
|
|
31
|
+
return await Request.post({
|
|
32
|
+
service: this.service,
|
|
33
|
+
namespace: 'shift-close',
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return Promise.reject(error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Posts work logs to the API
|
|
42
|
+
* @param {Object|Array<Object>} workLogs - Work log object or array of work log objects
|
|
43
|
+
* @param {string} workLogs.workLogTypeRefId - Reference ID of the work log type
|
|
44
|
+
* @param {Date} workLogs.startDate - Start date and time of the work log
|
|
45
|
+
* @param {Date} workLogs.endDate - End date and time of the work log
|
|
46
|
+
* @returns {Promise<Object>} - Response from the API
|
|
47
|
+
* @example
|
|
48
|
+
* // Single work log object
|
|
49
|
+
* postWorklog({
|
|
50
|
+
* workLogTypeRefId: "123",
|
|
51
|
+
* startDate: new Date("2024-01-01T09:00:00"),
|
|
52
|
+
* endDate: new Date("2024-01-01T17:00:00")
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Array of work log objects
|
|
56
|
+
* postWorklog([
|
|
57
|
+
* {
|
|
58
|
+
* workLogTypeRefId: "123",
|
|
59
|
+
* startDate: new Date("2024-01-01T09:00:00"),
|
|
60
|
+
* endDate: new Date("2024-01-01T12:00:00")
|
|
61
|
+
* },
|
|
62
|
+
* {
|
|
63
|
+
* workLogTypeRefId: "456",
|
|
64
|
+
* startDate: new Date("2024-01-01T13:00:00"),
|
|
65
|
+
* endDate: new Date("2024-01-01T17:00:00")
|
|
66
|
+
* }
|
|
67
|
+
* ]);
|
|
68
|
+
*/
|
|
69
|
+
async postWorklog(workLogs) {
|
|
70
|
+
try {
|
|
71
|
+
if (!Array.isArray(workLogs)) {
|
|
72
|
+
workLogs = [workLogs];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return await Request.post({
|
|
76
|
+
service: this.service,
|
|
77
|
+
namespace: 'work-log',
|
|
78
|
+
body: [...workLogs],
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return Promise.reject(error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getShiftsList(params) {
|
|
86
|
+
const {filters, sort} = params || {};
|
|
87
|
+
try {
|
|
88
|
+
return await Request.list({
|
|
89
|
+
service: this.service,
|
|
90
|
+
namespace: 'shift',
|
|
91
|
+
queryParams: {
|
|
92
|
+
...(filters && {filters}),
|
|
93
|
+
...(sort && {sort}),
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return Promise.reject(error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async getWorkLogTypes() {
|
|
102
|
+
try {
|
|
103
|
+
return await Request.list({
|
|
104
|
+
service: this.service,
|
|
105
|
+
namespace: 'work-log-type',
|
|
106
|
+
headers: {
|
|
107
|
+
pageSize: 100,
|
|
108
|
+
},
|
|
109
|
+
queryParams: {
|
|
110
|
+
filters: {
|
|
111
|
+
status: 'active',
|
|
112
|
+
type: ['work', 'pause', 'problem'],
|
|
113
|
+
isInternal: 'false',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return Promise.reject(error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getSetting(setting) {
|
|
123
|
+
try {
|
|
124
|
+
return await Request.get({
|
|
125
|
+
service: this.service,
|
|
126
|
+
namespace: 'setting',
|
|
127
|
+
pathParams: [setting],
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return Promise.reject(error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async getWorkLogsList(params) {
|
|
135
|
+
const {filters, sort} = params || {};
|
|
136
|
+
try {
|
|
137
|
+
return await Request.list({
|
|
138
|
+
service: this.service,
|
|
139
|
+
namespace: 'work-log',
|
|
140
|
+
queryParams: {
|
|
141
|
+
...(filters && {filters}),
|
|
142
|
+
...(sort && {sort}),
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return Promise.reject(error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default new StaffApiServices();
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import TimeTracker from './db/TimeTrackerService';
|
|
2
|
+
import {reverseArray} from './utils/helpers';
|
|
3
|
+
|
|
4
|
+
class TrackerRecords {
|
|
5
|
+
async getWorkLogsFromTimeTracker(filteredId) {
|
|
6
|
+
try {
|
|
7
|
+
if (!filteredId) throw new Error(`Excluding ID is required, but got ${filteredId}`);
|
|
8
|
+
|
|
9
|
+
const events = await this._filterEventsExcludingId(filteredId);
|
|
10
|
+
|
|
11
|
+
return events;
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return Promise.reject(error);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getClientShiftActivities(id) {
|
|
18
|
+
try {
|
|
19
|
+
const query = 'NOT (id CONTAINS[c] "picking" OR id CONTAINS[c] "delivery") AND id != $0';
|
|
20
|
+
const clientActivities = await TimeTracker.searchEventByQuery(query, id);
|
|
21
|
+
|
|
22
|
+
return clientActivities;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return Promise.reject(error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async getStartDateById(id) {
|
|
29
|
+
try {
|
|
30
|
+
if (!id) throw new Error(`ID is required, but got ${id}`);
|
|
31
|
+
|
|
32
|
+
const startShiftEvent = await this._getStartEventById(id);
|
|
33
|
+
|
|
34
|
+
return startShiftEvent?.time;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return Promise.reject(error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async getEndDateById(id) {
|
|
41
|
+
try {
|
|
42
|
+
if (!id) throw new Error(`ID is required, but got ${id}`);
|
|
43
|
+
|
|
44
|
+
const endShiftEvent = await this._getFinishEventById(id);
|
|
45
|
+
|
|
46
|
+
return endShiftEvent?.time;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return Promise.reject(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async _getStartEventById(id) {
|
|
53
|
+
try {
|
|
54
|
+
const startEvent = await this._filterEventByType(id, 'start');
|
|
55
|
+
|
|
56
|
+
const [firstEvent = {}] = startEvent;
|
|
57
|
+
|
|
58
|
+
return firstEvent;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return Promise.reject(error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async _getFinishEventById(id) {
|
|
65
|
+
try {
|
|
66
|
+
const finishEvent = await this._filterEventByType(id, 'finish');
|
|
67
|
+
|
|
68
|
+
const [lastEvent = {}] = reverseArray(finishEvent);
|
|
69
|
+
|
|
70
|
+
return lastEvent;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return Promise.reject(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async _filterEventByType(id, type) {
|
|
77
|
+
try {
|
|
78
|
+
if (!id) throw new Error('id is required');
|
|
79
|
+
if (!type) throw new Error('type is required');
|
|
80
|
+
|
|
81
|
+
const events = await TimeTracker.searchEventByQuery('id == $0 AND type == $1', id, type);
|
|
82
|
+
console.log('events', events);
|
|
83
|
+
|
|
84
|
+
return events;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return Promise.reject(error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async _filterEventsExcludingId(id) {
|
|
91
|
+
try {
|
|
92
|
+
if (!id) throw new Error('id is required');
|
|
93
|
+
|
|
94
|
+
const events = await TimeTracker.searchEventByQuery('id!= $0', id);
|
|
95
|
+
|
|
96
|
+
return events;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return Promise.reject(error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default new TrackerRecords();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {useShiftTracking} from '../../context/ShiftTrackingContext';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {React.Component} WrappedComponent - The component to wrap
|
|
6
|
+
* @param {Object} options - The options for the component
|
|
7
|
+
* @param {React.Component} options.pausedShiftComponent - The component to render when the shift is paused
|
|
8
|
+
* @returns {React.Component} - The wrapped component
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const MyComponent = () => {
|
|
12
|
+
* return <div>My Component</div>;
|
|
13
|
+
* };
|
|
14
|
+
*
|
|
15
|
+
* export default WithShiftTracking(MyComponent, {
|
|
16
|
+
* pausedShiftComponent: <div>Paused Shift</div>,
|
|
17
|
+
* });
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const WithShiftTracking = (WrappedComponent, options = {}) => {
|
|
21
|
+
const {pausedShiftComponent} = options;
|
|
22
|
+
|
|
23
|
+
return (props) => {
|
|
24
|
+
const shiftTrackingData = useShiftTracking();
|
|
25
|
+
const showPause = shiftTrackingData?.shiftStatus === 'paused' && pausedShiftComponent;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
{showPause && pausedShiftComponent}
|
|
30
|
+
<WrappedComponent {...props} shiftData={shiftTrackingData} />
|
|
31
|
+
</>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default WithShiftTracking;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// STORAGE KEYS
|
|
2
|
+
export const SHIFT_ID = 'shift.id';
|
|
3
|
+
export const SHIFT_STATUS = 'shift.status';
|
|
4
|
+
export const SHIFT_DATA = 'shift.data';
|
|
5
|
+
export const WORKLOG_TYPES_DATA = 'worklogTypes.data';
|
|
6
|
+
export const CURRENT_WORKLOG_ID = 'worklog.id';
|
|
7
|
+
export const CURRENT_WORKLOG_DATA = 'worklog.data';
|
|
8
|
+
export const STAFF_AUTH = 'staff.authorization';
|
|
9
|
+
export const OFFLINE_DATA = 'offline.data';
|
|
10
|
+
|
|
11
|
+
// EXPIRATION TIMES
|
|
12
|
+
export const WORKLOG_TYPES_EXPIRATION_TIME = 4 * 60 * 60 * 1000; // 4 hours
|
|
13
|
+
export const STAFF_MS_AUTHORIZATION_EXPIRATION_TIME = 24 * 60 * 60 * 1000; // 24 hour
|
|
14
|
+
|
|
15
|
+
// EXCLUDED WORKLOG TYPES
|
|
16
|
+
export const EXCLUDED_WORKLOG_TYPES = ['default-picking-work', 'default-delivery-work'];
|
|
17
|
+
|
|
18
|
+
// INTERNAL DEFAULT WORKLOGS
|
|
19
|
+
export const INTERNAL_WORKLOGS = {
|
|
20
|
+
PICKING_WORK: {
|
|
21
|
+
referenceId: 'default-picking-work',
|
|
22
|
+
type: 'work',
|
|
23
|
+
name: 'Default picking work',
|
|
24
|
+
},
|
|
25
|
+
DELIVERY_WORK: {
|
|
26
|
+
referenceId: 'default-delivery-work',
|
|
27
|
+
type: 'work',
|
|
28
|
+
name: 'Default delivery work',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// SHIFT CLOSE EXTENSION
|
|
33
|
+
export const ONE_HOUR_EXTENSION = 60 * 60 * 1000; // 1 hour
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {useMMKVString} from 'react-native-mmkv';
|
|
2
|
+
import {useMemo} from 'react';
|
|
3
|
+
import Crashlytics from '../../utils/crashlytics';
|
|
4
|
+
|
|
5
|
+
export const useMMKVObject = (key, defaultValue = null) => {
|
|
6
|
+
const [raw] = useMMKVString(key);
|
|
7
|
+
|
|
8
|
+
const storageValue = useMemo(() => {
|
|
9
|
+
if (!raw) return defaultValue;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(raw);
|
|
12
|
+
} catch (e) {
|
|
13
|
+
Crashlytics.recordError(e, `Invalid JSON in MMKV key: ${key}`);
|
|
14
|
+
return defaultValue;
|
|
15
|
+
}
|
|
16
|
+
}, [raw]);
|
|
17
|
+
|
|
18
|
+
return [storageValue];
|
|
19
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Shift from './Shift';
|
|
2
|
+
import ShiftTrackingProvider from './provider/ShiftTrackingProvider';
|
|
3
|
+
import {useShiftTracking} from './context/ShiftTrackingContext';
|
|
4
|
+
import WithShiftTracking from './components/WithShiftTracking';
|
|
5
|
+
import {INTERNAL_WORKLOGS} from './constant';
|
|
6
|
+
|
|
7
|
+
export {Shift, ShiftTrackingProvider, useShiftTracking, WithShiftTracking, INTERNAL_WORKLOGS};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React, {useEffect, useMemo, useState} from 'react';
|
|
2
|
+
import {useMMKVString} from 'react-native-mmkv';
|
|
3
|
+
import ShiftTrackingContext from '../context/ShiftTrackingContext';
|
|
4
|
+
import {
|
|
5
|
+
openShift,
|
|
6
|
+
downloadWorkLogTypes,
|
|
7
|
+
isAuthorizedToUseStaffMS,
|
|
8
|
+
getShiftWorkLogsFromJanis,
|
|
9
|
+
saveWorkLogTimesInDB,
|
|
10
|
+
} from '../utils/provider';
|
|
11
|
+
import {
|
|
12
|
+
CURRENT_WORKLOG_DATA,
|
|
13
|
+
CURRENT_WORKLOG_ID,
|
|
14
|
+
EXCLUDED_WORKLOG_TYPES,
|
|
15
|
+
SHIFT_DATA,
|
|
16
|
+
SHIFT_ID,
|
|
17
|
+
SHIFT_STATUS,
|
|
18
|
+
STAFF_AUTH,
|
|
19
|
+
WORKLOG_TYPES_DATA,
|
|
20
|
+
} from '../constant';
|
|
21
|
+
import {useMMKVObject} from '../hooks/useMMKVObject';
|
|
22
|
+
import {isValidObject, promiseWrapper} from '../utils/helpers';
|
|
23
|
+
import Crashlytics from '../utils/crashlytics';
|
|
24
|
+
import Storage from '../db/StorageService';
|
|
25
|
+
|
|
26
|
+
const ShiftTrackingProvider = ({children, onError = null}) => {
|
|
27
|
+
const [shiftStatus] = useMMKVString(SHIFT_STATUS);
|
|
28
|
+
const [shiftId] = useMMKVString(SHIFT_ID);
|
|
29
|
+
const [shiftData] = useMMKVObject(SHIFT_DATA, {});
|
|
30
|
+
|
|
31
|
+
const [currentWorkLogId] = useMMKVString(CURRENT_WORKLOG_ID);
|
|
32
|
+
const [workLogData] = useMMKVObject(WORKLOG_TYPES_DATA, {});
|
|
33
|
+
const [currentWorkLogData] = useMMKVObject(CURRENT_WORKLOG_DATA, {});
|
|
34
|
+
|
|
35
|
+
const [staffAuthData] = useMMKVObject(STAFF_AUTH, {});
|
|
36
|
+
|
|
37
|
+
const [error, setError] = useState(null);
|
|
38
|
+
const [openShiftResult, setOpenShiftResult] = useState({
|
|
39
|
+
id: null,
|
|
40
|
+
getWorkLogs: false,
|
|
41
|
+
});
|
|
42
|
+
const [isShiftLoading, setIsShiftLoading] = useState(false);
|
|
43
|
+
|
|
44
|
+
const {workLogTypes = []} = workLogData;
|
|
45
|
+
const {hasStaffAuthorization = false} = staffAuthData;
|
|
46
|
+
|
|
47
|
+
const contextValues = useMemo(() => {
|
|
48
|
+
return {
|
|
49
|
+
shiftId,
|
|
50
|
+
shiftStatus,
|
|
51
|
+
shiftData,
|
|
52
|
+
workLogTypes,
|
|
53
|
+
currentWorkLogData,
|
|
54
|
+
currentWorkLogId,
|
|
55
|
+
hasStaffAuthorization,
|
|
56
|
+
error,
|
|
57
|
+
isShiftLoading,
|
|
58
|
+
};
|
|
59
|
+
}, [
|
|
60
|
+
shiftId,
|
|
61
|
+
shiftStatus,
|
|
62
|
+
shiftData,
|
|
63
|
+
workLogTypes,
|
|
64
|
+
currentWorkLogData,
|
|
65
|
+
currentWorkLogId,
|
|
66
|
+
error,
|
|
67
|
+
hasStaffAuthorization,
|
|
68
|
+
isShiftLoading,
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
const handleShiftTrackingInit = async () => {
|
|
72
|
+
setIsShiftLoading(true);
|
|
73
|
+
const [isAuthorized, authError] = await promiseWrapper(isAuthorizedToUseStaffMS());
|
|
74
|
+
|
|
75
|
+
if (authError) {
|
|
76
|
+
setIsShiftLoading(false);
|
|
77
|
+
setError({
|
|
78
|
+
message: authError?.message,
|
|
79
|
+
type: 'staffMSAuthorization',
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!isAuthorized) {
|
|
85
|
+
setIsShiftLoading(false);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const [shiftResult, openError] = await promiseWrapper(openShift(onError));
|
|
90
|
+
|
|
91
|
+
if (openError) {
|
|
92
|
+
setIsShiftLoading(false);
|
|
93
|
+
setError({
|
|
94
|
+
message: openError?.message,
|
|
95
|
+
type: 'openShift',
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const {openShiftId, getWorkLogs} = shiftResult;
|
|
101
|
+
|
|
102
|
+
setOpenShiftResult((prev) => ({
|
|
103
|
+
...prev,
|
|
104
|
+
id: openShiftId,
|
|
105
|
+
getWorkLogs,
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
const [, downloadError] = await promiseWrapper(downloadWorkLogTypes(onError));
|
|
109
|
+
|
|
110
|
+
setIsShiftLoading(false);
|
|
111
|
+
if (downloadError) {
|
|
112
|
+
setError({
|
|
113
|
+
message: downloadError?.message,
|
|
114
|
+
type: 'downloadWorkLogTypes',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const getShiftWorkLogsHistory = async () => {
|
|
120
|
+
const [workLogs, workLogsError] = await promiseWrapper(
|
|
121
|
+
getShiftWorkLogsFromJanis(openShiftResult.id)
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (workLogsError) {
|
|
125
|
+
setError({
|
|
126
|
+
message: workLogsError?.message,
|
|
127
|
+
type: 'getWorkLogsFromJanis',
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const {openWorkLogs, closedWorkLogs} = workLogs;
|
|
133
|
+
const [currentWorkLog = {}] = openWorkLogs;
|
|
134
|
+
const isExcludedWork = EXCLUDED_WORKLOG_TYPES.includes(currentWorkLog?.referenceId);
|
|
135
|
+
|
|
136
|
+
if (isValidObject(currentWorkLog)) {
|
|
137
|
+
Storage.set(CURRENT_WORKLOG_ID, currentWorkLog.id);
|
|
138
|
+
Storage.set(CURRENT_WORKLOG_DATA, JSON.stringify(currentWorkLog));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (isValidObject(currentWorkLog) && !isExcludedWork) {
|
|
142
|
+
Storage.set(SHIFT_STATUS, 'paused');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const promises = [currentWorkLog, ...closedWorkLogs].map((workLog) =>
|
|
146
|
+
saveWorkLogTimesInDB(workLog).catch((workLogError) => {
|
|
147
|
+
Crashlytics.recordError(workLogError, 'error trying to save work log times in db', workLog);
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
await Promise.all(promises);
|
|
152
|
+
|
|
153
|
+
setOpenShiftResult((prev) => ({
|
|
154
|
+
...prev,
|
|
155
|
+
getWorkLogs: false,
|
|
156
|
+
}));
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
handleShiftTrackingInit();
|
|
161
|
+
}, []);
|
|
162
|
+
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (openShiftResult?.id && openShiftResult?.getWorkLogs) {
|
|
165
|
+
getShiftWorkLogsHistory();
|
|
166
|
+
}
|
|
167
|
+
}, [openShiftResult]);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<ShiftTrackingContext.Provider value={contextValues}>{children}</ShiftTrackingContext.Provider>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export default ShiftTrackingProvider;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const generateRandomId = () => Math.random().toString(32).slice(2);
|
|
2
|
+
|
|
3
|
+
export const isFunction = (fn) => !!({}.toString.call(fn) === '[object Function]');
|
|
4
|
+
|
|
5
|
+
export const isObject = (obj) => !!(obj && obj.constructor === Object);
|
|
6
|
+
|
|
7
|
+
export const isEmptyObject = (obj) => isObject(obj) && !Object.keys(obj).length;
|
|
8
|
+
|
|
9
|
+
export const promiseWrapper = (promise) =>
|
|
10
|
+
promise.then((data) => [data, null]).catch((error) => Promise.resolve([null, error]));
|
|
11
|
+
|
|
12
|
+
export const isArray = (arr) => Array.isArray(arr);
|
|
13
|
+
|
|
14
|
+
export const isEmptyArray = (arr) => isArray(arr) && !arr.length;
|
|
15
|
+
|
|
16
|
+
export const reverseArray = (arr) => arr.slice().reverse();
|
|
17
|
+
|
|
18
|
+
export const isNumber = (num) => typeof num === 'number' && !Number.isNaN(Number(num));
|
|
19
|
+
|
|
20
|
+
export const isValidObject = (obj) => isObject(obj) && !!Object.keys(obj).length;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Shift from '../../../Shift';
|
|
2
|
+
import {getWorkLogTypesData} from '../../storage';
|
|
3
|
+
import {WORKLOG_TYPES_DATA, WORKLOG_TYPES_EXPIRATION_TIME} from '../../../constant';
|
|
4
|
+
import Storage from '../../../db/StorageService';
|
|
5
|
+
import Crashlytics from '../../crashlytics';
|
|
6
|
+
import {isFunction} from '../../helpers';
|
|
7
|
+
|
|
8
|
+
const downloadWorkLogTypes = async (onDownloadError) => {
|
|
9
|
+
try {
|
|
10
|
+
const {isExpired} = getWorkLogTypesData();
|
|
11
|
+
|
|
12
|
+
if (!isExpired) return null;
|
|
13
|
+
|
|
14
|
+
const workLogTypes = await Shift.fetchWorklogTypes();
|
|
15
|
+
|
|
16
|
+
const data = {
|
|
17
|
+
workLogTypes,
|
|
18
|
+
expirationTime: Date.now() + WORKLOG_TYPES_EXPIRATION_TIME,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
Storage.set(WORKLOG_TYPES_DATA, JSON.stringify(data));
|
|
22
|
+
return null;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
Crashlytics.recordError(error, 'Error downloading worklog types');
|
|
25
|
+
if (isFunction(onDownloadError)) onDownloadError(error);
|
|
26
|
+
return Promise.reject(error?.result || error);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default downloadWorkLogTypes;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Formatter from '../../../Formatter';
|
|
2
|
+
import Shift from '../../../Shift';
|
|
3
|
+
|
|
4
|
+
const getShiftWorkLogsFromJanis = async (shiftId) => {
|
|
5
|
+
try {
|
|
6
|
+
const formattedWorkLogs = await Shift.getWorkLogs(shiftId);
|
|
7
|
+
const [openWorkLogs, closedWorkLogs] = Formatter.splitWorkLogsByStatus(formattedWorkLogs);
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
openWorkLogs,
|
|
11
|
+
closedWorkLogs,
|
|
12
|
+
};
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return Promise.reject(error);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default getShiftWorkLogsFromJanis;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export {default as downloadWorkLogTypes} from './downloadWorkLogTypes';
|
|
2
|
+
export {default as openShift} from './openShift';
|
|
3
|
+
export {default as isAuthorizedToUseStaffMS} from './isAuthorizedToUseStaffMS';
|
|
4
|
+
export {default as getShiftWorkLogsFromJanis} from './getShiftWorkLogsFromJanis';
|
|
5
|
+
export {default as saveWorkLogTimesInDB} from './saveWorkLogTimesInDB';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Crashlytics from '../../crashlytics';
|
|
2
|
+
import {STAFF_AUTH, STAFF_MS_AUTHORIZATION_EXPIRATION_TIME} from '../../../constant';
|
|
3
|
+
import Storage from '../../../db/StorageService';
|
|
4
|
+
import Shift from '../../../Shift';
|
|
5
|
+
import {getStaffAuthorizationData} from '../../storage';
|
|
6
|
+
|
|
7
|
+
const isAuthorizedToUseStaffMS = async () => {
|
|
8
|
+
try {
|
|
9
|
+
Crashlytics.log('checking staff MS authorization');
|
|
10
|
+
const {hasStaffAuthorization, isExpired} = getStaffAuthorizationData();
|
|
11
|
+
|
|
12
|
+
if (hasStaffAuthorization && !isExpired) return true;
|
|
13
|
+
if (!hasStaffAuthorization && !isExpired) return false;
|
|
14
|
+
|
|
15
|
+
const isAuthorized = await Shift.checkStaffMSAuthorization();
|
|
16
|
+
|
|
17
|
+
const data = {
|
|
18
|
+
hasStaffAuthorization: isAuthorized,
|
|
19
|
+
expirationTime: Date.now() + STAFF_MS_AUTHORIZATION_EXPIRATION_TIME,
|
|
20
|
+
isExpired: false,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
Storage.set(STAFF_AUTH, JSON.stringify(data));
|
|
24
|
+
|
|
25
|
+
return isAuthorized;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
Crashlytics.recordError(error, 'Error checking staff MS authorization');
|
|
28
|
+
const data = {
|
|
29
|
+
hasStaffAuthorization: false,
|
|
30
|
+
expirationTime: 0,
|
|
31
|
+
isExpired: true,
|
|
32
|
+
};
|
|
33
|
+
Storage.set(STAFF_AUTH, JSON.stringify(data));
|
|
34
|
+
|
|
35
|
+
return Promise.reject(error);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default isAuthorizedToUseStaffMS;
|