ui-soxo-bootstrap-core 2.4.24 → 2.4.25-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 (36) hide show
  1. package/.github/workflows/npm-publish.yml +37 -15
  2. package/core/components/extra-info/extra-info-details.js +109 -126
  3. package/core/components/landing-api/landing-api.js +22 -30
  4. package/core/lib/Store.js +20 -18
  5. package/core/lib/components/index.js +4 -1
  6. package/core/lib/components/sidemenu/sidemenu.js +153 -256
  7. package/core/lib/components/sidemenu/sidemenu.scss +39 -26
  8. package/core/lib/elements/basic/rangepicker/rangepicker.js +118 -29
  9. package/core/lib/elements/basic/switch/switch.js +34 -24
  10. package/core/lib/hooks/index.js +2 -12
  11. package/core/lib/hooks/use-otp-timer.js +99 -0
  12. package/core/lib/pages/login/login.js +255 -139
  13. package/core/lib/pages/login/login.scss +140 -32
  14. package/core/models/dashboard/dashboard.js +14 -0
  15. package/core/models/doctor/components/doctor-add/doctor-add.js +403 -0
  16. package/core/models/doctor/components/doctor-add/doctor-add.scss +32 -0
  17. package/core/models/menus/components/menu-add/menu-add.js +230 -268
  18. package/core/models/menus/components/menu-lists/menu-lists.js +126 -89
  19. package/core/models/menus/components/menu-lists/menu-lists.scss +9 -0
  20. package/core/models/menus/menus.js +247 -267
  21. package/core/models/roles/components/role-add/role-add.js +269 -227
  22. package/core/models/roles/components/role-list/role-list.js +8 -6
  23. package/core/models/roles/roles.js +182 -174
  24. package/core/models/staff/components/staff-add/staff-add.js +352 -0
  25. package/core/models/staff/components/staff-add/staff-add.scss +0 -0
  26. package/core/models/users/components/user-add/user-add.js +686 -364
  27. package/core/models/users/components/user-add/user-edit.js +90 -0
  28. package/core/models/users/users.js +318 -165
  29. package/core/modules/index.js +5 -8
  30. package/core/modules/reporting/components/index.js +5 -0
  31. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +65 -2
  32. package/core/modules/steps/action-buttons.js +79 -0
  33. package/core/modules/steps/steps.js +553 -0
  34. package/core/modules/steps/steps.scss +158 -0
  35. package/core/modules/steps/timeline.js +49 -0
  36. package/package.json +2 -2
@@ -1,248 +1,290 @@
1
1
  import React, { useState, useEffect, useContext } from 'react';
2
- import { useLocation,useParams } from 'react-router-dom';
3
- import { Skeleton, Typography, message, Form, Input, Select, Collapse, Checkbox } from 'antd';
4
- import { Table, Card, Button, JSONInput, Switch, GlobalContext } from './../../../../lib';
5
-
6
- import { ModelsAPI, PagesAPI, RolesAPI,MenusAPI } from '../../..';
2
+ import { useLocation, useParams } from 'react-router-dom';
3
+ import { Skeleton, Typography, message, Form, Input, Collapse, Checkbox } from 'antd';
4
+ import { GlobalContext } from './../../../../lib';
5
+ import {Button} from './../../../../lib';
6
+ import { ModelsAPI, PagesAPI, RolesAPI, MenusAPI } from '../../..';
7
7
 
8
8
  const { Title } = Typography;
9
- const { Option } = Select;
10
9
  const { Panel } = Collapse;
11
10
 
12
- const RoleAdd = ({ model, callback, edit, history, formContent, match, additional_queries, ...props }) => {
13
-
14
- let mode = 'Add';
15
- if (formContent.id) mode = 'Edit';
16
- else if (formContent.copy) mode = 'copy';
17
-
18
- // Parse attributes safely
19
- if (formContent.attributes) {
20
- if (typeof formContent.attributes === 'string' && formContent.attributes !== '') {
21
- formContent.attributes = JSON.parse(formContent.attributes);
22
- } else {
23
- formContent.attributes = {};
24
- }
25
- } else {
11
+ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_queries = [] }) => {
12
+ let mode = 'Add';
13
+ if (formContent.id) mode = 'Edit';
14
+ else if (formContent.copy) mode = 'copy';
15
+
16
+ // Parse attributes safely
17
+ if (formContent.attributes) {
18
+ if (typeof formContent.attributes === 'string' && formContent.attributes !== '') {
19
+ try {
20
+ formContent.attributes = JSON.parse(formContent.attributes);
21
+ } catch {
26
22
  formContent.attributes = {};
23
+ }
24
+ } else {
25
+ formContent.attributes = {};
27
26
  }
28
-
29
- const [loading, setLoading] = useState(true);
30
- const [form] = Form.useForm();
31
- const [pages, setPages] = useState([]);
32
- const [models, setModels] = useState([]);
33
- const [body, setBody] = useState(formContent);
34
-
35
- const [showMenus, setShowMenus] = useState(false);
36
- const [menuList, setMenuList] = useState([]);
37
-
38
- const { params } = match;
39
- const { dispatch, user = {} } = useContext(GlobalContext);
40
-
41
- const location = useLocation();
42
- const query = new URLSearchParams(location.search);
43
-
44
- let step = parseInt(query.get('step')) || 1;
45
-
46
- match = useParams();
47
- const id = match.id;
48
-
49
- useEffect(() => {
50
- getPages();
51
- getModels();
52
- loadMenus();
53
-
54
- setLoading(false);
55
- }, []);
56
- useEffect(() => {
57
-
58
- loadMenus();
59
- setShowMenus(true)
60
-
61
- // setLoading(false);
62
- }, [id]);
63
-
64
- // Load pages
65
- const getPages = () => {
66
- PagesAPI.getPages().then((result) => {
67
- setPages(result.result);
68
- });
69
- };
70
-
71
- // Load models
72
- const getModels = () => {
73
- ModelsAPI.get().then((result) => {
74
- console.log(result);
75
- setModels(result.result);
76
- });
77
- };
78
-
79
- // Load menus dynamically
80
- const loadMenus = () => {
81
- MenusAPI.get({
82
- queries: [
83
- { field: 'step', value: 1 },
84
- { field: 'header_id', value: null }
85
- ]
86
- })
87
- .then(res => setMenuList(res.result || []))
88
- .catch(console.error);
27
+ } else {
28
+ formContent.attributes = {};
29
+ }
30
+
31
+ const [loading, setLoading] = useState(true);
32
+ const [form] = Form.useForm();
33
+ const [pages, setPages] = useState([]);
34
+ const [models, setModels] = useState([]);
35
+ const [menuList, setMenuList] = useState([]);
36
+ const [showMenus, setShowMenus] = useState(false);
37
+ const [originalMenus, setOriginalMenus] = useState([]);// for deselected menu for editing
38
+
39
+
40
+ // Selected menus (array of IDs)
41
+ const [selectedMenus, setSelectedMenus] = useState([]);
42
+
43
+ const location = useLocation();
44
+ const query = new URLSearchParams(location.search);
45
+ let step = parseInt(query.get('step')) || 1;
46
+
47
+ const { user = {} } = useContext(GlobalContext);
48
+
49
+ // On component mount
50
+ useEffect(() => {
51
+ getPages();
52
+ getModels();
53
+ loadMenus();
54
+ setShowMenus(true);
55
+
56
+ // Preselect menus when editing
57
+ if (formContent && formContent.menu_ids) {
58
+ let menus = formContent.menu_ids;
59
+
60
+ setSelectedMenus(menus);
61
+ setOriginalMenus(menus); // keep original copy for deselect comparison
62
+ }
63
+
64
+ setLoading(false);
65
+ }, [formContent]);
66
+
67
+
68
+
69
+ // Load pages
70
+ const getPages = () => {
71
+ PagesAPI.getPages().then((res) => setPages(res.result || []));
72
+ };
73
+
74
+ // Load models
75
+ const getModels = () => {
76
+ ModelsAPI.get().then((res) => setModels(res.result || []));
77
+ };
78
+
79
+ // Load top-level menus
80
+ const loadMenus = () => {
81
+ MenusAPI.get({
82
+ queries: [
83
+ { field: 'step', value: 1 },
84
+ { field: 'header_id', value: null },
85
+ ],
86
+ })
87
+ .then((res) => setMenuList(res.result || []))
88
+ .catch(console.error);
89
+ };
90
+
91
+ // Toggle menu selection
92
+ const toggleMenu = (id, checked) => {
93
+ setSelectedMenus((prev) =>
94
+ checked ? [...prev, id] : prev.filter((m) => m !== id)
95
+ );
96
+ };
97
+
98
+ // Submit handler
99
+ // const onSubmit = (values) => {
100
+ // setLoading(true);
101
+
102
+ // const payload = {
103
+ // ...values,
104
+ // menu_ids
105
+ // : selectedMenus,
106
+ // attributes: JSON.stringify(values.attributes || {}),
107
+ // };
108
+
109
+ // if (formContent.id) {
110
+ // RolesAPI.updateRole({ id: formContent.id, formBody: payload })
111
+ // .then(() => {
112
+ // message.success('Role Updated');
113
+ // setLoading(false);
114
+ // callback();
115
+ // })
116
+ // .catch(() => setLoading(false));
117
+ // } else {
118
+ // additional_queries.forEach(({ field, value }) => {
119
+ // payload[field] = value;
120
+ // });
121
+ // RolesAPI.createRole(payload)
122
+ // .then(() => {
123
+ // message.success('Role Added');
124
+ // setLoading(false);
125
+ // callback();
126
+ // })
127
+ // .catch(() => setLoading(false));
128
+ // }
129
+ // };
130
+ const onSubmit = async (values) => {
131
+ setLoading(true);
132
+
133
+
134
+ // Find menus that were originally selected but now deselected
135
+ const deselectedMenus = originalMenus.filter(
136
+ (id) => !selectedMenus.includes(id)
137
+ );
138
+
139
+ const payload = {
140
+ ...values,
141
+ menu_ids: selectedMenus,
142
+ deselect_array: deselectedMenus, // add deselected menus when edit time
143
+ attributes: JSON.stringify(values.attributes || {}),
144
+ };
145
+
146
+ // include id ONLY for edit
147
+ if (formContent?.id) {
148
+ payload.id = formContent.id;
149
+ }
150
+
151
+ // include additional queries for both cases
152
+ additional_queries.forEach(({ field, value }) => {
153
+ payload[field] = value;
154
+ });
155
+
156
+ try {
157
+ await RolesAPI.createRole(payload); // single API
158
+ message.success(formContent?.id ? 'Role Updated' : 'Role Added');
159
+ callback();
160
+ } catch (err) {
161
+ // message.error('Something went wrong');
162
+ } finally {
163
+ setLoading(false);
164
+ }
89
165
  };
90
166
 
91
-
92
167
 
93
- // Submit
94
- const onSubmit = (values) => {
95
- setLoading(true);
96
- let id = formContent.id;
97
-
98
- if (values.attributes && typeof values === 'object') {
99
- values = {
100
- ...values,
101
- attributes: JSON.stringify(values.attributes)
102
- };
103
- }
104
-
105
- if (id) {
106
- RolesAPI.updateRole({ id, formBody: values }).then(() => {
107
- message.success('Role Updated');
108
- setLoading(false);
109
- callback();
110
- });
111
- } else {
112
- additional_queries.forEach(({ field, value }) => {
113
- values[field] = value;
114
- });
115
-
116
- RolesAPI.createRole(values).then(() => {
117
- message.success('Role Added');
118
- setLoading(false);
119
- callback();
120
- });
121
- }
122
- };
123
-
124
- return (
125
- <section className="collection-add">
126
-
127
- {loading ? (
128
- <Skeleton />
129
- ) : (
130
- <Form initialValues={{ ...body }} form={form} layout="vertical" onFinish={onSubmit}>
131
-
132
- {/* Role Name */}
133
- <Form.Item name="name" label="Enter Role Name" required>
134
- <Input placeholder="Enter name" />
135
- </Form.Item>
136
- {/* Name */}
137
- {/* <Form.Item name={"identifier"} label="Identifier" required>
138
- <Input placeholder="Enter identifier" />
139
- </Form.Item> */}
140
- {/* Name Ends */}
141
-
142
- {/* Path */}
143
- {/* <Form.Item name="weight" label="Weight" required>
144
- <Input placeholder="Enter weight" />
145
- </Form.Item> */}
146
- {/* Path Ends */}
147
-
148
- {/* Description */}
149
- <Form.Item name="description" label="Enter Description" required>
150
- <Input placeholder="Enter description" />
151
- </Form.Item>
152
-
153
- {/* MENUS COLLAPSE */}
154
- {showMenus && menuList.length > 0 && (
155
- <div style={{ marginTop: 30 }}>
156
- <Title level={5} style={{ marginBottom: 16 }}>Menu List</Title>
157
- <p style={{ marginTop: -10, marginBottom: 20, color: "#999" }}>
158
- Choose menus and set permissions
159
- </p>
160
-
161
- <Collapse expandIconPosition="left">
162
- {menuList.map(item => (
163
- <Panel
168
+
169
+ return (
170
+ <section className="collection-add">
171
+ {loading ? (
172
+ <Skeleton />
173
+ ) : (
174
+ <Form
175
+ initialValues={{ ...formContent }}
176
+ form={form}
177
+ layout="vertical"
178
+ onFinish={onSubmit}
179
+ >
180
+ {/* Role Name */}
181
+ <Form.Item name="name" label="Enter Role Name" required>
182
+ <Input placeholder="Enter name" />
183
+ </Form.Item>
184
+
185
+ {/* Description */}
186
+ <Form.Item name="description" label="Enter Description" required>
187
+ <Input placeholder="Enter description" />
188
+ </Form.Item>
189
+
190
+ {/* MENUS COLLAPSE */}
191
+ {showMenus && menuList.length > 0 && (
192
+ <div style={{ marginTop: 30 }}>
193
+ <Title level={5} style={{ marginBottom: 16 }}>
194
+ Menu List
195
+ </Title>
196
+ <p style={{ marginTop: -10, marginBottom: 20, color: '#999' }}>
197
+ Choose menus and set permissions
198
+ </p>
199
+
200
+ <Collapse expandIconPosition="left">
201
+ {menuList.map((item) => (
202
+ <Panel
164
203
  key={item.id}
165
204
  header={
166
- <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
167
- <Checkbox />
168
- <span>{item.title || item.caption}</span>
169
- </div>
205
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
206
+ <Checkbox
207
+ checked={selectedMenus.includes(item.id)}
208
+ onChange={(e) => toggleMenu(item.id, e.target.checked)}
209
+ />
210
+ <span>{item.title || item.caption}</span>
211
+ </div>
170
212
  }
171
- >
172
- {/* Load submenus recursively */}
173
- <NestedMenu parentId={item.id} step={step + 1} />
174
- </Panel>
175
- ))}
176
- </Collapse>
177
- </div>
178
- )}
179
- {/* <Button type="primary" onClick={loadMenus()}>
180
- Get Menus
181
- </Button> */}
182
-
183
-
184
- {/* Submit Button */}
185
- <Form.Item>
186
- <Button loading={loading} htmlType="submit" type="primary">
187
- Save
188
- </Button>
189
- </Form.Item>
190
-
191
- </Form>
192
- )}
193
-
194
- </section>
195
- );
213
+ >
214
+ {/* Nested submenus */}
215
+ <NestedMenu
216
+ parentId={item.id}
217
+ step={step + 1}
218
+ selectedMenus={selectedMenus}
219
+ toggleMenu={toggleMenu}
220
+ />
221
+ </Panel>
222
+ ))}
223
+ </Collapse>
224
+ </div>
225
+ )}
226
+
227
+ {/* Submit Button */}
228
+ <Form.Item>
229
+ <Button loading={loading} htmlType="submit" type="primary">
230
+ Save
231
+ </Button>
232
+ </Form.Item>
233
+ </Form>
234
+ )}
235
+ </section>
236
+ );
196
237
  };
197
238
 
198
239
  export default RoleAdd;
199
240
 
200
-
201
241
  // ------------------------
202
242
  // Recursive Nested Menu Component
203
243
  // ------------------------
204
- const NestedMenu = ({ parentId, step, api, additional_queries }) => {
205
- const [items, setItems] = useState([]);
206
- const [loading, setLoading] = useState(true);
207
-
208
- useEffect(() => {
209
- MenusAPI.get({
210
- queries: [
211
- // ...additional_queries,
212
- { field: 'header_id', value: parentId },
213
- { field: 'step', value: step }
214
- ],
215
- })
216
- .then(res => {
217
- setItems(res.result || []);
218
- setLoading(false);
219
- })
220
- .catch(() => setLoading(false));
221
- }, [parentId]);
222
-
223
- if (loading) return <Skeleton active />;
224
- if (!items.length) return null;
225
-
226
- return (
227
- <Collapse ghost style={{ marginLeft: 20 }}>
228
- {items.map(menu => (
229
- <Panel
230
- key={menu.id}
231
- header={
232
- <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
233
- <Checkbox />
234
- <span>{menu.title || menu.caption}</span>
235
- </div>
236
- }
237
- >
238
- <NestedMenu
239
- parentId={menu.id}
240
- step={step + 1}
241
- api={api}
242
- additional_queries={additional_queries}
243
- />
244
- </Panel>
245
- ))}
246
- </Collapse>
247
- );
244
+ const NestedMenu = ({ parentId, step, selectedMenus, toggleMenu }) => {
245
+ const [items, setItems] = useState([]);
246
+ const [loading, setLoading] = useState(true);
247
+
248
+ useEffect(() => {
249
+ MenusAPI.get({
250
+ queries: [
251
+ { field: 'header_id', value: parentId },
252
+ { field: 'step', value: step },
253
+ ],
254
+ })
255
+ .then((res) => {
256
+ setItems(res.result || []);
257
+ setLoading(false);
258
+ })
259
+ .catch(() => setLoading(false));
260
+ }, [parentId, step]);
261
+
262
+ if (loading) return <Skeleton active />;
263
+ if (!items.length) return null;
264
+
265
+ return (
266
+ <Collapse ghost style={{ marginLeft: 20 }}>
267
+ {items.map((menu) => (
268
+ <Panel
269
+ key={menu.id}
270
+ header={
271
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
272
+ <Checkbox
273
+ checked={selectedMenus.includes(Number(menu.id))} // ensure number
274
+ onChange={(e) => toggleMenu(Number(menu.id), e.target.checked)}
275
+ />
276
+ <span>{menu.title || menu.caption}</span>
277
+ </div>
278
+ }
279
+ >
280
+ <NestedMenu
281
+ parentId={menu.id}
282
+ step={step + 1}
283
+ selectedMenus={selectedMenus}
284
+ toggleMenu={toggleMenu}
285
+ />
286
+ </Panel>
287
+ ))}
288
+ </Collapse>
289
+ );
248
290
  };
@@ -11,6 +11,7 @@ import { Typography, Modal, Space, Switch, Popconfirm, Skeleton, Input, Drawer,S
11
11
  import { TableComponent, Card, Button } from './../../../../lib';
12
12
 
13
13
  import { ReloadOutlined, OrderedListOutlined, PicCenterOutlined, DeleteOutlined, EditOutlined, CopyOutlined } from '@ant-design/icons';
14
+ import { RolesAPI } from '../../..';
14
15
 
15
16
  const { Title } = Typography;
16
17
 
@@ -79,12 +80,13 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
79
80
  setLoading(true);
80
81
 
81
82
  // Get the records
82
- return model.get(config)
83
+ return RolesAPI.getRole()
83
84
 
84
85
  .then((res) => {
85
86
 
86
87
  // setRecords(res.result);
87
88
  var result = res;
89
+
88
90
 
89
91
  // if (id) {
90
92
 
@@ -157,13 +159,12 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
157
159
  // getRecords();
158
160
  // }
159
161
  }
160
-
161
162
  /**
162
163
  * On delete of each record
163
164
  */
164
- function onDelete(record) {
165
+ function onDelete(id) {
165
166
 
166
- model.delete(record).then((res) => {
167
+ RolesAPI.deleteRole(id).then((res) => {
167
168
  if (res) {
168
169
  getRecords();
169
170
 
@@ -259,7 +260,8 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
259
260
 
260
261
  {/* Delete Icon */}
261
262
  <Popconfirm title="Are you sure" onConfirm={() => {
262
- onDelete(record);
263
+
264
+ onDelete(record.id);
263
265
  }}>
264
266
  <Button size={'small'} type="dashed">
265
267
  <DeleteOutlined />
@@ -313,7 +315,7 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
313
315
  <div className="button-container">
314
316
  <Space size="small">
315
317
 
316
- <Button onClick={getRecords} size={'small'}>
318
+ <Button onClick={getRecords} size={'small'} type='default'>
317
319
  <ReloadOutlined />
318
320
  </Button>
319
321