ui-soxo-bootstrap-core 2.6.40-dev.16 → 2.6.40
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/DEVELOPER_GUIDE.md +9 -38
- package/core/components/index.js +11 -2
- package/core/components/landing-api/landing-api.js +5 -165
- package/core/lib/components/global-header/global-header.js +77 -20
- package/core/lib/components/index.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +147 -66
- package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +24 -91
- package/core/lib/models/menus/components/menu-add/menu-add.js +2 -2
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +8 -334
- package/core/lib/modules/generic/generic-list/generic-list.scss +0 -34
- package/core/models/core-scripts/core-scripts.js +1 -22
- package/core/models/menus/components/menu-add/menu-add.js +4 -31
- package/core/models/menus/components/menu-lists/menu-lists.js +9 -76
- package/core/models/menus/menus.js +1 -29
- package/core/models/roles/components/role-add/role-add.js +169 -54
- package/core/models/roles/components/role-list/role-list.js +0 -20
- package/core/models/roles/roles.js +0 -3
- package/core/models/users/components/assign-role/assign-role.js +8 -23
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +5 -202
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +0 -73
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +35 -56
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +0 -1
- package/package.json +1 -1
- package/core/components/license-management/license-alert.js +0 -97
- package/core/models/roles/components/role-add/menu-label.js +0 -14
- package/core/models/roles/components/role-add/menu-tree.js +0 -127
- package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +0 -519
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Tag } from 'antd';
|
|
3
3
|
import * as Icons from '@ant-design/icons';
|
|
4
4
|
import { Link } from 'react-router-dom';
|
|
5
|
-
import { GlobalContext, safeJSON, Location } from '../../../../../lib';
|
|
6
|
-
// import { PdfViewer } from '../../../../../lib';
|
|
7
|
-
import { CoreScripts } from '../../../../../models';
|
|
8
5
|
|
|
9
6
|
/**
|
|
10
7
|
* Utilities for rendering Reporting Dashboard display columns.
|
|
@@ -77,7 +74,7 @@ export function isActionTypeEntry(entry = {}) {
|
|
|
77
74
|
* @param {DisplayRecord} record
|
|
78
75
|
* @returns {string}
|
|
79
76
|
*/
|
|
80
|
-
export function getRedirectLink(entry = {}, record = {}
|
|
77
|
+
export function getRedirectLink(entry = {}, record = {}) {
|
|
81
78
|
let redirectLink = entry.redirect_link || '';
|
|
82
79
|
|
|
83
80
|
if (Array.isArray(entry.replace_variables)) {
|
|
@@ -90,41 +87,6 @@ export function getRedirectLink(entry = {}, record = {}, CustomComponents = {})
|
|
|
90
87
|
return redirectLink;
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
/**
|
|
94
|
-
* Resolves the PDF/file location for file-based action entries.
|
|
95
|
-
*
|
|
96
|
-
* Supports either:
|
|
97
|
-
* - `entry.file_location` as a direct URL/path
|
|
98
|
-
* - `entry.file_location` as a record field name
|
|
99
|
-
*
|
|
100
|
-
* @param {DisplayColumnEntry} entry
|
|
101
|
-
* @param {DisplayRecord} record
|
|
102
|
-
* @returns {string}
|
|
103
|
-
*/
|
|
104
|
-
export function getFileLocation(entry = {}, record = {}) {
|
|
105
|
-
if (entry.file_location && typeof entry.file_location === 'string' && record[entry.file_location]) {
|
|
106
|
-
return record[entry.file_location];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return record.file_location || entry.file_location || '';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Resolves a value from the row when a config field points to a record key,
|
|
114
|
-
* otherwise returns the provided literal value.
|
|
115
|
-
*
|
|
116
|
-
* @param {*} value
|
|
117
|
-
* @param {DisplayRecord} record
|
|
118
|
-
* @returns {*}
|
|
119
|
-
*/
|
|
120
|
-
function resolveRecordValue(value, record = {}) {
|
|
121
|
-
if (typeof value === 'string' && Object.prototype.hasOwnProperty.call(record, value)) {
|
|
122
|
-
return record[value];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return value;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
90
|
/**
|
|
129
91
|
* Resolves action label text with backward-compatible precedence.
|
|
130
92
|
*
|
|
@@ -208,154 +170,6 @@ function renderCustomComponent({ entry, record, CustomComponents, refresh }) {
|
|
|
208
170
|
);
|
|
209
171
|
}
|
|
210
172
|
|
|
211
|
-
/**
|
|
212
|
-
* Renders a PDF file action inside a modal viewer.
|
|
213
|
-
*
|
|
214
|
-
* @param {Object} root0
|
|
215
|
-
* @param {DisplayColumnEntry} root0.entry
|
|
216
|
-
* @param {DisplayRecord} root0.record
|
|
217
|
-
* @param {Object.<string, React.ComponentType<any>>} root0.CustomComponents
|
|
218
|
-
* @returns {React.ReactNode}
|
|
219
|
-
*/
|
|
220
|
-
function FileActionLink({ entry, record, CustomComponents }) {
|
|
221
|
-
const [isPdfVisible, setIsPdfVisible] = useState(false);
|
|
222
|
-
const fileUrl = getFileLocation(entry, record);
|
|
223
|
-
const actionLabel = getActionLabel(entry, record);
|
|
224
|
-
const FileLoaderComponent = CustomComponents?.FileLoader;
|
|
225
|
-
|
|
226
|
-
if (!fileUrl) {
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const fileLoaderProps = {
|
|
231
|
-
url: fileUrl,
|
|
232
|
-
type: resolveRecordValue(entry.file_type, record) || 'pdf',
|
|
233
|
-
defaultScale: resolveRecordValue(entry.default_scale, record),
|
|
234
|
-
viewerType: resolveRecordValue(entry.viewer_type, record),
|
|
235
|
-
config: {
|
|
236
|
-
requireLinuxPath: resolveRecordValue(entry.require_linux_path, record),
|
|
237
|
-
replaceBranch: resolveRecordValue(entry.replace_branch, record),
|
|
238
|
-
...(entry.file_loader_config || {}),
|
|
239
|
-
},
|
|
240
|
-
entry,
|
|
241
|
-
record,
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
return (
|
|
245
|
-
<>
|
|
246
|
-
{/* 1. Add the trigger link/button here */}
|
|
247
|
-
<a
|
|
248
|
-
onClick={(e) => {
|
|
249
|
-
e.preventDefault();
|
|
250
|
-
setIsPdfVisible(true);
|
|
251
|
-
}}
|
|
252
|
-
style={{ cursor: 'pointer' }}
|
|
253
|
-
>
|
|
254
|
-
{actionLabel}
|
|
255
|
-
</a>
|
|
256
|
-
|
|
257
|
-
<Modal
|
|
258
|
-
open={isPdfVisible}
|
|
259
|
-
onCancel={() => setIsPdfVisible(false)}
|
|
260
|
-
footer={null}
|
|
261
|
-
destroyOnClose
|
|
262
|
-
width={950}
|
|
263
|
-
style={{ top: 10 }}
|
|
264
|
-
title={actionLabel}
|
|
265
|
-
>
|
|
266
|
-
{/* 2. Ensure the loader component exists */}
|
|
267
|
-
{FileLoaderComponent ? <FileLoaderComponent {...fileLoaderProps} /> : <p>Loader not found</p>}
|
|
268
|
-
</Modal>
|
|
269
|
-
</>
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Returns the branch access list from the signed-in user's organization details.
|
|
275
|
-
*
|
|
276
|
-
* `organization_details` may arrive as a JSON string, so this helper keeps the
|
|
277
|
-
* parsing logic in one place before branch comparisons are made.
|
|
278
|
-
*
|
|
279
|
-
* @param {Object} user
|
|
280
|
-
* @returns {Array}
|
|
281
|
-
*/
|
|
282
|
-
function getAccessibleBranches(user = {}) {
|
|
283
|
-
const orgDetails = safeJSON(user?.organization_details);
|
|
284
|
-
return Array.isArray(orgDetails?.branch) ? orgDetails.branch : [];
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Resolves branch metadata used to decide whether an action link should prompt
|
|
289
|
-
* for a branch switch before navigation.
|
|
290
|
-
*
|
|
291
|
-
* When an action entry includes `replace_variables` with the `index` field, the
|
|
292
|
-
* row is treated as branch-aware content. We compare the record's branch id
|
|
293
|
-
* against the active branch from `localStorage.db_ptr`, and also resolve whether
|
|
294
|
-
* the user has access to the target branch.
|
|
295
|
-
*
|
|
296
|
-
* @param {DisplayColumnEntry} entry
|
|
297
|
-
* @param {DisplayRecord} record
|
|
298
|
-
* @param {Object} user
|
|
299
|
-
* @returns {{requiresBranchSwitch: boolean, hasTargetBranchAccess: boolean}}
|
|
300
|
-
*/
|
|
301
|
-
function getBranchNavigationState(entry = {}, record = {}, user = {}) {
|
|
302
|
-
const branchFieldConfig = entry.replace_variables?.find((variable) => variable.field === 'index');
|
|
303
|
-
|
|
304
|
-
if (!branchFieldConfig) {
|
|
305
|
-
return { requiresBranchSwitch: false, hasTargetBranchAccess: false };
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const accessibleBranches = getAccessibleBranches(user);
|
|
309
|
-
const activeDbPtr = localStorage.getItem('db_ptr');
|
|
310
|
-
const activeBranch = accessibleBranches.find((branch) => String(branch.dbPtr) === String(activeDbPtr));
|
|
311
|
-
const targetBranchId = record[branchFieldConfig.field];
|
|
312
|
-
const targetBranch = accessibleBranches.find((branch) => String(branch.branch_id) === String(targetBranchId));
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
requiresBranchSwitch: Boolean(targetBranchId) && String(activeBranch?.branch_id) !== String(targetBranchId),
|
|
316
|
-
hasTargetBranchAccess: Boolean(targetBranch),
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Renders a branch-switch confirmation link for cross-branch records.
|
|
322
|
-
*
|
|
323
|
-
* The actual branch change is handled by the landing page via the `index`
|
|
324
|
-
* query parameter. This link only confirms intent and blocks navigation when
|
|
325
|
-
* the user does not have access to the target branch.
|
|
326
|
-
*
|
|
327
|
-
* @param {Object} root0
|
|
328
|
-
* @param {string} root0.label
|
|
329
|
-
* @param {string} root0.redirectLink
|
|
330
|
-
* @param {boolean} root0.hasTargetBranchAccess
|
|
331
|
-
* @returns {React.ReactNode}
|
|
332
|
-
*/
|
|
333
|
-
function BranchAwareActionLink({ label, redirectLink, hasTargetBranchAccess }) {
|
|
334
|
-
return (
|
|
335
|
-
<a
|
|
336
|
-
onClick={(e) => {
|
|
337
|
-
e.preventDefault();
|
|
338
|
-
Modal.confirm({
|
|
339
|
-
title: 'Switch Branch?', // Adding a title makes it look more standard
|
|
340
|
-
content: 'This record belongs to another branch. Would you like to switch branches to view the details?',
|
|
341
|
-
onOk: () => {
|
|
342
|
-
if (hasTargetBranchAccess) {
|
|
343
|
-
Location.navigate({ url: redirectLink });
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
Modal.error({
|
|
348
|
-
title: 'Access Denied',
|
|
349
|
-
content: 'This data belongs to another branch.Would you like to switch branches to view the details?',
|
|
350
|
-
});
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
}}
|
|
354
|
-
>
|
|
355
|
-
{label}
|
|
356
|
-
</a>
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
173
|
/**
|
|
360
174
|
* Renders table cell content for a configured display column.
|
|
361
175
|
*
|
|
@@ -396,21 +210,10 @@ export function renderDisplayCell({ entry, record, CustomComponents, refresh })
|
|
|
396
210
|
}
|
|
397
211
|
return null;
|
|
398
212
|
}
|
|
399
|
-
if (isLegacyActionEntry(entry) || isActionTypeEntry(entry)) {
|
|
400
|
-
if (entry.redirect_link_type === 'file') {
|
|
401
|
-
return <FileActionLink entry={entry} record={record} CustomComponents={CustomComponents} />;
|
|
402
|
-
}
|
|
403
|
-
const { user = {} } = useContext(GlobalContext);
|
|
404
213
|
|
|
214
|
+
if (isLegacyActionEntry(entry) || isActionTypeEntry(entry)) {
|
|
405
215
|
const redirectLink = getRedirectLink(entry, record);
|
|
406
|
-
|
|
407
|
-
const { requiresBranchSwitch, hasTargetBranchAccess } = getBranchNavigationState(entry, record, user);
|
|
408
|
-
|
|
409
|
-
if (requiresBranchSwitch) {
|
|
410
|
-
return <BranchAwareActionLink label={label} redirectLink={redirectLink} hasTargetBranchAccess={hasTargetBranchAccess} />;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return <Link to={`${redirectLink}`}>{label}</Link>;
|
|
216
|
+
return <Link to={`${redirectLink}`}>{getActionLabel(entry, record)}</Link>;
|
|
414
217
|
}
|
|
415
218
|
|
|
416
219
|
if (entry.field === 'custom') {
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
isLegacyActionEntry,
|
|
4
4
|
isActionTypeEntry,
|
|
5
5
|
getRedirectLink,
|
|
6
|
-
getFileLocation,
|
|
7
6
|
getActionLabel,
|
|
8
7
|
renderDisplayCell,
|
|
9
8
|
} from './display-cell-renderer';
|
|
@@ -29,13 +28,6 @@ describe('display-cell-renderer', () => {
|
|
|
29
28
|
expect(link).toBe('/bill/10/visit/OP100');
|
|
30
29
|
});
|
|
31
30
|
|
|
32
|
-
test('resolves file location from direct config value or record field', () => {
|
|
33
|
-
expect(getFileLocation({ file_location: 'https://example.com/report.pdf' }, {})).toBe('https://example.com/report.pdf');
|
|
34
|
-
expect(getFileLocation({ file_location: 'document_url' }, { document_url: 'https://example.com/record.pdf' })).toBe(
|
|
35
|
-
'https://example.com/record.pdf',
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
31
|
test('keeps legacy action label behavior unchanged', () => {
|
|
40
32
|
const entry = {
|
|
41
33
|
field: 'action',
|
|
@@ -85,71 +77,6 @@ describe('display-cell-renderer', () => {
|
|
|
85
77
|
expect(element.props.opb_id).toBe(1);
|
|
86
78
|
});
|
|
87
79
|
|
|
88
|
-
test('renders file action entry as pdf viewer trigger', () => {
|
|
89
|
-
const element = renderDisplayCell({
|
|
90
|
-
entry: {
|
|
91
|
-
type: 'action',
|
|
92
|
-
field: 'action_text',
|
|
93
|
-
redirect_link_type: 'file',
|
|
94
|
-
file_location: 'https://example.com/report.pdf',
|
|
95
|
-
label: 'Open PDF',
|
|
96
|
-
},
|
|
97
|
-
record: { action_text: 'Preview PDF' },
|
|
98
|
-
refresh: jest.fn(),
|
|
99
|
-
CustomComponents: {},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
expect(typeof element.type).toBe('function');
|
|
103
|
-
expect(element.props.entry.redirect_link_type).toBe('file');
|
|
104
|
-
expect(element.props.entry.file_location).toBe('https://example.com/report.pdf');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test('passes file action props to CustomComponents.FileUpload when available', () => {
|
|
108
|
-
const FileUpload = () => null;
|
|
109
|
-
const element = renderDisplayCell({
|
|
110
|
-
entry: {
|
|
111
|
-
type: 'action',
|
|
112
|
-
field: 'action_text',
|
|
113
|
-
redirect_link_type: 'file',
|
|
114
|
-
file_location: 'document_path',
|
|
115
|
-
file_type: 'document_type',
|
|
116
|
-
default_scale: 'zoom_level',
|
|
117
|
-
viewer_type: 'inline_viewer',
|
|
118
|
-
require_linux_path: 'needs_linux_path',
|
|
119
|
-
replace_branch: 'should_replace_branch',
|
|
120
|
-
file_loader_config: {
|
|
121
|
-
sample: true,
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
record: {
|
|
125
|
-
action_text: 'Preview',
|
|
126
|
-
document_path: '\\\\server\\reports\\doc.pdf',
|
|
127
|
-
document_type: 'pdf',
|
|
128
|
-
zoom_level: 1.25,
|
|
129
|
-
inline_viewer: true,
|
|
130
|
-
needs_linux_path: true,
|
|
131
|
-
should_replace_branch: false,
|
|
132
|
-
},
|
|
133
|
-
refresh: jest.fn(),
|
|
134
|
-
CustomComponents: { FileUpload },
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
expect(typeof element.type).toBe('function');
|
|
138
|
-
|
|
139
|
-
const modalContent = element.props.children[1].props.children;
|
|
140
|
-
expect(modalContent.type).toBe(FileUpload);
|
|
141
|
-
expect(modalContent.props.url).toBe('\\\\server\\reports\\doc.pdf');
|
|
142
|
-
expect(modalContent.props.type).toBe('pdf');
|
|
143
|
-
expect(modalContent.props.defaultScale).toBe(1.25);
|
|
144
|
-
expect(modalContent.props.viewerType).toBe(true);
|
|
145
|
-
expect(modalContent.props.config).toEqual({
|
|
146
|
-
requireLinuxPath: true,
|
|
147
|
-
replaceBranch: false,
|
|
148
|
-
sample: true,
|
|
149
|
-
});
|
|
150
|
-
expect(modalContent.props.record.document_path).toBe('\\\\server\\reports\\doc.pdf');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
80
|
test('renders styled tag/span/icon/text for non-action path', () => {
|
|
154
81
|
const tagElement = renderDisplayCell({
|
|
155
82
|
entry: { field: 'status', enableColor: true, columnType: 'tag' },
|
|
@@ -70,10 +70,6 @@ export default function ReportingDashboard({
|
|
|
70
70
|
|
|
71
71
|
const [formContents, setformContents] = useState({});
|
|
72
72
|
const [liveFormContents, setLiveFormContents] = useState({});
|
|
73
|
-
const [reportMailRequest, setReportMailRequest] = useState({
|
|
74
|
-
scriptId: null,
|
|
75
|
-
inputParameters: {},
|
|
76
|
-
});
|
|
77
73
|
|
|
78
74
|
const scriptId = useRef(null);
|
|
79
75
|
|
|
@@ -304,11 +300,6 @@ export default function ReportingDashboard({
|
|
|
304
300
|
formBody = { body: { ...scope, ...paginationData } };
|
|
305
301
|
}
|
|
306
302
|
|
|
307
|
-
setReportMailRequest({
|
|
308
|
-
scriptId: coreScriptId,
|
|
309
|
-
inputParameters: formBody,
|
|
310
|
-
});
|
|
311
|
-
|
|
312
303
|
// Fetch result
|
|
313
304
|
const result = await CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr);
|
|
314
305
|
|
|
@@ -677,8 +668,6 @@ export default function ReportingDashboard({
|
|
|
677
668
|
selectedSearchFields={selectedSearchFields}
|
|
678
669
|
handleRemoveSearchField={handleRemoveSearchField}
|
|
679
670
|
fetchReportData={(paginationUpdate) => fetchReportData(id, formContents, dbPtr, paginationUpdate || pagination)}
|
|
680
|
-
reportScriptId={reportMailRequest.scriptId}
|
|
681
|
-
reportInputParameters={reportMailRequest.inputParameters}
|
|
682
671
|
/>
|
|
683
672
|
{/** GuestList component end*/}
|
|
684
673
|
</>
|
|
@@ -687,29 +676,6 @@ export default function ReportingDashboard({
|
|
|
687
676
|
);
|
|
688
677
|
}
|
|
689
678
|
|
|
690
|
-
/**
|
|
691
|
-
* Filters report records using the same search text that drives the table.
|
|
692
|
-
*
|
|
693
|
-
* @param {Array<Object>} records - Report rows.
|
|
694
|
-
* @param {string} searchText - Current table search text.
|
|
695
|
-
* @returns {Array<Object>} Rows matching the search text.
|
|
696
|
-
*/
|
|
697
|
-
function filterReportRecords(records, searchText) {
|
|
698
|
-
if (!Array.isArray(records)) return [];
|
|
699
|
-
|
|
700
|
-
const query = String(searchText || '').trim().toLowerCase();
|
|
701
|
-
|
|
702
|
-
if (!query) return records;
|
|
703
|
-
|
|
704
|
-
return records.filter((record) =>
|
|
705
|
-
Object.values(record).some((value) => {
|
|
706
|
-
if (value === undefined || value === null || typeof value === 'object') return false;
|
|
707
|
-
|
|
708
|
-
return String(value).toLowerCase().indexOf(query) !== -1;
|
|
709
|
-
})
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
679
|
/**
|
|
714
680
|
*
|
|
715
681
|
* @param root0
|
|
@@ -737,8 +703,6 @@ function GuestList({
|
|
|
737
703
|
selectedSearchFields,
|
|
738
704
|
handleRemoveSearchField,
|
|
739
705
|
fetchReportData,
|
|
740
|
-
reportScriptId,
|
|
741
|
-
reportInputParameters,
|
|
742
706
|
}) {
|
|
743
707
|
/**
|
|
744
708
|
* @param {*} propValues
|
|
@@ -827,13 +791,13 @@ function GuestList({
|
|
|
827
791
|
return col;
|
|
828
792
|
});
|
|
829
793
|
const summaryCols = columns.filter((col) => col.enable_summary);
|
|
830
|
-
let dataToExport = [...
|
|
794
|
+
let dataToExport = [...patients];
|
|
831
795
|
|
|
832
796
|
if (summaryCols.length > 0) {
|
|
833
797
|
// Build one synthetic row for CSV export that mirrors the table layout:
|
|
834
798
|
// numeric summary cells are populated from `calculateSummaryValues`, while
|
|
835
799
|
// non-summary columns stay blank unless a configured caption should be shown.
|
|
836
|
-
const summaryValues = calculateSummaryValues(summaryCols,
|
|
800
|
+
const summaryValues = calculateSummaryValues(summaryCols, patients);
|
|
837
801
|
const summaryRow = { isSummaryRow: true };
|
|
838
802
|
|
|
839
803
|
cols.forEach((col, index) => {
|
|
@@ -860,15 +824,37 @@ function GuestList({
|
|
|
860
824
|
}
|
|
861
825
|
let exportDatas = getExportData(dataToExport, exportCols);
|
|
862
826
|
|
|
863
|
-
if (exportDatas.exportDataHeaders.length) {
|
|
827
|
+
if (exportDatas.exportDataColumns.length && exportDatas.exportDataHeaders.length) {
|
|
864
828
|
setExportData({ exportDatas });
|
|
865
|
-
} else {
|
|
866
|
-
setExportData({});
|
|
867
829
|
}
|
|
868
830
|
}
|
|
869
|
-
}, [patients, columns
|
|
831
|
+
}, [patients, columns]);
|
|
832
|
+
|
|
833
|
+
let filtered;
|
|
834
|
+
|
|
835
|
+
if (patients) {
|
|
836
|
+
filtered = patients.filter((record) => {
|
|
837
|
+
if (query) {
|
|
838
|
+
// Keys
|
|
839
|
+
let keys = Object.keys(record);
|
|
840
|
+
|
|
841
|
+
let flag = false;
|
|
870
842
|
|
|
871
|
-
|
|
843
|
+
keys.forEach((key) => {
|
|
844
|
+
let ele = record[key];
|
|
845
|
+
|
|
846
|
+
if (ele && typeof ele === 'string' && ele.toLowerCase().indexOf(query.toLowerCase()) !== -1) {
|
|
847
|
+
flag = true;
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
/**Will return flag */
|
|
852
|
+
return flag;
|
|
853
|
+
} else {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
}
|
|
872
858
|
|
|
873
859
|
/**
|
|
874
860
|
* Checks for a match in the filtered patient list based on a scanned code,
|
|
@@ -991,18 +977,11 @@ function GuestList({
|
|
|
991
977
|
<Search className="table-search-input" placeholder="Enter Search Value" allowClear onChange={onSearch} />
|
|
992
978
|
<div className="table-export-button">
|
|
993
979
|
{exportData.exportDatas && (
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
fileName={`${config.caption || 'Report'}.xlsx`}
|
|
1000
|
-
pdfFileName={`${config.caption || 'Report'}.pdf`}
|
|
1001
|
-
scriptId={reportScriptId}
|
|
1002
|
-
inputParameters={reportInputParameters}
|
|
1003
|
-
dropdown
|
|
1004
|
-
/>
|
|
1005
|
-
</>
|
|
980
|
+
<ExportReactCSV
|
|
981
|
+
title={config.caption}
|
|
982
|
+
headers={exportData.exportDatas.exportDataHeaders}
|
|
983
|
+
csvData={exportData.exportDatas.exportDataColumns}
|
|
984
|
+
/>
|
|
1006
985
|
)}
|
|
1007
986
|
</div>
|
|
1008
987
|
|
|
@@ -1057,7 +1036,7 @@ function GuestList({
|
|
|
1057
1036
|
dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
|
|
1058
1037
|
columns={cols}
|
|
1059
1038
|
sticky
|
|
1060
|
-
pagination={
|
|
1039
|
+
pagination={false}
|
|
1061
1040
|
summary={(pageData) => {
|
|
1062
1041
|
const summaryCols = columns.filter((col) => col.enable_summary);
|
|
1063
1042
|
if (!summaryCols.length) return null;
|
package/package.json
CHANGED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { Alert } from 'antd';
|
|
2
|
-
import React, { useState, useEffect } from 'react';
|
|
3
|
-
|
|
4
|
-
export default function LicenseAlert({ data }) {
|
|
5
|
-
// setting visibility of alert based on license status
|
|
6
|
-
const [visible, setVisible] = useState(true);
|
|
7
|
-
// resolve alert configuration based on license data
|
|
8
|
-
const alertConfig = resolveLicenseAlert(data);
|
|
9
|
-
// auto-hide alert after 10 seconds or when data changes
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
if (alertConfig) {
|
|
12
|
-
setVisible(true);
|
|
13
|
-
|
|
14
|
-
const timer = setTimeout(() => {
|
|
15
|
-
setVisible(false);
|
|
16
|
-
}, 10000); // 10 seconds
|
|
17
|
-
|
|
18
|
-
return () => {
|
|
19
|
-
clearTimeout(timer);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
}, [data]);
|
|
23
|
-
// if no alert configuration or not visible, render nothing
|
|
24
|
-
if (!alertConfig || !visible) return null;
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
// render the alert with appropriate type, message, and description
|
|
28
|
-
<Alert
|
|
29
|
-
type={alertConfig.type}
|
|
30
|
-
message={alertConfig.message}
|
|
31
|
-
description={alertConfig.description}
|
|
32
|
-
showIcon
|
|
33
|
-
closable
|
|
34
|
-
onClose={() => setVisible(false)}
|
|
35
|
-
/>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
// function to determine alert configuration based on license data
|
|
39
|
-
function resolveLicenseAlert(data) {
|
|
40
|
-
if (!data) return null;
|
|
41
|
-
// destructure relevant fields from license data
|
|
42
|
-
const { status, expiresInDays, isExpiringSoon, gracePeriod } = data;
|
|
43
|
-
|
|
44
|
-
// ===== NOT INSTALLED =====
|
|
45
|
-
if (status === 'NOT_INSTALLED') {
|
|
46
|
-
return {
|
|
47
|
-
type: 'error',
|
|
48
|
-
message: 'License not found',
|
|
49
|
-
description: 'Please install a valid license to continue.',
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ===== GRACE PERIOD =====
|
|
54
|
-
if (gracePeriod) {
|
|
55
|
-
return {
|
|
56
|
-
type: 'warning',
|
|
57
|
-
message: 'Grace period mode',
|
|
58
|
-
description: 'License expired. Running in read-only mode.',
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ===== EXPIRING SOON =====
|
|
63
|
-
if (status === 'ACTIVE' && isExpiringSoon) {
|
|
64
|
-
let descriptionText = '';
|
|
65
|
-
// customize message based on how soon the license is expiring
|
|
66
|
-
if (expiresInDays === 0) {
|
|
67
|
-
descriptionText = 'Your license will expire today. Please renew immediately.';
|
|
68
|
-
} else {
|
|
69
|
-
descriptionText = `Your license will expire in ${expiresInDays} days. Please plan for renewal.`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
type: 'warning',
|
|
74
|
-
message: 'License expiring soon',
|
|
75
|
-
description: descriptionText,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ===== NOT INSTALLED =====
|
|
80
|
-
if (status === 'NOT_INSTALLED') {
|
|
81
|
-
return {
|
|
82
|
-
type: 'error',
|
|
83
|
-
message: 'License not found',
|
|
84
|
-
description: 'Please install a valid license to continue.',
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
// =====EXPIRED=====
|
|
88
|
-
if (status === 'EXPIRED') {
|
|
89
|
-
return {
|
|
90
|
-
type: 'error',
|
|
91
|
-
message: 'License expired',
|
|
92
|
-
description: 'Your license has expired. Please renew or install a new license.',
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// ------------------------
|
|
2
|
-
// Menu caption + path label
|
|
3
|
-
// ------------------------
|
|
4
|
-
const MenuLabel = ({ menu }) => {
|
|
5
|
-
const path = menu.path || menu.route;
|
|
6
|
-
return (
|
|
7
|
-
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 8, lineHeight: 1.3 }}>
|
|
8
|
-
<span>{menu.caption}</span>
|
|
9
|
-
{path ? <span style={{ color: '#999', fontSize: 12 }}>{path}</span> : null}
|
|
10
|
-
</div>
|
|
11
|
-
);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default MenuLabel;
|