ui-soxo-bootstrap-core 2.6.1-dev.5 → 2.6.1-dev.7
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/core/models/base/base.js +7 -3
- package/core/models/menus/components/menu-lists/menu-lists.js +2 -2
- package/core/models/menus/menus.js +15 -4
- package/core/models/users/components/assign-role/assign-role.js +114 -40
- package/core/models/users/components/assign-role/assign-role.scss +182 -44
- package/core/models/users/components/assign-role/avatar-props.js +45 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +7 -5
- package/package.json +1 -1
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) {
|
|
@@ -474,7 +474,7 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
|
|
|
474
474
|
</Button>
|
|
475
475
|
)}
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
<Button
|
|
478
478
|
size="small"
|
|
479
479
|
type="default"
|
|
480
480
|
onClick={() => {
|
|
@@ -484,7 +484,7 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
|
|
|
484
484
|
}}
|
|
485
485
|
>
|
|
486
486
|
<CopyOutlined />
|
|
487
|
-
</Button>
|
|
487
|
+
</Button>
|
|
488
488
|
|
|
489
489
|
<Popconfirm title="Are you sure?" onConfirm={() => deleteRecord(item)}>
|
|
490
490
|
<Button danger size="small" type="default">
|
|
@@ -177,23 +177,34 @@ 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
|
+
},
|
|
190
195
|
}).then((result) => result);
|
|
191
196
|
};
|
|
192
197
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
getMenubyUser = (user_id) => {
|
|
199
|
+
const url = `menus/get-menus?userId=${user_id}`;
|
|
200
|
+
return this.get({
|
|
201
|
+
url,
|
|
202
|
+
}).then((result) => result);
|
|
203
|
+
};
|
|
196
204
|
|
|
205
|
+
// get core-menu list with submenu
|
|
206
|
+
getCoreMenuLists = (role_id) => {
|
|
207
|
+
const url = `menus/get-menus-by-role/${role_id}`;
|
|
197
208
|
return this.get({
|
|
198
209
|
url,
|
|
199
210
|
}).then((result) => result);
|
|
@@ -21,6 +21,7 @@ import { UserRolesAPI } from '../../..';
|
|
|
21
21
|
import { useParams } from 'react-router-dom';
|
|
22
22
|
import { Button } from '../../../../lib';
|
|
23
23
|
import { MenuTree } from '../../../../lib/elements/basic/menu-tree/menu-tree';
|
|
24
|
+
import { getAvatarProps } from './avatar-props';
|
|
24
25
|
import './assign-role.scss';
|
|
25
26
|
|
|
26
27
|
const { Text } = Typography;
|
|
@@ -42,7 +43,6 @@ export default function AssignRole() {
|
|
|
42
43
|
const [modules, setModules] = useState([]);
|
|
43
44
|
// loading
|
|
44
45
|
const [selectedRoles, setSelectedRoles] = useState([]);
|
|
45
|
-
const [loadingUser, setLoadingUser] = useState(false);
|
|
46
46
|
const [loadingRoles, setLoadingRoles] = useState(false);
|
|
47
47
|
const [loadingMenus, setLoadingMenus] = useState(false);
|
|
48
48
|
// for save
|
|
@@ -54,6 +54,8 @@ export default function AssignRole() {
|
|
|
54
54
|
// for initial roles
|
|
55
55
|
const [initialRoles, setInitialRoles] = useState([]);
|
|
56
56
|
|
|
57
|
+
const userAvatar = getAvatarProps(user?.name);
|
|
58
|
+
|
|
57
59
|
/**
|
|
58
60
|
* Load user details and assigned roles when user ID changes
|
|
59
61
|
*/
|
|
@@ -72,8 +74,6 @@ export default function AssignRole() {
|
|
|
72
74
|
*/
|
|
73
75
|
|
|
74
76
|
const loadUser = async () => {
|
|
75
|
-
setLoadingUser(true);
|
|
76
|
-
|
|
77
77
|
try {
|
|
78
78
|
// Call both APIs in parallel
|
|
79
79
|
const [userRes, roleRes] = await Promise.all([UsersAPI.getUser({ id }), UsersAPI.getUserRole({ id })]);
|
|
@@ -100,8 +100,6 @@ export default function AssignRole() {
|
|
|
100
100
|
} catch (e) {
|
|
101
101
|
console.error(e);
|
|
102
102
|
message.error('Unable to load user details');
|
|
103
|
-
} finally {
|
|
104
|
-
setLoadingUser(false);
|
|
105
103
|
}
|
|
106
104
|
};
|
|
107
105
|
|
|
@@ -158,11 +156,13 @@ export default function AssignRole() {
|
|
|
158
156
|
setActiveRole(role);
|
|
159
157
|
setSelectedMenus(role.menu_ids || []);
|
|
160
158
|
setLoadingMenus(true);
|
|
159
|
+
|
|
160
|
+
const role_id = role.id;
|
|
161
161
|
try {
|
|
162
|
-
const res = await MenusAPI.getCoreMenuLists();
|
|
162
|
+
const res = await MenusAPI.getCoreMenuLists(role_id);
|
|
163
163
|
const allMenus = res.result || [];
|
|
164
|
-
|
|
165
|
-
setModules(
|
|
164
|
+
|
|
165
|
+
setModules(allMenus);
|
|
166
166
|
} catch (e) {
|
|
167
167
|
console.error(e);
|
|
168
168
|
setModules([]);
|
|
@@ -260,60 +260,134 @@ export default function AssignRole() {
|
|
|
260
260
|
return sortedInitial.some((value, index) => value !== sortedSelected[index]);
|
|
261
261
|
}, [initialRoles, selectedRoles]);
|
|
262
262
|
|
|
263
|
+
// View All Role
|
|
264
|
+
const handleViewAll = async () => {
|
|
265
|
+
setLoadingMenus(true);
|
|
266
|
+
setModules([]);
|
|
267
|
+
setActiveRole({ name: 'All Roles' });
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const res = await MenusAPI.getMenubyUser(id);
|
|
271
|
+
setModules(res?.result?.menus ?? []);
|
|
272
|
+
setLoadingMenus(false);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
setLoadingMenus(false);
|
|
275
|
+
console.error(err);
|
|
276
|
+
setModules([]);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
263
280
|
return (
|
|
264
281
|
<section className="assign-role">
|
|
265
282
|
{/* LEFT PANEL */}
|
|
266
|
-
<Card
|
|
267
|
-
|
|
268
|
-
|
|
283
|
+
<Card
|
|
284
|
+
className="left-panel"
|
|
285
|
+
bodyStyle={{
|
|
286
|
+
padding: 16,
|
|
287
|
+
height: '100%',
|
|
288
|
+
display: 'flex',
|
|
289
|
+
flexDirection: 'column',
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<div className="user-card">
|
|
293
|
+
<Avatar size={44} style={userAvatar.style}>
|
|
294
|
+
{userAvatar.letter}
|
|
295
|
+
</Avatar>
|
|
269
296
|
|
|
270
297
|
<div className="user-info">
|
|
271
|
-
<div>{user?.name || '--'}</div>
|
|
298
|
+
<div className="name">{user?.name || '--'}</div>
|
|
272
299
|
<Text className="user-id">ID : {user?.id || '--'}</Text>
|
|
273
300
|
</div>
|
|
274
301
|
</div>
|
|
275
302
|
|
|
276
303
|
<div className="role-list-header">
|
|
277
|
-
<
|
|
304
|
+
<div>
|
|
305
|
+
<Text strong>Role List</Text>
|
|
306
|
+
<div className="count">{roles.length} roles</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<Button size="small" onClick={handleViewAll}>
|
|
310
|
+
View Access
|
|
311
|
+
</Button>
|
|
278
312
|
</div>
|
|
279
313
|
|
|
280
|
-
<Search placeholder="
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
</
|
|
314
|
+
<Search placeholder="Search roles..." allowClear onChange={(e) => setSearch(e.target.value)} className="role-search" />
|
|
315
|
+
|
|
316
|
+
<div className="selected-summary">{selectedRoles.length} selected</div>
|
|
317
|
+
|
|
318
|
+
{/* SCROLL AREA */}
|
|
319
|
+
<div className="role-list-wrapper">
|
|
320
|
+
{loadingRoles ? (
|
|
321
|
+
<div className="role-skeleton">
|
|
322
|
+
{Array.from({ length: 6 }).map((_, i) => (
|
|
323
|
+
<div key={i} className="role-skeleton-item">
|
|
324
|
+
<Skeleton.Avatar active size={32} shape="circle" />
|
|
325
|
+
|
|
326
|
+
<div className="meta">
|
|
327
|
+
<Skeleton.Input active size="small" style={{ width: 140 }} />
|
|
328
|
+
<Skeleton.Input active size="small" style={{ width: 200 }} />
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<Skeleton.Button active size="small" shape="square" />
|
|
332
|
+
</div>
|
|
333
|
+
))}
|
|
334
|
+
</div>
|
|
335
|
+
) : (
|
|
336
|
+
<List
|
|
337
|
+
itemLayout="horizontal"
|
|
338
|
+
dataSource={filteredRoles}
|
|
339
|
+
className="role-list"
|
|
340
|
+
renderItem={(role) => {
|
|
341
|
+
const roleAvatar = getAvatarProps(role?.name);
|
|
342
|
+
|
|
343
|
+
return (
|
|
344
|
+
<List.Item
|
|
345
|
+
key={role.id}
|
|
346
|
+
onClick={() => loadRoleMenus(role)}
|
|
347
|
+
className={`role-item ${activeRole?.id === role.id ? 'active' : ''}`}
|
|
348
|
+
actions={[
|
|
349
|
+
<Checkbox
|
|
350
|
+
checked={selectedRoles.includes(role.id)}
|
|
351
|
+
onChange={(e) => toggleRole(role.id, e.target.checked)}
|
|
352
|
+
onClick={(e) => e.stopPropagation()}
|
|
353
|
+
/>,
|
|
354
|
+
]}
|
|
355
|
+
>
|
|
356
|
+
<List.Item.Meta
|
|
357
|
+
avatar={<Avatar style={roleAvatar.style}>{roleAvatar?.letter}</Avatar>}
|
|
358
|
+
title={role.name}
|
|
359
|
+
description={role.description}
|
|
360
|
+
/>
|
|
361
|
+
</List.Item>
|
|
362
|
+
);
|
|
363
|
+
}}
|
|
364
|
+
/>
|
|
301
365
|
)}
|
|
302
|
-
|
|
366
|
+
</div>
|
|
303
367
|
</Card>
|
|
304
368
|
|
|
305
369
|
{/* RIGHT PANEL */}
|
|
306
|
-
<Card
|
|
370
|
+
<Card
|
|
371
|
+
className="right-panel"
|
|
372
|
+
bodyStyle={{
|
|
373
|
+
height: '100%',
|
|
374
|
+
display: 'flex',
|
|
375
|
+
flexDirection: 'column',
|
|
376
|
+
padding: 16,
|
|
377
|
+
}}
|
|
378
|
+
>
|
|
307
379
|
<div className="menus-header">
|
|
308
|
-
<
|
|
309
|
-
<div className="sub-text">
|
|
380
|
+
<div className="title">Menus {activeRole ? `– ${activeRole.name}` : ''}</div>
|
|
381
|
+
<div className="sub-text">Not editable here. Go to role settings to modify.</div>
|
|
310
382
|
</div>
|
|
311
383
|
|
|
312
384
|
<div className="menus-content">
|
|
313
385
|
{loadingMenus ? (
|
|
314
386
|
<Skeleton active paragraph={{ rows: 6 }} />
|
|
315
387
|
) : modules.length === 0 ? (
|
|
316
|
-
<
|
|
388
|
+
<div className="empty-state">
|
|
389
|
+
<Empty description="Select a role to view menus" />
|
|
390
|
+
</div>
|
|
317
391
|
) : (
|
|
318
392
|
<MenuTree menus={modules} selectedMenus={selectedMenus} toggleMenu={toggleMenu} showCheckbox={false} />
|
|
319
393
|
)}
|
|
@@ -322,7 +396,7 @@ export default function AssignRole() {
|
|
|
322
396
|
<div className="footer-actions">
|
|
323
397
|
{rolesChanged && (
|
|
324
398
|
<Button type="primary" onClick={handleSaveUserRole} loading={saving}>
|
|
325
|
-
Save
|
|
399
|
+
Save Changes
|
|
326
400
|
</Button>
|
|
327
401
|
)}
|
|
328
402
|
</div>
|
|
@@ -1,25 +1,82 @@
|
|
|
1
1
|
.assign-role {
|
|
2
2
|
display: flex;
|
|
3
|
-
gap:
|
|
3
|
+
gap: 8px;
|
|
4
4
|
|
|
5
|
+
/* Checkbox Color Override */
|
|
6
|
+
.ant-checkbox-checked .ant-checkbox-inner {
|
|
7
|
+
background-color: rgb(68, 106, 169);
|
|
8
|
+
border-color: rgb(68, 106, 169);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ant-checkbox:hover .ant-checkbox-inner,
|
|
12
|
+
.ant-checkbox-wrapper:hover .ant-checkbox-inner {
|
|
13
|
+
border-color: rgb(68, 106, 169);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.ant-checkbox-checked::after {
|
|
17
|
+
border-color: rgb(68, 106, 169);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* TAKE FULL SCREEN HEIGHT */
|
|
21
|
+
height: calc(100vh - 80px);
|
|
22
|
+
min-height: 0;
|
|
23
|
+
|
|
24
|
+
.ant-card {
|
|
25
|
+
border-radius: 6px;
|
|
26
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
27
|
+
border: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* LEFT PANEL */
|
|
5
31
|
.left-panel {
|
|
6
|
-
width:
|
|
32
|
+
width: 360px;
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
height: 100%;
|
|
36
|
+
min-height: 0;
|
|
7
37
|
|
|
38
|
+
/* User Card */
|
|
8
39
|
.user-card {
|
|
9
|
-
margin-bottom: 12px;
|
|
10
|
-
background: #fafafa;
|
|
11
|
-
border: none;
|
|
12
40
|
display: flex;
|
|
13
41
|
align-items: center;
|
|
14
42
|
gap: 12px;
|
|
15
|
-
padding:
|
|
43
|
+
padding: 14px;
|
|
44
|
+
|
|
45
|
+
background: #ffffff;
|
|
46
|
+
border-radius: 10px;
|
|
47
|
+
|
|
48
|
+
border: 1px solid #f0f0f0;
|
|
49
|
+
|
|
50
|
+
box-shadow:
|
|
51
|
+
0 2px 6px rgba(0, 0, 0, 0.06),
|
|
52
|
+
0 6px 16px rgba(0, 0, 0, 0.04);
|
|
53
|
+
|
|
54
|
+
margin-bottom: 16px;
|
|
55
|
+
|
|
56
|
+
transition: all 0.2s ease;
|
|
57
|
+
|
|
58
|
+
&:hover {
|
|
59
|
+
box-shadow:
|
|
60
|
+
0 4px 10px rgba(0, 0, 0, 0.08),
|
|
61
|
+
0 10px 22px rgba(0, 0, 0, 0.06);
|
|
62
|
+
transform: translateY(-1px);
|
|
63
|
+
}
|
|
16
64
|
|
|
17
65
|
.user-info {
|
|
18
|
-
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
line-height: 1.2;
|
|
69
|
+
|
|
70
|
+
.name {
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
font-size: 14px;
|
|
73
|
+
color: #1f1f1f;
|
|
74
|
+
}
|
|
19
75
|
|
|
20
76
|
.user-id {
|
|
21
77
|
font-size: 12px;
|
|
22
|
-
color:
|
|
78
|
+
color: #8c8c8c;
|
|
79
|
+
margin-top: 2px;
|
|
23
80
|
}
|
|
24
81
|
}
|
|
25
82
|
}
|
|
@@ -28,8 +85,11 @@
|
|
|
28
85
|
display: flex;
|
|
29
86
|
justify-content: space-between;
|
|
30
87
|
align-items: center;
|
|
31
|
-
|
|
32
|
-
|
|
88
|
+
margin-bottom: 10px;
|
|
89
|
+
|
|
90
|
+
.count {
|
|
91
|
+
font-size: 12px;
|
|
92
|
+
color: #888;
|
|
33
93
|
}
|
|
34
94
|
}
|
|
35
95
|
|
|
@@ -40,16 +100,70 @@
|
|
|
40
100
|
}
|
|
41
101
|
|
|
42
102
|
.role-search {
|
|
43
|
-
margin:
|
|
103
|
+
margin-bottom: 10px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.selected-summary {
|
|
107
|
+
font-size: 12px;
|
|
108
|
+
color: rgb(68, 106, 169);
|
|
109
|
+
margin-bottom: 10px;
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* SCROLL AREA */
|
|
114
|
+
.role-list-wrapper {
|
|
115
|
+
flex: 1;
|
|
116
|
+
overflow-y: auto;
|
|
117
|
+
min-height: 0;
|
|
118
|
+
padding-right: 4px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Scrollbar */
|
|
122
|
+
.role-list-wrapper,
|
|
123
|
+
.menus-content {
|
|
124
|
+
scrollbar-width: thin; /* Firefox */
|
|
125
|
+
scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
|
|
126
|
+
|
|
127
|
+
&::-webkit-scrollbar-thumb {
|
|
128
|
+
background: rgba(0, 0, 0, 0);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
&:hover::-webkit-scrollbar-thumb {
|
|
132
|
+
background: rgba(0, 0, 0, 0.25);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
&::-webkit-scrollbar {
|
|
136
|
+
width: 6px;
|
|
137
|
+
height: 6px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
&::-webkit-scrollbar-track {
|
|
141
|
+
background: transparent;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
&::-webkit-scrollbar-thumb {
|
|
145
|
+
background: rgba(0, 0, 0, 0.25);
|
|
146
|
+
border-radius: 10px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
150
|
+
background: rgba(0, 0, 0, 0.4);
|
|
151
|
+
}
|
|
44
152
|
}
|
|
45
153
|
|
|
46
154
|
.role-item {
|
|
47
155
|
cursor: pointer;
|
|
48
156
|
border-radius: 6px;
|
|
49
|
-
padding:
|
|
157
|
+
padding: 10px 12px;
|
|
158
|
+
transition: 0.2s;
|
|
159
|
+
|
|
160
|
+
&:hover {
|
|
161
|
+
background: #f5f7fa;
|
|
162
|
+
}
|
|
50
163
|
|
|
51
164
|
&.active {
|
|
52
|
-
background: #
|
|
165
|
+
background: #e6f4ff;
|
|
166
|
+
// border-left: 3px solid rgb(68, 106, 169);
|
|
53
167
|
}
|
|
54
168
|
|
|
55
169
|
.ant-list-item-meta-title {
|
|
@@ -58,66 +172,90 @@
|
|
|
58
172
|
|
|
59
173
|
.ant-list-item-meta-description {
|
|
60
174
|
font-size: 12px;
|
|
61
|
-
color:
|
|
175
|
+
color: #777;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Skeleton Loader */
|
|
180
|
+
.role-skeleton {
|
|
181
|
+
display: flex;
|
|
182
|
+
flex-direction: column;
|
|
183
|
+
gap: 8px;
|
|
184
|
+
padding: 2px;
|
|
185
|
+
|
|
186
|
+
.role-skeleton-item {
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
gap: 12px;
|
|
190
|
+
padding: 10px 12px;
|
|
191
|
+
border-radius: 6px;
|
|
192
|
+
|
|
193
|
+
background: #fff;
|
|
194
|
+
border: 1px solid #f0f0f0;
|
|
195
|
+
|
|
196
|
+
.meta {
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
gap: 6px;
|
|
200
|
+
flex: 1;
|
|
201
|
+
}
|
|
62
202
|
}
|
|
63
203
|
}
|
|
64
204
|
}
|
|
65
205
|
|
|
206
|
+
/* RIGHT PANEL */
|
|
66
207
|
.right-panel {
|
|
67
208
|
flex: 1;
|
|
68
209
|
display: flex;
|
|
69
210
|
flex-direction: column;
|
|
70
|
-
|
|
71
|
-
|
|
211
|
+
height: 100%;
|
|
212
|
+
min-height: 0;
|
|
72
213
|
|
|
73
214
|
.menus-header {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
215
|
+
margin-bottom: 12px;
|
|
216
|
+
|
|
217
|
+
.title {
|
|
218
|
+
font-size: 16px;
|
|
219
|
+
font-weight: 600;
|
|
220
|
+
}
|
|
77
221
|
|
|
78
222
|
.sub-text {
|
|
79
223
|
font-size: 12px;
|
|
80
224
|
color: #888;
|
|
81
|
-
margin-top: 4px;
|
|
82
225
|
}
|
|
83
226
|
}
|
|
84
227
|
|
|
85
228
|
.menus-content {
|
|
86
|
-
|
|
229
|
+
flex: 1;
|
|
230
|
+
overflow-y: auto;
|
|
231
|
+
min-height: 0;
|
|
232
|
+
padding: 8px 4px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.empty-state {
|
|
236
|
+
height: 300px;
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: center;
|
|
239
|
+
justify-content: center;
|
|
87
240
|
}
|
|
88
241
|
|
|
89
242
|
.footer-actions {
|
|
90
|
-
margin-top: 16px;
|
|
91
243
|
display: flex;
|
|
92
244
|
justify-content: flex-end;
|
|
93
|
-
|
|
245
|
+
padding-top: 12px;
|
|
246
|
+
border-top: 1px solid #f0f0f0;
|
|
94
247
|
}
|
|
95
248
|
}
|
|
96
249
|
|
|
97
|
-
/*
|
|
98
|
-
📱 iPad Mini (481px – 768px)
|
|
99
|
-
=============================== */
|
|
250
|
+
/* TABLET / MOBILE */
|
|
100
251
|
@media (max-width: 768px) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
gap: 12px;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.assign-role .left-panel {
|
|
107
|
-
width: 100%;
|
|
108
|
-
}
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
height: auto;
|
|
109
254
|
|
|
110
|
-
.
|
|
255
|
+
.left-panel,
|
|
256
|
+
.right-panel {
|
|
111
257
|
width: 100%;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
/* Better spacing for tablets */
|
|
115
|
-
.assign-role .right-panel {
|
|
116
|
-
padding: 14px;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.assign-role .footer-actions {
|
|
120
|
-
justify-content: flex-end;
|
|
258
|
+
height: auto;
|
|
121
259
|
}
|
|
122
260
|
}
|
|
123
261
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const avatarColors = [
|
|
2
|
+
'#5B8FF9',
|
|
3
|
+
'#61DDAA',
|
|
4
|
+
'#65789B',
|
|
5
|
+
'#a0d911',
|
|
6
|
+
'#F6BD16',
|
|
7
|
+
'#7262FD',
|
|
8
|
+
'#faad14',
|
|
9
|
+
'#78D3F8',
|
|
10
|
+
'#9661BC',
|
|
11
|
+
'#F6903D',
|
|
12
|
+
'#008685',
|
|
13
|
+
'#F08BB4',
|
|
14
|
+
'#722ed1',
|
|
15
|
+
'#eb2f96',
|
|
16
|
+
'#13c2c2',
|
|
17
|
+
'#eb2f96',
|
|
18
|
+
'#fa8c16',
|
|
19
|
+
'#52c41a',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export const getAvatarProps = (name) => {
|
|
23
|
+
const safeName = (name ?? '').toString().trim();
|
|
24
|
+
|
|
25
|
+
// Find first alphabetic character
|
|
26
|
+
const match = safeName.match(/[A-Za-z]/);
|
|
27
|
+
const letter = match ? match[0].toUpperCase() : '-';
|
|
28
|
+
|
|
29
|
+
// deterministic color based on string
|
|
30
|
+
let hash = 0;
|
|
31
|
+
for (let i = 0; i < safeName.length; i++) {
|
|
32
|
+
hash = safeName.charCodeAt(i) + ((hash << 5) - hash);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const color = avatarColors[Math.abs(hash) % avatarColors.length] || '#999';
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
letter,
|
|
39
|
+
style: {
|
|
40
|
+
backgroundColor: color,
|
|
41
|
+
color: '#fff',
|
|
42
|
+
fontWeight: 600,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -269,7 +269,7 @@ export default function ReportingDashboard({
|
|
|
269
269
|
getPatientDetails();
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
const fetchReportData = async (id, values, dbPtr, pagination, parsedColumns,paramsString) => {
|
|
272
|
+
const fetchReportData = async (id, values, dbPtr, pagination, parsedColumns, paramsString) => {
|
|
273
273
|
const { current, pageSize } = pagination || {};
|
|
274
274
|
// If card script id is exist load that id otherwise load id
|
|
275
275
|
const coreScriptId = scriptId.current ? scriptId.current : id;
|
|
@@ -314,8 +314,12 @@ export default function ReportingDashboard({
|
|
|
314
314
|
|
|
315
315
|
// Fetch result
|
|
316
316
|
const result = await CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr);
|
|
317
|
+
|
|
318
|
+
const apiData = Array.isArray(result) ? result : Array.isArray(result?.result) ? result.result : [];
|
|
319
|
+
|
|
317
320
|
// Handle both result formats
|
|
318
|
-
let resultDetails =
|
|
321
|
+
let resultDetails = apiData[0] || [];
|
|
322
|
+
|
|
319
323
|
if (result?.result && result?.result[0]) {
|
|
320
324
|
resultDetails = result.result[0];
|
|
321
325
|
}
|
|
@@ -346,7 +350,6 @@ export default function ReportingDashboard({
|
|
|
346
350
|
}
|
|
347
351
|
} catch (error) {
|
|
348
352
|
console.error('Error fetching report data:', error);
|
|
349
|
-
message.warn('Please try again');
|
|
350
353
|
} finally {
|
|
351
354
|
// Always runs, success or error
|
|
352
355
|
setLoading(false);
|
|
@@ -445,7 +448,7 @@ export default function ReportingDashboard({
|
|
|
445
448
|
|
|
446
449
|
// Call API
|
|
447
450
|
try {
|
|
448
|
-
await fetchReportData(id, values, dbPtr, paginationData, parsedColumns,paramsString);
|
|
451
|
+
await fetchReportData(id, values, dbPtr, paginationData, parsedColumns, paramsString);
|
|
449
452
|
} finally {
|
|
450
453
|
setLoading(false);
|
|
451
454
|
setCardLoading(false);
|
|
@@ -608,7 +611,6 @@ function GuestList({
|
|
|
608
611
|
attributes,
|
|
609
612
|
fetchReportData,
|
|
610
613
|
}) {
|
|
611
|
-
|
|
612
614
|
/**
|
|
613
615
|
* @param {*} propValues
|
|
614
616
|
*/
|