ui-soxo-bootstrap-core 2.6.1-dev.1 → 2.6.1-dev.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.
Files changed (51) hide show
  1. package/core/components/extra-info/extra-info-details.js +2 -2
  2. package/core/lib/Store.js +3 -3
  3. package/core/lib/components/global-header/global-header.js +2 -2
  4. package/core/lib/components/sidemenu/sidemenu.js +19 -13
  5. package/core/lib/elements/basic/country-phone-input/country-phone-input.js +14 -8
  6. package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
  7. package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
  8. package/core/lib/models/forms/components/form-creator/form-creator.scss +5 -4
  9. package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
  10. package/core/lib/pages/change-password/change-password.js +17 -24
  11. package/core/lib/pages/change-password/change-password.scss +45 -48
  12. package/core/lib/pages/login/commnication-mode-selection.js +46 -0
  13. package/core/lib/pages/login/communication-mode-selection.scss +60 -0
  14. package/core/lib/pages/login/login.js +126 -22
  15. package/core/lib/pages/login/login.scss +229 -334
  16. package/core/lib/pages/login/reset-password.js +124 -0
  17. package/core/lib/pages/login/reset-password.scss +31 -0
  18. package/core/lib/pages/profile/themes.json +4 -4
  19. package/core/lib/utils/api/api.utils.js +30 -18
  20. package/core/lib/utils/common/common.utils.js +85 -0
  21. package/core/lib/utils/http/http.utils.js +1 -0
  22. package/core/lib/utils/index.js +4 -1
  23. package/core/models/base/base.js +7 -3
  24. package/core/models/core-scripts/core-scripts.js +9 -0
  25. package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
  26. package/core/models/menus/components/menu-add/menu-add.js +1 -1
  27. package/core/models/menus/components/menu-lists/menu-lists.js +5 -9
  28. package/core/models/menus/menus.js +21 -2
  29. package/core/models/roles/components/role-add/role-add.js +92 -59
  30. package/core/models/roles/components/role-list/role-list.js +1 -1
  31. package/core/models/staff/components/staff-add/staff-add.js +20 -32
  32. package/core/models/users/components/assign-role/assign-role.js +145 -50
  33. package/core/models/users/components/assign-role/assign-role.scss +209 -45
  34. package/core/models/users/components/assign-role/avatar-props.js +45 -0
  35. package/core/models/users/components/user-add/user-add.js +46 -55
  36. package/core/models/users/components/user-add/user-edit.js +25 -4
  37. package/core/models/users/users.js +16 -1
  38. package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
  39. package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
  40. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +266 -0
  41. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +75 -0
  42. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
  43. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +252 -0
  44. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
  45. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +285 -399
  46. package/core/modules/steps/action-buttons.js +42 -44
  47. package/core/modules/steps/action-buttons.scss +35 -6
  48. package/core/modules/steps/steps.js +12 -10
  49. package/core/modules/steps/steps.scss +229 -31
  50. package/core/modules/steps/timeline.js +21 -19
  51. package/package.json +2 -1
@@ -39,7 +39,7 @@ function ChangePassword({ history }) {
39
39
 
40
40
  const [loading, setLoading] = useState(false);
41
41
 
42
- console.log(user);
42
+
43
43
 
44
44
  const onFinish = (values) => {
45
45
 
@@ -55,34 +55,27 @@ function ChangePassword({ history }) {
55
55
  confirm_password: values.conpassword
56
56
  }
57
57
 
58
- ApiUtils.post({
59
- url: `users/change-password`,
60
- formBody,
61
- hideError: true
62
- }).then((result) => {
63
-
64
- var msg = Object.values(result)
65
-
66
- if (msg[0] === 'INCORRECT PASSWORD') {
67
- //If current Password is entered wrong
68
- message.error('Incorrect Password')
69
- setLoading(false);
70
- return false
71
- }
72
- else {
58
+ ApiUtils.post({
59
+ url: `users/change-password`,
60
+ headers: {
61
+ db_ptr: 'nuraho'
62
+ },
63
+ formBody,
64
+ hideError: true
65
+ })
66
+ .then((result) => {
73
67
 
74
68
  setLoading(false);
75
69
 
76
70
  history.goBack();
77
71
 
78
- // Update successful.
79
- message.success('Your password has been updated!')
80
- }
81
-
82
- }).catch(function (error) {
83
-
84
- // An error happened.
85
- message.error(error.result || error.message)
72
+ // Update successful.
73
+ message.success(result.result || 'Your password has been updated!');
74
+
75
+ }).catch(function (error) {
76
+
77
+ // An error happened.
78
+ message.warning(error.result || error.message);
86
79
 
87
80
  setLoading(false);
88
81
 
@@ -1,63 +1,51 @@
1
- .change-password {
2
- margin: 15px;
3
- min-height: 90vh;
4
- display: flex;
5
- // margin: 10px 3%;
6
- // align-items: center;
7
-
8
- @media only screen and (min-width: 768px) {
9
- justify-content: left;
10
- align-items: left;
1
+ .change-password.card {
2
+ margin: -6px 8px;
3
+ .ant-card-body {
4
+ padding: 16px;
11
5
  }
12
6
 
13
- .homescreen{
14
- width: 100%;
15
- margin: 10px 0px;
16
- border-radius: 4px;
17
- background-color: aliceblue;
18
- padding:10px;
19
- height: 40px;
20
- }
21
7
  .auth-form-wrapper {
22
- padding: 0px;
23
- width: 100%;
24
- box-sizing: border-box;
25
- @media only screen and (max-width: 768px) {
26
- padding: 0;
27
- // min-width: 275px;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 18px;
11
+ }
12
+
13
+ .form-title {
14
+ h4 {
15
+ margin-bottom: 6px;
16
+ font-weight: 600;
17
+ color: #111827;
28
18
  }
29
19
 
30
- .form-title {
31
- h3 {
32
- color: #071822;
33
- font-weight: 600;
34
- size: 18px;
20
+ p {
21
+ margin: 0;
22
+ font-size: 14px;
23
+ line-height: 1.6;
24
+ color: #6b7280;
25
+ }
26
+ }
35
27
 
36
- margin: 20px 0px;
37
- font-size: 32px;
38
- }
28
+ .ant-form-item {
29
+ margin-bottom: 18px;
30
+
31
+ .ant-form-item-label > label {
32
+ font-weight: 500;
33
+ font-size: 14px;
34
+ color: #374151;
39
35
  }
40
36
 
41
- .ant-form {
42
- // height: 100%;
43
- .ant-form-item {
44
- margin: 30px 0px;
45
- .ant-form-item-label {
46
- margin: 0 0 8px 0;
47
- line-height: 020px;
48
- padding: 0px;
49
- }
50
- }
51
- .ant-btn-primary {
52
- margin-top: 0px;
53
- }
37
+ .ant-input-password {
38
+ border-radius: 6px;
39
+ padding: 10px 12px;
40
+ transition: border 0.2s ease, box-shadow 0.2s ease;
41
+
54
42
  }
55
43
 
56
- .ant-form-explain {
57
- position: absolute;
44
+ .ant-form-item-explain-error {
58
45
  font-size: 12px;
59
- margin-left: 2px;
46
+ margin-top: 4px;
60
47
  }
48
+ }
61
49
 
62
50
  .ant-btn-primary {
63
51
  width: 110px;
@@ -70,6 +58,15 @@
70
58
  .ant-btn-secondary {
71
59
  width: 100px;
72
60
  }
61
+
62
+ @media (max-width: 768px) {
63
+ margin: 0px ;
64
+ .ant-card-body {
65
+ padding: 24px;
66
+ }
67
+ }
68
+ @media only screen and (max-width: 1024px) {
69
+ margin: 60px 8px;
73
70
  }
74
71
 
75
72
  }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * CommunicationModeSelection Component
3
+ *
4
+ * Renders radio options for selecting OTP delivery method.
5
+ * Supports Email and SMS modes.
6
+ *
7
+ * Props:
8
+ * @param {string} communicationMode - Currently selected mode ('email' | 'mobile')
9
+ * @param {Function} setCommunicationMode - Updates selected mode
10
+ * @param {boolean} modeError - Displays validation error if true
11
+ */
12
+
13
+ import React from 'react';
14
+ import { Radio, Divider, Typography } from 'antd';
15
+ import { MailOutlined, MessageOutlined } from '@ant-design/icons';
16
+
17
+ import './communication-mode-selection.scss';
18
+
19
+ const { Text } = Typography;
20
+
21
+ function CommunicationModeSelection({ communicationMode, setCommunicationMode, modeError }) {
22
+ return (
23
+ <>
24
+ <div className="otp-method-section">
25
+ <Text type="primary">Select Preferred OTP Verification Method</Text>
26
+ <div className="otp-method-group">
27
+
28
+ {/* Email Option */}
29
+ <Radio checked={communicationMode === 'email'} onChange={() => setCommunicationMode('email')}>
30
+ Email <MailOutlined className="otp-icon" style={{ marginLeft: 6 }} />
31
+ </Radio>
32
+
33
+ {/* SMS Option */}
34
+ <Radio checked={communicationMode === 'mobile'} disabled onChange={() => setCommunicationMode('mobile')}>
35
+ SMS <MessageOutlined className="otp-icon" style={{ marginLeft: 6 }} />
36
+ </Radio>
37
+ </div>
38
+
39
+ {/* Validation Error */}
40
+ {modeError && <p className="otp-mode-error">Please select a communication mode.</p>}
41
+ </div>
42
+ </>
43
+ );
44
+ }
45
+
46
+ export default CommunicationModeSelection;
@@ -0,0 +1,60 @@
1
+ $error-color: red;
2
+
3
+ .otp-method-section {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 6px;
7
+
8
+ .otp-method-title {
9
+ font-size: 16px;
10
+ font-weight: 600;
11
+ margin-bottom: 8px;
12
+ }
13
+
14
+ .otp-method-group {
15
+ display: flex;
16
+ align-items: center;
17
+ margin-bottom: 10px;
18
+ gap: 30px;
19
+ font-size: 12px;
20
+
21
+ .ant-radio-wrapper {
22
+ display: flex;
23
+ gap: 6px;
24
+
25
+ svg {
26
+ font-size: 18px;
27
+ opacity: 0.8;
28
+ }
29
+ }
30
+
31
+ @media only screen and (max-width: 600px) {
32
+ flex-direction: column !important;
33
+ align-items: flex-start !important;
34
+ gap: 12px !important;
35
+ }
36
+
37
+ @media only screen and (min-width: 601px) and (max-width: 1024px) {
38
+ flex-direction: row !important;
39
+ align-items: center !important;
40
+ justify-content: space-between !important;
41
+ gap: 20px !important;
42
+ width: 100%;
43
+
44
+ .ant-radio-wrapper {
45
+ flex: 1;
46
+ }
47
+ }
48
+ }
49
+
50
+ .otp-mode-error {
51
+ margin: 5px;
52
+ font-size: 13px;
53
+ color: $error-color;
54
+ }
55
+
56
+ .otp-icon {
57
+ position: relative;
58
+ top: 2px;
59
+ }
60
+ }
@@ -30,10 +30,12 @@ import { getAccessToken, getRefreshToken } from '../../utils/http/auth.helper';
30
30
 
31
31
  import { Location } from '../../utils';
32
32
 
33
- import { checkLicenseStatus, formatMobile, safeJSON } from '../../utils/common/common.utils';
33
+ import { checkLicenseStatus, formatMobile, checkExpiryStatus, safeJSON } from '../../utils/common/common.utils';
34
34
 
35
35
  import { MailOutlined, MessageOutlined, WhatsAppOutlined } from '@ant-design/icons';
36
36
 
37
+ import ResetPassword from './reset-password';
38
+
37
39
  const { Text, Title } = Typography;
38
40
 
39
41
  const layout = {
@@ -48,6 +50,9 @@ const tailLayout = {
48
50
 
49
51
  const LICENSE_EXPIRY = '2026-12-12';
50
52
 
53
+ //password valdity expire
54
+ const PASSWORD_VALIDITY_DAYS = 90;
55
+
51
56
  const headers = {
52
57
  db_ptr: 'nuraho',
53
58
  };
@@ -89,11 +94,71 @@ function LoginPhone({ history, appSettings }) {
89
94
  const [communicationMode, setCommunicationMode] = useState(null); // default selected email
90
95
  const [modeError, setModeError] = useState(false);
91
96
 
97
+ //for forgot password show
98
+ const [showResetpassword, setShowResetpassword] = useState(false);
99
+
100
+ //for expired password show
101
+ const [expiredPassword, setExpiredPassword] = useState(false);
102
+
103
+ //for default name select when expire case
104
+ const [defaultUsername, setDefaultUsername] = useState('');
105
+
92
106
  const isAuthenticated = Boolean(getAccessToken());
93
107
  const isRefreshTokenExist = Boolean(getRefreshToken());
94
108
 
95
109
  const path = window.location.pathname;
96
110
 
111
+ /**
112
+ * handlePasswordExpiryCheck
113
+ * --------------------------
114
+ * Validates whether a user's password is expired or nearing expiry.
115
+ *
116
+ * - Parses `last_password_change` from user.other_details.
117
+ * - Calculates expiry using PASSWORD_VALIDITY_DAYS.
118
+ * - Uses `checkExpiryStatus()` to determine status.
119
+ * - Shows Ant Design warning message if expired or within warning period.
120
+ * - Warning message includes navigation to `/change-password`.
121
+ *
122
+ * Requires:
123
+ * - PASSWORD_VALIDITY_DAYS constant
124
+ * - checkExpiryStatus utility
125
+ * - React Router history
126
+ * - antd message component
127
+ */
128
+ const handlePasswordExpiryCheck = (user) => {
129
+ const otherDetails = user?.other_details ? JSON.parse(user.other_details) : null;
130
+
131
+ const lastPasswordChange = otherDetails?.last_password_change;
132
+
133
+ if (lastPasswordChange) {
134
+ const onlyDate = new Date(lastPasswordChange).toISOString().split('T')[0];
135
+ const passwordExpiryDate = new Date(onlyDate);
136
+ passwordExpiryDate.setDate(passwordExpiryDate.getDate() + PASSWORD_VALIDITY_DAYS);
137
+
138
+ const passwordStatus = checkExpiryStatus({
139
+ expiryDate: passwordExpiryDate,
140
+ warningDays: 2,
141
+ expiredMessage: 'Your password has expired. Please reset it.',
142
+ warningMessage: (d) => (
143
+ <span>
144
+ Your password will expire in {d} day(s).{' '}
145
+ <a
146
+ onClick={() => {
147
+ history.push('/change-password');
148
+ }}
149
+ >
150
+ Click here to update.
151
+ </a>
152
+ </span>
153
+ ),
154
+ });
155
+
156
+ if (passwordStatus.message) {
157
+ message.warning(passwordStatus.message);
158
+ }
159
+ }
160
+ };
161
+
97
162
  const onFinish = (values) => {
98
163
  setLoading(true);
99
164
 
@@ -131,6 +196,8 @@ function LoginPhone({ history, appSettings }) {
131
196
  if (insider_token) localStorage.insider_token = insider_token;
132
197
 
133
198
  if (result.success) {
199
+ handlePasswordExpiryCheck(user);
200
+
134
201
  //two_factor_authentication variable is present then proceed Two factor authentication
135
202
  if (result.data && result.data.two_factor_authentication) {
136
203
  let data;
@@ -183,7 +250,19 @@ function LoginPhone({ history, appSettings }) {
183
250
  history.push('/');
184
251
  }
185
252
  } else {
186
- message.error(result.message);
253
+ if (result.passwordChange) {
254
+ message.warning(result.message);
255
+
256
+ //time for redirect when expire
257
+ setTimeout(() => {
258
+ setExpiredPassword(true);
259
+ setDefaultUsername(values.email);
260
+
261
+ setShowResetpassword(true);
262
+ }, 1500);
263
+ } else {
264
+ message.warning(result.message);
265
+ }
187
266
  }
188
267
  })
189
268
  .catch((error) => {
@@ -272,10 +351,10 @@ function LoginPhone({ history, appSettings }) {
272
351
  if (result.success) {
273
352
  // for expiry_time
274
353
  startFromExpiry(result?.expiry_time);
275
- // if the api is sucess then go for otpverification step
276
- setotpVerification(true);
277
354
  // set button loading false
278
355
  setLoading(false);
356
+ // if the api is sucess then go for otpverification step
357
+ setotpVerification(true);
279
358
  } else {
280
359
  setLoading(false);
281
360
  message.error(result.message);
@@ -332,7 +411,7 @@ function LoginPhone({ history, appSettings }) {
332
411
  if (result?.user?.organization_details) {
333
412
  const data = safeJSON(result?.user?.organization_details);
334
413
 
335
- const defaultBranch = data.branch.find((b) => b.defaultBranch === 'true');
414
+ const defaultBranch = data.branch.find((b) => b.defaultBranch === true);
336
415
  const defaultDbptr = defaultBranch?.dbPtr;
337
416
 
338
417
  localStorage.setItem('db_ptr', defaultDbptr);
@@ -342,6 +421,8 @@ function LoginPhone({ history, appSettings }) {
342
421
  // set user info into local storage
343
422
  localStorage.setItem('userInfo', JSON.stringify(userInfo));
344
423
 
424
+ handlePasswordExpiryCheck(result.user);
425
+
345
426
  setTimeout(() => history.push('/'), 500);
346
427
  } else {
347
428
  // OTP FAILED (wrong OTP)
@@ -543,28 +624,28 @@ function LoginPhone({ history, appSettings }) {
543
624
  return user.username;
544
625
  };
545
626
 
546
- const { globalCustomerHeader = () => {} } = appSettings;
627
+ const { globalCustomerHeader = () => { } } = appSettings;
547
628
 
548
629
  const themeName = process.env.REACT_APP_THEME; // e.g., 'purple'
549
630
  const isPurple = themeName === 'purple';
550
631
 
551
632
  const sectionStyle = isPurple
552
633
  ? {
553
- width: '100%',
554
- height: '100vh',
555
- backgroundImage: `${state.theme.colors.loginPageBackground}`,
556
- backgroundPosition: 'center bottom, center',
557
- backgroundRepeat: 'no-repeat, no-repeat',
558
- backgroundSize: 'cover, cover',
559
- }
634
+ width: '100%',
635
+ height: '100vh',
636
+ backgroundImage: `${state.theme.colors.loginPageBackground}`,
637
+ backgroundPosition: 'center bottom, center',
638
+ backgroundRepeat: 'no-repeat, no-repeat',
639
+ backgroundSize: 'cover, cover',
640
+ }
560
641
  : {
561
- width: '100%',
562
- height: '100vh',
563
- backgroundImage: `url(${backgroundImage}), ${state.theme.colors.loginPageBackground}`,
564
- backgroundPosition: 'center bottom, center',
565
- backgroundRepeat: 'no-repeat, no-repeat',
566
- backgroundSize: 'cover, cover',
567
- };
642
+ width: '100%',
643
+ height: '100vh',
644
+ background: 'linear-gradient(to bottom, #F7F6E3 0%, #EEF1DE 20%, #D5E4DA 45%, #9DBFC8 75%, #4F89A6 100%)',
645
+ backgroundPosition: 'center bottom, center',
646
+ backgroundRepeat: 'no-repeat, no-repeat',
647
+ backgroundSize: 'cover, cover',
648
+ };
568
649
 
569
650
  return (
570
651
  <section className="full-page" style={sectionStyle}>
@@ -706,7 +787,7 @@ function LoginPhone({ history, appSettings }) {
706
787
  )}
707
788
 
708
789
  {/* Login Form Section */}
709
- {!otpVerification && !otpVisible && (
790
+ {!otpVerification && !otpVisible && !showResetpassword && (
710
791
  <Form {...layout} layout="vertical" name="basic" onFinish={onFinish} onFinishFailed={onFinishFailed}>
711
792
  <div className="form-title">
712
793
  <h4></h4>
@@ -728,19 +809,42 @@ function LoginPhone({ history, appSettings }) {
728
809
  <Input.Password autoComplete="off" />
729
810
  </Form.Item>
730
811
 
731
- <Form.Item {...tailLayout}>
812
+ <Form.Item {...tailLayout} style={{ marginBottom: '0px' }}>
732
813
  <Button loading={loading} type="primary" htmlType="submit" className="SubmitBtn">
733
814
  &nbsp;&nbsp; Submit
734
815
  </Button>
816
+ <div className="forgot-password">
817
+ <Link onClick={() => setShowResetpassword(true)}>Forgot Password?</Link>
818
+ </div>
735
819
  </Form.Item>
736
820
  </Form>
737
821
  )}
822
+
823
+ {/* Forgot Password Section */}
824
+ {showResetpassword && (
825
+ <ResetPassword
826
+ defaultUsername={expiredPassword ? defaultUsername : undefined}
827
+ disabledUserName={expiredPassword}
828
+ onBack={() => {
829
+ setShowResetpassword(false);
830
+ setExpiredPassword(false);
831
+ }}
832
+ title={expiredPassword ? 'Password Expired!' : 'Forgot Password?'}
833
+ // subtitle={
834
+ // expiredPassword
835
+ // ? 'Your password has expired. Select a preferred communication method to receive the One-Time Password (OTP) to continue.'
836
+ // : 'Enter your username and Select a preferred communication method to receive the One-Time Password (OTP) to continue..'
837
+ // }
838
+ buttonText={expiredPassword ? 'Send Reset Link' : 'Send Reset Link'}
839
+ />
840
+ )}
738
841
  </div>
739
842
  </div>
740
843
  {!otpSuccess && otpVerification && (
741
844
  <div className="otp-actions">
742
845
  <div className="resend-action">
743
846
  <Text disabled>Didn't receive OTP?</Text>
847
+
744
848
  <Link className="resend-otp-link" disabled={!otpExpired} onClick={handleResendOTP}>
745
849
  Resend OTP
746
850
  </Link>