ui-soxo-bootstrap-core 2.6.1-dev.17 → 2.6.1-dev.18

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.
@@ -17,6 +17,7 @@ import ReportingDashboard from '../../modules/reporting/components/reporting-das
17
17
  import PropTypes from 'prop-types';
18
18
 
19
19
  import { MenusAPI, CoreScripts } from '../../models';
20
+ import LicenseAlert from '../license-management/license-alert';
20
21
 
21
22
  const motivatingMessages = [
22
23
  'Setting things up for a great start...',
@@ -59,11 +60,23 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
59
60
 
60
61
  const [meta, setMeta] = useState({});
61
62
  const [loadingMessage, setLoadingMessage] = useState('');
63
+ // License data state
64
+ const [licenseData, setLicenseData] = useState({});
62
65
 
63
66
  // const [reports, setReports] = useState([]);
64
67
 
65
68
  var config = {};
66
-
69
+ //fetch license summary
70
+ const fetchSummary = async () => {
71
+ try {
72
+ const res = await MenusAPI.getSummary();
73
+ if (res?.data) {
74
+ setLicenseData(res?.data);
75
+ }
76
+ } catch (err) {
77
+ console.error(err);
78
+ }
79
+ };
67
80
  // Variable decides the control of homepage
68
81
  // #TODO This is a temporary fix - Homemage
69
82
 
@@ -138,9 +151,12 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
138
151
  */
139
152
  async function initializeUserMenus() {
140
153
  // need to find what implement, with a login who has the respective value ("wug_custreportids")
154
+
141
155
  const report = await loadScripts(user);
142
156
 
143
157
  await loadMenus(report);
158
+ // fetch license summary
159
+ fetchSummary();
144
160
  }
145
161
 
146
162
  // const keyMap = {
@@ -159,7 +175,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
159
175
  * @param reports
160
176
  */
161
177
  async function loadMenus(reports) {
162
-
163
178
  setLoader(true);
164
179
 
165
180
  // setReports(report)
@@ -169,16 +184,12 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
169
184
  // console.log(result);
170
185
 
171
186
  if (result && Array.isArray(result.result) && result.result.length) {
172
-
173
187
  // setModules(result.result);
174
-
175
188
  // result.result.map((ele) => {
176
189
  // let languageString = JSON.parse(ele.attributes)
177
190
  // console.log('language_string', languageString);
178
191
  // if (languageString && languageString.languages) {
179
-
180
192
  // const language = i18n.language;
181
-
182
193
  // i18n.addResourceBundle(language, 'translation', languageString.languages[i18n.language]);
183
194
  // }
184
195
  // })
@@ -189,7 +200,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
189
200
  dispatch({ type: 'settings', payload: result.result.settings });
190
201
  }
191
202
 
192
-
193
203
  // Reports length
194
204
  if (reports.length) {
195
205
  reportMenus = [
@@ -224,7 +234,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
224
234
  //If there is no roles assigned to the user
225
235
  setAllModules([...coreModules]);
226
236
  }
227
-
228
237
  } else {
229
238
  // for nura
230
239
  if (result && result.result.menus && reportMenus) {
@@ -233,14 +242,10 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
233
242
  //If there is no roles assigned to the user
234
243
  setAllModules([...coreModules]);
235
244
  }
236
-
237
-
238
245
  }
239
246
  setLoader(false);
240
-
241
247
  }
242
248
 
243
-
244
249
  /**
245
250
  * Load the scripts
246
251
  *
@@ -313,6 +318,18 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
313
318
  </Card>
314
319
  ) : (
315
320
  <>
321
+ {licenseData && (
322
+ <div
323
+ style={{
324
+ marginTop: '4rem',
325
+ right: '2%',
326
+ position: 'fixed',
327
+ zIndex: 999,
328
+ }}
329
+ >
330
+ <LicenseAlert data={licenseData} />
331
+ </div>
332
+ )}
316
333
  {/* <Switch> */}
317
334
 
318
335
  {/* Homepage */}
@@ -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 === 1) {
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
+ }
@@ -1,18 +1,92 @@
1
1
  export const boxVariants = {
2
- entering: { x: -50, opacity: 0},
2
+ entering: { x: -24, opacity: 0, scale: 0.985 },
3
3
  entered: {
4
4
  x: 0,
5
5
  opacity: 1,
6
+ scale: 1,
6
7
  transition: {
7
8
  x: {
8
- duration: 0.5,
9
+ duration: 0.45,
9
10
  ease: [.62,.28,.23,.99]
10
11
  },
11
12
  opacity: {
12
- duration: 0.5,
13
+ duration: 0.35,
13
14
  ease: [.62,.28,.23,.99]
15
+ },
16
+ scale: {
17
+ duration: 0.45,
18
+ ease: [.22,1,.36,1]
14
19
  }
15
20
  },
16
21
 
17
22
  }
18
- }
23
+ };
24
+
25
+ export const headerShellVariants = {
26
+ hidden: {
27
+ y: -14,
28
+ opacity: 0,
29
+ scale: 0.995,
30
+ filter: 'blur(6px)',
31
+ },
32
+ visible: {
33
+ y: 0,
34
+ opacity: 1,
35
+ scale: 1,
36
+ filter: 'blur(0px)',
37
+ transition: {
38
+ duration: 0.45,
39
+ ease: [.22,1,.36,1],
40
+ when: 'beforeChildren',
41
+ staggerChildren: 0.045,
42
+ },
43
+ },
44
+ };
45
+
46
+ export const headerClusterVariants = {
47
+ hidden: { opacity: 0, x: -12 },
48
+ visible: {
49
+ opacity: 1,
50
+ x: 0,
51
+ transition: {
52
+ duration: 0.32,
53
+ ease: [.22,1,.36,1],
54
+ },
55
+ },
56
+ };
57
+
58
+ export const headerActionsVariants = {
59
+ hidden: {},
60
+ visible: {
61
+ transition: {
62
+ staggerChildren: 0.05,
63
+ delayChildren: 0.08,
64
+ },
65
+ },
66
+ };
67
+
68
+ export const headerActionItemVariants = {
69
+ hidden: { opacity: 0, y: -8, scale: 0.98 },
70
+ visible: {
71
+ opacity: 1,
72
+ y: 0,
73
+ scale: 1,
74
+ transition: {
75
+ duration: 0.28,
76
+ ease: [.22,1,.36,1],
77
+ },
78
+ },
79
+ };
80
+
81
+ export const contentRevealVariants = {
82
+ hidden: { opacity: 0, y: 10 },
83
+ visible: {
84
+ opacity: 1,
85
+ y: 0,
86
+ transition: {
87
+ duration: 0.35,
88
+ ease: [.22,1,.36,1],
89
+ delay: 0.08,
90
+ },
91
+ },
92
+ };
@@ -5,10 +5,15 @@
5
5
 
6
6
  import React, { useState, useEffect, useContext,useRef } from "react";
7
7
 
8
- import { animationControls, motion, useAnimation } from "framer-motion";
8
+ import { motion, useAnimation, useReducedMotion } from "framer-motion";
9
9
 
10
10
  import {
11
11
  boxVariants,
12
+ contentRevealVariants,
13
+ headerActionItemVariants,
14
+ headerActionsVariants,
15
+ headerClusterVariants,
16
+ headerShellVariants,
12
17
  } from "./animations"
13
18
 
14
19
  import { GlobalContext } from "./../../Store";
@@ -94,6 +99,8 @@ export default function GlobalHeader({
94
99
  const { globalCustomerHeader = () => { } } = appSettings;
95
100
 
96
101
  const { t, i18n } = useTranslation();
102
+ const reduceMotion = useReducedMotion();
103
+ const shouldAnimate = !reduceMotion;
97
104
 
98
105
  const spotlightRef = useRef();
99
106
 
@@ -156,6 +163,11 @@ export default function GlobalHeader({
156
163
  const boxControls = useAnimation();
157
164
 
158
165
  async function animate() {
166
+ if (!shouldAnimate) {
167
+ boxControls.set("entered");
168
+ return;
169
+ }
170
+
159
171
  await boxControls.start("entered");
160
172
 
161
173
  //await boxControls.start("show");
@@ -164,7 +176,7 @@ export default function GlobalHeader({
164
176
  useEffect(() => {
165
177
 
166
178
  animate();
167
- }, [])
179
+ }, [shouldAnimate])
168
180
 
169
181
  useEffect(() => {
170
182
 
@@ -176,6 +188,8 @@ export default function GlobalHeader({
176
188
 
177
189
 
178
190
 
191
+ const motionProps = shouldAnimate ? { initial: "hidden", animate: "visible" } : { initial: false, animate: false };
192
+
179
193
  return (
180
194
  <div
181
195
  className={`global-header ${process.env.REACT_APP_THEME} ${isConnected && !kiosk ? "connected" : ""
@@ -250,11 +264,14 @@ export default function GlobalHeader({
250
264
  {/* For not connected section */}
251
265
  {!isConnected && !kiosk ? (
252
266
 
253
- <div className={`page-wrapper ${!collapsed ? "open" : "close"}`}
267
+ <motion.div
268
+ variants={headerShellVariants}
269
+ {...motionProps}
270
+ className={`page-wrapper ${!collapsed ? "open" : "close"}`}
254
271
  // style={{ background: state.theme.colors.headerBg }}
255
272
  >
256
273
  {/* */}
257
- <div className="page-header-name">
274
+ <motion.div className="page-header-name" variants={headerClusterVariants}>
258
275
 
259
276
  <ProgressBar isLoading={loading} />
260
277
 
@@ -285,16 +302,15 @@ export default function GlobalHeader({
285
302
  {menu.caption}
286
303
  </h4>
287
304
  ) : null}
288
- </div>
305
+ </motion.div>
289
306
 
290
307
  {/* Page Menu Actions */}
291
308
 
292
309
  {user.role || user.id ? (
293
- <div className="page-menu">
310
+ <motion.div className="page-menu" variants={headerActionsVariants}>
294
311
  {/* Search Input in header start */}
295
312
  { !isMobile && (
296
-
297
- <div>
313
+ <motion.div className="header-action header-action-search" variants={headerActionItemVariants}>
298
314
  <Input
299
315
  placeholder="Search (Shift + F)"
300
316
  prefix={<SearchOutlined />}
@@ -307,14 +323,14 @@ export default function GlobalHeader({
307
323
 
308
324
  <SpotlightSearch ref={(elem) => SettingsUtil.registerModal(elem)} props={props}/>
309
325
 
310
- </div>
326
+ </motion.div>
311
327
  )}
312
328
  {/* Search Input in header start */}
313
329
 
314
330
  {/** branchswitcher Option */}
315
331
  {/* branch switcher controlled with env for matria and nura */}
316
332
  {!process.env.REACT_APP_SHOW_BRANCH_SWITCHER ? (
317
- <div className="branch-switcher">{globalCustomerHeader()}</div>
333
+ <motion.div className="branch-switcher header-action" variants={headerActionItemVariants}>{globalCustomerHeader()}</motion.div>
318
334
  ):null}
319
335
  {/* <div className="branch-switcher">{globalCustomerHeader()}</div> */}
320
336
 
@@ -330,12 +346,14 @@ export default function GlobalHeader({
330
346
  <>
331
347
  {/* Models */}
332
348
  {menu && menu.id ? (
333
- <Link to={`/menus/${menu.id}`}>
349
+ <motion.div className="header-action" variants={headerActionItemVariants}>
350
+ <Link to={`/menus/${menu.id}`}>
334
351
 
335
- <Button type="default" size={"small"} icon={<SettingOutlined />}>
352
+ <Button type="default" size={"small"} icon={<SettingOutlined />}>
336
353
 
337
- </Button>
338
- </Link>
354
+ </Button>
355
+ </Link>
356
+ </motion.div>
339
357
  ) : null}
340
358
  {/* Models Ends */}
341
359
  </>
@@ -349,31 +367,39 @@ export default function GlobalHeader({
349
367
 
350
368
  {/* Reload Button */}
351
369
 
352
- <Button onClick={reload} icon={<ReloadOutlined />} type="default" size={"small"} >
370
+ <motion.div className="header-action" variants={headerActionItemVariants}>
371
+ <Button onClick={reload} icon={<ReloadOutlined />} type="default" size={"small"} >
353
372
 
354
- </Button>
373
+ </Button>
374
+ </motion.div>
355
375
 
356
376
  {/* Reload Button Ends */}
357
377
 
358
378
  {/** Switch Languages starts */}
359
- {process.env.REACT_APP_ENABLE_LANGUAGE_SWITCHER ? <LanguageSwitcher /> : null}
379
+ {process.env.REACT_APP_ENABLE_LANGUAGE_SWITCHER ? (
380
+ <motion.div className="header-action" variants={headerActionItemVariants}>
381
+ <LanguageSwitcher />
382
+ </motion.div>
383
+ ) : null}
360
384
  {/** Switch Languages ends */}
361
385
 
362
386
  {/* User Profile */}
363
- <div style={{ padding: "10px" }}>
387
+ <motion.div className="header-profile-wrap header-action" variants={headerActionItemVariants}>
364
388
  <ProfileAvatar />
365
- <span style={{ color: state.theme.colors.colorText }}> {user.name} </span>
366
- </div>
389
+ <span className="header-profile-name" style={{ color: state.theme.colors.colorText }}> {user.name} </span>
390
+ </motion.div>
367
391
  {/* User Profile Ends */}
368
- </div>
392
+ </motion.div>
369
393
  ) : null}
370
394
 
371
395
  {/* Page Menu Actions Ends */}
372
- </div>
396
+ </motion.div>
373
397
  ) : null}
374
398
 
375
399
  {/* The children is rendered */}
376
- {children}
400
+ <motion.div className="page-content-stage" variants={contentRevealVariants} {...motionProps}>
401
+ {children}
402
+ </motion.div>
377
403
  {/* The children is rendered */}
378
404
  </div>
379
405
  {/* Right Section of the Component Loader Ends */}