@capsitech/react-utilities 0.1.9 → 0.1.11
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/Components/SuspenseRoute.js +29 -0
- package/lib/Components/index.js +1 -0
- package/lib/Hooks/index.d.ts +2 -2
- package/lib/Hooks/index.js +98 -0
- package/lib/Hooks/useInfiniteScroll.d.ts +1 -1
- package/lib/Hooks/useInfiniteScroll.js +22 -0
- package/lib/Hooks/useNetworkState.js +41 -0
- package/lib/Hooks/useShortcuts.js +91 -0
- package/lib/Utilities/ApiUtility.axios.js +326 -0
- package/lib/Utilities/BrowserInfo.js +153 -0
- package/lib/Utilities/Countries.js +290 -0
- package/lib/Utilities/CustomEventEmitter.js +30 -0
- package/lib/Utilities/FastCompare.js +128 -0
- package/lib/Utilities/HideablePromise.js +10 -0
- package/lib/Utilities/LoadScripts.js +51 -0
- package/lib/Utilities/MTDFraudPrevention.js +157 -0
- package/lib/Utilities/Nationalities.js +245 -0
- package/lib/Utilities/RouteUtils.d.ts +1 -1
- package/lib/Utilities/RouteUtils.js +223 -0
- package/lib/Utilities/TimeZones.js +1069 -0
- package/lib/Utilities/Types.js +1 -0
- package/lib/Utilities/Utils.d.ts +1 -1
- package/lib/Utilities/Utils.js +331 -0
- package/lib/Utilities/dayjs.d.ts +11 -10
- package/lib/Utilities/dayjs.js +57 -0
- package/lib/Utilities/index.js +14 -0
- package/lib/index.js +3 -72
- package/package.json +10 -57
- package/lib/Components.d.ts +0 -2
- package/lib/Components.js +0 -5
- package/lib/Components.js.map +0 -1
- package/lib/Hooks.d.ts +0 -2
- package/lib/Hooks.js +0 -67
- package/lib/Hooks.js.map +0 -1
- package/lib/Utilities.d.ts +0 -2
- package/lib/Utilities.js +0 -54
- package/lib/Utilities.js.map +0 -1
- package/lib/dayjs-aKjfjjRl.mjs +0 -605
- package/lib/dayjs-aKjfjjRl.mjs.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/logo.png +0 -0
- package/lib/src/Components/SuspenseRoute.d.ts +0 -2
- package/lib/src/Components/SuspenseRoute.js +0 -22
- package/lib/src/Components/SuspenseRoute.js.map +0 -1
- package/lib/src/Components/index.d.ts +0 -2
- package/lib/src/Components/index.js +0 -5
- package/lib/src/Components/index.js.map +0 -1
- package/lib/src/Hooks/index.d.ts +0 -2
- package/lib/src/Hooks/index.js +0 -20
- package/lib/src/Hooks/index.js.map +0 -1
- package/lib/src/Hooks/useInfiniteScroll.d.ts +0 -2
- package/lib/src/Hooks/useInfiniteScroll.js +0 -20
- package/lib/src/Hooks/useInfiniteScroll.js.map +0 -1
- package/lib/src/Hooks/useNetworkState.d.ts +0 -2
- package/lib/src/Hooks/useNetworkState.js +0 -33
- package/lib/src/Hooks/useNetworkState.js.map +0 -1
- package/lib/src/Hooks/useShortcuts.d.ts +0 -2
- package/lib/src/Hooks/useShortcuts.js +0 -43
- package/lib/src/Hooks/useShortcuts.js.map +0 -1
- package/lib/src/Utilities/ApiUtility.axios.d.ts +0 -2
- package/lib/src/Utilities/ApiUtility.axios.js +0 -222
- package/lib/src/Utilities/ApiUtility.axios.js.map +0 -1
- package/lib/src/Utilities/BrowserInfo.d.ts +0 -2
- package/lib/src/Utilities/BrowserInfo.js +0 -51
- package/lib/src/Utilities/BrowserInfo.js.map +0 -1
- package/lib/src/Utilities/Countries.d.ts +0 -2
- package/lib/src/Utilities/Countries.js +0 -512
- package/lib/src/Utilities/Countries.js.map +0 -1
- package/lib/src/Utilities/CustomEventEmitter.d.ts +0 -2
- package/lib/src/Utilities/CustomEventEmitter.js +0 -24
- package/lib/src/Utilities/CustomEventEmitter.js.map +0 -1
- package/lib/src/Utilities/FastCompare.d.ts +0 -2
- package/lib/src/Utilities/FastCompare.js +0 -56
- package/lib/src/Utilities/FastCompare.js.map +0 -1
- package/lib/src/Utilities/HideablePromise.d.ts +0 -2
- package/lib/src/Utilities/HideablePromise.js +0 -12
- package/lib/src/Utilities/HideablePromise.js.map +0 -1
- package/lib/src/Utilities/LoadScripts.d.ts +0 -2
- package/lib/src/Utilities/LoadScripts.js +0 -26
- package/lib/src/Utilities/LoadScripts.js.map +0 -1
- package/lib/src/Utilities/MTDFraudPrevention.d.ts +0 -2
- package/lib/src/Utilities/MTDFraudPrevention.js +0 -60
- package/lib/src/Utilities/MTDFraudPrevention.js.map +0 -1
- package/lib/src/Utilities/Nationalities.d.ts +0 -2
- package/lib/src/Utilities/Nationalities.js +0 -250
- package/lib/src/Utilities/Nationalities.js.map +0 -1
- package/lib/src/Utilities/RouteUtils.d.ts +0 -2
- package/lib/src/Utilities/RouteUtils.js +0 -192
- package/lib/src/Utilities/RouteUtils.js.map +0 -1
- package/lib/src/Utilities/TimeZones.d.ts +0 -2
- package/lib/src/Utilities/TimeZones.js +0 -1074
- package/lib/src/Utilities/TimeZones.js.map +0 -1
- package/lib/src/Utilities/Types.d.ts +0 -1
- package/lib/src/Utilities/Types.js +0 -2
- package/lib/src/Utilities/Types.js.map +0 -1
- package/lib/src/Utilities/Utils.d.ts +0 -2
- package/lib/src/Utilities/Utils.js +0 -271
- package/lib/src/Utilities/Utils.js.map +0 -1
- package/lib/src/Utilities/dayjs.d.ts +0 -2
- package/lib/src/Utilities/dayjs.js +0 -6
- package/lib/src/Utilities/dayjs.js.map +0 -1
- package/lib/src/Utilities/index.d.ts +0 -2
- package/lib/src/Utilities/index.js +0 -54
- package/lib/src/Utilities/index.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
const isSuspenseComponent = (component) => typeof component === "object" &&
|
|
4
|
+
component &&
|
|
5
|
+
component.hasOwnProperty("$$typeof") &&
|
|
6
|
+
component.hasOwnProperty("_payload") &&
|
|
7
|
+
component.hasOwnProperty("_init");
|
|
8
|
+
export const SuspenseRoute = ({ children: Component, label = "Loading components...", fallback: FallbackComponent, }) => {
|
|
9
|
+
if (isSuspenseComponent(Component)) {
|
|
10
|
+
if (!FallbackComponent)
|
|
11
|
+
FallbackComponent = SuspenseRouteFallback;
|
|
12
|
+
return _jsx(React.Suspense, { fallback: _jsx(FallbackComponent, { label: label }), children: _jsx(Component, {}) });
|
|
13
|
+
}
|
|
14
|
+
return (_jsx(Component, {}));
|
|
15
|
+
};
|
|
16
|
+
const SuspenseRouteFallback = ({ label = "Loading components..." }) => {
|
|
17
|
+
return _jsx("div", { style: {
|
|
18
|
+
position: 'fixed',
|
|
19
|
+
top: 0,
|
|
20
|
+
left: 0,
|
|
21
|
+
width: '100%',
|
|
22
|
+
height: '100%',
|
|
23
|
+
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
24
|
+
zIndex: 9999,
|
|
25
|
+
display: 'flex',
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
alignItems: 'center'
|
|
28
|
+
}, children: label });
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SuspenseRoute';
|
package/lib/Hooks/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
2
|
export * from './useInfiniteScroll';
|
|
3
3
|
export * from './useNetworkState';
|
|
4
4
|
export * from './useShortcuts';
|
|
@@ -13,7 +13,7 @@ export declare const useUpdateEffect: typeof useEffect;
|
|
|
13
13
|
*
|
|
14
14
|
* @param value the value or function to persist
|
|
15
15
|
*/
|
|
16
|
-
export declare function useLatestRef<T>(value: T): MutableRefObject<T>;
|
|
16
|
+
export declare function useLatestRef<T>(value: T): import("react").MutableRefObject<T>;
|
|
17
17
|
/**
|
|
18
18
|
* useCallbackRef hook
|
|
19
19
|
*
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
export * from './useInfiniteScroll';
|
|
3
|
+
export * from './useNetworkState';
|
|
4
|
+
export * from './useShortcuts';
|
|
5
|
+
/**
|
|
6
|
+
* React effect hook that invokes only on update.
|
|
7
|
+
* It doesn't invoke on mount
|
|
8
|
+
*/
|
|
9
|
+
export const useUpdateEffect = (effect, deps) => {
|
|
10
|
+
const mounted = useRef(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (mounted.current) {
|
|
13
|
+
return effect();
|
|
14
|
+
}
|
|
15
|
+
mounted.current = true;
|
|
16
|
+
return undefined;
|
|
17
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18
|
+
}, deps);
|
|
19
|
+
return mounted.current;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* React hook to persist any value between renders,
|
|
23
|
+
* but keeps it up-to-date if it changes.
|
|
24
|
+
*
|
|
25
|
+
* @param value the value or function to persist
|
|
26
|
+
*/
|
|
27
|
+
export function useLatestRef(value) {
|
|
28
|
+
const ref = useRef(value);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
ref.current = value;
|
|
31
|
+
}, [value]);
|
|
32
|
+
return ref;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* useCallbackRef hook
|
|
36
|
+
*
|
|
37
|
+
* A custom hook that converts a callback to a ref to avoid triggering re-renders
|
|
38
|
+
* ..when passed as a prop or avoid re-executing effects when passed as a dependency
|
|
39
|
+
*
|
|
40
|
+
* @param callback The callback to write to a ref object
|
|
41
|
+
*/
|
|
42
|
+
export const useCallbackRef = (callback) => {
|
|
43
|
+
const callbackRef = useRef(callback);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
callbackRef.current = callback;
|
|
46
|
+
});
|
|
47
|
+
return useCallback(((...args) => {
|
|
48
|
+
return callbackRef.current?.(...args);
|
|
49
|
+
}), []);
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* useFirstRenderState hook
|
|
53
|
+
*
|
|
54
|
+
* Returns "true" if component is just mounted (first render), else "false".
|
|
55
|
+
*/
|
|
56
|
+
export const useFirstRenderState = () => {
|
|
57
|
+
const isFirst = useRef(true);
|
|
58
|
+
if (isFirst.current) {
|
|
59
|
+
isFirst.current = false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return isFirst.current;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* React Hook that provides a declarative `setInterval`
|
|
66
|
+
*
|
|
67
|
+
* @param callback the callback to execute at interval
|
|
68
|
+
* @param delay the `setInterval` delay (in ms)
|
|
69
|
+
*/
|
|
70
|
+
export function useInterval(callback, delay) {
|
|
71
|
+
const savedCallback = useLatestRef(callback);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const tick = () => {
|
|
74
|
+
savedCallback.current?.();
|
|
75
|
+
};
|
|
76
|
+
if (delay !== null) {
|
|
77
|
+
const id = setInterval(tick, delay);
|
|
78
|
+
return () => clearInterval(id);
|
|
79
|
+
}
|
|
80
|
+
}, [delay, savedCallback]);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* React hook that provides a declarative `setTimeout`
|
|
84
|
+
*
|
|
85
|
+
* @param callback the callback to run after specified delay
|
|
86
|
+
* @param delay the delay (in ms)
|
|
87
|
+
*/
|
|
88
|
+
export function useTimeout(callback, delay) {
|
|
89
|
+
const savedCallback = useLatestRef(callback);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (delay == null)
|
|
92
|
+
return;
|
|
93
|
+
const timeoutId = setTimeout(() => {
|
|
94
|
+
savedCallback.current?.();
|
|
95
|
+
}, delay);
|
|
96
|
+
return () => clearTimeout(timeoutId);
|
|
97
|
+
}, [delay, savedCallback]);
|
|
98
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Infinite scrolling with intersection observer
|
|
4
|
+
* @param scrollRef Reference object for observe bottom boundary
|
|
5
|
+
* @param dispatch Trigger an action that updates the page number
|
|
6
|
+
*/
|
|
7
|
+
export const useInfiniteScroll = (scrollRef, dispatch) => {
|
|
8
|
+
const scrollObserver = React.useCallback((node) => {
|
|
9
|
+
new IntersectionObserver((entries) => {
|
|
10
|
+
entries.forEach((en) => {
|
|
11
|
+
if (en.intersectionRatio > 0) {
|
|
12
|
+
dispatch({ type: 'next-page' });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}).observe(node);
|
|
16
|
+
}, [dispatch]);
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (scrollRef.current) {
|
|
19
|
+
scrollObserver(scrollRef.current);
|
|
20
|
+
}
|
|
21
|
+
}, [scrollObserver, scrollRef]);
|
|
22
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const isBrowser = typeof window !== 'undefined';
|
|
3
|
+
export const isNavigator = typeof navigator !== 'undefined';
|
|
4
|
+
const nav = isNavigator ? navigator : undefined;
|
|
5
|
+
const conn = nav && (nav.connection || nav.mozConnection || nav.webkitConnection);
|
|
6
|
+
function getConnectionState(previousState) {
|
|
7
|
+
const online = nav?.onLine;
|
|
8
|
+
const previousOnline = previousState?.online;
|
|
9
|
+
return {
|
|
10
|
+
online,
|
|
11
|
+
previous: previousOnline,
|
|
12
|
+
since: online !== previousOnline ? new Date() : previousState?.since,
|
|
13
|
+
downlink: conn?.downlink,
|
|
14
|
+
downlinkMax: conn?.downlinkMax,
|
|
15
|
+
effectiveType: conn?.effectiveType,
|
|
16
|
+
rtt: conn?.rtt,
|
|
17
|
+
saveData: conn?.saveData,
|
|
18
|
+
type: conn?.type,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function useNetworkState(initialState) {
|
|
22
|
+
const [state, setState] = React.useState(initialState ?? getConnectionState);
|
|
23
|
+
React.useEffect(() => {
|
|
24
|
+
const handleStateChange = () => {
|
|
25
|
+
setState(getConnectionState);
|
|
26
|
+
};
|
|
27
|
+
window.addEventListener('online', handleStateChange, { passive: true });
|
|
28
|
+
window.addEventListener('offline', handleStateChange, { passive: true });
|
|
29
|
+
if (conn) {
|
|
30
|
+
window.addEventListener('change', handleStateChange, { passive: true });
|
|
31
|
+
}
|
|
32
|
+
return () => {
|
|
33
|
+
window.removeEventListener('online', handleStateChange);
|
|
34
|
+
window.removeEventListener('offline', handleStateChange);
|
|
35
|
+
if (conn) {
|
|
36
|
+
window.removeEventListener('change', handleStateChange);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
40
|
+
return state;
|
|
41
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
const blacklistedTargets = ['INPUT', 'TEXTAREA', 'SELECT'];
|
|
3
|
+
const keysReducer = (state, action) => {
|
|
4
|
+
switch (action.type) {
|
|
5
|
+
case 'set-key-down':
|
|
6
|
+
const keydownState = { ...state, [action.key]: true };
|
|
7
|
+
return keydownState;
|
|
8
|
+
case 'set-key-up':
|
|
9
|
+
const keyUpState = { ...state, [action.key]: false };
|
|
10
|
+
return keyUpState;
|
|
11
|
+
case 'reset-keys':
|
|
12
|
+
const resetState = { ...action.data };
|
|
13
|
+
return resetState;
|
|
14
|
+
default:
|
|
15
|
+
return state;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export const useShortcuts = (shortcutKeys, callback, options) => {
|
|
19
|
+
if (!Array.isArray(shortcutKeys))
|
|
20
|
+
throw new Error('The first parameter to `useShortcuts` must be an ordered array of `KeyboardEvent.key` strings.');
|
|
21
|
+
if (!shortcutKeys.length)
|
|
22
|
+
throw new Error('The first parameter to `useShortcuts` must contain atleast one `KeyboardEvent.key` string.');
|
|
23
|
+
if (!callback || typeof callback !== 'function')
|
|
24
|
+
throw new Error('The second parameter to `useShortcuts` must be a function that will be envoked when the keys are pressed.');
|
|
25
|
+
const { overrideSystem } = options || {};
|
|
26
|
+
const initalKeyMapping = shortcutKeys.reduce((currentKeys, key) => {
|
|
27
|
+
currentKeys[key.toLowerCase()] = false;
|
|
28
|
+
return currentKeys;
|
|
29
|
+
}, {});
|
|
30
|
+
const [keys, setKeys] = React.useReducer(keysReducer, initalKeyMapping);
|
|
31
|
+
const keydownListener = React.useCallback((assignedKey) => (keydownEvent) => {
|
|
32
|
+
const loweredKey = assignedKey.toLowerCase();
|
|
33
|
+
if (keydownEvent.repeat)
|
|
34
|
+
return;
|
|
35
|
+
if (blacklistedTargets.includes(keydownEvent.target.tagName))
|
|
36
|
+
return;
|
|
37
|
+
if (loweredKey !== keydownEvent.key.toLowerCase())
|
|
38
|
+
return;
|
|
39
|
+
if (keys[loweredKey] === undefined)
|
|
40
|
+
return;
|
|
41
|
+
if (overrideSystem) {
|
|
42
|
+
keydownEvent.preventDefault();
|
|
43
|
+
disabledEventPropagation(keydownEvent);
|
|
44
|
+
}
|
|
45
|
+
setKeys({ type: 'set-key-down', key: loweredKey });
|
|
46
|
+
return false;
|
|
47
|
+
}, [keys, overrideSystem]);
|
|
48
|
+
const keyupListener = React.useCallback((assignedKey) => (keyupEvent) => {
|
|
49
|
+
const raisedKey = assignedKey.toLowerCase();
|
|
50
|
+
if (blacklistedTargets.includes(keyupEvent.target.tagName))
|
|
51
|
+
return;
|
|
52
|
+
if (keyupEvent.key.toLowerCase() !== raisedKey)
|
|
53
|
+
return;
|
|
54
|
+
if (keys[raisedKey] === undefined)
|
|
55
|
+
return;
|
|
56
|
+
if (overrideSystem) {
|
|
57
|
+
keyupEvent.preventDefault();
|
|
58
|
+
disabledEventPropagation(keyupEvent);
|
|
59
|
+
}
|
|
60
|
+
setKeys({ type: 'set-key-up', key: raisedKey });
|
|
61
|
+
return false;
|
|
62
|
+
}, [keys, overrideSystem]);
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
//console.log('keys', keys);
|
|
65
|
+
if (!Object.values(keys).filter((value) => !value).length) {
|
|
66
|
+
callback(keys);
|
|
67
|
+
setKeys({ type: 'reset-keys', data: initalKeyMapping });
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
setKeys({ type: '' });
|
|
71
|
+
}
|
|
72
|
+
}, [callback, keys]);
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
shortcutKeys.forEach((k) => window.addEventListener('keydown', keydownListener(k)));
|
|
75
|
+
return () => shortcutKeys.forEach((k) => window.removeEventListener('keydown', keydownListener(k)));
|
|
76
|
+
}, []);
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
shortcutKeys.forEach((k) => window.addEventListener('keyup', keyupListener(k)));
|
|
79
|
+
return () => shortcutKeys.forEach((k) => window.removeEventListener('keyup', keyupListener(k)));
|
|
80
|
+
}, []);
|
|
81
|
+
};
|
|
82
|
+
export function disabledEventPropagation(e) {
|
|
83
|
+
if (e) {
|
|
84
|
+
if (e.stopPropagation) {
|
|
85
|
+
e.stopPropagation();
|
|
86
|
+
}
|
|
87
|
+
else if (window.event) {
|
|
88
|
+
window.event.cancelBubble = true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import Axios from 'axios';
|
|
2
|
+
import { saveAs } from 'file-saver';
|
|
3
|
+
import { stringify } from 'qs';
|
|
4
|
+
export var HttpMethods;
|
|
5
|
+
(function (HttpMethods) {
|
|
6
|
+
HttpMethods["get"] = "get";
|
|
7
|
+
HttpMethods["post"] = "post";
|
|
8
|
+
})(HttpMethods || (HttpMethods = {}));
|
|
9
|
+
export var FileDownloadStatus;
|
|
10
|
+
(function (FileDownloadStatus) {
|
|
11
|
+
FileDownloadStatus["downloading"] = "downloading";
|
|
12
|
+
FileDownloadStatus["done"] = "done";
|
|
13
|
+
FileDownloadStatus["error"] = "error";
|
|
14
|
+
})(FileDownloadStatus || (FileDownloadStatus = {}));
|
|
15
|
+
class ApiUtilityBase {
|
|
16
|
+
config = {};
|
|
17
|
+
get accessToken() {
|
|
18
|
+
return this.config.accessToken;
|
|
19
|
+
}
|
|
20
|
+
set accessToken(token) {
|
|
21
|
+
this.config.accessToken = token;
|
|
22
|
+
}
|
|
23
|
+
get handleError() {
|
|
24
|
+
return this.config.handleError || ((error, errors) => { });
|
|
25
|
+
}
|
|
26
|
+
set handleError(handler) {
|
|
27
|
+
this.config.handleError = handler;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Configure the API utility with base URL and other options
|
|
31
|
+
* @param config Configuration object
|
|
32
|
+
*/
|
|
33
|
+
configure(config) {
|
|
34
|
+
this.config = { ...this.config, ...config };
|
|
35
|
+
}
|
|
36
|
+
// private getParams = (params?: any) => {
|
|
37
|
+
// if (params) {
|
|
38
|
+
// for (const key in params) {
|
|
39
|
+
// if (params[key] == null || params[key] === undefined || params[key] === '')
|
|
40
|
+
// delete params[key];
|
|
41
|
+
// }
|
|
42
|
+
// }
|
|
43
|
+
// return params;
|
|
44
|
+
// };
|
|
45
|
+
getResponse = async (endpoint, params, options) => {
|
|
46
|
+
return await Axios.get(endpoint, {
|
|
47
|
+
params,
|
|
48
|
+
paramsSerializer: {
|
|
49
|
+
serialize: (p, o) => {
|
|
50
|
+
return stringify(params, {
|
|
51
|
+
arrayFormat: 'indices',
|
|
52
|
+
allowDots: true,
|
|
53
|
+
skipNulls: true,
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
...this._axiosOptions(options || {}),
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
get = async (endpoint, params, throwErrorOn401, options) => {
|
|
61
|
+
const response = await this.getResponse(endpoint, params, options);
|
|
62
|
+
const data = this.handleResponse(response, throwErrorOn401);
|
|
63
|
+
return data;
|
|
64
|
+
};
|
|
65
|
+
getResult = async (endpoint, params, throwErrorOn401, options) => {
|
|
66
|
+
try {
|
|
67
|
+
const data = await this.get(endpoint, params, undefined, options);
|
|
68
|
+
if (data.status) {
|
|
69
|
+
return data.result;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.handleErrorResponse(data.message, data.errors);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.isAxiosError)
|
|
77
|
+
this.handleAxiosError(error, throwErrorOn401);
|
|
78
|
+
else
|
|
79
|
+
this.handleResponse(error, throwErrorOn401);
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
};
|
|
83
|
+
post = async (endpoint, body, contentType, options) => {
|
|
84
|
+
try {
|
|
85
|
+
const response = await Axios.post(endpoint, body, this._axiosOptions({ contentType, ...(options || {}) }));
|
|
86
|
+
const data = this.handleResponse(response);
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
89
|
+
catch (ex) {
|
|
90
|
+
if (ex?.isAxiosError) {
|
|
91
|
+
return this.handleAxiosError(ex);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {};
|
|
95
|
+
};
|
|
96
|
+
put = async (endpoint, body, contentType, options) => {
|
|
97
|
+
try {
|
|
98
|
+
const response = await Axios.put(endpoint, body, this._axiosOptions({ contentType, ...(options || {}) }));
|
|
99
|
+
const data = this.handleResponse(response);
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
102
|
+
catch (ex) {
|
|
103
|
+
if (ex?.isAxiosError) {
|
|
104
|
+
return this.handleAxiosError(ex);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {};
|
|
108
|
+
};
|
|
109
|
+
appendFormDataValues = (form, values, preFix) => {
|
|
110
|
+
if (!preFix || preFix.length <= 0)
|
|
111
|
+
preFix = '';
|
|
112
|
+
//check if given object is a string/number/boolean then add directly to the list with prefix
|
|
113
|
+
if ((typeof values !== 'function' && typeof values !== 'object' && typeof values !== 'symbol') || values instanceof File) {
|
|
114
|
+
if (values && preFix) {
|
|
115
|
+
form.append(preFix, values);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const key in values) {
|
|
120
|
+
//prepare a field name
|
|
121
|
+
const fieldName = `${preFix}${preFix && !preFix.endsWith('.') ? '.' : ''}${key}`;
|
|
122
|
+
if (values[key] instanceof FileList) {
|
|
123
|
+
const fileList = values[key];
|
|
124
|
+
for (let fIndex = 0; fIndex < fileList.length; fIndex++) {
|
|
125
|
+
form.append(`${fieldName}[${fIndex}]`, fileList[fIndex]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
if (Array.isArray(values[key])) {
|
|
130
|
+
values[key].forEach((el, idx) => {
|
|
131
|
+
this.appendFormDataValues(form, el, `${fieldName}[${idx}]`);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
if (typeof values[key] === 'object') {
|
|
136
|
+
this.appendFormDataValues(form, values[key], fieldName);
|
|
137
|
+
}
|
|
138
|
+
else if (values[key]) {
|
|
139
|
+
form.append(fieldName, values[key]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
postForm = async (endpoint, params, options) => {
|
|
146
|
+
let formData = new FormData();
|
|
147
|
+
if (params instanceof FormData) {
|
|
148
|
+
formData = params;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.appendFormDataValues(formData, params);
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const response = await Axios.post(endpoint, formData, this._axiosOptions({ contentType: 'multipart/form-data', ...(options || {}) }));
|
|
155
|
+
const data = this.handleResponse(response);
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
catch (ex) {
|
|
159
|
+
if (ex?.isAxiosError) {
|
|
160
|
+
return this.handleAxiosError(ex);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {};
|
|
164
|
+
};
|
|
165
|
+
putForm = async (endpoint, params, options) => {
|
|
166
|
+
let formData = new FormData();
|
|
167
|
+
if (params instanceof FormData) {
|
|
168
|
+
formData = params;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.appendFormDataValues(formData, params);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const response = await Axios.putForm(endpoint, formData, this._axiosOptions({ contentType: 'multipart/form-data', ...(options || {}) }));
|
|
175
|
+
const data = this.handleResponse(response);
|
|
176
|
+
return data;
|
|
177
|
+
}
|
|
178
|
+
catch (ex) {
|
|
179
|
+
if (ex?.isAxiosError) {
|
|
180
|
+
return this.handleAxiosError(ex);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {};
|
|
184
|
+
};
|
|
185
|
+
delete = async (endpoint, contentType, options) => {
|
|
186
|
+
const response = await Axios.delete(endpoint, this._axiosOptions({ contentType, ...(options || {}) }));
|
|
187
|
+
const data = this.handleResponse(response);
|
|
188
|
+
return data;
|
|
189
|
+
};
|
|
190
|
+
getFileName = (contentDispositionValue) => {
|
|
191
|
+
var filename = undefined;
|
|
192
|
+
if (contentDispositionValue && contentDispositionValue.indexOf('attachment') !== -1) {
|
|
193
|
+
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
|
194
|
+
var matches = filenameRegex.exec(contentDispositionValue);
|
|
195
|
+
if (matches != null && matches[1]) {
|
|
196
|
+
filename = matches[1].replace(/['"]/g, '');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return filename;
|
|
200
|
+
};
|
|
201
|
+
downloadFile = async (endpoint, params, method, options) => {
|
|
202
|
+
let response;
|
|
203
|
+
const headers = {};
|
|
204
|
+
// return authorization header with jwt token
|
|
205
|
+
const token = this.accessToken; // || AuthService.getAuthToken();
|
|
206
|
+
if (token) {
|
|
207
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
208
|
+
}
|
|
209
|
+
const axiosOptions = { headers, ...(options || {}) };
|
|
210
|
+
if (method === HttpMethods.post) {
|
|
211
|
+
response = Axios.post(endpoint, params, {
|
|
212
|
+
...this._axiosOptions(axiosOptions),
|
|
213
|
+
responseType: 'blob',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
response = Axios.get(endpoint, {
|
|
218
|
+
params,
|
|
219
|
+
paramsSerializer: {
|
|
220
|
+
serialize: (p, o) => {
|
|
221
|
+
return stringify(params, {
|
|
222
|
+
arrayFormat: 'indices',
|
|
223
|
+
allowDots: true,
|
|
224
|
+
skipNulls: true,
|
|
225
|
+
});
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
...this._axiosOptions(axiosOptions),
|
|
229
|
+
responseType: 'blob',
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const result_1 = await response;
|
|
234
|
+
//extract file name from Content-Disposition header
|
|
235
|
+
const fileName = this.getFileName(result_1.headers.get('Content-Disposition'));
|
|
236
|
+
//invoke 'Save As' dialog
|
|
237
|
+
saveAs(result_1.data, fileName);
|
|
238
|
+
return { status: FileDownloadStatus.done };
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (error.isAxiosError) {
|
|
242
|
+
this.handleResponse(error.response);
|
|
243
|
+
}
|
|
244
|
+
else
|
|
245
|
+
this.handleResponse(error);
|
|
246
|
+
if (error.response) {
|
|
247
|
+
return {
|
|
248
|
+
status: FileDownloadStatus.error,
|
|
249
|
+
error: error.response.status,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
return { status: FileDownloadStatus.error, error: error.message };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
getAuthHeader = (contentType) => {
|
|
258
|
+
const headers = {
|
|
259
|
+
'Content-Type': contentType || 'application/json',
|
|
260
|
+
Accept: 'application/json',
|
|
261
|
+
};
|
|
262
|
+
// return authorization header with jwt token
|
|
263
|
+
const token = this.accessToken; //||AuthService.getAuthToken();
|
|
264
|
+
if (token) {
|
|
265
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
266
|
+
}
|
|
267
|
+
return headers;
|
|
268
|
+
};
|
|
269
|
+
handleResponse = (response, throwErrorOn401) => {
|
|
270
|
+
if (!response) {
|
|
271
|
+
if (this.handleError)
|
|
272
|
+
this.handleError('No response from the server, please try after some time.');
|
|
273
|
+
}
|
|
274
|
+
else if ([401, 403].indexOf(response.status) !== -1) {
|
|
275
|
+
if (throwErrorOn401) {
|
|
276
|
+
throw response;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
console.error('401 Unauthorized or 403 Forbidden response returned from api');
|
|
280
|
+
// // auto logout if 401 Unauthorized or 403 Forbidden response returned from api
|
|
281
|
+
// AuthService.logout();
|
|
282
|
+
// window.location.reload();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return response?.data;
|
|
286
|
+
};
|
|
287
|
+
handleAxiosError = (error, throwErrorOn401) => {
|
|
288
|
+
if (throwErrorOn401 && error.response?.status === 401)
|
|
289
|
+
throw error;
|
|
290
|
+
else if (error.response?.status === 400) {
|
|
291
|
+
const data = error.response.data;
|
|
292
|
+
if (data && data.errors) {
|
|
293
|
+
const arr = [];
|
|
294
|
+
for (const key in data.errors) {
|
|
295
|
+
if (data.errors[key] && data.errors[key].length > 0) {
|
|
296
|
+
arr.push(data.errors[key][0]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
result: null,
|
|
301
|
+
status: false,
|
|
302
|
+
message: arr.length > 0 ? arr[0] : '',
|
|
303
|
+
errors: arr,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return {};
|
|
308
|
+
};
|
|
309
|
+
handleErrorResponse = (message, errors) => {
|
|
310
|
+
if (this.handleError)
|
|
311
|
+
this.handleError(message, errors);
|
|
312
|
+
};
|
|
313
|
+
_axiosOptions = (options) => {
|
|
314
|
+
const { contentType, headers, ...rest } = options || {};
|
|
315
|
+
return {
|
|
316
|
+
headers: headers || this.getAuthHeader(contentType),
|
|
317
|
+
baseURL: this.getBaseUrl(),
|
|
318
|
+
...rest,
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
getBaseUrl = () => {
|
|
322
|
+
// Priority: 1. Configured baseURL, 2. Environment variable, 3. undefined
|
|
323
|
+
return this.config.baseURL || process.env.REACT_APP_API_URL || (typeof window !== 'undefined' && window.__API_BASE_URL__);
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
export const ApiUtility = new ApiUtilityBase();
|