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.
Files changed (57) hide show
  1. package/.github/workflows/npm-publish.yml +49 -19
  2. package/core/components/extra-info/extra-info-details.js +2 -2
  3. package/core/components/menu-template-api/menu-template-api.js +2 -2
  4. package/core/lib/Store.js +3 -3
  5. package/core/lib/components/global-header/global-header.js +2 -2
  6. package/core/lib/components/sidemenu/sidemenu.js +19 -13
  7. package/core/lib/elements/basic/country-phone-input/country-phone-input.js +35 -60
  8. package/core/lib/elements/basic/country-phone-input/phone-input.scss +14 -0
  9. package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
  10. package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
  11. package/core/lib/models/forms/components/form-creator/form-creator.js +468 -502
  12. package/core/lib/models/forms/components/form-creator/form-creator.scss +5 -4
  13. package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
  14. package/core/lib/pages/change-password/change-password.js +17 -24
  15. package/core/lib/pages/change-password/change-password.scss +45 -48
  16. package/core/lib/pages/login/commnication-mode-selection.js +46 -0
  17. package/core/lib/pages/login/communication-mode-selection.scss +60 -0
  18. package/core/lib/pages/login/login.js +153 -24
  19. package/core/lib/pages/login/login.scss +229 -334
  20. package/core/lib/pages/login/reset-password.js +124 -0
  21. package/core/lib/pages/login/reset-password.scss +31 -0
  22. package/core/lib/pages/profile/themes.json +4 -4
  23. package/core/lib/utils/api/api.utils.js +71 -48
  24. package/core/lib/utils/common/common.utils.js +109 -0
  25. package/core/lib/utils/http/http.utils.js +1 -0
  26. package/core/lib/utils/index.js +25 -28
  27. package/core/models/base/base.js +7 -3
  28. package/core/models/core-scripts/core-scripts.js +9 -0
  29. package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
  30. package/core/models/menus/components/menu-add/menu-add.js +1 -1
  31. package/core/models/menus/components/menu-lists/menu-lists.js +5 -9
  32. package/core/models/menus/menus.js +21 -2
  33. package/core/models/roles/components/role-add/role-add.js +92 -59
  34. package/core/models/roles/components/role-list/role-list.js +1 -1
  35. package/core/models/roles/roles.js +9 -0
  36. package/core/models/staff/components/staff-add/staff-add.js +20 -32
  37. package/core/models/users/components/assign-role/assign-role.js +145 -50
  38. package/core/models/users/components/assign-role/assign-role.scss +209 -45
  39. package/core/models/users/components/assign-role/avatar-props.js +45 -0
  40. package/core/models/users/components/user-add/user-add.js +47 -56
  41. package/core/models/users/components/user-add/user-edit.js +25 -4
  42. package/core/models/users/users.js +34 -8
  43. package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
  44. package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
  45. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +120 -0
  46. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +75 -0
  47. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
  48. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +252 -0
  49. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
  50. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +222 -376
  51. package/core/modules/steps/action-buttons.js +47 -45
  52. package/core/modules/steps/action-buttons.scss +35 -6
  53. package/core/modules/steps/steps.js +12 -10
  54. package/core/modules/steps/steps.scss +229 -31
  55. package/core/modules/steps/timeline.js +21 -19
  56. package/package.json +3 -2
  57. package/core/components/external-window/DEVELOPER_GUIDE.md +0 -705
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useCallback, useEffect } from 'react';
2
- import { Space, Popconfirm, Input, Drawer, Skeleton, Collapse, message } from 'antd';
2
+ import { Space, Popconfirm, Input, Drawer, Skeleton, Collapse, message, Tag } from 'antd';
3
3
  import { ReloadOutlined, DeleteOutlined, EditOutlined, PlusCircleFilled, CopyOutlined } from '@ant-design/icons';
4
4
  import { Button, Card, Switch, DraggableWrapper } from '../../../../lib';
5
5
  // for draggable menu list import { DndProvider } from "react-dnd";
@@ -55,7 +55,6 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
55
55
  .catch(() => setLoading(false));
56
56
  };
57
57
 
58
- console.log('record', records);
59
58
  /**
60
59
  *
61
60
  */
@@ -206,7 +205,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
206
205
 
207
206
  const filtered = records.filter((r) => r.name?.toUpperCase().includes(query.toUpperCase()));
208
207
 
209
- const visibleItems = dragMode ? records : filtered;
208
+ const visibleItems = filtered;
210
209
 
211
210
  const onSearch = (event) => {
212
211
  setQuery(event.target.value);
@@ -440,7 +439,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
440
439
  function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord) {
441
440
  return (
442
441
  <Space onClick={(e) => e.stopPropagation()}>
443
- {/* NEW BUTTON Add Submenu */}
442
+ <Tag color={item.is_visible === true ? 'green' : 'blue'}>{item.is_visible === true ? 'VISIBLE' : 'HIDDEN'}</Tag>{' '}
444
443
  <Button
445
444
  size="small"
446
445
  type="dashed"
@@ -459,7 +458,6 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
459
458
  <PlusCircleFilled />
460
459
  Add Sub Menu
461
460
  </Button>
462
-
463
461
  {model.ModalAddComponent && (
464
462
  <Button
465
463
  size="small"
@@ -473,8 +471,7 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
473
471
  <EditOutlined />
474
472
  </Button>
475
473
  )}
476
-
477
- {/* <Button
474
+ <Button
478
475
  size="small"
479
476
  type="default"
480
477
  onClick={() => {
@@ -484,8 +481,7 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
484
481
  }}
485
482
  >
486
483
  <CopyOutlined />
487
- </Button> */}
488
-
484
+ </Button>
489
485
  <Popconfirm title="Are you sure?" onConfirm={() => deleteRecord(item)}>
490
486
  <Button danger size="small" type="default">
491
487
  <DeleteOutlined />
@@ -177,28 +177,47 @@ class MenusAPI extends Base {
177
177
  * @param {*} menu
178
178
  * @returns
179
179
  */
180
- getMenus = (config) => {
180
+ getMenus = (config, dbPtr = null) => {
181
181
  // Use 'core-menus' endpoint if REACT_APP_USE_CORE_MENUS is true (used for Matria)
182
182
  const url =
183
183
  process.env.REACT_APP_USE_CORE_MENUS === 'true'
184
184
  ? 'core-menus/get-menus' // Matria
185
185
  : 'menus/get-menus'; // NURA
186
186
 
187
+ if (!dbPtr) dbPtr = localStorage.db_ptr;
188
+
187
189
  return this.get({
188
190
  url,
189
191
  config,
192
+ headers: {
193
+ db_ptr: dbPtr,
194
+ },
195
+ }).then((result) => result);
196
+ };
197
+
198
+ getMenubyUser = (user_id) => {
199
+ const url = `menus/get-menus?userId=${user_id}`;
200
+ return this.get({
201
+ url,
190
202
  }).then((result) => result);
191
203
  };
192
204
 
193
205
  // get core-menu list with submenu
194
206
  getCoreMenuLists = () => {
195
207
  const url = 'core-menus/core-menus?step=1&header_id=null';
196
-
197
208
  return this.get({
198
209
  url,
199
210
  }).then((result) => result);
200
211
  };
201
212
 
213
+ getCoreMenuByRoleId = async (role_id) => {
214
+ const url = `menus/get-menus-by-role/${role_id}`;
215
+ const result = await this.get({
216
+ url,
217
+ });
218
+ return result;
219
+ };
220
+
202
221
  getCoreMenus = () => {
203
222
  return [
204
223
  // {
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useEffect, useContext } from 'react';
2
2
  import { useLocation, useParams } from 'react-router-dom';
3
- import { Skeleton, Typography, message, Form, Input, Collapse, Checkbox } from 'antd';
3
+ import { Skeleton, Typography, message, Form, Input, Collapse, Checkbox, Tag } from 'antd';
4
4
  import { GlobalContext } from './../../../../lib';
5
5
  import { Button } from './../../../../lib';
6
6
  import { ModelsAPI, PagesAPI, RolesAPI, MenusAPI } from '../../..';
@@ -29,7 +29,9 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
29
29
  formContent.attributes = {};
30
30
  }
31
31
 
32
- const [loading, setLoading] = useState(true);
32
+ const isEdit = !!(formContent.id || formContent.copy);
33
+ const [initialLoading, setInitialLoading] = useState(isEdit);
34
+ const [loading, setLoading] = useState(false);
33
35
  const [form] = Form.useForm();
34
36
  const [pages, setPages] = useState([]);
35
37
  const [models, setModels] = useState([]);
@@ -49,36 +51,43 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
49
51
 
50
52
  // On component mount
51
53
  useEffect(() => {
52
- getPages();
53
- getModels();
54
- loadMenus();
55
- setShowMenus(true);
56
-
57
- // Preselect menus when editing
58
- if (formContent && formContent.menu_ids) {
59
- let menus = formContent.menu_ids;
60
-
61
- setSelectedMenus(menus);
62
- // keep original copy for deselect comparison
63
- setOriginalMenus(menus);
64
- }
54
+ const fetchData = async () => {
55
+ if (isEdit) setInitialLoading(true);
56
+ try {
57
+ await Promise.all([getPages(), getModels(), loadMenus()]);
58
+ setShowMenus(true);
59
+
60
+ // Preselect menus when editing
61
+ if (formContent && formContent.menu_ids) {
62
+ let menus = formContent.menu_ids;
63
+
64
+ setSelectedMenus(menus);
65
+ // keep original copy for deselect comparison
66
+ setOriginalMenus(menus);
67
+ }
68
+ } catch (error) {
69
+ console.error('Error fetching data:', error);
70
+ } finally {
71
+ setInitialLoading(false);
72
+ }
73
+ };
65
74
 
66
- setLoading(false);
67
- }, [formContent]);
75
+ fetchData();
76
+ }, [formContent, isEdit]);
68
77
 
69
78
  // Load pages
70
79
  const getPages = () => {
71
- PagesAPI.getPages().then((res) => setPages(res.result || []));
80
+ return PagesAPI.getPages().then((res) => setPages(res.result || []));
72
81
  };
73
82
 
74
83
  // Load models
75
84
  const getModels = () => {
76
- ModelsAPI.get().then((res) => setModels(res.result || []));
85
+ return ModelsAPI.get().then((res) => setModels(res.result || []));
77
86
  };
78
87
 
79
88
  // Load top-level menus
80
89
  const loadMenus = () => {
81
- MenusAPI.getCoreMenuLists()
90
+ return MenusAPI.getCoreMenuLists()
82
91
  .then((res) => setMenuList(res.result || []))
83
92
  .catch(console.error);
84
93
  };
@@ -156,13 +165,13 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
156
165
 
157
166
  return (
158
167
  <section className="collection-add">
159
- {loading ? (
160
- <Skeleton />
168
+ {initialLoading ? (
169
+ <Skeleton active paragraph={{ rows: 12 }} />
161
170
  ) : (
162
171
  <Form initialValues={{ ...formContent }} form={form} layout="vertical" onFinish={onSubmit}>
163
172
  {/* Role Name */}
164
173
  <Form.Item name="name" label="Enter Role Name" rules={[{ required: true, message: 'Role name is required' }]}>
165
- <Input placeholder="Enter name" />
174
+ <Input placeholder="Enter name" autoocus />
166
175
  </Form.Item>
167
176
 
168
177
  {/* Description */}
@@ -180,19 +189,31 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
180
189
  {/* MENU TREE */}
181
190
  {showMenus && menuList.length > 0 && (
182
191
  <div style={{ marginTop: 30 }}>
183
- <Title level={5}>Menu List</Title>
184
- <p style={{ color: '#999' }}>Choose menus and set permissions</p>
192
+ <div
193
+ style={{
194
+ display: 'flex',
195
+ justifyContent: 'space-between',
196
+ alignItems: 'center',
197
+ marginBottom: 16,
198
+ }}
199
+ >
200
+ <div>
201
+ <Title level={5} style={{ marginBottom: 0 }}>
202
+ Menu List
203
+ </Title>
204
+ <p style={{ color: '#999', marginBottom: 0 }}>Choose menus and set permissions</p>
205
+ </div>
206
+
207
+ <Form.Item style={{ marginBottom: 0 }}>
208
+ <Button loading={loading} htmlType="submit" type="primary">
209
+ Save
210
+ </Button>
211
+ </Form.Item>
212
+ </div>
185
213
 
186
214
  <MenuTree menus={menuList} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
187
215
  </div>
188
216
  )}
189
-
190
- {/* Submit Button */}
191
- <Form.Item style={{ marginTop: 20 }}>
192
- <Button loading={loading} htmlType="submit" type="primary">
193
- Save
194
- </Button>
195
- </Form.Item>
196
217
  </Form>
197
218
  )}
198
219
  </section>
@@ -248,32 +269,41 @@ const MenuTree = ({ menus, selectedMenus, toggleMenu, parentId = null }) => {
248
269
  if (children.length === 0) {
249
270
  return (
250
271
  <div
251
- key={menu.id}
252
272
  style={{
273
+ justifyContent: 'space-between',
274
+ display: 'flex',
275
+ alignItems: 'center',
253
276
  border: '1px solid rgba(198, 195, 195, 0.85)',
254
277
  // borderRadius: 6,
255
278
  padding: '12px 16px',
256
279
  marginBottom: 6,
257
280
  background: '#fff',
258
- display: 'flex',
259
- alignItems: 'center',
260
- gap: 8,
261
281
  }}
262
282
  >
263
- <Checkbox
264
- checked={selectedMenus.includes(menu.id)}
265
- onChange={(e) => {
266
- const checked = e.target.checked;
267
-
268
- toggleMenu(menu.id, checked);
269
-
270
- // ✅ FORCE parent selection
271
- if (checked && parentId) {
272
- toggleMenu(parentId, true);
273
- }
283
+ <div
284
+ key={menu.id}
285
+ style={{
286
+ display: 'flex',
287
+ alignItems: 'center',
288
+ gap: 8,
274
289
  }}
275
- />
276
- <span>{menu.title || menu.caption}</span>
290
+ >
291
+ <Checkbox
292
+ checked={selectedMenus.includes(menu.id)}
293
+ onChange={(e) => {
294
+ const checked = e.target.checked;
295
+
296
+ toggleMenu(menu.id, checked);
297
+
298
+ // FORCE parent selection
299
+ if (checked && parentId) {
300
+ toggleMenu(parentId, true);
301
+ }
302
+ }}
303
+ />
304
+ <span>{menu.caption}</span>
305
+ </div>
306
+ <Tag color={menu.is_visible === true ? 'green' : 'blue'}>{menu.is_visible === true ? 'VISIBLE' : 'HIDDEN'}</Tag>{' '}
277
307
  </div>
278
308
  );
279
309
  }
@@ -287,16 +317,19 @@ const MenuTree = ({ menus, selectedMenus, toggleMenu, parentId = null }) => {
287
317
  <Panel
288
318
  key={menu.id}
289
319
  header={
290
- <div
291
- style={{
292
- display: 'flex',
293
- alignItems: 'center',
294
- gap: 8,
295
- }}
296
- onClick={(e) => e.stopPropagation()}
297
- >
298
- <Checkbox checked={parentChecked} indeterminate={parentIndeterminate} onChange={(e) => onParentChange(e.target.checked)} />
299
- <span>{menu.title || menu.caption}</span>
320
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
321
+ <div
322
+ style={{
323
+ display: 'flex',
324
+ alignItems: 'center',
325
+ gap: 8,
326
+ }}
327
+ onClick={(e) => e.stopPropagation()}
328
+ >
329
+ <Checkbox checked={parentChecked} indeterminate={parentIndeterminate} onChange={(e) => onParentChange(e.target.checked)} />
330
+ <span>{menu.caption}</span>
331
+ </div>
332
+ <Tag color={menu.is_visible === true ? 'green' : 'blue'}>{menu.is_visible === true ? 'VISIBLE' : 'HIDDEN'}</Tag>{' '}
300
333
  </div>
301
334
  }
302
335
  >
@@ -331,7 +331,7 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
331
331
  {/* Table Header Ends */}
332
332
 
333
333
  {loading ? (
334
- <Skeleton />
334
+ <Skeleton active paragraph={{ rows: 10 }} />
335
335
  ) : (
336
336
  <>
337
337
  <>
@@ -155,6 +155,15 @@ class RolesAPI extends BaseAPI {
155
155
 
156
156
  /**
157
157
  * Cutsom Api for creating user
158
+ getRole = () => {
159
+ return ApiUtils.get({
160
+ url: `core-roles/get-core-roles?active=Y`,
161
+ });
162
+ };
163
+
164
+
165
+ /**
166
+ * Cutsom Api for updating user
158
167
  *
159
168
  * @param {*} values
160
169
  * @returns
@@ -1,8 +1,9 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
2
 
3
3
  import { Modal, Tabs, Input, Form, Row, Col, message, Checkbox, Select } from 'antd';
4
- import { useTranslation, Button } from './../../../../lib/';
4
+ import { useTranslation, Button, CountryPhoneInput, phoneValidator as libPhoneValidator, formatPhoneForForm } from './../../../../lib/';
5
5
  import { UsersAPI } from '../../..';
6
+ import { formatPhoneWithCountryCode } from '../../../../lib/utils/common/common.utils';
6
7
 
7
8
  const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
8
9
  const [form] = Form.useForm();
@@ -61,18 +62,6 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
61
62
  }
62
63
  }, [visible]);
63
64
 
64
- const phoneValidator = (_, value) => {
65
- if (!value) {
66
- return Promise.resolve(); // not required
67
- }
68
-
69
- if (!/^\d{10}$/.test(value)) {
70
- return Promise.reject('Phone number must be 10 digits');
71
- }
72
-
73
- return Promise.resolve();
74
- };
75
-
76
65
  /** -------------------------------
77
66
  * API CALL – Designations
78
67
  * ------------------------------- */
@@ -201,8 +190,8 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
201
190
  shortName: data.shortName,
202
191
  description: data.description,
203
192
  designation: data.designationPtr,
204
- phone1: data.phone || data.mobile,
205
- phone2: data.alternateMobile,
193
+ phone1: formatPhoneForForm(data.phone || data.mobile),
194
+ phone2: formatPhoneForForm(data.alternateMobile),
206
195
  email1: data.email,
207
196
  email2: data.alternateEmail,
208
197
  slno: data.slNo,
@@ -223,14 +212,17 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
223
212
  const handleFinish = async (values) => {
224
213
  setLoading(true);
225
214
 
215
+ let phone1Value = formatPhoneWithCountryCode(values.phone1);
216
+ let phone2Value = formatPhoneWithCountryCode(values.phone2);
217
+
226
218
  const payload = {
227
219
  id: values.id,
228
220
  shortName: values.shortName,
229
221
  description: values.description,
230
222
  designationPtr: values.designation ?? existing.designationPtr ?? null,
231
- phone: values.phone1 ?? existing.phone ?? null,
232
- mobile: values.phone1 ?? existing.mobile ?? null,
233
- alternateMobile: values.phone2 ?? existing.alternateMobile ?? null,
223
+ phone: phone1Value ?? existing.phone ?? null,
224
+ mobile: phone1Value ?? existing.mobile ?? null,
225
+ alternateMobile: phone2Value ?? existing.alternateMobile ?? null,
234
226
  email: values.email1 ?? existing.email ?? null,
235
227
  alternateEmail: values.email2 ?? existing.alternateEmail ?? null,
236
228
  remarks: values.remarks ?? existing.remarks ?? null,
@@ -331,26 +323,22 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
331
323
  </Form.Item>
332
324
  </Col> */}
333
325
  <Col span={6}>
334
- <Form.Item label="Phone Number" name="phone1" rules={[{ validator: phoneValidator }]}>
335
- <Input
336
- maxLength={10}
337
- autoComplete="off"
338
- placeholder="Enter Phone Number"
339
- inputMode="numeric"
340
- onChange={(e) => (e.target.value = e.target.value.replace(/\D/g, ''))}
326
+ <Form.Item label="Phone Number" name="phone1" validateTrigger="onBlur" rules={[{ validator: libPhoneValidator }]}>
327
+ <CountryPhoneInput
328
+ defaultCountryCode={process.env.REACT_APP_COUNTRYCODE}
329
+ enableSearch
330
+ inputStyle={{ width: '100%' }}
341
331
  onKeyDown={handleEnterKey}
342
332
  />
343
333
  </Form.Item>
344
334
  </Col>
345
335
 
346
336
  <Col span={6}>
347
- <Form.Item label="Alternate Phone Number" name="phone2" rules={[{ validator: phoneValidator }]}>
348
- <Input
349
- maxLength={10}
350
- placeholder="Enter Phone Number"
351
- autoComplete="off"
352
- inputMode="numeric"
353
- onChange={(e) => (e.target.value = e.target.value.replace(/\D/g, ''))}
337
+ <Form.Item label="Alternate Phone Number" name="phone2" validateTrigger="onBlur" rules={[{ validator: libPhoneValidator }]}>
338
+ <CountryPhoneInput
339
+ defaultCountryCode={process.env.REACT_APP_COUNTRYCODE}
340
+ enableSearch
341
+ inputStyle={{ width: '100%' }}
354
342
  onKeyDown={handleEnterKey}
355
343
  />
356
344
  </Form.Item>