ui-soxo-bootstrap-core 2.6.0 → 2.6.1-dev.10
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/.github/workflows/npm-publish.yml +49 -19
- package/core/components/extra-info/extra-info-details.js +2 -2
- package/core/components/menu-template-api/menu-template-api.js +2 -2
- package/core/lib/Store.js +3 -3
- package/core/lib/components/global-header/global-header.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +19 -13
- package/core/lib/elements/basic/country-phone-input/country-phone-input.js +35 -60
- package/core/lib/elements/basic/country-phone-input/phone-input.scss +14 -0
- 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 +468 -502
- package/core/lib/models/forms/components/form-creator/form-creator.scss +5 -4
- package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
- 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 +46 -0
- package/core/lib/pages/login/communication-mode-selection.scss +60 -0
- package/core/lib/pages/login/login.js +153 -24
- package/core/lib/pages/login/login.scss +229 -334
- package/core/lib/pages/login/reset-password.js +124 -0
- package/core/lib/pages/login/reset-password.scss +31 -0
- package/core/lib/pages/profile/themes.json +4 -4
- package/core/lib/utils/api/api.utils.js +71 -48
- package/core/lib/utils/common/common.utils.js +109 -0
- package/core/lib/utils/http/http.utils.js +1 -0
- package/core/lib/utils/index.js +25 -28
- package/core/models/base/base.js +7 -3
- package/core/models/core-scripts/core-scripts.js +9 -0
- 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 +5 -9
- package/core/models/menus/menus.js +21 -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/roles/roles.js +9 -0
- 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 +47 -56
- package/core/models/users/components/user-add/user-edit.js +25 -4
- package/core/models/users/users.js +34 -8
- 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 +120 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +75 -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 +252 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +222 -376
- package/core/modules/steps/action-buttons.js +47 -45
- package/core/modules/steps/action-buttons.scss +35 -6
- package/core/modules/steps/steps.js +12 -10
- package/core/modules/steps/steps.scss +229 -31
- package/core/modules/steps/timeline.js +21 -19
- package/package.json +3 -2
- package/core/components/external-window/DEVELOPER_GUIDE.md +0 -705
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResetPassword Component
|
|
3
|
+
*
|
|
4
|
+
* Handles staff password reset flow:
|
|
5
|
+
* - Accepts username
|
|
6
|
+
* - Selects communication mode (email/SMS)
|
|
7
|
+
* - Calls API to send reset link
|
|
8
|
+
* - Displays success/error messages
|
|
9
|
+
* - Allows navigation back to login
|
|
10
|
+
*
|
|
11
|
+
* Props:
|
|
12
|
+
* @param {Function} onBack - Triggered when "Back to Login" is clicked
|
|
13
|
+
* @param {string} title - Page title
|
|
14
|
+
* @param {string} subtitle - Page subtitle
|
|
15
|
+
* @param {string} buttonText - Submit button text
|
|
16
|
+
* @param {string} defaultUsername - Pre-filled username
|
|
17
|
+
* @param {boolean} disabledUserName - Disable username field
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
21
|
+
import { Divider, Form, Input, message } from 'antd';
|
|
22
|
+
import { Button } from '../../elements';
|
|
23
|
+
import CommunicationModeSelection from './commnication-mode-selection';
|
|
24
|
+
import { motion } from 'framer-motion';
|
|
25
|
+
import './reset-password.scss';
|
|
26
|
+
import { UsersAPI } from '../../../models';
|
|
27
|
+
|
|
28
|
+
function ResetPassword({ onBack, title, subtitle, buttonText, defaultUsername, disabledUserName }) {
|
|
29
|
+
// Selected communication mode (default: email) */
|
|
30
|
+
const [communicationMode, setCommunicationMode] = useState('email');
|
|
31
|
+
|
|
32
|
+
const [modeError, setModeError] = useState(false);
|
|
33
|
+
|
|
34
|
+
// Loading state for submit button */
|
|
35
|
+
const [loading, setLoading] = useState(false);
|
|
36
|
+
|
|
37
|
+
const [form] = Form.useForm();
|
|
38
|
+
|
|
39
|
+
// Ref to username input auto focus
|
|
40
|
+
const inputRef = useRef(null);
|
|
41
|
+
|
|
42
|
+
// Pre-fills username if provided and disabled.
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (disabledUserName && defaultUsername) {
|
|
45
|
+
form.setFieldsValue({ username: defaultUsername });
|
|
46
|
+
}
|
|
47
|
+
}, [disabledUserName, defaultUsername]);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
inputRef.current?.focus();
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Sends forgot password request to backend.
|
|
55
|
+
* Payload: { mode, username, user_type: 'staff' }
|
|
56
|
+
*/
|
|
57
|
+
const handleSendForgetPassword = async (values) => {
|
|
58
|
+
const payload = {
|
|
59
|
+
mode: communicationMode,
|
|
60
|
+
username: values.username,
|
|
61
|
+
user_type: 'staff',
|
|
62
|
+
};
|
|
63
|
+
setLoading(true);
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const res = await UsersAPI.createForgotePassword(payload);
|
|
67
|
+
|
|
68
|
+
if (!res?.success) {
|
|
69
|
+
throw new Error(res?.message || 'Reset password failed');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
message.success(res?.message || 'Reset password link sent');
|
|
73
|
+
} catch (err) {
|
|
74
|
+
message.warning(err?.message || 'Reset password failed');
|
|
75
|
+
} finally {
|
|
76
|
+
setLoading(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Clears auth storage and navigates back.
|
|
82
|
+
*/
|
|
83
|
+
const handleBackToLogin = () => {
|
|
84
|
+
localStorage.removeItem('access_token');
|
|
85
|
+
localStorage.removeItem('refresh_token');
|
|
86
|
+
sessionStorage.clear();
|
|
87
|
+
onBack();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<motion.div
|
|
92
|
+
className="forgot-password-container"
|
|
93
|
+
initial={{ opacity: 0, y: 30 }}
|
|
94
|
+
animate={{ opacity: 1, y: 0 }}
|
|
95
|
+
exit={{ opacity: 0, y: -20 }}
|
|
96
|
+
transition={{
|
|
97
|
+
duration: 0.30,
|
|
98
|
+
ease: 'easeOut',
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<h3 className="password-title">{title}</h3>
|
|
102
|
+
{/* <p className="password-subtitle">{subtitle}</p>
|
|
103
|
+
<Divider /> */}
|
|
104
|
+
<Form layout="vertical" form={form} onFinish={handleSendForgetPassword}>
|
|
105
|
+
<Form.Item label="Username" name="username" rules={[{ required: true, message: 'Please input your username' }]}>
|
|
106
|
+
<Input placeholder="Enter your username" disabled={disabledUserName} ref={inputRef}/>
|
|
107
|
+
</Form.Item>
|
|
108
|
+
|
|
109
|
+
<CommunicationModeSelection communicationMode={communicationMode} setCommunicationMode={setCommunicationMode} modeError={modeError} />
|
|
110
|
+
|
|
111
|
+
<Button type="primary" htmlType="submit" loading={loading}>
|
|
112
|
+
{buttonText}
|
|
113
|
+
</Button>
|
|
114
|
+
<div className="back-to-login">
|
|
115
|
+
<a onClick={handleBackToLogin}>
|
|
116
|
+
Back to Login
|
|
117
|
+
</a>
|
|
118
|
+
</div>
|
|
119
|
+
</Form>
|
|
120
|
+
</motion.div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default ResetPassword;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.forgot-password-container {
|
|
2
|
+
|
|
3
|
+
padding: 4px;
|
|
4
|
+
.password-title{
|
|
5
|
+
color: #1f1f1f;
|
|
6
|
+
margin: 0 0 8px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.password-subtitle {
|
|
10
|
+
font-size: 14px;
|
|
11
|
+
font-weight: 400;
|
|
12
|
+
color: #6b7280;
|
|
13
|
+
line-height: 1.5;
|
|
14
|
+
margin: 0 0 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.ant-divider {
|
|
18
|
+
margin: 0;
|
|
19
|
+
border-top: 1px solid #e5e7eb;
|
|
20
|
+
}
|
|
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": "#
|
|
91
|
+
"primaryButtonBg": "#204b7e",
|
|
92
92
|
"primaryButtonHoverBg": "#2E4873",
|
|
93
93
|
"primaryButtonText": "#FFFFFF",
|
|
94
|
-
"primaryButtonActiveBg": "#
|
|
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": "#
|
|
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": "#
|
|
114
|
+
"linkButtonText": "#204b7e",
|
|
115
115
|
"linkButtonActiveBg": "#D1E3FF",
|
|
116
116
|
"linkButtonDisabledText": "#A9BCC6",
|
|
117
117
|
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PostData,
|
|
3
|
-
GetData,
|
|
4
|
-
PutData,
|
|
5
|
-
PatchData,
|
|
6
|
-
DeleteData,
|
|
7
|
-
} from "./../http/http.utils";
|
|
1
|
+
import { PostData, GetData, PutData, PatchData, DeleteData } from './../http/http.utils';
|
|
8
2
|
|
|
9
3
|
let headers = {};
|
|
10
4
|
|
|
@@ -29,75 +23,103 @@ export default class ApiUtils {
|
|
|
29
23
|
return this.user;
|
|
30
24
|
};
|
|
31
25
|
|
|
32
|
-
static get = ({ url, config = { queries: [], order: {} }, ...props }) => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
static get = async ({ url, config = { queries: [], order: {} }, headers: customHeaders = {}, ...props }) => {
|
|
27
|
+
const dbPtr = localStorage.getItem('db_ptr');
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
return await GetData({
|
|
31
|
+
url: createUrlParams(url, config),
|
|
32
|
+
settings,
|
|
33
|
+
headers: {
|
|
34
|
+
...(settings.headers || {}),
|
|
35
|
+
...(dbPtr ? { db_ptr: dbPtr } : {}),
|
|
36
|
+
...customHeaders,
|
|
37
|
+
},
|
|
38
|
+
...props,
|
|
39
|
+
});
|
|
40
|
+
} catch (result_1) {
|
|
41
|
+
console.log(result_1);
|
|
42
|
+
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
static getRecordDetail = ({ url, config = { queries: [] }, ...props }) => {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
static getRecordDetail = async ({ url, config = { queries: [] }, headers: customHeaders = {}, ...props }) => {
|
|
46
|
+
const dbPtr = localStorage.getItem('db_ptr');
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
return await GetData({
|
|
50
|
+
url: createUrlParams(url, config),
|
|
51
|
+
settings,
|
|
52
|
+
headers: {
|
|
53
|
+
...(settings.headers || {}),
|
|
54
|
+
...(dbPtr ? { db_ptr: dbPtr } : {}),
|
|
55
|
+
...customHeaders,
|
|
56
|
+
},
|
|
57
|
+
...props,
|
|
58
|
+
});
|
|
59
|
+
} catch (result_1) {
|
|
60
|
+
console.log(result_1);
|
|
61
|
+
}
|
|
56
62
|
};
|
|
57
63
|
|
|
58
|
-
static post = ({ url, formBody, ...props }) => {
|
|
59
|
-
const
|
|
64
|
+
static post = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
|
|
65
|
+
const dbPtr = localStorage.getItem('db_ptr');
|
|
60
66
|
|
|
61
67
|
return PostData({
|
|
62
68
|
url,
|
|
63
69
|
formBody,
|
|
64
70
|
settings,
|
|
65
|
-
headers
|
|
71
|
+
headers: {
|
|
72
|
+
...(settings.headers || {}),
|
|
73
|
+
...(dbPtr ? { db_ptr: dbPtr } : {}),
|
|
74
|
+
...customHeaders,
|
|
75
|
+
},
|
|
66
76
|
...props,
|
|
67
77
|
});
|
|
68
78
|
};
|
|
69
79
|
|
|
70
|
-
static put = ({ url, formBody, ...props }) => {
|
|
71
|
-
const
|
|
80
|
+
static put = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
|
|
81
|
+
const dbPtr = localStorage.getItem('db_ptr');
|
|
72
82
|
|
|
73
83
|
return PutData({
|
|
74
84
|
url,
|
|
75
85
|
settings,
|
|
76
86
|
formBody,
|
|
77
|
-
headers
|
|
87
|
+
headers: {
|
|
88
|
+
...(settings.headers || {}),
|
|
89
|
+
...(dbPtr ? { db_ptr: dbPtr } : {}),
|
|
90
|
+
...customHeaders,
|
|
91
|
+
},
|
|
78
92
|
...props,
|
|
79
93
|
});
|
|
80
94
|
};
|
|
81
95
|
|
|
82
|
-
static patch = ({ url, formBody, ...props }) => {
|
|
83
|
-
const
|
|
96
|
+
static patch = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
|
|
97
|
+
const dbPtr = localStorage.getItem('db_ptr');
|
|
84
98
|
|
|
85
99
|
return PatchData({
|
|
86
100
|
url,
|
|
87
101
|
settings,
|
|
88
102
|
formBody,
|
|
89
|
-
headers
|
|
103
|
+
headers: {
|
|
104
|
+
...(settings.headers || {}),
|
|
105
|
+
...(dbPtr ? { db_ptr: dbPtr } : {}),
|
|
106
|
+
...customHeaders,
|
|
107
|
+
},
|
|
90
108
|
...props,
|
|
91
109
|
});
|
|
92
110
|
};
|
|
93
111
|
|
|
94
|
-
static delete = ({ url, formBody, ...props }) => {
|
|
95
|
-
const
|
|
112
|
+
static delete = ({ url, formBody, headers: customHeaders = {}, ...props }) => {
|
|
113
|
+
const dbPtr = localStorage.getItem('db_ptr');
|
|
96
114
|
|
|
97
115
|
return DeleteData({
|
|
98
116
|
url,
|
|
99
117
|
settings,
|
|
100
|
-
headers
|
|
118
|
+
headers: {
|
|
119
|
+
...(settings.headers || {}),
|
|
120
|
+
...(dbPtr ? { db_ptr: dbPtr } : {}),
|
|
121
|
+
...customHeaders,
|
|
122
|
+
},
|
|
101
123
|
...props,
|
|
102
124
|
});
|
|
103
125
|
};
|
|
@@ -105,11 +127,11 @@ export default class ApiUtils {
|
|
|
105
127
|
static upload = ({ url, data }) => {
|
|
106
128
|
return fetch(process.env.REACT_APP_endpoint + url, {
|
|
107
129
|
// Your POST endpoint
|
|
108
|
-
method:
|
|
130
|
+
method: 'POST',
|
|
109
131
|
headers: {
|
|
110
132
|
// 'App-Type': 313,
|
|
111
133
|
// 'App-Version': '1.0.1',
|
|
112
|
-
Authorization:
|
|
134
|
+
Authorization: 'Bearer ' + localStorage.access_token,
|
|
113
135
|
// type:'multipart/formData'
|
|
114
136
|
},
|
|
115
137
|
// credentials: 'include',
|
|
@@ -136,16 +158,17 @@ export default class ApiUtils {
|
|
|
136
158
|
* @returns
|
|
137
159
|
*/
|
|
138
160
|
static getAuthStatus = ({ settings: config, token }) => {
|
|
139
|
-
headers = headers;
|
|
161
|
+
// headers = headers;
|
|
140
162
|
|
|
141
163
|
settings = config;
|
|
142
164
|
|
|
143
165
|
return GetData({
|
|
144
166
|
url: `auth/profile`,
|
|
145
167
|
settings,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
168
|
+
headers: {
|
|
169
|
+
db_ptr: 'nuraho',
|
|
170
|
+
},
|
|
171
|
+
token,
|
|
149
172
|
});
|
|
150
173
|
};
|
|
151
174
|
}
|
|
@@ -159,7 +182,7 @@ function createUrlParams(url, config) {
|
|
|
159
182
|
let { queries = [], limit, order } = config;
|
|
160
183
|
|
|
161
184
|
if (queries.length > 0 || limit > 0) {
|
|
162
|
-
base_url +=
|
|
185
|
+
base_url += '?';
|
|
163
186
|
}
|
|
164
187
|
|
|
165
188
|
queries.forEach((ele) => {
|
|
@@ -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,6 +123,42 @@ export const checkLicenseStatus = (expiryDate) => {
|
|
|
122
123
|
return { valid: true, daysLeft, message: null, level: null };
|
|
123
124
|
};
|
|
124
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Checks password expiry status.
|
|
128
|
+
*
|
|
129
|
+
* @param {string|Date} expiryDate
|
|
130
|
+
* @param {number} warningDays
|
|
131
|
+
* @param {string} expiredMessage
|
|
132
|
+
* @param {(daysLeft: number) => string} warningMessage
|
|
133
|
+
*
|
|
134
|
+
* @returns {{ valid: boolean, daysLeft: number, message: string|null, level: "error"|"warning"|null }}
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
export const checkExpiryStatus = ({ expiryDate, warningDays, expiredMessage, warningMessage }) => {
|
|
138
|
+
const expiry = new Date(expiryDate);
|
|
139
|
+
|
|
140
|
+
if (isNaN(expiry)) {
|
|
141
|
+
return { valid: false, daysLeft: 0, message: 'Invalid date', level: 'error' };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
expiry.setHours(0, 0, 0, 0);
|
|
145
|
+
const today = new Date();
|
|
146
|
+
today.setHours(0, 0, 0, 0);
|
|
147
|
+
|
|
148
|
+
const msDiff = expiry.getTime() - today.getTime();
|
|
149
|
+
const daysLeft = Math.ceil(msDiff / (1000 * 60 * 60 * 24));
|
|
150
|
+
|
|
151
|
+
if (daysLeft < 0) {
|
|
152
|
+
return { valid: false, daysLeft, message: expiredMessage, level: 'error' };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (daysLeft <= warningDays) {
|
|
156
|
+
return { valid: true, daysLeft, message: warningMessage(daysLeft), level: 'warning' };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { valid: true, daysLeft, message: null, level: null };
|
|
160
|
+
};
|
|
161
|
+
|
|
125
162
|
/**
|
|
126
163
|
* Masks a mobile number by hiding all but the last `visibleDigits`.
|
|
127
164
|
* Adds spacing only to the masked portion (groups of 3),
|
|
@@ -161,3 +198,75 @@ export const formatMobile = (mobile, visibleDigits = 4) => {
|
|
|
161
198
|
// Combine masked and visible parts without spacing the visible digits
|
|
162
199
|
return `${maskedWithSpaces} ${visible}`;
|
|
163
200
|
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Safely parse a JSON value.
|
|
204
|
+
*
|
|
205
|
+
* - Accepts JSON strings or plain objects
|
|
206
|
+
* - Returns null on any invalid or missing data
|
|
207
|
+
*/
|
|
208
|
+
export function safeJSON(value) {
|
|
209
|
+
if (value == null) return null;
|
|
210
|
+
|
|
211
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
return JSON.parse(value);
|
|
221
|
+
} catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Convert +countrycode phone into form compatible structure
|
|
228
|
+
* @param {string} phone
|
|
229
|
+
* @returns {object|null}
|
|
230
|
+
*/
|
|
231
|
+
export const formatPhoneForForm = (phone) => {
|
|
232
|
+
if (!phone) return null;
|
|
233
|
+
|
|
234
|
+
const parsed = parsePhoneNumberFromString(phone);
|
|
235
|
+
|
|
236
|
+
if (!parsed) return null;
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
value: parsed.nationalNumber,
|
|
240
|
+
code: {
|
|
241
|
+
dialCode: parsed.countryCallingCode,
|
|
242
|
+
countryCode: parsed.country?.toLowerCase(),
|
|
243
|
+
},
|
|
244
|
+
valid: true,
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Validates a phone number from CountryPhoneInput.
|
|
250
|
+
* Builds full number using country dial code and checks validity.
|
|
251
|
+
*
|
|
252
|
+
* @param {Object} _ - Ant Design rule parameter (unused)
|
|
253
|
+
* @param {Object} val - Phone value object from form
|
|
254
|
+
* @returns {Promise<void>}
|
|
255
|
+
*/
|
|
256
|
+
export const phoneValidator = (_, val) => {
|
|
257
|
+
if (!val || !val.value || !val?.code?.dialCode) {
|
|
258
|
+
return Promise.resolve();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const fullPhone = `+${val.code.dialCode}${val.value}`;
|
|
262
|
+
|
|
263
|
+
return isValidPhoneNumber(fullPhone) ? Promise.resolve() : Promise.reject('Invalid mobile number');
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Returns phone number with country code (e.g., +919876543210)
|
|
267
|
+
export const formatPhoneWithCountryCode = (phone) => {
|
|
268
|
+
if (phone && typeof phone === 'object' && phone.code) {
|
|
269
|
+
return `+${phone.code.dialCode}${phone.value}`;
|
|
270
|
+
}
|
|
271
|
+
return phone;
|
|
272
|
+
};
|
package/core/lib/utils/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { Location } from './location/location.utils'
|
|
1
|
+
import { Location } from './location/location.utils';
|
|
4
2
|
|
|
5
3
|
import Notification from './notification.utils';
|
|
6
4
|
|
|
7
|
-
import DateUtils from './date/date.utils'
|
|
5
|
+
import DateUtils from './date/date.utils';
|
|
8
6
|
|
|
9
7
|
import ApiUtils from './api/api.utils';
|
|
10
8
|
|
|
@@ -16,31 +14,30 @@ import { GetData, PostData, PutData, DeleteData } from './http/http.utils';
|
|
|
16
14
|
|
|
17
15
|
import { getExportData } from './generic/generic.utils';
|
|
18
16
|
|
|
19
|
-
import { ConvertBytesToArray } from './common/common.utils';
|
|
17
|
+
import { ConvertBytesToArray, safeJSON, formatPhoneForForm, phoneValidator, formatPhoneWithCountryCode } from './common/common.utils';
|
|
20
18
|
|
|
21
19
|
import SettingsUtil from './setting.utils';
|
|
22
20
|
|
|
23
|
-
|
|
24
21
|
export {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
22
|
+
// FirebaseUtils,
|
|
23
|
+
|
|
24
|
+
Notification,
|
|
25
|
+
Location,
|
|
26
|
+
DateUtils,
|
|
27
|
+
ApiUtils,
|
|
28
|
+
UploadUtils,
|
|
29
|
+
GetData,
|
|
30
|
+
PostData,
|
|
31
|
+
PutData,
|
|
32
|
+
DeleteData,
|
|
33
|
+
getExportData,
|
|
34
|
+
|
|
35
|
+
// Common Functions
|
|
36
|
+
ConvertBytesToArray,
|
|
37
|
+
SettingsUtil,
|
|
38
|
+
FormUtils,
|
|
39
|
+
safeJSON,
|
|
40
|
+
formatPhoneForForm,
|
|
41
|
+
phoneValidator,
|
|
42
|
+
formatPhoneWithCountryCode,
|
|
43
|
+
};
|
package/core/models/base/base.js
CHANGED
|
@@ -88,9 +88,13 @@ class BaseAPI {
|
|
|
88
88
|
* Get the data from the table
|
|
89
89
|
*/
|
|
90
90
|
get(config = {}) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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) {
|
|
@@ -6,7 +6,8 @@ import { useTranslation, Button } from './../../../../lib/';
|
|
|
6
6
|
import { UsersAPI } from '../../..';
|
|
7
7
|
import SignatureCanvasComponent from '../../../../lib/components/consent/signature-pad';
|
|
8
8
|
|
|
9
|
-
const DoctorAdd = ({ visible, onCancel,
|
|
9
|
+
const DoctorAdd = ({ visible, onCancel, doctorId, doctorData, onSuccess }) => {
|
|
10
|
+
// console.log('attributes', attributes);
|
|
10
11
|
const [form] = Form.useForm();
|
|
11
12
|
const { t } = useTranslation();
|
|
12
13
|
|
|
@@ -21,8 +22,8 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
|
|
|
21
22
|
const [doctorID, setDoctorID] = useState(false);
|
|
22
23
|
const [selectedDoctor, setSelectedDoctor] = useState(null);
|
|
23
24
|
|
|
24
|
-
const propValues = attributes || {};
|
|
25
|
-
const doctorType = propValues?.type;
|
|
25
|
+
// const propValues = attributes || {};
|
|
26
|
+
// const doctorType = propValues?.type;
|
|
26
27
|
|
|
27
28
|
const editMode = Boolean(doctorId);
|
|
28
29
|
|
|
@@ -37,6 +38,10 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
|
|
|
37
38
|
message: '',
|
|
38
39
|
});
|
|
39
40
|
|
|
41
|
+
const getDoctorTypeOptions = () => [
|
|
42
|
+
{ label: 'In House', value: 'DRINH' },
|
|
43
|
+
{ label: 'Out Side', value: 'DROTR' },
|
|
44
|
+
];
|
|
40
45
|
/** Autofocus on modal open */
|
|
41
46
|
useEffect(() => {
|
|
42
47
|
if (visible) {
|
|
@@ -266,7 +271,7 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
|
|
|
266
271
|
<Row gutter={16}>
|
|
267
272
|
<Col span={8}>
|
|
268
273
|
<Form.Item label="Type" name="type">
|
|
269
|
-
<Select placeholder="Select Type" options={
|
|
274
|
+
<Select placeholder="Select Type" options={getDoctorTypeOptions() || []} showSearch />
|
|
270
275
|
</Form.Item>
|
|
271
276
|
</Col>
|
|
272
277
|
|
|
@@ -164,7 +164,7 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
164
164
|
<div className="left-container">
|
|
165
165
|
{/* Caption */}
|
|
166
166
|
<Form.Item name={'caption'} label="Caption" rules={[{ required: true, message: 'Caption is required' }]}>
|
|
167
|
-
<Input placeholder="Enter caption" />
|
|
167
|
+
<Input placeholder="Enter caption" autoFocus />
|
|
168
168
|
</Form.Item>
|
|
169
169
|
{/* Caption Ends */}
|
|
170
170
|
|