ui-soxo-bootstrap-core 2.6.1-dev.3 → 2.6.1-dev.31
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/core/components/extra-info/extra-info-details.js +2 -2
- package/core/components/index.js +2 -11
- package/core/components/landing-api/landing-api.js +216 -18
- package/core/components/landing-api/landing-api.scss +22 -0
- package/core/components/license-management/license-alert.js +97 -0
- package/core/lib/Store.js +8 -4
- package/core/lib/components/global-header/global-header.js +217 -242
- package/core/lib/components/index.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +19 -13
- package/core/lib/components/sidemenu/sidemenu.scss +1 -1
- package/core/lib/elements/basic/country-phone-input/country-phone-input.js +14 -9
- package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
- package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
- package/core/lib/models/forms/components/form-creator/form-creator.js +525 -468
- package/core/lib/models/forms/components/form-creator/form-creator.scss +30 -26
- package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
- package/core/lib/models/process/components/process-dashboard/process-dashboard.js +469 -3
- package/core/lib/models/process/components/process-dashboard/process-dashboard.scss +4 -0
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +28 -2
- package/core/lib/pages/change-password/change-password.js +17 -24
- package/core/lib/pages/change-password/change-password.scss +45 -48
- package/core/lib/pages/login/commnication-mode-selection.js +2 -2
- package/core/lib/pages/login/login.js +53 -64
- package/core/lib/pages/login/login.scss +9 -0
- package/core/lib/pages/login/reset-password.js +17 -17
- package/core/lib/pages/login/reset-password.scss +10 -1
- package/core/lib/pages/profile/themes.json +4 -4
- package/core/lib/utils/api/api.utils.js +53 -45
- package/core/lib/utils/common/common.utils.js +49 -35
- package/core/lib/utils/generic/generic.utils.js +2 -1
- package/core/lib/utils/http/http.utils.js +33 -4
- package/core/lib/utils/index.js +4 -1
- package/core/models/base/base.js +7 -3
- package/core/models/core-scripts/core-scripts.js +147 -126
- package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
- package/core/models/menus/components/menu-add/menu-add.js +1 -1
- package/core/models/menus/components/menu-lists/menu-lists.js +53 -54
- package/core/models/menus/menus.js +49 -2
- package/core/models/roles/components/role-add/role-add.js +92 -59
- package/core/models/roles/components/role-list/role-list.js +1 -1
- package/core/models/staff/components/staff-add/staff-add.js +20 -32
- package/core/models/users/components/assign-role/assign-role.js +145 -50
- package/core/models/users/components/assign-role/assign-role.scss +209 -45
- package/core/models/users/components/assign-role/avatar-props.js +45 -0
- package/core/models/users/components/user-add/user-add.js +46 -55
- package/core/models/users/components/user-add/user-edit.js +25 -4
- package/core/models/users/users.js +9 -1
- package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
- package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +174 -0
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +90 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +448 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +199 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +195 -822
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +43 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +517 -0
- package/core/modules/steps/action-buttons.js +30 -16
- package/core/modules/steps/action-buttons.scss +55 -9
- package/core/modules/steps/chat-assistant.js +141 -0
- package/core/modules/steps/openai-realtime.js +275 -0
- package/core/modules/steps/readme.md +167 -0
- package/core/modules/steps/steps.js +1286 -60
- package/core/modules/steps/steps.scss +703 -86
- package/core/modules/steps/timeline.js +21 -19
- package/core/modules/steps/voice-navigation.js +709 -0
- package/package.json +2 -1
|
@@ -21,7 +21,7 @@ import ExtraInfo from './extra-info';
|
|
|
21
21
|
|
|
22
22
|
const { TabPane } = Tabs;
|
|
23
23
|
|
|
24
|
-
export default function ExtraInfoDetail({ modeValue, icon, title, tabPosition = 'left', showDrawerData, dbPtr, callback, ...record }) {
|
|
24
|
+
export default function ExtraInfoDetail({ modeValue, icon, title, tabPosition = 'left', showDrawerData, dbPtr, callback, drawerWidth = '35%', ...record }) {
|
|
25
25
|
// State to control drawer
|
|
26
26
|
const [open, setOpen] = useState(false);
|
|
27
27
|
|
|
@@ -121,7 +121,7 @@ export default function ExtraInfoDetail({ modeValue, icon, title, tabPosition =
|
|
|
121
121
|
{/* */}
|
|
122
122
|
|
|
123
123
|
{/* */}
|
|
124
|
-
<Drawer width={
|
|
124
|
+
<Drawer width={drawerWidth} title={title} onClose={onClose} open={open}>
|
|
125
125
|
<div className="main-drawer-content">
|
|
126
126
|
<div className="drawer-container">
|
|
127
127
|
<div className="drawer-click">
|
package/core/components/index.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
1
|
import LandingAPI from './landing-api/landing-api';
|
|
6
2
|
|
|
7
3
|
import ExtraInfoDetail from './extra-info/extra-info-details';
|
|
@@ -11,11 +7,6 @@ import RootApplicationAPI from './root-application-api/root-application-api';
|
|
|
11
7
|
import { HomePageAPI } from '../modules';
|
|
12
8
|
|
|
13
9
|
import { ExternalWindow } from './external-window/external-window';
|
|
10
|
+
import LicenseAlert from './license-management/license-alert';
|
|
14
11
|
|
|
15
|
-
export {
|
|
16
|
-
LandingAPI,
|
|
17
|
-
RootApplicationAPI,
|
|
18
|
-
ExtraInfoDetail,
|
|
19
|
-
HomePageAPI,
|
|
20
|
-
ExternalWindow
|
|
21
|
-
}
|
|
12
|
+
export { LandingAPI, RootApplicationAPI, ExtraInfoDetail, HomePageAPI, ExternalWindow, LicenseAlert };
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import React, { useState, useEffect, useContext } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Route, Switch } from 'react-router-dom';
|
|
4
4
|
|
|
5
|
-
import { Skeleton } from 'antd';
|
|
6
|
-
|
|
7
|
-
import {
|
|
5
|
+
import { Skeleton, message, Modal } from 'antd';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
GlobalHeader,
|
|
9
|
+
ChangePassword,
|
|
10
|
+
useTranslation,
|
|
11
|
+
GlobalContext,
|
|
12
|
+
ModuleRoutes,
|
|
13
|
+
SpotlightSearch,
|
|
14
|
+
SettingsUtil,
|
|
15
|
+
Profile,
|
|
16
|
+
Card,
|
|
17
|
+
safeJSON,
|
|
18
|
+
} from '../../lib';
|
|
8
19
|
|
|
9
20
|
import './landing-api.scss';
|
|
10
21
|
|
|
@@ -18,15 +29,34 @@ import PropTypes from 'prop-types';
|
|
|
18
29
|
|
|
19
30
|
import { MenusAPI, CoreScripts } from '../../models';
|
|
20
31
|
|
|
32
|
+
const motivatingMessages = [
|
|
33
|
+
'Setting things up for a great start...',
|
|
34
|
+
'Good things are loading. Stay with us.',
|
|
35
|
+
'Almost there. Preparing your workspace.',
|
|
36
|
+
'You are one moment away from your dashboard.',
|
|
37
|
+
'Getting everything ready for you.',
|
|
38
|
+
'Great care takes a second. Loading now.',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
function getRandomMessage(previousMessage = '') {
|
|
42
|
+
const options = motivatingMessages.filter((message) => message !== previousMessage);
|
|
43
|
+
|
|
44
|
+
if (!options.length) {
|
|
45
|
+
return motivatingMessages[0];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const randomIndex = Math.floor(Math.random() * options.length);
|
|
49
|
+
return options[randomIndex];
|
|
50
|
+
}
|
|
51
|
+
|
|
21
52
|
/**
|
|
22
53
|
* Landing API
|
|
23
54
|
*
|
|
24
55
|
* @param {*} param0
|
|
25
56
|
* @returns
|
|
26
57
|
*/
|
|
27
|
-
export default function LandingApi({ history, CustomComponents, CustomModels, appSettings, ...props }) {
|
|
58
|
+
export default function LandingApi({ history, CustomComponents, CustomModels, appSettings, transitionPending = false, onHomeReady, ...props }) {
|
|
28
59
|
const [loader, setLoader] = useState(false);
|
|
29
|
-
|
|
30
60
|
// const [modules, setModules] = useState([]);
|
|
31
61
|
|
|
32
62
|
const [connected] = useState();
|
|
@@ -34,13 +64,22 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
34
64
|
const { dispatch, user = {} } = useContext(GlobalContext);
|
|
35
65
|
|
|
36
66
|
const [allModules, setAllModules] = useState();
|
|
67
|
+
const homeReadyNotifiedRef = useRef(false);
|
|
68
|
+
const messageIntervalRef = useRef(null);
|
|
37
69
|
|
|
38
70
|
const [meta, setMeta] = useState({});
|
|
71
|
+
const [loadingMessage, setLoadingMessage] = useState('');
|
|
72
|
+
const [licenseData, setLicenseData] = useState(null);
|
|
73
|
+
|
|
74
|
+
const [licAlert, setLicAlert] = useState(false);
|
|
75
|
+
// License data state
|
|
39
76
|
|
|
40
77
|
// const [reports, setReports] = useState([]);
|
|
41
78
|
|
|
42
79
|
var config = {};
|
|
43
80
|
|
|
81
|
+
//fetch license summary
|
|
82
|
+
|
|
44
83
|
// Variable decides the control of homepage
|
|
45
84
|
// #TODO This is a temporary fix - Homemage
|
|
46
85
|
|
|
@@ -50,6 +89,70 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
50
89
|
disableHomepage = JSON.parse(process.env.REACT_APP_DISABLEHOMEPAGE);
|
|
51
90
|
}
|
|
52
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Normalizes the user's branch access list from `organization_details`.
|
|
94
|
+
*
|
|
95
|
+
* The API can return `organization_details` as a JSON string, so we always
|
|
96
|
+
* parse it through `safeJSON` before reading the branch collection.
|
|
97
|
+
*
|
|
98
|
+
* @returns {Array} List of branches the current user can access.
|
|
99
|
+
*/
|
|
100
|
+
const getAccessibleBranches = () => {
|
|
101
|
+
const orgDetails = safeJSON(user?.organization_details);
|
|
102
|
+
return Array.isArray(orgDetails?.branch) ? orgDetails.branch : [];
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Resolves the currently selected branch record using the persisted db pointer.
|
|
107
|
+
*
|
|
108
|
+
* @param {Array} branches
|
|
109
|
+
* @returns {Object|null}
|
|
110
|
+
*/
|
|
111
|
+
const getCurrentBranchRecord = (branches) => {
|
|
112
|
+
const currentDbPtr = localStorage.getItem('db_ptr');
|
|
113
|
+
return branches.find((branch) => String(branch.dbPtr) === String(currentDbPtr)) || null;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resolves the target branch from the URL `index` query parameter.
|
|
118
|
+
*
|
|
119
|
+
* @param {Array} branches
|
|
120
|
+
* @param {string|null} branchId
|
|
121
|
+
* @returns {Object|null}
|
|
122
|
+
*/
|
|
123
|
+
const getBranchRecordById = (branches, branchId) => {
|
|
124
|
+
return branches.find((branch) => String(branch.branch_id) === String(branchId)) || null;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Persists branch-specific auth data after a successful switch.
|
|
129
|
+
*
|
|
130
|
+
* @param {Object} tokenBundle
|
|
131
|
+
* @param {string} dbPtr
|
|
132
|
+
*/
|
|
133
|
+
const persistBranchSession = (tokenBundle, dbPtr) => {
|
|
134
|
+
const accessToken = tokenBundle?.access_token;
|
|
135
|
+
const refreshToken = tokenBundle?.refresh_token;
|
|
136
|
+
|
|
137
|
+
if (accessToken) localStorage.setItem('access_token', accessToken);
|
|
138
|
+
if (refreshToken) localStorage.setItem('refresh_token', refreshToken);
|
|
139
|
+
if (dbPtr) localStorage.setItem('db_ptr', dbPtr);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const fetchSummary = async () => {
|
|
143
|
+
try {
|
|
144
|
+
const res = await MenusAPI.getSummary();
|
|
145
|
+
if (res?.data) {
|
|
146
|
+
setLicenseData(res?.data);
|
|
147
|
+
setLicAlert(true);
|
|
148
|
+
} else {
|
|
149
|
+
setLicenseData(null);
|
|
150
|
+
setLicAlert(false);
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
console.error(err);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
53
156
|
// useEffect(() => {
|
|
54
157
|
|
|
55
158
|
// // Initialize the menus for the logged in user
|
|
@@ -65,19 +168,122 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
65
168
|
// }
|
|
66
169
|
// }, [loader]);
|
|
67
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Synchronizes the active branch with the `index` query parameter.
|
|
173
|
+
*
|
|
174
|
+
* Flow:
|
|
175
|
+
* 1. Read the target branch id from the URL.
|
|
176
|
+
* 2. Compare it against the branch represented by the current `db_ptr`.
|
|
177
|
+
* 3. Switch branch only when the user has access and the branch actually differs.
|
|
178
|
+
* 4. Refresh auth/profile state and reload menus for the new branch context.
|
|
179
|
+
*/
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
const handleUrlBranchSwitch = async () => {
|
|
182
|
+
const searchParams = new URLSearchParams(history.location.search);
|
|
183
|
+
const urlDbPtr = searchParams.get('index');
|
|
184
|
+
if (!urlDbPtr) return;
|
|
185
|
+
|
|
186
|
+
const accessibleBranches = getAccessibleBranches();
|
|
187
|
+
const currentBranch = getCurrentBranchRecord(accessibleBranches);
|
|
188
|
+
const targetBranch = getBranchRecordById(accessibleBranches, urlDbPtr);
|
|
189
|
+
|
|
190
|
+
if (!targetBranch || String(currentBranch?.branch_id) === String(urlDbPtr)) return;
|
|
191
|
+
|
|
192
|
+
setLoader(true);
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const switchResult = await MenusAPI.switchBranch(
|
|
196
|
+
{
|
|
197
|
+
firm_id: targetBranch.firm_ptr,
|
|
198
|
+
branch_id: targetBranch.branch_id,
|
|
199
|
+
},
|
|
200
|
+
targetBranch.dbPtr
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (!switchResult?.success) {
|
|
204
|
+
Modal.error({
|
|
205
|
+
title: 'Branch Switch Failed',
|
|
206
|
+
content: switchResult?.message || 'An error occurred while attempting to switch branches.',
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
persistBranchSession(switchResult?.token, targetBranch.dbPtr);
|
|
212
|
+
window.dispatchEvent(new CustomEvent('branchChanged', { detail: targetBranch.dbPtr }));
|
|
213
|
+
|
|
214
|
+
const accessToken = switchResult?.token?.access_token;
|
|
215
|
+
const profileResult = await MenusAPI.getProfile(accessToken);
|
|
216
|
+
const updatedUser = { ...profileResult, loggedCheckDone: true };
|
|
217
|
+
|
|
218
|
+
dispatch({ type: 'user', payload: updatedUser });
|
|
219
|
+
localStorage.setItem('userInfo', JSON.stringify(updatedUser));
|
|
220
|
+
|
|
221
|
+
await initializeUserMenus();
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('Auto branch switch failed:', error);
|
|
224
|
+
} finally {
|
|
225
|
+
setLoader(false);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
if (user?.id && history?.location) handleUrlBranchSwitch();
|
|
230
|
+
}, [history?.location?.search, user?.id]);
|
|
231
|
+
|
|
68
232
|
useEffect(() => {
|
|
69
233
|
// Initialize the menus for the logged in user
|
|
70
234
|
initializeUserMenus();
|
|
71
235
|
}, []);
|
|
72
236
|
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
if (!transitionPending) {
|
|
239
|
+
homeReadyNotifiedRef.current = false;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!loader && allModules && !homeReadyNotifiedRef.current) {
|
|
244
|
+
homeReadyNotifiedRef.current = true;
|
|
245
|
+
if (typeof onHomeReady === 'function') {
|
|
246
|
+
onHomeReady();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}, [transitionPending, loader, allModules, onHomeReady]);
|
|
250
|
+
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
if (!loader) {
|
|
253
|
+
setLoadingMessage('');
|
|
254
|
+
|
|
255
|
+
if (messageIntervalRef.current) {
|
|
256
|
+
clearInterval(messageIntervalRef.current);
|
|
257
|
+
messageIntervalRef.current = null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
setLoadingMessage((previousMessage) => getRandomMessage(previousMessage));
|
|
264
|
+
|
|
265
|
+
messageIntervalRef.current = setInterval(() => {
|
|
266
|
+
setLoadingMessage((previousMessage) => getRandomMessage(previousMessage));
|
|
267
|
+
}, 2200);
|
|
268
|
+
|
|
269
|
+
return () => {
|
|
270
|
+
if (messageIntervalRef.current) {
|
|
271
|
+
clearInterval(messageIntervalRef.current);
|
|
272
|
+
messageIntervalRef.current = null;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}, [loader]);
|
|
276
|
+
|
|
73
277
|
/**
|
|
74
278
|
* Initialize the user menus
|
|
75
279
|
*/
|
|
76
280
|
async function initializeUserMenus() {
|
|
77
281
|
// need to find what implement, with a login who has the respective value ("wug_custreportids")
|
|
282
|
+
|
|
78
283
|
const report = await loadScripts(user);
|
|
79
284
|
|
|
80
285
|
await loadMenus(report);
|
|
286
|
+
// fetch license summary
|
|
81
287
|
}
|
|
82
288
|
|
|
83
289
|
// const keyMap = {
|
|
@@ -96,26 +302,21 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
96
302
|
* @param reports
|
|
97
303
|
*/
|
|
98
304
|
async function loadMenus(reports) {
|
|
99
|
-
|
|
100
305
|
setLoader(true);
|
|
101
306
|
|
|
102
307
|
// setReports(report)
|
|
103
|
-
|
|
308
|
+
fetchSummary();
|
|
104
309
|
const result = await MenusAPI.getMenus(user);
|
|
105
310
|
|
|
106
311
|
// console.log(result);
|
|
107
312
|
|
|
108
313
|
if (result && Array.isArray(result.result) && result.result.length) {
|
|
109
|
-
|
|
110
314
|
// setModules(result.result);
|
|
111
|
-
|
|
112
315
|
// result.result.map((ele) => {
|
|
113
316
|
// let languageString = JSON.parse(ele.attributes)
|
|
114
317
|
// console.log('language_string', languageString);
|
|
115
318
|
// if (languageString && languageString.languages) {
|
|
116
|
-
|
|
117
319
|
// const language = i18n.language;
|
|
118
|
-
|
|
119
320
|
// i18n.addResourceBundle(language, 'translation', languageString.languages[i18n.language]);
|
|
120
321
|
// }
|
|
121
322
|
// })
|
|
@@ -126,7 +327,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
126
327
|
dispatch({ type: 'settings', payload: result.result.settings });
|
|
127
328
|
}
|
|
128
329
|
|
|
129
|
-
|
|
130
330
|
// Reports length
|
|
131
331
|
if (reports.length) {
|
|
132
332
|
reportMenus = [
|
|
@@ -161,7 +361,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
161
361
|
//If there is no roles assigned to the user
|
|
162
362
|
setAllModules([...coreModules]);
|
|
163
363
|
}
|
|
164
|
-
|
|
165
364
|
} else {
|
|
166
365
|
// for nura
|
|
167
366
|
if (result && result.result.menus && reportMenus) {
|
|
@@ -170,14 +369,10 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
170
369
|
//If there is no roles assigned to the user
|
|
171
370
|
setAllModules([...coreModules]);
|
|
172
371
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
372
|
}
|
|
176
373
|
setLoader(false);
|
|
177
|
-
|
|
178
374
|
}
|
|
179
375
|
|
|
180
|
-
|
|
181
376
|
/**
|
|
182
377
|
* Load the scripts
|
|
183
378
|
*
|
|
@@ -240,11 +435,14 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
240
435
|
modules={allModules}
|
|
241
436
|
user={user}
|
|
242
437
|
history={history}
|
|
438
|
+
licenseData={licenseData}
|
|
439
|
+
licAlert={licAlert}
|
|
243
440
|
>
|
|
244
441
|
{loader ? (
|
|
245
442
|
<Card className="skeleton-card">
|
|
246
443
|
<div className="skeleton-wrapper">
|
|
247
444
|
<Skeleton paragraph={{ rows: 4 }} />
|
|
445
|
+
<p className="motivating-text">{loadingMessage}</p>
|
|
248
446
|
</div>
|
|
249
447
|
</Card>
|
|
250
448
|
) : (
|
|
@@ -11,9 +11,31 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.skeleton-wrapper {
|
|
14
|
+
.motivating-text {
|
|
15
|
+
margin-top: 14px;
|
|
16
|
+
margin-bottom: 4px;
|
|
17
|
+
font-size: 14px;
|
|
18
|
+
line-height: 20px;
|
|
19
|
+
text-align: center;
|
|
20
|
+
color: #5e6d86;
|
|
21
|
+
font-weight: 500;
|
|
22
|
+
animation: skeletonTextFade 0.4s ease-in-out;
|
|
23
|
+
}
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
.wrapper-loader {
|
|
17
27
|
margin: 20px;
|
|
18
28
|
}
|
|
19
29
|
}
|
|
30
|
+
|
|
31
|
+
@keyframes skeletonTextFade {
|
|
32
|
+
from {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transform: translateY(2px);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
to {
|
|
38
|
+
opacity: 1;
|
|
39
|
+
transform: translateY(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Alert } from 'antd';
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
export default function LicenseAlert({ data }) {
|
|
5
|
+
// setting visibility of alert based on license status
|
|
6
|
+
const [visible, setVisible] = useState(true);
|
|
7
|
+
// resolve alert configuration based on license data
|
|
8
|
+
const alertConfig = resolveLicenseAlert(data);
|
|
9
|
+
// auto-hide alert after 10 seconds or when data changes
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (alertConfig) {
|
|
12
|
+
setVisible(true);
|
|
13
|
+
|
|
14
|
+
const timer = setTimeout(() => {
|
|
15
|
+
setVisible(false);
|
|
16
|
+
}, 10000); // 10 seconds
|
|
17
|
+
|
|
18
|
+
return () => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}, [data]);
|
|
23
|
+
// if no alert configuration or not visible, render nothing
|
|
24
|
+
if (!alertConfig || !visible) return null;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
// render the alert with appropriate type, message, and description
|
|
28
|
+
<Alert
|
|
29
|
+
type={alertConfig.type}
|
|
30
|
+
message={alertConfig.message}
|
|
31
|
+
description={alertConfig.description}
|
|
32
|
+
showIcon
|
|
33
|
+
closable
|
|
34
|
+
onClose={() => setVisible(false)}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
// function to determine alert configuration based on license data
|
|
39
|
+
function resolveLicenseAlert(data) {
|
|
40
|
+
if (!data) return null;
|
|
41
|
+
// destructure relevant fields from license data
|
|
42
|
+
const { status, expiresInDays, isExpiringSoon, gracePeriod } = data;
|
|
43
|
+
|
|
44
|
+
// ===== NOT INSTALLED =====
|
|
45
|
+
if (status === 'NOT_INSTALLED') {
|
|
46
|
+
return {
|
|
47
|
+
type: 'error',
|
|
48
|
+
message: 'License not found',
|
|
49
|
+
description: 'Please install a valid license to continue.',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ===== GRACE PERIOD =====
|
|
54
|
+
if (gracePeriod) {
|
|
55
|
+
return {
|
|
56
|
+
type: 'warning',
|
|
57
|
+
message: 'Grace period mode',
|
|
58
|
+
description: 'License expired. Running in read-only mode.',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ===== EXPIRING SOON =====
|
|
63
|
+
if (status === 'ACTIVE' && isExpiringSoon) {
|
|
64
|
+
let descriptionText = '';
|
|
65
|
+
// customize message based on how soon the license is expiring
|
|
66
|
+
if (expiresInDays === 0) {
|
|
67
|
+
descriptionText = 'Your license will expire today. Please renew immediately.';
|
|
68
|
+
} else {
|
|
69
|
+
descriptionText = `Your license will expire in ${expiresInDays} days. Please plan for renewal.`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
type: 'warning',
|
|
74
|
+
message: 'License expiring soon',
|
|
75
|
+
description: descriptionText,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ===== NOT INSTALLED =====
|
|
80
|
+
if (status === 'NOT_INSTALLED') {
|
|
81
|
+
return {
|
|
82
|
+
type: 'error',
|
|
83
|
+
message: 'License not found',
|
|
84
|
+
description: 'Please install a valid license to continue.',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// =====EXPIRED=====
|
|
88
|
+
if (status === 'EXPIRED') {
|
|
89
|
+
return {
|
|
90
|
+
type: 'error',
|
|
91
|
+
message: 'License expired',
|
|
92
|
+
description: 'Your license has expired. Please renew or install a new license.',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
}
|
package/core/lib/Store.js
CHANGED
|
@@ -50,7 +50,7 @@ const initialTheme = () => {
|
|
|
50
50
|
// manage theme with env
|
|
51
51
|
const isEnvThemeTrue = process.env.REACT_APP_THEME;
|
|
52
52
|
|
|
53
|
-
console.log('REACT_APP_THEME:', isEnvThemeTrue);
|
|
53
|
+
// console.log('REACT_APP_THEME:', isEnvThemeTrue);
|
|
54
54
|
const matchedTheme = themes.find((t) => t.name === isEnvThemeTrue);
|
|
55
55
|
|
|
56
56
|
if (matchedTheme) return matchedTheme;
|
|
@@ -64,7 +64,7 @@ const initialTheme = () => {
|
|
|
64
64
|
// Fallback to default
|
|
65
65
|
return themes[0];
|
|
66
66
|
} catch (e) {
|
|
67
|
-
console.error('Error loading theme:', e);
|
|
67
|
+
// console.error('Error loading theme:', e);
|
|
68
68
|
return themes[0];
|
|
69
69
|
}
|
|
70
70
|
};
|
|
@@ -72,12 +72,16 @@ const initialTheme = () => {
|
|
|
72
72
|
const initialState = {
|
|
73
73
|
defaultBranch: {},
|
|
74
74
|
theme: initialTheme(), // Set the initial theme from themes.json
|
|
75
|
+
settings: {},
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
79
|
* Context for sharing state accross app
|
|
79
80
|
*/
|
|
80
|
-
export const GlobalContext = createContext(
|
|
81
|
+
export const GlobalContext = createContext({
|
|
82
|
+
...initialState,
|
|
83
|
+
state: initialState,
|
|
84
|
+
});
|
|
81
85
|
|
|
82
86
|
let app = {};
|
|
83
87
|
|
|
@@ -94,7 +98,7 @@ export const GlobalProvider = ({ children, CustomModels,CustomComponents, appSet
|
|
|
94
98
|
|
|
95
99
|
const [state, dispatch] = useReducer(AppReducer, initialState);
|
|
96
100
|
|
|
97
|
-
console.log(state);
|
|
101
|
+
// console.log(state);
|
|
98
102
|
|
|
99
103
|
const { isMobile } = useDeviceDetect();
|
|
100
104
|
|