ui-soxo-bootstrap-core 2.4.25-dev.30 → 2.4.25-dev.32

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.
@@ -0,0 +1,114 @@
1
+ /**
2
+ * MenuTree Component
3
+ *
4
+ * @description
5
+ * Renders a hierarchical menu structure with optional checkboxes.
6
+ * Supports:
7
+ * - Recursive menu rendering
8
+ * - Parent–child checkbox synchronization
9
+ * - Indeterminate state for partially selected parents
10
+ *
11
+ * Common use cases:
12
+ * - Role-based menu preview
13
+ * - Permission assignment trees
14
+ *
15
+ * @param {Object} props
16
+ * @param {Array<Object>} props.menus - Menu tree data
17
+ * @param {Array<number>} props.selectedMenus - Selected menu IDs
18
+ * @param {(menuId:number, checked:boolean) => void} props.toggleMenu - Callback for menu selection
19
+ * @param {number|null} props.parentId - Parent menu ID (used internally for recursion)
20
+ * @param {boolean} props.showCheckbox - Whether to display checkboxes
21
+ *
22
+ * @returns {JSX.Element}
23
+ */
24
+
25
+ import React from 'react';
26
+ import { Checkbox, Collapse } from 'antd';
27
+
28
+ const { Panel } = Collapse;
29
+
30
+ export const MenuTree = ({ menus, selectedMenus = [], toggleMenu, parentId = null, showCheckbox = true }) => {
31
+ const renderTree = (menuList, parentId = null) => {
32
+ return menuList.map((menu) => {
33
+ const children = menu.sub_menus || [];
34
+
35
+ /**
36
+ * Toggle menu selection recursively for all child menus
37
+ *
38
+ * @param {Object} m - Menu object
39
+ * @param {boolean} checked - Checkbox state
40
+ */
41
+ const toggleMenuRecursive = (m, checked) => {
42
+ toggleMenu && toggleMenu(m.id, checked);
43
+ if (m.sub_menus && m.sub_menus.length > 0) {
44
+ m.sub_menus.forEach((c) => toggleMenuRecursive(c, checked));
45
+ }
46
+ };
47
+
48
+ /**
49
+ * Handle parent checkbox change
50
+ * - Updates parent menu
51
+ * - Cascades selection to all children
52
+ *
53
+ * @param {boolean} checked
54
+ */
55
+
56
+ const onParentChange = (checked) => {
57
+ toggleMenu && toggleMenu(menu.id, checked);
58
+ children.forEach((c) => toggleMenuRecursive(c, checked));
59
+ };
60
+
61
+ /**
62
+ * Leaf menu (no sub-menus)
63
+ */
64
+
65
+ if (children.length === 0) {
66
+ return (
67
+ <div
68
+ key={menu.id}
69
+ style={{
70
+ border: '1px solid rgba(198, 195, 195, 0.85)',
71
+ padding: '12px 16px',
72
+ marginBottom: 6,
73
+ background: '#fff',
74
+ display: 'flex',
75
+ alignItems: 'center',
76
+ gap: 8,
77
+ }}
78
+ >
79
+ {showCheckbox && <Checkbox checked={selectedMenus.includes(menu.id)} onChange={(e) => onParentChange(e.target.checked)} />}
80
+ <span>{menu.title || menu.caption}</span>
81
+ </div>
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Parent menu (with sub-menus)
87
+ */
88
+
89
+ return (
90
+ <Collapse key={menu.id} style={{ marginBottom: 6 }}>
91
+ <Panel
92
+ key={menu.id}
93
+ header={
94
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
95
+ {showCheckbox && (
96
+ <Checkbox
97
+ checked={children.every((c) => selectedMenus.includes(c.id))}
98
+ indeterminate={children.some((c) => selectedMenus.includes(c.id)) && !children.every((c) => selectedMenus.includes(c.id))}
99
+ onChange={(e) => onParentChange(e.target.checked)}
100
+ />
101
+ )}
102
+ <span>{menu.title || menu.caption}</span>
103
+ </div>
104
+ }
105
+ >
106
+ <div style={{ paddingLeft: 20 }}>{renderTree(children, menu.id)}</div>
107
+ </Panel>
108
+ </Collapse>
109
+ );
110
+ });
111
+ };
112
+
113
+ return <>{renderTree(menus, parentId)}</>;
114
+ };
@@ -105,6 +105,11 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
105
105
  return Promise.resolve();
106
106
  }
107
107
 
108
+ if (!/^[a-zA-Z0-9]*$/.test(value)) {
109
+ setCodeStatus({ status: 'error', message: 'Code can only contain letters and numbers.' });
110
+ return Promise.reject(new Error('Code can only contain letters and numbers.'));
111
+ }
112
+
108
113
  if (editMode && doctorData?.do_code === value) {
109
114
  setCodeStatus({ status: 'success', message: 'Code is unchanged' });
110
115
  return Promise.resolve();
@@ -218,7 +223,17 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
218
223
  {/* NAME + CODE */}
219
224
  <Row gutter={16}>
220
225
  <Col span={12}>
221
- <Form.Item label="Name" name="name" rules={[{ required: true }]}>
226
+ <Form.Item
227
+ label="Name"
228
+ name="name"
229
+ rules={[
230
+ { required: true },
231
+ {
232
+ pattern: /^[A-Za-z]+$/,
233
+ message: 'Name should contain only alphabets',
234
+ },
235
+ ]}
236
+ >
222
237
  <Input autoComplete="off" ref={nameInputRef} placeholder="Enter Name" onKeyDown={handleEnterKey} />
223
238
  </Form.Item>
224
239
  </Col>
@@ -369,7 +384,11 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
369
384
  </Col>
370
385
 
371
386
  <Col span={8}>
372
- <Form.Item label="Email ID" name="email_id">
387
+ <Form.Item
388
+ label="Email ID"
389
+ name="email_id"
390
+ rules={[{ type: 'email', message: 'Please enter a valid email address' }]}
391
+ >
373
392
  <Input placeholder="Enter Email ID" onKeyDown={handleEnterKey} />
374
393
  </Form.Item>
375
394
  </Col>
@@ -13,6 +13,7 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
13
13
  * ------------------------------- */
14
14
  const [loading, setLoading] = useState(false);
15
15
  const [designations, setDesignations] = useState([]);
16
+ const [staffList, setStaffList] = useState([]);
16
17
  const [isSubmitDisabled, setIsSubmitDisabled] = useState(true);
17
18
  const [codeStatus, setCodeStatus] = useState({ status: '', message: '' });
18
19
 
@@ -47,6 +48,7 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
47
48
  useEffect(() => {
48
49
  if (visible) {
49
50
  getDesignations();
51
+ if (!editMode) getStaffList();
50
52
  }
51
53
  }, [visible]);
52
54
 
@@ -95,6 +97,18 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
95
97
  .finally(() => setLoading(false));
96
98
  };
97
99
 
100
+ /** -------------------------------
101
+ * API CALL – Staff List
102
+ * ------------------------------- */
103
+ const getStaffList = () => {
104
+ UsersAPI.getAllStaff()
105
+ .then((res) => {
106
+ if (Array.isArray(res)) {
107
+ setStaffList(res);
108
+ }
109
+ })
110
+ .catch((err) => console.error(err));
111
+ };
98
112
 
99
113
  /** -------------------------------
100
114
  * VALIDATION – Input
@@ -149,6 +163,16 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
149
163
  return Promise.resolve();
150
164
  };
151
165
 
166
+ const validateSerial = (_, value) => {
167
+ if (!value) {
168
+ return Promise.resolve();
169
+ }
170
+ const exists = staffList.some((staff) => String(staff.slNo) === value);
171
+ if (exists) {
172
+ return Promise.reject('Serial Number already exists');
173
+ }
174
+ return Promise.resolve();
175
+ };
152
176
 
153
177
  /** -------------------------------
154
178
  * UTIL – Enter Key Navigation
@@ -350,7 +374,11 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
350
374
  </Col>
351
375
 
352
376
  <Col span={8}>
353
- <Form.Item label="Serial Number" name="slno" rules={[{ message: 'only numbers are allowed', pattern: /^\d*$/ }]}>
377
+ <Form.Item
378
+ label="Serial Number"
379
+ name="slno"
380
+ rules={[{ message: 'only numbers are allowed', pattern: /^\d*$/ }, { validator: validateSerial }]}
381
+ >
354
382
  <Input autoComplete="off" maxLength={5} placeholder="Enter Serial Number" onKeyDown={handleEnterKey} disabled={editMode} />
355
383
  </Form.Item>
356
384
  </Col>
@@ -0,0 +1,298 @@
1
+ /**
2
+ * AssignRole Component
3
+ *
4
+ * @description
5
+ * Allows administrators to:
6
+ * - View user details
7
+ * - Assign one or more roles to a user
8
+ * - View menus associated with a selected role (read-only)
9
+ *
10
+ * UI Layout:
11
+ * - Left Panel: User info + role list with search and checkboxes
12
+ * - Right Panel: Menu tree preview for the selected role
13
+ *
14
+ * @returns {JSX.Element}
15
+ */
16
+ import React, { useMemo, useState, useEffect } from 'react';
17
+ import { Checkbox, Input, Typography, Empty, Avatar, List, Card, message, Skeleton } from 'antd';
18
+
19
+ import { UsersAPI, RolesAPI, MenusAPI } from '../../..';
20
+ import { UserRolesAPI } from '../../..';
21
+ import { useParams } from 'react-router-dom';
22
+ import { Button } from '../../../../lib';
23
+ import { MenuTree } from '../../../../lib/elements/basic/menu-tree/menu-tree';
24
+ import './assign-role.scss';
25
+
26
+ const { Text } = Typography;
27
+
28
+ const { Search } = Input;
29
+
30
+ export default function AssignRole() {
31
+ /**
32
+ * User ID from route params
33
+ * @type {{ id: string }}
34
+ */
35
+ const { id } = useParams();
36
+ // for user
37
+ const [user, setUser] = useState(null);
38
+ // for roles
39
+ const [roles, setRoles] = useState([]);
40
+ // for active roles
41
+ const [activeRole, setActiveRole] = useState(null);
42
+ const [modules, setModules] = useState([]);
43
+ // loading
44
+ const [selectedRoles, setSelectedRoles] = useState([]);
45
+ const [loadingUser, setLoadingUser] = useState(false);
46
+ const [loadingRoles, setLoadingRoles] = useState(false);
47
+ const [loadingMenus, setLoadingMenus] = useState(false);
48
+
49
+ const [selectedMenus, setSelectedMenus] = useState([]);
50
+ const [search, setSearch] = useState('');
51
+
52
+ /**
53
+ * Load user details and assigned roles when user ID changes
54
+ */
55
+ useEffect(() => {
56
+ if (id) loadUser();
57
+ }, [id]);
58
+
59
+ /**
60
+ * Fetch user details and assigned roles
61
+ *
62
+ * - Extracts user info from first record
63
+ * - Deduplicates role IDs
64
+ *
65
+ * @async
66
+ * @returns {Promise<void>}
67
+ */
68
+
69
+ const loadUser = async () => {
70
+ setLoadingUser(true);
71
+ try {
72
+ const res = await UsersAPI.getUserRole({ id });
73
+ const list = Array.isArray(res?.result) ? res.result : [];
74
+
75
+ if (!list.length) {
76
+ setUser(null);
77
+ setSelectedRoles([]);
78
+ return;
79
+ }
80
+
81
+ // Extract user from first record
82
+ const userInfo = list[0].user;
83
+ setUser({
84
+ id: userInfo.id,
85
+ name: userInfo.name,
86
+ });
87
+
88
+ // Extract VALID role IDs (ignore nulls & duplicates)
89
+ const roleIds = [...new Set(list.filter((item) => item.role_id).map((item) => Number(item.role_id)))];
90
+
91
+ setSelectedRoles(roleIds);
92
+ } catch (e) {
93
+ console.error(e);
94
+ message.error('Unable to load user details');
95
+ } finally {
96
+ setLoadingUser(false);
97
+ }
98
+ };
99
+
100
+ /**
101
+ * Load active roles on component mount
102
+ */
103
+ useEffect(() => {
104
+ getRoles();
105
+ }, []);
106
+
107
+ /**
108
+ * Fetch all active roles
109
+ *
110
+ * @async
111
+ * @returns {Promise<void>}
112
+ */
113
+
114
+ const getRoles = async () => {
115
+ setLoadingRoles(true);
116
+ try {
117
+ const res = await RolesAPI.getRole();
118
+ const activeRoles = Array.isArray(res?.result) ? res.result.filter((r) => r.active === 'Y') : [];
119
+ setRoles(activeRoles);
120
+ } catch (e) {
121
+ console.error(e);
122
+ setRoles([]);
123
+ } finally {
124
+ setLoadingRoles(false);
125
+ }
126
+ };
127
+
128
+ /**
129
+ * Recursively filter menus by allowed menu IDs
130
+ *
131
+ * @param {Array<Object>} menus - Full menu tree
132
+ * @param {Array<number>} allowedIds - Menu IDs assigned to role
133
+ * @returns {Array<Object>}
134
+ */
135
+
136
+ const filterAndSortMenus = (menus, allowedIds) => {
137
+ return menus
138
+ .filter((m) => allowedIds.includes(m.id))
139
+ .map((m) => ({
140
+ ...m,
141
+ sub_menus: filterAndSortMenus(m.sub_menus || [], allowedIds),
142
+ }));
143
+ };
144
+
145
+ /**
146
+ * Load menus for a selected role
147
+ *
148
+ * @param {Object} role - Selected role object
149
+ * @async
150
+ * @returns {Promise<void>}
151
+ */
152
+
153
+ const loadRoleMenus = async (role) => {
154
+ setActiveRole(role);
155
+ setSelectedMenus(role.menu_ids || []);
156
+ setLoadingMenus(true);
157
+ try {
158
+ const res = await MenusAPI.getCoreMenuLists();
159
+ const allMenus = res.result || [];
160
+ const filteredMenus = filterAndSortMenus(allMenus, role.menu_ids);
161
+ setModules(filteredMenus);
162
+ } catch (e) {
163
+ console.error(e);
164
+ setModules([]);
165
+ } finally {
166
+ setLoadingMenus(false);
167
+ }
168
+ };
169
+
170
+ /**
171
+ * Toggle menu selection (currently read-only usage)
172
+ *
173
+ * @param {number} menuId
174
+ * @param {boolean} checked
175
+ */
176
+ const toggleMenu = (menuId, checked) => {
177
+ setSelectedMenus((prev) => (checked ? [...prev, menuId] : prev.filter((id) => id !== menuId)));
178
+ };
179
+
180
+ /** Filtered roles */
181
+ const filteredRoles = useMemo(() => {
182
+ if (!search) return roles;
183
+ return roles.filter((r) => r.name.toLowerCase().includes(search.toLowerCase()));
184
+ }, [roles, search]);
185
+
186
+ /**
187
+ * Toggle role selection
188
+ *
189
+ * @param {number} roleId
190
+ * @param {boolean} checked
191
+ */
192
+
193
+ const toggleRole = (roleId, checked) => {
194
+ const next = checked ? [...selectedRoles, roleId] : selectedRoles.filter((v) => v !== roleId);
195
+ setSelectedRoles(next);
196
+ };
197
+
198
+ /**
199
+ * Save selected roles for the user
200
+ *
201
+ * @async
202
+ * @returns {Promise<void>}
203
+ */
204
+ const handleSaveUserRole = async () => {
205
+ if (!id || !selectedRoles.length) {
206
+ message.warning('User or roles missing');
207
+ return;
208
+ }
209
+
210
+ try {
211
+ message.loading({ key: 'save', content: 'Saving user roles...' });
212
+
213
+ await Promise.all(
214
+ selectedRoles.map((roleId) =>
215
+ UserRolesAPI.addUserRole({
216
+ values: {
217
+ user_id: id,
218
+ role_id: roleId,
219
+ },
220
+ })
221
+ )
222
+ );
223
+
224
+ message.success({ key: 'save', content: 'User roles saved successfully' });
225
+ } catch (err) {
226
+ console.error(err);
227
+ message.error({ key: 'save', content: 'Failed to save user roles' });
228
+ }
229
+ };
230
+
231
+ return (
232
+ <section className="assign-role">
233
+ {/* LEFT PANEL */}
234
+ <Card className="left-panel" bodyStyle={{ padding: 12 }}>
235
+ <div size="small" className="user-card">
236
+ <Avatar size={40}>{user?.name[0]}</Avatar>
237
+
238
+ <div className="user-info">
239
+ <div>{user?.name || '--'}</div>
240
+ <Text className="user-id">ID : {user?.id || '--'}</Text>
241
+ </div>
242
+ </div>
243
+
244
+ <div className="role-list-header">
245
+ <Text strong>Role List ({roles.length})</Text>
246
+ </div>
247
+
248
+ <Search placeholder="Enter Search Value" allowClear style={{ width: 300, marginBottom: '0px' }} onChange={(e) => setSearch(e.target.value)} />
249
+ {/* <Input className="role-search" placeholder="Search Here" allowClear value={search} onChange={(e) => setSearch(e.target.value)} /> */}
250
+
251
+ <List
252
+ itemLayout="horizontal"
253
+ dataSource={filteredRoles}
254
+ renderItem={(role) => (
255
+ <List.Item
256
+ key={role.id}
257
+ onClick={() => loadRoleMenus(role)}
258
+ className={`role-item ${activeRole?.id === role.id ? 'active' : ''}`}
259
+ actions={[
260
+ <Checkbox
261
+ checked={selectedRoles.includes(role.id)}
262
+ onChange={(e) => toggleRole(role.id, e.target.checked)}
263
+ onClick={(e) => e.stopPropagation()}
264
+ />,
265
+ ]}
266
+ >
267
+ <List.Item.Meta avatar={<Avatar size={28}>{role.name[0]}</Avatar>} title={role.name} description={role.description} />
268
+ </List.Item>
269
+ )}
270
+ />
271
+ </Card>
272
+
273
+ {/* RIGHT PANEL */}
274
+ <Card className="right-panel">
275
+ <div className="menus-header">
276
+ <Text>Menus {activeRole ? `– ${activeRole.name}` : ''}</Text>
277
+ <div className="sub-text">It's not editable. To edit it, go to the role editing page.</div>
278
+ </div>
279
+
280
+ <div className="menus-content">
281
+ {loadingMenus ? (
282
+ <Skeleton active paragraph={{ rows: 6 }} />
283
+ ) : modules.length === 0 ? (
284
+ <Empty description="Click a role to view menus" />
285
+ ) : (
286
+ <MenuTree menus={modules} selectedMenus={selectedMenus} toggleMenu={toggleMenu} showCheckbox={false} />
287
+ )}
288
+ </div>
289
+
290
+ <div className="footer-actions">
291
+ <Button type="primary" onClick={handleSaveUserRole}>
292
+ Save
293
+ </Button>
294
+ </div>
295
+ </Card>
296
+ </section>
297
+ );
298
+ }
@@ -0,0 +1,117 @@
1
+ .assign-role {
2
+ display: flex;
3
+ gap: 6px;
4
+
5
+ .left-panel {
6
+ width: 340px;
7
+
8
+ .user-card {
9
+ margin-bottom: 12px;
10
+ background: #fafafa;
11
+ border: none;
12
+ display: flex;
13
+ align-items: center;
14
+ gap: 12px;
15
+ padding: 8px;
16
+
17
+ .user-info {
18
+ font-weight: 500;
19
+
20
+ .user-id {
21
+ font-size: 12px;
22
+ color: rgba(0, 0, 0, 0.45);
23
+ }
24
+ }
25
+ }
26
+
27
+ .role-list-header {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ margin-bottom: 12px;
31
+ strong {
32
+ font-weight: 600;
33
+ }
34
+ }
35
+
36
+ .role-search {
37
+ margin: 12px 0;
38
+ }
39
+
40
+ .role-item {
41
+ cursor: pointer;
42
+ border-radius: 6px;
43
+ padding: 8px 12px;
44
+
45
+ &.active {
46
+ background: #e6f7ff;
47
+ }
48
+
49
+ .ant-list-item-meta-title {
50
+ font-weight: 500;
51
+ }
52
+
53
+ .ant-list-item-meta-description {
54
+ font-size: 12px;
55
+ color: rgba(0, 0, 0, 0.45);
56
+ }
57
+ }
58
+ }
59
+
60
+ .right-panel {
61
+ flex: 1;
62
+ display: flex;
63
+ flex-direction: column;
64
+ justify-content: space-between;
65
+ padding: 16px;
66
+
67
+ .menus-header {
68
+ font-weight: 500;
69
+ margin-bottom: 4px;
70
+ color: #000;
71
+
72
+ .sub-text {
73
+ font-size: 12px;
74
+ color: #888;
75
+ margin-top: 4px;
76
+ }
77
+ }
78
+
79
+ .menus-content {
80
+ margin-top: 16px;
81
+ }
82
+
83
+ .footer-actions {
84
+ margin-top: 16px;
85
+ display: flex;
86
+ justify-content: flex-end;
87
+ gap: 8px;
88
+ }
89
+ }
90
+
91
+ /* ===============================
92
+ 📱 iPad Mini (481px – 768px)
93
+ =============================== */
94
+ @media (max-width: 768px) {
95
+ .assign-role {
96
+ flex-direction: column;
97
+ gap: 12px;
98
+ }
99
+
100
+ .assign-role .left-panel {
101
+ width: 100%;
102
+ }
103
+
104
+ .assign-role .right-panel {
105
+ width: 100%;
106
+ }
107
+
108
+ /* Better spacing for tablets */
109
+ .assign-role .right-panel {
110
+ padding: 14px;
111
+ }
112
+
113
+ .assign-role .footer-actions {
114
+ justify-content: flex-end;
115
+ }
116
+ }
117
+ }
@@ -728,7 +728,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
728
728
  <Input placeholder="Enter User Group" />
729
729
  </Form.Item>
730
730
  </Col>
731
- <Col span={8}>
731
+ {/* <Col span={8}>
732
732
  <Form.Item name="role_id" label="Role" rules={[{ required: true, message: 'Please select a Role' }]}>
733
733
  <Select placeholder="Select Role">
734
734
  {roles.map((role) => (
@@ -738,7 +738,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
738
738
  ))}
739
739
  </Select>
740
740
  </Form.Item>
741
- </Col>
741
+ </Col> */}
742
742
 
743
743
  {/* <Form.Item name="mobile" label="Mobile" required={mobileRequired}>
744
744
  <Input placeholder="Enter Mobile" />
@@ -273,8 +273,23 @@ class Users extends Base {
273
273
  updateUser = ({ id, formBody }) => {
274
274
  return ApiUtils.put({ url: `users/update-user/${id}`, formBody });
275
275
  };
276
+
277
+ /* The `getUser` method is a function that takes an object with an `id` property as a parameter. It
278
+ then uses the `ApiUtils.get` function to make a GET request to the endpoint `users/` to fetch
279
+ user data based on the provided `id`. This method is used to retrieve a specific user's
280
+ information from the API. */
281
+
276
282
  getUser = ({ id }) => {
277
283
  return ApiUtils.get({ url: `users/${id}` });
284
+ };
285
+ /* The `getUserRole` method is a function that takes an object with an `id` property as a parameter.
286
+ It then uses the `ApiUtils.get` function to make a GET request to the endpoint `core-user-roles`
287
+ with the `user_id` parameter set to the provided `id`. This method is used to fetch the role or
288
+ roles associated with a specific user from the API. */
289
+
290
+ getUserRole = ({ id }) => {
291
+ return ApiUtils.get({ url: `core-user-roles?user_id=${id}` });
292
+
278
293
  };
279
294
 
280
295
  /**
@@ -8,6 +8,8 @@ import GenericDetail from './generic/components/generic-detail/generic-detail';
8
8
 
9
9
  import ModuleRoutes from './module-routes/module-routes';
10
10
 
11
+ import { AssignRole } from './reporting/components';
12
+
11
13
  // All Dashboard Components
12
14
 
13
15
  import DashboardCard from './dashboard/components/dashboard-card/dashboard-card';
@@ -30,6 +32,7 @@ export {
30
32
  GenericDetail,
31
33
  ModuleRoutes,
32
34
  DashboardCard,
35
+ AssignRole,
33
36
  PopQueryDashboard,
34
37
  HomePageAPI,
35
38
  ReportingDashboard,
@@ -1,5 +1,6 @@
1
1
  import UserEdit from '../../../models/users/components/user-add/user-edit';
2
2
 
3
3
  import UserAdd from '../../../models/users/components/user-add/user-add';
4
+ import AssignRole from '../../../models/users/components/assign-role/assign-role';
4
5
 
5
- export { UserEdit, UserAdd };
6
+ export { UserEdit, UserAdd, AssignRole };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.4.25-dev.30",
3
+ "version": "2.4.25-dev.32",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"