ui-soxo-bootstrap-core 2.6.1-dev.3 → 2.6.1-dev.31
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/components/extra-info/extra-info-details.js +2 -2
- package/core/components/index.js +2 -11
- package/core/components/landing-api/landing-api.js +216 -18
- package/core/components/landing-api/landing-api.scss +22 -0
- package/core/components/license-management/license-alert.js +97 -0
- package/core/lib/Store.js +8 -4
- package/core/lib/components/global-header/global-header.js +217 -242
- package/core/lib/components/index.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +19 -13
- package/core/lib/components/sidemenu/sidemenu.scss +1 -1
- package/core/lib/elements/basic/country-phone-input/country-phone-input.js +14 -9
- 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 +525 -468
- package/core/lib/models/forms/components/form-creator/form-creator.scss +30 -26
- package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
- package/core/lib/models/process/components/process-dashboard/process-dashboard.js +469 -3
- package/core/lib/models/process/components/process-dashboard/process-dashboard.scss +4 -0
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +28 -2
- 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 +2 -2
- package/core/lib/pages/login/login.js +53 -64
- package/core/lib/pages/login/login.scss +9 -0
- package/core/lib/pages/login/reset-password.js +17 -17
- package/core/lib/pages/login/reset-password.scss +10 -1
- package/core/lib/pages/profile/themes.json +4 -4
- package/core/lib/utils/api/api.utils.js +53 -45
- package/core/lib/utils/common/common.utils.js +49 -35
- package/core/lib/utils/generic/generic.utils.js +2 -1
- package/core/lib/utils/http/http.utils.js +33 -4
- package/core/lib/utils/index.js +4 -1
- package/core/models/base/base.js +7 -3
- package/core/models/core-scripts/core-scripts.js +147 -126
- 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 +53 -54
- package/core/models/menus/menus.js +49 -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/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 +46 -55
- package/core/models/users/components/user-add/user-edit.js +25 -4
- package/core/models/users/users.js +9 -1
- 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 +174 -0
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +90 -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 +448 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +199 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +195 -822
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +43 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +517 -0
- package/core/modules/steps/action-buttons.js +30 -16
- package/core/modules/steps/action-buttons.scss +55 -9
- package/core/modules/steps/chat-assistant.js +141 -0
- package/core/modules/steps/openai-realtime.js +275 -0
- package/core/modules/steps/readme.md +167 -0
- package/core/modules/steps/steps.js +1286 -60
- package/core/modules/steps/steps.scss +703 -86
- package/core/modules/steps/timeline.js +21 -19
- package/core/modules/steps/voice-navigation.js +709 -0
- package/package.json +2 -1
|
@@ -6,7 +6,7 @@ import { Skeleton, Typography, message, Switch, Form, Input, Select, Checkbox, R
|
|
|
6
6
|
|
|
7
7
|
import AsyncSelect from 'react-select/async';
|
|
8
8
|
|
|
9
|
-
import { Table, Card, Button, JSONInput, GlobalContext, safeJSON } from './../../../../lib/';
|
|
9
|
+
import { Table, Card, Button, JSONInput, GlobalContext, safeJSON, CountryPhoneInput, phoneValidator } from './../../../../lib/';
|
|
10
10
|
|
|
11
11
|
import { ModelsAPI, PagesAPI, RolesAPI } from '../../..';
|
|
12
12
|
|
|
@@ -30,7 +30,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
30
30
|
const [selectedBranches, setSelectedBranches] = useState([]);
|
|
31
31
|
|
|
32
32
|
// for default branch
|
|
33
|
-
const [defaultBranch, setDefaultBranch] = useState(null);
|
|
33
|
+
// const [defaultBranch, setDefaultBranch] = useState(null);
|
|
34
34
|
//Need to check this condition
|
|
35
35
|
const [authentication, setAuthentication] = useState(false);
|
|
36
36
|
|
|
@@ -95,7 +95,10 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
95
95
|
const [body, setBody] = useState(formContent);
|
|
96
96
|
|
|
97
97
|
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
// state for doctor and staff visibility
|
|
100
|
+
const [doctorVisible, setDoctorVisible] = useState(false);
|
|
101
|
+
const [staffVisible, setStaffVisible] = useState(false);
|
|
99
102
|
|
|
100
103
|
const formData = {
|
|
101
104
|
...body,
|
|
@@ -270,8 +273,6 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
270
273
|
const getStaff = () => {
|
|
271
274
|
UsersAPI.getAllStaff()
|
|
272
275
|
.then((res) => {
|
|
273
|
-
console.log('Staff List Response:', res);
|
|
274
|
-
|
|
275
276
|
if (Array.isArray(res)) {
|
|
276
277
|
const list = res.map((staff) => ({
|
|
277
278
|
label: `${staff.shortName || 'No Name'} (${staff.id})`,
|
|
@@ -325,20 +326,25 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
325
326
|
}
|
|
326
327
|
if (!formContent) return;
|
|
327
328
|
|
|
328
|
-
// normalize branch ids to NUMBER
|
|
329
329
|
const org =
|
|
330
330
|
typeof formContent.organization_details === 'string' ? JSON.parse(formContent.organization_details) : formContent.organization_details;
|
|
331
331
|
|
|
332
|
+
// normalize branch ids to NUMBER
|
|
332
333
|
const branchIds = (org?.branch_ids || []).map(Number);
|
|
333
|
-
|
|
334
|
+
|
|
335
|
+
// find default branch pointer
|
|
336
|
+
const defaultBranchObj = org?.branch?.find((br) => br.defaultBranch);
|
|
337
|
+
|
|
338
|
+
// extract dbPtr (branch code)
|
|
339
|
+
const defaultBranchCode = defaultBranchObj?.branch_id ? Number(defaultBranchObj.branch_id) : undefined;
|
|
334
340
|
|
|
335
341
|
// state (for filtering)
|
|
336
342
|
setSelectedBranches(branchIds);
|
|
337
343
|
|
|
338
|
-
// form
|
|
344
|
+
// form values
|
|
339
345
|
form.setFieldsValue({
|
|
340
346
|
selectedBranches: branchIds,
|
|
341
|
-
defaultBranch:
|
|
347
|
+
defaultBranch: defaultBranchCode,
|
|
342
348
|
});
|
|
343
349
|
}, [formContent, form]);
|
|
344
350
|
// Generate branch options for Select component
|
|
@@ -351,6 +357,10 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
351
357
|
* Submit values
|
|
352
358
|
*/
|
|
353
359
|
const onSubmit = (values) => {
|
|
360
|
+
const mobileData = values.mobile;
|
|
361
|
+
|
|
362
|
+
const mobileWithCountryCode = `+${mobileData.code.dialCode}${mobileData.value}`;
|
|
363
|
+
|
|
354
364
|
values.defaultBranch = String(values.defaultBranch);
|
|
355
365
|
|
|
356
366
|
/**If PanelOpen is open and edit mode then password will be existing password else new password*/
|
|
@@ -372,6 +382,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
372
382
|
values = {
|
|
373
383
|
...values,
|
|
374
384
|
auth_type: 'LDAP',
|
|
385
|
+
mobile: mobileWithCountryCode,
|
|
375
386
|
FA: authentication,
|
|
376
387
|
};
|
|
377
388
|
|
|
@@ -538,31 +549,17 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
538
549
|
borderTop: '1px solid #f0f0f0',
|
|
539
550
|
color: '#1890ff',
|
|
540
551
|
}}
|
|
541
|
-
onClick={() =>
|
|
552
|
+
onClick={() => setDoctorVisible(true)}
|
|
542
553
|
>
|
|
543
554
|
+ Add New Doctor
|
|
544
555
|
</div>
|
|
545
556
|
</>
|
|
546
557
|
)}
|
|
547
558
|
/>
|
|
548
|
-
{/* Render DoctorAdd OUTSIDE the Select */}
|
|
549
|
-
<DoctorAdd
|
|
550
|
-
visible={visible}
|
|
551
|
-
onCancel={() => setVisible(false)}
|
|
552
|
-
attributes={attributes}
|
|
553
|
-
doctorData={selectedDoctor}
|
|
554
|
-
doctorId={doctorID}
|
|
555
|
-
onSuccess={getDoctors}
|
|
556
|
-
/>
|
|
557
559
|
</Form.Item>
|
|
558
560
|
)}
|
|
559
561
|
{userType === 'STAFF' && (
|
|
560
|
-
<Form.Item
|
|
561
|
-
name="staff_code"
|
|
562
|
-
preserve={false} // THIS FIXES IT
|
|
563
|
-
label="Staff Code"
|
|
564
|
-
rules={[{ required: true, message: 'Please select a staff code' }]}
|
|
565
|
-
>
|
|
562
|
+
<Form.Item name="staff_code" label="Staff Code" rules={[{ required: true, message: 'Please select a staff code' }]}>
|
|
566
563
|
<Select
|
|
567
564
|
placeholder="Select Code"
|
|
568
565
|
options={staffList}
|
|
@@ -581,18 +578,13 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
581
578
|
borderTop: '1px solid #f0f0f0',
|
|
582
579
|
color: '#1890ff',
|
|
583
580
|
}}
|
|
584
|
-
onClick={() =>
|
|
581
|
+
onClick={() => setStaffVisible(true)}
|
|
585
582
|
>
|
|
586
583
|
+ Add New Staff
|
|
587
584
|
</div>
|
|
588
585
|
</>
|
|
589
586
|
)}
|
|
590
587
|
/>
|
|
591
|
-
{/* Render DoctorAdd OUTSIDE the Select */}
|
|
592
|
-
|
|
593
|
-
<StaffAdd visible={visible} onCancel={() => setVisible(false)} staffData={selectedStaff} staffId={staffID} onSuccess={getStaff} />
|
|
594
|
-
|
|
595
|
-
<></>
|
|
596
588
|
</Form.Item>
|
|
597
589
|
)}
|
|
598
590
|
</Col>
|
|
@@ -610,23 +602,10 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
610
602
|
<Form.Item
|
|
611
603
|
name="mobile"
|
|
612
604
|
label="Mobile"
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
{
|
|
616
|
-
pattern: /^[0-9]{10}$/,
|
|
617
|
-
message: 'Mobile number must be exactly 10 digits',
|
|
618
|
-
},
|
|
619
|
-
]}
|
|
605
|
+
validateTrigger="onBlur"
|
|
606
|
+
rules={[{ required: true, message: 'Mobile number required' }, { validator: phoneValidator }]}
|
|
620
607
|
>
|
|
621
|
-
<
|
|
622
|
-
placeholder="Enter Mobile"
|
|
623
|
-
maxLength={10}
|
|
624
|
-
onKeyPress={(e) => {
|
|
625
|
-
if (!/[0-9]/.test(e.key)) {
|
|
626
|
-
e.preventDefault();
|
|
627
|
-
}
|
|
628
|
-
}}
|
|
629
|
-
/>
|
|
608
|
+
<CountryPhoneInput defaultCountryCode={process.env.REACT_APP_COUNTRYCODE} enableSearch inputStyle={{ width: '100%' }} />
|
|
630
609
|
</Form.Item>
|
|
631
610
|
</Col>
|
|
632
611
|
<Col span={8}>
|
|
@@ -669,7 +648,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
669
648
|
<Col span={8}>
|
|
670
649
|
{/* Default Branch */}
|
|
671
650
|
<Form.Item label="Default Branch" name="defaultBranch" rules={[{ required: true, message: 'Please select default branch' }]}>
|
|
672
|
-
<Select placeholder="Select Default Branch"
|
|
651
|
+
<Select placeholder="Select Default Branch">
|
|
673
652
|
{branchOptions
|
|
674
653
|
.filter((opt) => selectedBranches.includes(Number(opt.value)))
|
|
675
654
|
.map((opt) => (
|
|
@@ -682,15 +661,20 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
682
661
|
</Col>
|
|
683
662
|
</Row>
|
|
684
663
|
<Row gutter={16}>
|
|
664
|
+
<Col span={8}>
|
|
665
|
+
<Form.Item name="user_group" label="User Group" rules={[{ required: true, message: 'Please enter your user group' }]}>
|
|
666
|
+
<Input placeholder="Enter User Group" />
|
|
667
|
+
</Form.Item>
|
|
668
|
+
</Col>
|
|
685
669
|
<Col span={8}>
|
|
686
670
|
{formContent?.id ? (
|
|
687
671
|
<>
|
|
688
|
-
<Form.Item>
|
|
672
|
+
{/* <Form.Item>
|
|
689
673
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
690
674
|
<Checkbox onChange={handleCheackChange} />
|
|
691
675
|
<span>Select the option if you want to change the password</span>
|
|
692
676
|
</div>
|
|
693
|
-
</Form.Item>
|
|
677
|
+
</Form.Item> */}
|
|
694
678
|
|
|
695
679
|
{isPasswordVisible && (
|
|
696
680
|
<Form.Item
|
|
@@ -743,11 +727,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
743
727
|
{/* Path Ends */}
|
|
744
728
|
|
|
745
729
|
{/* Path */}
|
|
746
|
-
|
|
747
|
-
<Form.Item name="user_group" label="User Group" rules={[{ required: true, message: 'Please enter your user group' }]}>
|
|
748
|
-
<Input placeholder="Enter User Group" />
|
|
749
|
-
</Form.Item>
|
|
750
|
-
</Col>
|
|
730
|
+
|
|
751
731
|
{/* <Col span={8}>
|
|
752
732
|
<Form.Item name="role_id" label="Role" rules={[{ required: true, message: 'Please select a Role' }]}>
|
|
753
733
|
<Select placeholder="Select Role">
|
|
@@ -846,6 +826,17 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
|
|
|
846
826
|
</Form.Item>
|
|
847
827
|
</Form>
|
|
848
828
|
)}
|
|
829
|
+
{/* Render DoctorAdd OUTSIDE the Select */}
|
|
830
|
+
<DoctorAdd
|
|
831
|
+
visible={doctorVisible}
|
|
832
|
+
onCancel={() => setDoctorVisible(false)}
|
|
833
|
+
// attributes={attributes}
|
|
834
|
+
doctorData={selectedDoctor}
|
|
835
|
+
doctorId={doctorID}
|
|
836
|
+
onSuccess={getDoctors}
|
|
837
|
+
/>
|
|
838
|
+
|
|
839
|
+
<StaffAdd visible={staffVisible} onCancel={() => setStaffVisible(false)} staffData={selectedStaff} staffId={staffID} onSuccess={getStaff} />
|
|
849
840
|
</section>
|
|
850
841
|
);
|
|
851
842
|
};
|
|
@@ -3,6 +3,7 @@ import { Modal, Button, Skeleton } from 'antd';
|
|
|
3
3
|
import { EditOutlined } from '@ant-design/icons';
|
|
4
4
|
import UserAdd from '../../../../models/users/components/user-add/user-add';
|
|
5
5
|
import { UsersAPI } from '../../..';
|
|
6
|
+
import { formatPhoneForForm } from '../../../../lib';
|
|
6
7
|
|
|
7
8
|
export default function UserEdit(record) {
|
|
8
9
|
const [visible, setVisible] = useState(false);
|
|
@@ -37,25 +38,35 @@ export default function UserEdit(record) {
|
|
|
37
38
|
} catch (e) {
|
|
38
39
|
orgDetails = {};
|
|
39
40
|
}
|
|
41
|
+
|
|
42
|
+
// find default branch pointer
|
|
43
|
+
const defaultBranchObj = orgDetails?.branch?.find((br) => br.defaultBranch);
|
|
44
|
+
|
|
45
|
+
// extract dbPtr (branch code)
|
|
46
|
+
const defaultBranchCode = defaultBranchObj?.branch_id ? Number(defaultBranchObj.branch_id) : undefined;
|
|
47
|
+
|
|
48
|
+
const formattedMobile = formatPhoneForForm(apiData.mobile);
|
|
49
|
+
|
|
40
50
|
// Construct mapped data object
|
|
41
51
|
const mappedData = {
|
|
42
52
|
id: apiData.id,
|
|
43
53
|
user_type: otherDetails.user_type || apiData.user_type,
|
|
44
54
|
name: apiData.name,
|
|
45
55
|
email: apiData.email,
|
|
46
|
-
mobile:
|
|
56
|
+
mobile: formattedMobile,
|
|
47
57
|
designation: apiData.designation_code,
|
|
48
|
-
department: apiData.department_id,
|
|
58
|
+
department: Number(apiData.department_id),
|
|
49
59
|
// Handle selected branches and default branch
|
|
50
60
|
organization_details: orgDetails,
|
|
51
61
|
selectedBranches: orgDetails.branch_ids || [],
|
|
52
|
-
defaultBranch:
|
|
62
|
+
defaultBranch: defaultBranchCode || null,
|
|
53
63
|
role_id: apiData.role_id,
|
|
54
64
|
password: apiData.password,
|
|
55
65
|
user_group: apiData.user_group,
|
|
56
66
|
// Handle doctor codes
|
|
57
67
|
default_code: apiData.doctor_code,
|
|
58
68
|
doctor_code: apiData.doctor_code,
|
|
69
|
+
staff_code: apiData.staff_id,
|
|
59
70
|
auth_type: apiData.auth_type,
|
|
60
71
|
FA: apiData.FA,
|
|
61
72
|
active: apiData.active ? true : false,
|
|
@@ -81,7 +92,17 @@ export default function UserEdit(record) {
|
|
|
81
92
|
<Skeleton active />
|
|
82
93
|
</div>
|
|
83
94
|
) : (
|
|
84
|
-
<UserAdd
|
|
95
|
+
<UserAdd
|
|
96
|
+
mode="Edit"
|
|
97
|
+
formContent={userData}
|
|
98
|
+
callback={() => {
|
|
99
|
+
setVisible(false);
|
|
100
|
+
if (record.callback) {
|
|
101
|
+
record.callback();
|
|
102
|
+
}
|
|
103
|
+
}}
|
|
104
|
+
edit={true}
|
|
105
|
+
/>
|
|
85
106
|
)}
|
|
86
107
|
</Modal>
|
|
87
108
|
</div>
|
|
@@ -292,7 +292,12 @@ class Users extends Base {
|
|
|
292
292
|
information from the API. */
|
|
293
293
|
|
|
294
294
|
getUser = ({ id }) => {
|
|
295
|
-
return ApiUtils.get({
|
|
295
|
+
return ApiUtils.get({
|
|
296
|
+
url: `users/${id}`,
|
|
297
|
+
headers: {
|
|
298
|
+
db_ptr: 'nuraho',
|
|
299
|
+
},
|
|
300
|
+
});
|
|
296
301
|
};
|
|
297
302
|
/* The `getUserRole` method is a function that takes an object with an `id` property as a parameter.
|
|
298
303
|
It then uses the `ApiUtils.get` function to make a GET request to the endpoint `core-user-roles`
|
|
@@ -364,6 +369,9 @@ class Users extends Base {
|
|
|
364
369
|
return ApiUtils.post({
|
|
365
370
|
url: `bookings/trigger-reset-password-link`,
|
|
366
371
|
formBody,
|
|
372
|
+
headers: {
|
|
373
|
+
db_ptr: 'nuraho',
|
|
374
|
+
},
|
|
367
375
|
});
|
|
368
376
|
};
|
|
369
377
|
}
|
|
@@ -207,7 +207,7 @@ export default function MenuDashboardCard({ record, selectedCardId, scope, dbPtr
|
|
|
207
207
|
|
|
208
208
|
'.statistic-card-container': {
|
|
209
209
|
'.ant-statistic-title': {
|
|
210
|
-
color: 'rgba(0, 0, 0, 0.
|
|
210
|
+
color: 'rgba(0, 0, 0, 0.85)',
|
|
211
211
|
fontSize: '10px',
|
|
212
212
|
marginBottom: '0px',
|
|
213
213
|
},
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Reporting Dashboard
|
|
2
|
+
|
|
3
|
+
This module renders configurable reports using:
|
|
4
|
+
- backend-driven `input_parameters` (filter form)
|
|
5
|
+
- backend-driven `display_columns` (table columns)
|
|
6
|
+
- report data from `CoreScripts.getReportingLisitng(...)`
|
|
7
|
+
|
|
8
|
+
Main component:
|
|
9
|
+
- `core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js`
|
|
10
|
+
|
|
11
|
+
Display column internals:
|
|
12
|
+
- `core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js`
|
|
13
|
+
- `core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js`
|
|
14
|
+
|
|
15
|
+
## Data Flow Overview
|
|
16
|
+
|
|
17
|
+
1. Dashboard loads `CoreScripts.getRecord({ id, dbPtr })`.
|
|
18
|
+
2. It parses:
|
|
19
|
+
- `input_parameters` JSON string -> filter form configuration
|
|
20
|
+
- `display_columns` JSON string -> table column configuration
|
|
21
|
+
3. It submits filter values to `CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr)`.
|
|
22
|
+
4. It renders the table with configured display columns.
|
|
23
|
+
|
|
24
|
+
If `display_columns` is missing, columns are auto-generated from response keys.
|
|
25
|
+
|
|
26
|
+
## ReportingDashboard Props
|
|
27
|
+
|
|
28
|
+
Common props:
|
|
29
|
+
- `match` / `reportId`: report id source (`reportId` overrides route id)
|
|
30
|
+
- `dbPtr`: db pointer (falls back to `localStorage.db_ptr`)
|
|
31
|
+
- `CustomComponents`: map of reusable custom components
|
|
32
|
+
- `scope`: optional object to override filter payload body
|
|
33
|
+
- `dashBoardIds`: optional cards for dashboard switching
|
|
34
|
+
- `showScanner`: enables QR scanner button
|
|
35
|
+
- `barcodeFilterKey`: field used to match scanned QR value
|
|
36
|
+
- `isFixedIndex`: fixes index `#` column to left
|
|
37
|
+
- `attributes`: JSON string for extra UI controls (for example `buttonAttributes`)
|
|
38
|
+
|
|
39
|
+
## Input Parameters Configuration
|
|
40
|
+
|
|
41
|
+
`input_parameters` is stored as a JSON string in backend and parsed at runtime.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
[
|
|
47
|
+
{
|
|
48
|
+
"title": "From Date",
|
|
49
|
+
"field": "start",
|
|
50
|
+
"type": "date",
|
|
51
|
+
"default": "startOfDay",
|
|
52
|
+
"required": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"title": "To Date",
|
|
56
|
+
"field": "end",
|
|
57
|
+
"type": "date",
|
|
58
|
+
"default": "endOfDay",
|
|
59
|
+
"required": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"title": "Department",
|
|
63
|
+
"field": "dept_id",
|
|
64
|
+
"type": "reference-select",
|
|
65
|
+
"modelName": "Department",
|
|
66
|
+
"required": false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"title": "Status",
|
|
70
|
+
"field": "status",
|
|
71
|
+
"type": "select",
|
|
72
|
+
"options": [
|
|
73
|
+
{ "label": "Pending", "value": "Pending" },
|
|
74
|
+
{ "label": "Completed", "value": "Completed" }
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Supported default date values
|
|
81
|
+
|
|
82
|
+
For `type: "date"`, supported `default` values:
|
|
83
|
+
- `startOfDay`
|
|
84
|
+
- `endOfDay`
|
|
85
|
+
- `startOfWeek`
|
|
86
|
+
- `endOfWeek`
|
|
87
|
+
- `currentDate`
|
|
88
|
+
|
|
89
|
+
If no date default is provided, current date is used.
|
|
90
|
+
|
|
91
|
+
### URL synchronization
|
|
92
|
+
|
|
93
|
+
- On load, if URL contains `?field=value`, that value is used over defaults.
|
|
94
|
+
- On submit, active input values are written back to URL query params.
|
|
95
|
+
- Date values are URL-formatted as `YYYY-MM-DD`.
|
|
96
|
+
|
|
97
|
+
### Hiding input parameter form
|
|
98
|
+
|
|
99
|
+
If `other_details1` JSON includes:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{ "isDisableInputParameters": true }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
the input form is hidden, but report data still loads.
|
|
106
|
+
|
|
107
|
+
## Display Columns Configuration
|
|
108
|
+
|
|
109
|
+
`display_columns` is also a backend JSON string.
|
|
110
|
+
|
|
111
|
+
Each entry maps to one table column.
|
|
112
|
+
|
|
113
|
+
Basic example:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
[
|
|
117
|
+
{ "title": "OP No", "field": "opno", "isSortingEnabled": true },
|
|
118
|
+
{ "title": "Guest Name", "field": "guest_name", "isFilterEnabled": true },
|
|
119
|
+
{ "title": "Amount", "field": "amount", "type": "number", "width": 120 }
|
|
120
|
+
]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Common column keys
|
|
124
|
+
|
|
125
|
+
- `title`: header text
|
|
126
|
+
- `tooltip`: optional header tooltip override
|
|
127
|
+
- `field`: record key to display
|
|
128
|
+
- `width`: numeric width (default `160`)
|
|
129
|
+
- `isFixedColumn`: AntD fixed position (`"left"` / `"right"`)
|
|
130
|
+
- `isFilterEnabled`: generates unique-value filters from table data
|
|
131
|
+
- `isSortingEnabled`: enables string sort on `field`
|
|
132
|
+
- `color`: default text color
|
|
133
|
+
- `type: "number"`: right-aligns cell
|
|
134
|
+
|
|
135
|
+
## Action Columns (Backward-Compatible Rules)
|
|
136
|
+
|
|
137
|
+
Action logic is intentionally layered to avoid breaking existing reports.
|
|
138
|
+
|
|
139
|
+
Compatibility rules:
|
|
140
|
+
1. `entry.field === "action"` -> **legacy action** behavior
|
|
141
|
+
2. else if `entry.type === "action"` -> **new action layer**
|
|
142
|
+
3. all other types use existing non-action behavior
|
|
143
|
+
|
|
144
|
+
### Legacy action example (existing reports)
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"title": "Action",
|
|
149
|
+
"field": "action",
|
|
150
|
+
"redirect_link": "/process/@opb_id;",
|
|
151
|
+
"replace_variables": [{ "field": "opb_id" }],
|
|
152
|
+
"display_name_link": "View"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Legacy label behavior:
|
|
157
|
+
- `display_name_link` or `"View"`
|
|
158
|
+
|
|
159
|
+
### New action layer example (dynamic API label)
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"title": "Action",
|
|
164
|
+
"type": "action",
|
|
165
|
+
"field": "action_text",
|
|
166
|
+
"redirect_link": "/process/@opb_id;",
|
|
167
|
+
"replace_variables": [{ "field": "opb_id" }],
|
|
168
|
+
"label": "View"
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
New action label fallback order:
|
|
173
|
+
1. `record[entry.field]` (example: `record.action_text`)
|
|
174
|
+
2. `entry.label`
|
|
175
|
+
3. `entry.display_name_link`
|
|
176
|
+
4. `"View"`
|
|
177
|
+
|
|
178
|
+
## Other Display Types
|
|
179
|
+
|
|
180
|
+
### External link column
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{ "title": "Report", "field": "report_url", "type": "link" }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Renders anchor with `View` label if URL exists.
|
|
187
|
+
|
|
188
|
+
### Color tag or span
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{ "title": "Status", "field": "status", "enableColor": true, "columnType": "tag" }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
or
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{ "title": "Status", "field": "status", "enableColor": true, "columnType": "span" }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Uses `record.color_code`.
|
|
201
|
+
|
|
202
|
+
### Dynamic icon mapping
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"title": "State",
|
|
207
|
+
"field": "state",
|
|
208
|
+
"displayIcons": {
|
|
209
|
+
"Pending": { "icon": "ClockCircleOutlined", "color": "orange", "size": "14px" },
|
|
210
|
+
"Done": { "icon": "CheckCircleOutlined", "color": "green", "size": "14px" }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Icon names are taken from `@ant-design/icons`.
|
|
216
|
+
|
|
217
|
+
### Custom component column
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"title": "Info",
|
|
222
|
+
"field": "custom",
|
|
223
|
+
"component": "ResultEntryInfo",
|
|
224
|
+
"props": [
|
|
225
|
+
{ "field": "description_text", "value": "description" },
|
|
226
|
+
{ "field": "opb_id", "value": "opbId" }
|
|
227
|
+
],
|
|
228
|
+
"config": { "allowEdit": true }
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
How it resolves:
|
|
233
|
+
- `component` must exist in merged `CustomComponents`
|
|
234
|
+
- each `props` item maps `record[field]` to prop key `value`
|
|
235
|
+
- `config` is spread as static props
|
|
236
|
+
- `callback` prop triggers dashboard `refresh()`
|
|
237
|
+
|
|
238
|
+
## Redirect Template Variables
|
|
239
|
+
|
|
240
|
+
Action redirects support template replacement in `redirect_link`:
|
|
241
|
+
- placeholder format: `@field;`
|
|
242
|
+
- replacements defined in `replace_variables`
|
|
243
|
+
|
|
244
|
+
Example:
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"redirect_link": "/lab/@opb_id;/visit/@opno;",
|
|
249
|
+
"replace_variables": [{ "field": "opb_id" }, { "field": "opno" }]
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
With row `{ "opb_id": 12, "opno": "OP-55" }`:
|
|
254
|
+
- result -> `/lab/12/visit/OP-55`
|
|
255
|
+
|
|
256
|
+
## QR Scanner Behavior
|
|
257
|
+
|
|
258
|
+
When scanner is enabled:
|
|
259
|
+
- scanned value is matched against `patient[barcodeFilterKey]`
|
|
260
|
+
- redirect uses first configured action column with this priority:
|
|
261
|
+
1. legacy action column (`field === "action"`)
|
|
262
|
+
2. new action column (`type === "action"`)
|
|
263
|
+
|
|
264
|
+
## Export Behavior
|
|
265
|
+
|
|
266
|
+
- Export uses current table columns.
|
|
267
|
+
- For `field === "custom"`, export attempts to read the prop mapping where `value === "description"` and exports that source field.
|
|
268
|
+
- Other columns export `record[field]`.
|
|
269
|
+
|
|
270
|
+
## End-to-End Example
|
|
271
|
+
|
|
272
|
+
`display_columns`:
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
[
|
|
276
|
+
{ "title": "OP No", "field": "opno", "isSortingEnabled": true, "width": 140 },
|
|
277
|
+
{ "title": "Guest", "field": "guest_name", "isFilterEnabled": true, "width": 220 },
|
|
278
|
+
{
|
|
279
|
+
"title": "Status",
|
|
280
|
+
"field": "status",
|
|
281
|
+
"enableColor": true,
|
|
282
|
+
"columnType": "tag"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"title": "Action",
|
|
286
|
+
"type": "action",
|
|
287
|
+
"field": "action_text",
|
|
288
|
+
"redirect_link": "/process-detail/@opb_id;",
|
|
289
|
+
"replace_variables": [{ "field": "opb_id" }],
|
|
290
|
+
"label": "View"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Sample API row:
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"opb_id": 1001,
|
|
300
|
+
"opno": "OP-1001",
|
|
301
|
+
"guest_name": "Aisha Rahman",
|
|
302
|
+
"status": "Pending",
|
|
303
|
+
"color_code": "orange",
|
|
304
|
+
"action_text": "Start Consultation"
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Rendered action label:
|
|
309
|
+
- `Start Consultation` (dynamic value from API)
|
|
310
|
+
|
|
311
|
+
## Notes for Developers
|
|
312
|
+
|
|
313
|
+
- Keep `display_columns` as JSON string in DB for backward compatibility.
|
|
314
|
+
- Prefer `type: "action"` for new dynamic action labels.
|
|
315
|
+
- Reserve `field: "action"` for legacy behavior.
|
|
316
|
+
- If adding new display types, implement in `display-cell-renderer.js` and keep order explicit.
|