ui-soxo-bootstrap-core 2.6.1-dev.2 → 2.6.1-dev.20

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 (66) hide show
  1. package/core/components/extra-info/extra-info-details.js +2 -2
  2. package/core/components/index.js +2 -11
  3. package/core/components/landing-api/landing-api.js +91 -15
  4. package/core/components/landing-api/landing-api.scss +22 -0
  5. package/core/components/license-management/license-alert.js +97 -0
  6. package/core/lib/Store.js +3 -3
  7. package/core/lib/components/global-header/animations.js +78 -4
  8. package/core/lib/components/global-header/global-header.js +224 -255
  9. package/core/lib/components/global-header/global-header.scss +162 -24
  10. package/core/lib/components/sidemenu/animations.js +84 -2
  11. package/core/lib/components/sidemenu/sidemenu.js +191 -65
  12. package/core/lib/components/sidemenu/sidemenu.scss +221 -14
  13. package/core/lib/elements/basic/country-phone-input/country-phone-input.js +14 -8
  14. package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
  15. package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
  16. package/core/lib/models/forms/components/form-creator/form-creator.scss +4 -3
  17. package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
  18. package/core/lib/models/process/components/process-dashboard/process-dashboard.js +469 -3
  19. package/core/lib/models/process/components/process-dashboard/process-dashboard.scss +4 -0
  20. package/core/lib/pages/change-password/change-password.js +17 -24
  21. package/core/lib/pages/change-password/change-password.scss +45 -48
  22. package/core/lib/pages/login/commnication-mode-selection.js +2 -2
  23. package/core/lib/pages/login/login.js +47 -62
  24. package/core/lib/pages/login/login.scss +9 -0
  25. package/core/lib/pages/login/reset-password.js +17 -17
  26. package/core/lib/pages/login/reset-password.scss +10 -1
  27. package/core/lib/pages/profile/themes.json +4 -4
  28. package/core/lib/utils/api/api.utils.js +30 -18
  29. package/core/lib/utils/common/common.utils.js +49 -35
  30. package/core/lib/utils/http/http.utils.js +2 -1
  31. package/core/lib/utils/index.js +4 -1
  32. package/core/models/base/base.js +7 -3
  33. package/core/models/core-scripts/core-scripts.js +134 -126
  34. package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
  35. package/core/models/menus/components/menu-add/menu-add.js +1 -1
  36. package/core/models/menus/components/menu-lists/menu-lists.js +53 -54
  37. package/core/models/menus/menus.js +27 -2
  38. package/core/models/roles/components/role-add/role-add.js +92 -59
  39. package/core/models/roles/components/role-list/role-list.js +1 -1
  40. package/core/models/staff/components/staff-add/staff-add.js +20 -32
  41. package/core/models/users/components/assign-role/assign-role.js +145 -50
  42. package/core/models/users/components/assign-role/assign-role.scss +209 -45
  43. package/core/models/users/components/assign-role/avatar-props.js +45 -0
  44. package/core/models/users/components/user-add/user-add.js +46 -55
  45. package/core/models/users/components/user-add/user-edit.js +25 -4
  46. package/core/models/users/users.js +9 -1
  47. package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
  48. package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
  49. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +147 -0
  50. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
  51. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +90 -0
  52. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
  53. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +252 -0
  54. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
  55. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +326 -436
  56. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +7 -0
  57. package/core/modules/steps/action-buttons.js +33 -15
  58. package/core/modules/steps/action-buttons.scss +55 -9
  59. package/core/modules/steps/chat-assistant.js +141 -0
  60. package/core/modules/steps/openai-realtime.js +275 -0
  61. package/core/modules/steps/readme.md +167 -0
  62. package/core/modules/steps/steps.js +1078 -57
  63. package/core/modules/steps/steps.scss +539 -90
  64. package/core/modules/steps/timeline.js +21 -19
  65. package/core/modules/steps/voice-navigation.js +709 -0
  66. package/package.json +2 -1
@@ -30,7 +30,7 @@ import { getAccessToken, getRefreshToken } from '../../utils/http/auth.helper';
30
30
 
31
31
  import { Location } from '../../utils';
32
32
 
33
- import { checkLicenseStatus, formatMobile, safeJSON ,checkExpiryStatus} from '../../utils/common/common.utils';
33
+ import { checkLicenseStatus, formatMobile, safeJSON } from '../../utils/common/common.utils';
34
34
 
35
35
  import { MailOutlined, MessageOutlined, WhatsAppOutlined } from '@ant-design/icons';
36
36
 
@@ -50,11 +50,10 @@ const tailLayout = {
50
50
 
51
51
  const LICENSE_EXPIRY = '2026-12-12';
52
52
 
53
+
53
54
  const headers = {
54
55
  db_ptr: 'nuraho',
55
56
  };
56
- //password valdity expire
57
- const PASSWORD_VALIDITY_DAYS = 90;
58
57
 
59
58
  /**
60
59
  *
@@ -114,46 +113,32 @@ function LoginPhone({ history, appSettings }) {
114
113
  *
115
114
  * - Parses `last_password_change` from user.other_details.
116
115
  * - Calculates expiry using PASSWORD_VALIDITY_DAYS.
117
- * - Uses `checkExpiryStatus()` to determine status.
118
116
  * - Shows Ant Design warning message if expired or within warning period.
119
117
  * - Warning message includes navigation to `/change-password`.
120
118
  *
121
119
  * Requires:
122
120
  * - PASSWORD_VALIDITY_DAYS constant
123
- * - checkExpiryStatus utility
124
121
  * - React Router history
125
122
  * - antd message component
126
123
  */
127
- const handlePasswordExpiryCheck = (user) => {
128
- const otherDetails = user?.other_details ? JSON.parse(user.other_details) : null;
129
-
130
- const lastPasswordChange = otherDetails?.last_password_change;
131
-
132
- if (lastPasswordChange) {
133
- const passwordExpiryDate = new Date(lastPasswordChange);
134
- passwordExpiryDate.setDate(passwordExpiryDate.getDate() + PASSWORD_VALIDITY_DAYS);
135
-
136
- const passwordStatus = checkExpiryStatus({
137
- expiryDate: passwordExpiryDate,
138
- warningDays: 2,
139
- expiredMessage: 'Your password has expired. Please reset it.',
140
- warningMessage: (d) => (
141
- <span>
142
- Your password will expire in {d} day(s).{' '}
143
- <a
144
- onClick={() => {
145
- history.push('/change-password');
146
- }}
147
- >
148
- Click here to update.
149
- </a>
150
- </span>
151
- ),
152
- });
124
+ const handlePasswordExpiryCheck = (expiryDays) => {
125
+ if (expiryDays == null) return;
153
126
 
154
- if (passwordStatus.message) {
155
- message.warning(passwordStatus.message);
156
- }
127
+ if (expiryDays <= 0) {
128
+ message.error('Your password has expired. Please reset it.');
129
+
130
+ setExpiredPassword(true);
131
+ setShowResetpassword(true);
132
+
133
+ return;
134
+ }
135
+
136
+ if (expiryDays <= 2) {
137
+ message.warning(
138
+ <span>
139
+ Your password will expire in {expiryDays} day(s). <a onClick={() => history.push('/change-password')}>Click here to update.</a>
140
+ </span>
141
+ );
157
142
  }
158
143
  };
159
144
 
@@ -194,7 +179,7 @@ function LoginPhone({ history, appSettings }) {
194
179
  if (insider_token) localStorage.insider_token = insider_token;
195
180
 
196
181
  if (result.success) {
197
- handlePasswordExpiryCheck(user);
182
+ handlePasswordExpiryCheck(result.expiry_in_days);
198
183
 
199
184
  //two_factor_authentication variable is present then proceed Two factor authentication
200
185
  if (result.data && result.data.two_factor_authentication) {
@@ -349,10 +334,10 @@ function LoginPhone({ history, appSettings }) {
349
334
  if (result.success) {
350
335
  // for expiry_time
351
336
  startFromExpiry(result?.expiry_time);
352
- // if the api is sucess then go for otpverification step
353
- setotpVerification(true);
354
337
  // set button loading false
355
338
  setLoading(false);
339
+ // if the api is sucess then go for otpverification step
340
+ setotpVerification(true);
356
341
  } else {
357
342
  setLoading(false);
358
343
  message.error(result.message);
@@ -409,7 +394,7 @@ function LoginPhone({ history, appSettings }) {
409
394
  if (result?.user?.organization_details) {
410
395
  const data = safeJSON(result?.user?.organization_details);
411
396
 
412
- const defaultBranch = data.branch.find((b) => b.defaultBranch === 'true');
397
+ const defaultBranch = data.branch.find((b) => b.defaultBranch === true);
413
398
  const defaultDbptr = defaultBranch?.dbPtr;
414
399
 
415
400
  localStorage.setItem('db_ptr', defaultDbptr);
@@ -419,7 +404,7 @@ function LoginPhone({ history, appSettings }) {
419
404
  // set user info into local storage
420
405
  localStorage.setItem('userInfo', JSON.stringify(userInfo));
421
406
 
422
- handlePasswordExpiryCheck(result.user);
407
+ handlePasswordExpiryCheck(result.expiry_in_days);
423
408
 
424
409
  setTimeout(() => history.push('/'), 500);
425
410
  } else {
@@ -622,28 +607,28 @@ function LoginPhone({ history, appSettings }) {
622
607
  return user.username;
623
608
  };
624
609
 
625
- const { globalCustomerHeader = () => {} } = appSettings;
610
+ const { globalCustomerHeader = () => { } } = appSettings;
626
611
 
627
612
  const themeName = process.env.REACT_APP_THEME; // e.g., 'purple'
628
613
  const isPurple = themeName === 'purple';
629
614
 
630
615
  const sectionStyle = isPurple
631
616
  ? {
632
- width: '100%',
633
- height: '100vh',
634
- backgroundImage: `${state.theme.colors.loginPageBackground}`,
635
- backgroundPosition: 'center bottom, center',
636
- backgroundRepeat: 'no-repeat, no-repeat',
637
- backgroundSize: 'cover, cover',
638
- }
617
+ width: '100%',
618
+ height: '100vh',
619
+ backgroundImage: `${state.theme.colors.loginPageBackground}`,
620
+ backgroundPosition: 'center bottom, center',
621
+ backgroundRepeat: 'no-repeat, no-repeat',
622
+ backgroundSize: 'cover, cover',
623
+ }
639
624
  : {
640
- width: '100%',
641
- height: '100vh',
642
- background: 'linear-gradient(to bottom, #F7F6E3 0%, #EEF1DE 20%, #D5E4DA 45%, #9DBFC8 75%, #4F89A6 100%)',
643
- backgroundPosition: 'center bottom, center',
644
- backgroundRepeat: 'no-repeat, no-repeat',
645
- backgroundSize: 'cover, cover',
646
- };
625
+ width: '100%',
626
+ height: '100vh',
627
+ background: 'linear-gradient(to bottom, #F7F6E3 0%, #EEF1DE 20%, #D5E4DA 45%, #9DBFC8 75%, #4F89A6 100%)',
628
+ backgroundPosition: 'center bottom, center',
629
+ backgroundRepeat: 'no-repeat, no-repeat',
630
+ backgroundSize: 'cover, cover',
631
+ };
647
632
 
648
633
  return (
649
634
  <section className="full-page" style={sectionStyle}>
@@ -811,7 +796,7 @@ function LoginPhone({ history, appSettings }) {
811
796
  <Button loading={loading} type="primary" htmlType="submit" className="SubmitBtn">
812
797
  &nbsp;&nbsp; Submit
813
798
  </Button>
814
- <div className="forgot-password" style={{ marginTop: '8px', textAlign: 'center' }}>
799
+ <div className="forgot-password">
815
800
  <Link onClick={() => setShowResetpassword(true)}>Forgot Password?</Link>
816
801
  </div>
817
802
  </Form.Item>
@@ -827,13 +812,13 @@ function LoginPhone({ history, appSettings }) {
827
812
  setShowResetpassword(false);
828
813
  setExpiredPassword(false);
829
814
  }}
830
- title={expiredPassword ? 'Password Expired' : 'Forgot Password'}
831
- subtitle={
832
- expiredPassword
833
- ? 'Enter your username and choose your preferred OTP method to receive a reset link.'
834
- : 'Enter your username to reset your password and select your preferred OTP method.'
835
- }
836
- buttonText={expiredPassword ? 'Send Reset Link' : 'Reset Password'}
815
+ title={expiredPassword ? 'Password Expired!' : 'Forgot Password?'}
816
+ // subtitle={
817
+ // expiredPassword
818
+ // ? 'Your password has expired. Select a preferred communication method to receive the One-Time Password (OTP) to continue.'
819
+ // : 'Enter your username and Select a preferred communication method to receive the One-Time Password (OTP) to continue..'
820
+ // }
821
+ buttonText={expiredPassword ? 'Send Reset Link' : 'Send Reset Link'}
837
822
  />
838
823
  )}
839
824
  </div>
@@ -228,6 +228,15 @@ body {
228
228
  text-transform: capitalize;
229
229
  }
230
230
  }
231
+
232
+ .forgot-password {
233
+ margin-top: 8px;
234
+ text-align: center;
235
+
236
+ a {
237
+ color: #204b7e;
238
+ }
239
+ }
231
240
  }
232
241
  }
233
242
 
@@ -17,7 +17,7 @@
17
17
  * @param {boolean} disabledUserName - Disable username field
18
18
  */
19
19
 
20
- import React, { useState, useEffect } from 'react';
20
+ import React, { useState, useEffect, useRef } from 'react';
21
21
  import { Divider, Form, Input, message } from 'antd';
22
22
  import { Button } from '../../elements';
23
23
  import CommunicationModeSelection from './commnication-mode-selection';
@@ -36,6 +36,9 @@ function ResetPassword({ onBack, title, subtitle, buttonText, defaultUsername, d
36
36
 
37
37
  const [form] = Form.useForm();
38
38
 
39
+ // Ref to username input auto focus
40
+ const inputRef = useRef(null);
41
+
39
42
  // Pre-fills username if provided and disabled.
40
43
  useEffect(() => {
41
44
  if (disabledUserName && defaultUsername) {
@@ -43,6 +46,10 @@ function ResetPassword({ onBack, title, subtitle, buttonText, defaultUsername, d
43
46
  }
44
47
  }, [disabledUserName, defaultUsername]);
45
48
 
49
+ useEffect(() => {
50
+ inputRef.current?.focus();
51
+ }, []);
52
+
46
53
  /**
47
54
  * Sends forgot password request to backend.
48
55
  * Payload: { mode, username, user_type: 'staff' }
@@ -83,26 +90,20 @@ function ResetPassword({ onBack, title, subtitle, buttonText, defaultUsername, d
83
90
  return (
84
91
  <motion.div
85
92
  className="forgot-password-container"
86
- initial={{ opacity: 0, y: 70 }}
93
+ initial={{ opacity: 0, y: 30 }}
87
94
  animate={{ opacity: 1, y: 0 }}
88
- exit={{ opacity: 0, y: -50 }}
95
+ exit={{ opacity: 0, y: -20 }}
89
96
  transition={{
90
- y: {
91
- duration: 1,
92
- ease: [0.22, 0.08, 0.26, 1],
93
- },
94
- opacity: {
95
- duration: 0.45,
96
- ease: 'easeOut',
97
- },
97
+ duration: 0.30,
98
+ ease: 'easeOut',
98
99
  }}
99
100
  >
100
101
  <h3 className="password-title">{title}</h3>
101
- <p className="password-subtitle">{subtitle}</p>
102
- <Divider />
102
+ {/* <p className="password-subtitle">{subtitle}</p>
103
+ <Divider /> */}
103
104
  <Form layout="vertical" form={form} onFinish={handleSendForgetPassword}>
104
105
  <Form.Item label="Username" name="username" rules={[{ required: true, message: 'Please input your username' }]}>
105
- <Input placeholder="Enter your username" disabled={disabledUserName} />
106
+ <Input placeholder="Enter your username" disabled={disabledUserName} ref={inputRef}/>
106
107
  </Form.Item>
107
108
 
108
109
  <CommunicationModeSelection communicationMode={communicationMode} setCommunicationMode={setCommunicationMode} modeError={modeError} />
@@ -110,9 +111,8 @@ function ResetPassword({ onBack, title, subtitle, buttonText, defaultUsername, d
110
111
  <Button type="primary" htmlType="submit" loading={loading}>
111
112
  {buttonText}
112
113
  </Button>
113
-
114
- <div style={{ marginTop: '10px', textAlign: 'center' }}>
115
- <a style={{ cursor: 'pointer' }} onClick={handleBackToLogin}>
114
+ <div className="back-to-login">
115
+ <a onClick={handleBackToLogin}>
116
116
  Back to Login
117
117
  </a>
118
118
  </div>
@@ -18,5 +18,14 @@
18
18
  margin: 0;
19
19
  border-top: 1px solid #e5e7eb;
20
20
  }
21
- }
22
21
 
22
+ .back-to-login {
23
+ margin-top: 10px;
24
+ text-align: center;
25
+
26
+ a {
27
+ cursor: pointer;
28
+ color: #204b7e;
29
+ }
30
+ }
31
+ }
@@ -88,10 +88,10 @@
88
88
  "loginPageBackground": "linear-gradient(#D5F1FB, #24AEB8)",
89
89
  "bodyBackground": "#F0F6FF",
90
90
  "progressBar": "#24AEB8",
91
- "primaryButtonBg": "#446AA9",
91
+ "primaryButtonBg": "#204b7e",
92
92
  "primaryButtonHoverBg": "#2E4873",
93
93
  "primaryButtonText": "#FFFFFF",
94
- "primaryButtonActiveBg": "#446AA9",
94
+ "primaryButtonActiveBg": "#204b7e",
95
95
  "primaryButtonDisabledBg": "#C4C6CC",
96
96
  "primaryButtonDisabledText": "#6B7280",
97
97
 
@@ -104,14 +104,14 @@
104
104
 
105
105
  "dashedButtonBg": "#FFFFFF",
106
106
  "dashedButtonHoverBg": "#D1E3FF",
107
- "dashedButtonText": "#446AA9",
107
+ "dashedButtonText": "#204b7e",
108
108
  "dashedButtonActiveBg": "#B8C7FF",
109
109
  "dashedButtonDisabledBg": "#F0F6FF",
110
110
  "dashedButtonDisabledText": "#A9BCC6",
111
111
 
112
112
  "linkButtonBg": "transparent",
113
113
  "linkButtonHoverBg": "#F5F5F5",
114
- "linkButtonText": "#446AA9",
114
+ "linkButtonText": "#204b7e",
115
115
  "linkButtonActiveBg": "#D1E3FF",
116
116
  "linkButtonDisabledText": "#A9BCC6",
117
117
 
@@ -24,15 +24,15 @@ export default class ApiUtils {
24
24
  };
25
25
 
26
26
  static get = async ({ url, config = { queries: [], order: {} }, headers: customHeaders = {}, ...props }) => {
27
- const { headers: defaultHeaders } = settings;
27
+ const dbPtr = localStorage.getItem('db_ptr');
28
28
 
29
29
  try {
30
30
  return await GetData({
31
31
  url: createUrlParams(url, config),
32
32
  settings,
33
33
  headers: {
34
- ...defaultHeaders,
35
- // override here
34
+ ...(settings.headers || {}),
35
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
36
36
  ...customHeaders,
37
37
  },
38
38
  ...props,
@@ -43,15 +43,15 @@ export default class ApiUtils {
43
43
  };
44
44
 
45
45
  static getRecordDetail = async ({ url, config = { queries: [] }, headers: customHeaders = {}, ...props }) => {
46
- const { headers: defaultHeaders } = settings;
46
+ const dbPtr = localStorage.getItem('db_ptr');
47
47
 
48
48
  try {
49
49
  return await GetData({
50
50
  url: createUrlParams(url, config),
51
51
  settings,
52
52
  headers: {
53
- ...defaultHeaders,
54
- // override here
53
+ ...(settings.headers || {}),
54
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
55
55
  ...customHeaders,
56
56
  },
57
57
  ...props,
@@ -62,52 +62,64 @@ export default class ApiUtils {
62
62
  };
63
63
 
64
64
  static post = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
65
- const { headers: defaultHeaders } = settings;
65
+ const dbPtr = localStorage.getItem('db_ptr');
66
66
 
67
67
  return PostData({
68
68
  url,
69
69
  formBody,
70
70
  settings,
71
71
  headers: {
72
- ...defaultHeaders,
73
- // override here
72
+ ...(settings.headers || {}),
73
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
74
74
  ...customHeaders,
75
75
  },
76
76
  ...props,
77
77
  });
78
78
  };
79
79
 
80
- static put = ({ url, formBody, ...props }) => {
81
- const { headers } = settings;
80
+ static put = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
81
+ const dbPtr = localStorage.getItem('db_ptr');
82
82
 
83
83
  return PutData({
84
84
  url,
85
85
  settings,
86
86
  formBody,
87
- headers,
87
+ headers: {
88
+ ...(settings.headers || {}),
89
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
90
+ ...customHeaders,
91
+ },
88
92
  ...props,
89
93
  });
90
94
  };
91
95
 
92
- static patch = ({ url, formBody, ...props }) => {
93
- const { headers } = settings;
96
+ static patch = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
97
+ const dbPtr = localStorage.getItem('db_ptr');
94
98
 
95
99
  return PatchData({
96
100
  url,
97
101
  settings,
98
102
  formBody,
99
- headers,
103
+ headers: {
104
+ ...(settings.headers || {}),
105
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
106
+ ...customHeaders,
107
+ },
100
108
  ...props,
101
109
  });
102
110
  };
103
111
 
104
- static delete = ({ url, formBody, ...props }) => {
105
- const { headers } = settings;
112
+ static delete = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
113
+ const dbPtr = localStorage.getItem('db_ptr');
106
114
 
107
115
  return DeleteData({
108
116
  url,
109
117
  settings,
110
- headers,
118
+ headers: {
119
+ ...(settings.headers || {}),
120
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
121
+ ...customHeaders,
122
+ },
111
123
  ...props,
112
124
  });
113
125
  };
@@ -4,6 +4,7 @@ Implements utility functions to be used across project
4
4
 
5
5
  /*eslint no-useless-escape:"off",eqeqeq: "off"*/
6
6
  import moment from 'moment';
7
+ import { parsePhoneNumberFromString, isValidPhoneNumber } from 'libphonenumber-js';
7
8
 
8
9
  export function IsObjectHaveKeys(obj) {
9
10
  return obj && typeof obj == 'object' && Object.keys(obj).length;
@@ -122,41 +123,6 @@ export const checkLicenseStatus = (expiryDate) => {
122
123
  return { valid: true, daysLeft, message: null, level: null };
123
124
  };
124
125
 
125
- /**
126
- * Checks password expiry status.
127
- *
128
- * @param {string|Date} expiryDate
129
- * @param {number} warningDays
130
- * @param {string} expiredMessage
131
- * @param {(daysLeft: number) => string} warningMessage
132
- *
133
- * @returns {{ valid: boolean, daysLeft: number, message: string|null, level: "error"|"warning"|null }}
134
- */
135
-
136
- export const checkExpiryStatus = ({ expiryDate, warningDays, expiredMessage, warningMessage }) => {
137
- const expiry = new Date(expiryDate);
138
-
139
- if (isNaN(expiry)) {
140
- return { valid: false, daysLeft: 0, message: 'Invalid date', level: 'error' };
141
- }
142
-
143
- expiry.setHours(0, 0, 0, 0);
144
- const today = new Date();
145
- today.setHours(0, 0, 0, 0);
146
-
147
- const msDiff = expiry.getTime() - today.getTime();
148
- const daysLeft = Math.ceil(msDiff / (1000 * 60 * 60 * 24));
149
-
150
- if (daysLeft < 0) {
151
- return { valid: false, daysLeft, message: expiredMessage, level: 'error' };
152
- }
153
-
154
- if (daysLeft <= warningDays) {
155
- return { valid: true, daysLeft, message: warningMessage(daysLeft), level: 'warning' };
156
- }
157
-
158
- return { valid: true, daysLeft, message: null, level: null };
159
- };
160
126
 
161
127
  /**
162
128
  * Masks a mobile number by hiding all but the last `visibleDigits`.
@@ -221,3 +187,51 @@ export function safeJSON(value) {
221
187
  return null;
222
188
  }
223
189
  }
190
+
191
+ /**
192
+ * Convert +countrycode phone into form compatible structure
193
+ * @param {string} phone
194
+ * @returns {object|null}
195
+ */
196
+ export const formatPhoneForForm = (phone) => {
197
+ if (!phone) return null;
198
+
199
+ const parsed = parsePhoneNumberFromString(phone);
200
+
201
+ if (!parsed) return null;
202
+
203
+ return {
204
+ value: parsed.nationalNumber,
205
+ code: {
206
+ dialCode: parsed.countryCallingCode,
207
+ countryCode: parsed.country?.toLowerCase(),
208
+ },
209
+ valid: true,
210
+ };
211
+ };
212
+
213
+ /**
214
+ * Validates a phone number from CountryPhoneInput.
215
+ * Builds full number using country dial code and checks validity.
216
+ *
217
+ * @param {Object} _ - Ant Design rule parameter (unused)
218
+ * @param {Object} val - Phone value object from form
219
+ * @returns {Promise<void>}
220
+ */
221
+ export const phoneValidator = (_, val) => {
222
+ if (!val || !val.value || !val?.code?.dialCode) {
223
+ return Promise.resolve();
224
+ }
225
+
226
+ const fullPhone = `+${val.code.dialCode}${val.value}`;
227
+
228
+ return isValidPhoneNumber(fullPhone) ? Promise.resolve() : Promise.reject('Invalid mobile number');
229
+ };
230
+
231
+ // Returns phone number with country code (e.g., +919876543210)
232
+ export const formatPhoneWithCountryCode = (phone) => {
233
+ if (phone && typeof phone === 'object' && phone.code) {
234
+ return `+${phone.code.dialCode}${phone.value}`;
235
+ }
236
+ return phone;
237
+ };
@@ -82,7 +82,7 @@ export async function ApiCall({ url, formBody, method, settings, ...props }) {
82
82
  ...settings.headers,
83
83
  ...headers,
84
84
  ...(props.headers || {}),
85
- Authorization: `Bearer ${token}`,
85
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
86
86
  'Content-Type': 'application/json',
87
87
  },
88
88
  body: formBody ? JSON.stringify(formBody) : null,
@@ -153,5 +153,6 @@ export async function ApiCall({ url, formBody, method, settings, ...props }) {
153
153
  return result;
154
154
  } catch (err) {
155
155
  console.error('Login error -->', err?.message);
156
+ throw err;
156
157
  }
157
158
  }
@@ -14,7 +14,7 @@ import { GetData, PostData, PutData, DeleteData } from './http/http.utils';
14
14
 
15
15
  import { getExportData } from './generic/generic.utils';
16
16
 
17
- import { ConvertBytesToArray, safeJSON } from './common/common.utils';
17
+ import { ConvertBytesToArray, safeJSON, formatPhoneForForm, phoneValidator, formatPhoneWithCountryCode } from './common/common.utils';
18
18
 
19
19
  import SettingsUtil from './setting.utils';
20
20
 
@@ -37,4 +37,7 @@ export {
37
37
  SettingsUtil,
38
38
  FormUtils,
39
39
  safeJSON,
40
+ formatPhoneForForm,
41
+ phoneValidator,
42
+ formatPhoneWithCountryCode,
40
43
  };
@@ -88,9 +88,13 @@ class BaseAPI {
88
88
  * Get the data from the table
89
89
  */
90
90
  get(config = {}) {
91
- // Get the records from firebase
92
-
93
- return ApiUtils.get({ url: config.url || this.endpoint, config });
91
+ const { url, headers, ...rest } = config;
92
+ return ApiUtils.get({
93
+ url: url || this.endpoint,
94
+ headers,
95
+ config: rest,
96
+ ...rest,
97
+ });
94
98
  }
95
99
 
96
100
  getRelations(id) {