hise-flow-graphs 1.0.21 → 1.1.0
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/.env.dev +1 -0
- package/.env.prod +1 -0
- package/.env.stage +1 -0
- package/dist/main.js +1 -1
- package/package.json +7 -2
- package/src/@types/CertificateTypes.ts +25 -15
- package/src/components/Certificates/CertificateGraph/LayerBackButton.tsx +1 -1
- package/src/components/Certificates/CertificateGraph/NodeAttributes.tsx +10 -10
- package/src/components/Certificates/CertificateGraph/helpers.tsx +24 -17
- package/src/components/Certificates/CertificateGraph/index.tsx +23 -28
- package/src/components/Certificates/EditCertificate/EditNode.tsx +8 -12
- package/src/components/Certificates/EditCertificate/{EditNodeModal.jsx → EditNodeModal.tsx} +57 -50
- package/src/components/Certificates/EditCertificate/MaskMetadataModal/EMRMaskingTables.tsx +69 -0
- package/src/components/Certificates/EditCertificate/MaskMetadataModal/MaskMetadataTable.tsx +105 -0
- package/src/components/Certificates/EditCertificate/MaskMetadataModal/helpers.ts +41 -0
- package/src/components/Certificates/EditCertificate/MaskMetadataModal/index.tsx +167 -0
- package/src/components/Certificates/EditCertificate/helpers.ts +2 -44
- package/src/components/Certificates/EditCertificate/{index.jsx → index.tsx} +9 -4
- package/src/components/Certificates/FileLayerNode/index.tsx +12 -23
- package/src/components/Certificates/ReadCertificate/MetadataModal/MetadataTable.tsx +100 -0
- package/src/components/Certificates/ReadCertificate/MetadataModal/helpers.tsx +46 -0
- package/src/components/Certificates/ReadCertificate/MetadataModal/index.tsx +142 -0
- package/src/components/Certificates/ReadCertificate/ReadNode.tsx +7 -2
- package/src/components/Certificates/ReadCertificate/index.tsx +2 -3
- package/src/components/Certificates/ReviewCertificate/RestrictNode/index.tsx +161 -0
- package/src/components/Certificates/ReviewCertificate/ReviewNode.tsx +8 -5
- package/src/components/Certificates/ReviewCertificate/ReviewNodes/index.tsx +5 -4
- package/src/components/Certificates/ReviewCertificate/helpers.ts +18 -60
- package/src/components/Certificates/ReviewCertificate/{index.jsx → index.tsx} +14 -10
- package/src/components/Certificates/WorkspaceCertificate/ReplicaIDENode/index.tsx +5 -2
- package/src/components/Certificates/WorkspaceCertificate/ReplicaPipelineNode/index.tsx +4 -2
- package/src/components/Certificates/WorkspaceCertificate/RestrictedNode/index.tsx +106 -0
- package/src/components/Certificates/WorkspaceCertificate/WorkspaceFileNode/index.tsx +6 -2
- package/src/components/Certificates/WorkspaceCertificate/WorkspaceIDENode/index.tsx +10 -5
- package/src/components/Certificates/WorkspaceCertificate/WorkspaceNode/index.tsx +9 -3
- package/src/components/Certificates/WorkspaceCertificate/WorkspacePipelineNode/index.tsx +5 -5
- package/src/components/Certificates/WorkspaceCertificate/helpers.ts +7 -41
- package/src/components/Certificates/WorkspaceCertificate/index.tsx +10 -11
- package/src/components/Certificates/WorkspaceCertificate/style.css +0 -10
- package/src/index.ts +2 -2
- package/src/state/GraphContext.tsx +23 -22
- package/src/utils/getCertificateBody.ts +89 -0
- package/webpack.config.js +71 -54
- package/src/components/Certificates/EditCertificate/MaskMetadataModal.jsx +0 -299
- package/src/components/Certificates/FileLayerNode/helpers.ts +0 -31
- package/src/components/Certificates/ReadCertificate/MetadataModal/helpers.jsx +0 -22
- package/src/components/Certificates/ReadCertificate/MetadataModal/index.jsx +0 -169
- package/src/components/Certificates/ReadCertificate/helpers.ts +0 -38
- package/src/components/Certificates/ReviewCertificate/ReviewNodes/AllNodesModal.jsx +0 -63
- package/src/components/Certificates/ReviewCertificate/ReviewNodes/SingleNodeModal.jsx +0 -83
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Button } from 'hise-components';
|
|
2
|
+
import * as R from 'ramda';
|
|
3
|
+
import { useGraph } from '../../../../state/GraphContext';
|
|
4
|
+
import { getKey, getValue } from '../../CertificateGraph/helpers';
|
|
5
|
+
import { maskRow, maskEMRRow } from './helpers';
|
|
6
|
+
import { labels } from './index';
|
|
7
|
+
import { Metadata, MetadataType } from '../../../../@types/CertificateTypes';
|
|
8
|
+
|
|
9
|
+
const MaskAllSectionButton = (props: {
|
|
10
|
+
update: Function,
|
|
11
|
+
currMetadata: object,
|
|
12
|
+
dataType: string,
|
|
13
|
+
}) => {
|
|
14
|
+
const onMask = () => props.update({
|
|
15
|
+
...props.currMetadata,
|
|
16
|
+
[props.dataType]: {},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Button
|
|
21
|
+
className="mt-3 mb-2 float-right"
|
|
22
|
+
onClick={onMask}
|
|
23
|
+
>
|
|
24
|
+
{`${labels[props.dataType]}: Mask All`}
|
|
25
|
+
</Button>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const Table = ({ children }: { children: React.ReactNode }) => (
|
|
30
|
+
<table className="table">
|
|
31
|
+
<thead>
|
|
32
|
+
<tr>
|
|
33
|
+
<th scope="col" />
|
|
34
|
+
<th scope="col">Variable</th>
|
|
35
|
+
<th scope="col">Value</th>
|
|
36
|
+
</tr>
|
|
37
|
+
</thead>
|
|
38
|
+
<tbody>
|
|
39
|
+
{children}
|
|
40
|
+
</tbody>
|
|
41
|
+
</table>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const MaskMetadataTable = ({
|
|
45
|
+
type, metadata, setMetadata, emrEntry, emrIndex,
|
|
46
|
+
}: {
|
|
47
|
+
type: MetadataType,
|
|
48
|
+
metadata: Metadata,
|
|
49
|
+
setMetadata: Function,
|
|
50
|
+
emrEntry: { [key: string]: any },
|
|
51
|
+
emrIndex: number,
|
|
52
|
+
}) => {
|
|
53
|
+
const { schemes } = useGraph().certificate;
|
|
54
|
+
|
|
55
|
+
const scheme = schemes[type];
|
|
56
|
+
const metadataEntries = Object.entries((type !== MetadataType['emrData']) ? metadata[type] : emrEntry.patientData);
|
|
57
|
+
|
|
58
|
+
const onMaskRow = (entry: Array<any>) => {
|
|
59
|
+
if (type === MetadataType['emrData']) {
|
|
60
|
+
maskEMRRow(entry[0], metadata, setMetadata, emrEntry, emrIndex);
|
|
61
|
+
} else {
|
|
62
|
+
maskRow(entry[0], metadata, type, setMetadata);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const getEntryRows = () => (
|
|
67
|
+
metadataEntries.map((entry) => (
|
|
68
|
+
<tr key={entry[0]}>
|
|
69
|
+
<td>
|
|
70
|
+
<Button
|
|
71
|
+
key={`${entry}-mask-btn`}
|
|
72
|
+
icon="fas fa-times"
|
|
73
|
+
color="link"
|
|
74
|
+
className="btn-sm text-danger"
|
|
75
|
+
noText
|
|
76
|
+
onClick={() => onMaskRow(entry)}
|
|
77
|
+
/>
|
|
78
|
+
</td>
|
|
79
|
+
<td>
|
|
80
|
+
{getKey(entry[0], scheme, type)}
|
|
81
|
+
</td>
|
|
82
|
+
<td>
|
|
83
|
+
{getValue(entry[0], entry[1], scheme, type)}
|
|
84
|
+
</td>
|
|
85
|
+
</tr>
|
|
86
|
+
))
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<>
|
|
91
|
+
<MaskAllSectionButton dataType={type} currMetadata={metadata} update={setMetadata} />
|
|
92
|
+
|
|
93
|
+
<Table>
|
|
94
|
+
{getEntryRows()}
|
|
95
|
+
</Table>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default MaskMetadataTable;
|
|
101
|
+
|
|
102
|
+
MaskMetadataTable.defaultProps = {
|
|
103
|
+
emrEntry: null,
|
|
104
|
+
emrIndex: null,
|
|
105
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as R from 'ramda';
|
|
2
|
+
import { Metadata } from '../../../../@types/CertificateTypes';
|
|
3
|
+
|
|
4
|
+
export const maskRow = (
|
|
5
|
+
variable: string,
|
|
6
|
+
currMetadata: { [key: string]: any },
|
|
7
|
+
dataType: string,
|
|
8
|
+
update: Function,
|
|
9
|
+
) => {
|
|
10
|
+
const omitted = R.omit(
|
|
11
|
+
[variable],
|
|
12
|
+
currMetadata[dataType],
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
update({
|
|
16
|
+
...currMetadata,
|
|
17
|
+
[dataType]: omitted,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const maskEMRRow = (
|
|
22
|
+
variable: string,
|
|
23
|
+
currMetadata: Metadata,
|
|
24
|
+
update: Function,
|
|
25
|
+
emrEntry: {
|
|
26
|
+
[key: string]: any,
|
|
27
|
+
},
|
|
28
|
+
emrIndex: number,
|
|
29
|
+
) => {
|
|
30
|
+
update({
|
|
31
|
+
...currMetadata,
|
|
32
|
+
emrData: [
|
|
33
|
+
...currMetadata?.emrData?.slice(0, emrIndex),
|
|
34
|
+
{
|
|
35
|
+
...emrEntry,
|
|
36
|
+
patientData: R.omit([variable], emrEntry.patientData),
|
|
37
|
+
},
|
|
38
|
+
...currMetadata?.emrData?.slice((Number(emrIndex) + 1)),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { NodeProps } from 'reactflow';
|
|
3
|
+
import * as R from 'ramda';
|
|
4
|
+
import { Button, ConfirmationModal, HISETabs, MessageStripe } from 'hise-components';
|
|
5
|
+
import MaskMetadataTable from './MaskMetadataTable';
|
|
6
|
+
import EMRMaskingTables from './EMRMaskingTables';
|
|
7
|
+
import { useGraph } from '../../../../state/GraphContext';
|
|
8
|
+
import { Response } from '../../../../@types/GeneralTypes';
|
|
9
|
+
import { MetadataType } from '../../../../@types/CertificateTypes';
|
|
10
|
+
|
|
11
|
+
export const labels: { [key: string]: string } = {
|
|
12
|
+
labData: 'Lab Results',
|
|
13
|
+
surveyData: 'Survey Data',
|
|
14
|
+
emrData: 'EMR Data',
|
|
15
|
+
demographicsData: 'Demographics Data',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const MaskMetadataButton = ({ node }: {
|
|
19
|
+
node: NodeProps,
|
|
20
|
+
}) => {
|
|
21
|
+
const { certificate, setNode } = useGraph();
|
|
22
|
+
const { metadata } = certificate;
|
|
23
|
+
const nodeMetadata = metadata[node.id];
|
|
24
|
+
|
|
25
|
+
const isHidden = !nodeMetadata || R.isEmpty(nodeMetadata);
|
|
26
|
+
|
|
27
|
+
if (isHidden) return null;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Button
|
|
31
|
+
id="mask-btn"
|
|
32
|
+
key="mask-btn"
|
|
33
|
+
color="link"
|
|
34
|
+
className="text-warning px-1 pt-0"
|
|
35
|
+
onClick={() => {
|
|
36
|
+
node.data.toggleMaskModal();
|
|
37
|
+
setNode(node);
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
MASK
|
|
41
|
+
</Button>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const MaskMetadataModal = ({
|
|
46
|
+
isOpen,
|
|
47
|
+
loading,
|
|
48
|
+
toggle,
|
|
49
|
+
handleUpdate,
|
|
50
|
+
refresh,
|
|
51
|
+
}: {
|
|
52
|
+
isOpen: boolean,
|
|
53
|
+
loading: boolean,
|
|
54
|
+
toggle: Function,
|
|
55
|
+
handleUpdate: Function,
|
|
56
|
+
refresh: Function,
|
|
57
|
+
}) => {
|
|
58
|
+
const { node, certificate, setNode } = useGraph();
|
|
59
|
+
const { metadata } = certificate;
|
|
60
|
+
|
|
61
|
+
const [metadataBody, setMetadataBody] = useState({});
|
|
62
|
+
const [error, setError] = useState('');
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (R.isEmpty(metadata) || R.isEmpty(node)) return;
|
|
66
|
+
|
|
67
|
+
setMetadataBody(metadata[node.id]);
|
|
68
|
+
}, [node, metadata]);
|
|
69
|
+
|
|
70
|
+
const tabs = metadataBody ? Object.keys(metadataBody) : [];
|
|
71
|
+
|
|
72
|
+
const onCancel = () => {
|
|
73
|
+
toggle();
|
|
74
|
+
setNode({});
|
|
75
|
+
setError('');
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const onUpdate = () => {
|
|
79
|
+
const body = {
|
|
80
|
+
metadata: {
|
|
81
|
+
...metadata,
|
|
82
|
+
[node.id]: metadataBody,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
handleUpdate(body).then((response: Response) => {
|
|
87
|
+
if (response.status === 200) {
|
|
88
|
+
toggle();
|
|
89
|
+
refresh();
|
|
90
|
+
setError('');
|
|
91
|
+
} else {
|
|
92
|
+
setError(response.data.Message);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<ConfirmationModal
|
|
99
|
+
id="mask-metadata-modal"
|
|
100
|
+
scrollable
|
|
101
|
+
isOpen={isOpen}
|
|
102
|
+
onClose={toggle}
|
|
103
|
+
headerContent="Mask Node Metadata"
|
|
104
|
+
noSubmit
|
|
105
|
+
noCancel
|
|
106
|
+
content={(
|
|
107
|
+
<>
|
|
108
|
+
{(error && !loading) && (
|
|
109
|
+
<MessageStripe message={error} color="danger" dismissible={false} />
|
|
110
|
+
)}
|
|
111
|
+
|
|
112
|
+
<HISETabs
|
|
113
|
+
activeTab={tabs[0]}
|
|
114
|
+
tabs={tabs.map((tab) => {
|
|
115
|
+
const metadataType: MetadataType = tab as MetadataType;
|
|
116
|
+
|
|
117
|
+
return ({
|
|
118
|
+
key: tab,
|
|
119
|
+
label: labels[tab] || tab,
|
|
120
|
+
content: (tab === 'emrData')
|
|
121
|
+
? (
|
|
122
|
+
<EMRMaskingTables
|
|
123
|
+
type={metadataType}
|
|
124
|
+
metadata={metadataBody}
|
|
125
|
+
setMetadata={setMetadataBody}
|
|
126
|
+
/>
|
|
127
|
+
) : (
|
|
128
|
+
<MaskMetadataTable
|
|
129
|
+
type={metadataType}
|
|
130
|
+
metadata={metadataBody}
|
|
131
|
+
setMetadata={setMetadataBody}
|
|
132
|
+
/>
|
|
133
|
+
),
|
|
134
|
+
});
|
|
135
|
+
})}
|
|
136
|
+
/>
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
customFooter={(
|
|
140
|
+
<div className="float-right">
|
|
141
|
+
{loading && (
|
|
142
|
+
<div className="spinner-grow spinner-grow-sm text-warning" role="status" />
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
<Button
|
|
146
|
+
id="cancel-btn"
|
|
147
|
+
icon="fas fa-times"
|
|
148
|
+
noText
|
|
149
|
+
className="mx-2"
|
|
150
|
+
onClick={onCancel}
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
<Button
|
|
154
|
+
id="ok-btn"
|
|
155
|
+
icon="fas fa-check"
|
|
156
|
+
color="primary"
|
|
157
|
+
onClick={onUpdate}
|
|
158
|
+
>
|
|
159
|
+
Update Metadata
|
|
160
|
+
</Button>
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
/>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default MaskMetadataModal;
|
|
@@ -1,48 +1,6 @@
|
|
|
1
|
-
import { Node } from 'reactflow';
|
|
2
|
-
import * as R from 'ramda';
|
|
3
|
-
import { getSchemes } from '../CertificateGraph/helpers';
|
|
4
|
-
import { Certificate } from '../../../@types/CertificateTypes';
|
|
5
|
-
|
|
6
|
-
export const getEditNodeData = (
|
|
7
|
-
vertices: Certificate['vertices'],
|
|
8
|
-
certificate: Certificate,
|
|
9
|
-
toggleEditModal: Function,
|
|
10
|
-
toggleMaskModal: Function,
|
|
11
|
-
) => {
|
|
12
|
-
const data: { [key: string]: Node['data'] } = {};
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
isExecutable,
|
|
16
|
-
generatedLabels,
|
|
17
|
-
customLabels,
|
|
18
|
-
serviceLabels,
|
|
19
|
-
schemes,
|
|
20
|
-
metadata,
|
|
21
|
-
} = certificate;
|
|
22
|
-
|
|
23
|
-
if (R.isEmpty(vertices) || R.isNil(vertices)) return data;
|
|
24
|
-
|
|
25
|
-
vertices.forEach((vertex: string) => {
|
|
26
|
-
const label = vertex.split('/')[0];
|
|
27
|
-
const description = customLabels[vertex] || generatedLabels[vertex];
|
|
28
|
-
|
|
29
|
-
data[vertex] = {
|
|
30
|
-
label,
|
|
31
|
-
vertex,
|
|
32
|
-
description,
|
|
33
|
-
customLabels,
|
|
34
|
-
metadata,
|
|
35
|
-
schemes: getSchemes(vertex, serviceLabels, schemes),
|
|
36
|
-
isExecutable,
|
|
37
|
-
toggleEditModal,
|
|
38
|
-
toggleMaskModal,
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return data;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
1
|
export const getFormControls = (label: string, description: object) => {
|
|
2
|
+
if (!description) return null;
|
|
3
|
+
|
|
46
4
|
switch (label) {
|
|
47
5
|
case 'Cohort':
|
|
48
6
|
case 'IDE':
|
|
@@ -3,9 +3,14 @@ import CertificateGraph from '../CertificateGraph';
|
|
|
3
3
|
import EditNodeModal from './EditNodeModal';
|
|
4
4
|
import MaskMetadataModal from './MaskMetadataModal';
|
|
5
5
|
import { getNodesAndEdges } from '../CertificateGraph/helpers';
|
|
6
|
-
import {
|
|
6
|
+
import { Certificate } from '../../../@types/CertificateTypes';
|
|
7
7
|
|
|
8
|
-
const EditCertificate = ({ certificate, loading, handleUpdate, refresh }
|
|
8
|
+
const EditCertificate = ({ certificate, loading, handleUpdate, refresh }: {
|
|
9
|
+
certificate: Certificate,
|
|
10
|
+
loading: boolean,
|
|
11
|
+
handleUpdate: Function,
|
|
12
|
+
refresh: Function,
|
|
13
|
+
}) => {
|
|
9
14
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
|
10
15
|
const toggleEditModal = () => setIsEditModalOpen(!isEditModalOpen);
|
|
11
16
|
|
|
@@ -13,11 +18,11 @@ const EditCertificate = ({ certificate, loading, handleUpdate, refresh }) => {
|
|
|
13
18
|
const toggleMaskModal = () => setIsMaskModalOpen(!isMaskModalOpen);
|
|
14
19
|
|
|
15
20
|
const type = 'editNode';
|
|
16
|
-
const data =
|
|
21
|
+
const data = { toggleEditModal, toggleMaskModal };
|
|
17
22
|
const graph = getNodesAndEdges(
|
|
18
23
|
certificate.vertices,
|
|
19
24
|
certificate.edges,
|
|
20
|
-
certificate.
|
|
25
|
+
certificate.restrictedAssets,
|
|
21
26
|
type,
|
|
22
27
|
data,
|
|
23
28
|
);
|
|
@@ -4,14 +4,10 @@ import { useGraph } from '../../../state/GraphContext';
|
|
|
4
4
|
import { Label } from '../CertificateGraph/NodeAttributes';
|
|
5
5
|
import CopyFileLayerNodeButton, { CopyFileLayerModal } from './CopyFileLayer';
|
|
6
6
|
import { getNodesAndEdges } from '../CertificateGraph/helpers';
|
|
7
|
-
import {
|
|
8
|
-
import { getData } from './helpers';
|
|
7
|
+
import { getWorkspaceNodesAndEdges } from '../WorkspaceCertificate/helpers';
|
|
9
8
|
import { Certificate } from '../../../@types/CertificateTypes';
|
|
10
9
|
|
|
11
10
|
const FileLayerNode = (props: NodeProps) => {
|
|
12
|
-
const { id, data } = props;
|
|
13
|
-
const { label, description } = data;
|
|
14
|
-
|
|
15
11
|
const {
|
|
16
12
|
setNode,
|
|
17
13
|
certificate,
|
|
@@ -22,8 +18,13 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
22
18
|
nodeLayers,
|
|
23
19
|
setNodeLayers,
|
|
24
20
|
} = useGraph();
|
|
21
|
+
const { customLabels, generatedLabels } = certificate;
|
|
22
|
+
|
|
23
|
+
const { data } = props;
|
|
24
|
+
const label = props.id.split('/')[0];
|
|
25
|
+
const description = customLabels[props.id] || generatedLabels[props.id];
|
|
25
26
|
|
|
26
|
-
const subgraph = certificate.subgraphs[id];
|
|
27
|
+
const subgraph = certificate.subgraphs[props.id];
|
|
27
28
|
|
|
28
29
|
const [_, setNodes] = nodesState;
|
|
29
30
|
const [__, setEdges] = edgesState;
|
|
@@ -33,17 +34,6 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
33
34
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
34
35
|
const toggleModal = () => setIsModalOpen(!isModalOpen);
|
|
35
36
|
|
|
36
|
-
const getSubgraphData = (
|
|
37
|
-
vertices: Certificate['vertices'],
|
|
38
|
-
data: Node['data'],
|
|
39
|
-
) => {
|
|
40
|
-
if (type === 'fileLayerNode') {
|
|
41
|
-
return getWorkspaceData(vertices, certificate, replica, data.refresh);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return getData(type, vertices, certificate, replica, data);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
37
|
const getSubgraph = (
|
|
48
38
|
vertices: Certificate['vertices'],
|
|
49
39
|
data: { [key: string]: Node['data'] },
|
|
@@ -52,6 +42,7 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
52
42
|
return getWorkspaceNodesAndEdges(
|
|
53
43
|
vertices,
|
|
54
44
|
subgraph.edges,
|
|
45
|
+
replica,
|
|
55
46
|
data,
|
|
56
47
|
);
|
|
57
48
|
}
|
|
@@ -59,7 +50,7 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
59
50
|
return getNodesAndEdges(
|
|
60
51
|
vertices,
|
|
61
52
|
subgraph.edges,
|
|
62
|
-
certificate.
|
|
53
|
+
certificate.restrictedAssets,
|
|
63
54
|
type,
|
|
64
55
|
data,
|
|
65
56
|
);
|
|
@@ -67,11 +58,9 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
67
58
|
|
|
68
59
|
const onClick = () => {
|
|
69
60
|
const subgraphVertices = Object.keys(subgraph.vertices);
|
|
70
|
-
const subgraphData = getSubgraphData(subgraphVertices, data);
|
|
71
|
-
|
|
72
61
|
const subgraphNodesEdges = getSubgraph(
|
|
73
62
|
subgraphVertices,
|
|
74
|
-
|
|
63
|
+
data,
|
|
75
64
|
);
|
|
76
65
|
|
|
77
66
|
setNodes(subgraphNodesEdges.nodes);
|
|
@@ -118,7 +107,7 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
118
107
|
/>
|
|
119
108
|
|
|
120
109
|
<CopyFileLayerNodeButton
|
|
121
|
-
vertexId={id}
|
|
110
|
+
vertexId={props.id}
|
|
122
111
|
certificateId={certificate.id}
|
|
123
112
|
type={type}
|
|
124
113
|
isNodeHover={isHover}
|
|
@@ -126,7 +115,7 @@ const FileLayerNode = (props: NodeProps) => {
|
|
|
126
115
|
/>
|
|
127
116
|
|
|
128
117
|
<CopyFileLayerModal
|
|
129
|
-
vertexId={id}
|
|
118
|
+
vertexId={props.id}
|
|
130
119
|
certificateId={certificate.id}
|
|
131
120
|
refresh={data.refresh}
|
|
132
121
|
isOpen={isModalOpen}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { getKey, getValue } from '../../CertificateGraph/helpers';
|
|
3
|
+
|
|
4
|
+
const Table = ({ entries, scheme, tab }: {
|
|
5
|
+
entries: Array<any>,
|
|
6
|
+
scheme: { [key: string]: any },
|
|
7
|
+
tab: string,
|
|
8
|
+
}) => {
|
|
9
|
+
return (
|
|
10
|
+
<table className="table">
|
|
11
|
+
<tbody>
|
|
12
|
+
{entries.map(([key, value]) => (
|
|
13
|
+
<tr key={key}>
|
|
14
|
+
<th scope="row">
|
|
15
|
+
{getKey(key, scheme, tab)}
|
|
16
|
+
</th>
|
|
17
|
+
<td>{getValue(key, value, scheme, tab)}</td>
|
|
18
|
+
</tr>
|
|
19
|
+
))}
|
|
20
|
+
</tbody>
|
|
21
|
+
</table>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const EMRDropdown = ({ activeEntry, setActiveEntry, emrData }: {
|
|
26
|
+
activeEntry: string,
|
|
27
|
+
setActiveEntry: Function,
|
|
28
|
+
emrData: Array<{
|
|
29
|
+
daysSinceFirstResearchVisit: string,
|
|
30
|
+
}>,
|
|
31
|
+
}) => (
|
|
32
|
+
<div className="text-center">
|
|
33
|
+
<div className="btn-group mb-3">
|
|
34
|
+
<button className="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
35
|
+
Visit Day
|
|
36
|
+
{' '}
|
|
37
|
+
{activeEntry}
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
<ul className="dropdown-menu">
|
|
41
|
+
{emrData.map((emr) => (
|
|
42
|
+
<li key={emr.daysSinceFirstResearchVisit}>
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
className="dropdown-item"
|
|
46
|
+
onClick={() => setActiveEntry(emr.daysSinceFirstResearchVisit)}
|
|
47
|
+
>
|
|
48
|
+
Visit Day
|
|
49
|
+
{' '}
|
|
50
|
+
{emr.daysSinceFirstResearchVisit}
|
|
51
|
+
</button>
|
|
52
|
+
</li>
|
|
53
|
+
))}
|
|
54
|
+
</ul>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const EMRTable = ({ emrData, scheme }: {
|
|
60
|
+
emrData: { [key: string]: any },
|
|
61
|
+
scheme: { [key: string]: any },
|
|
62
|
+
}) => {
|
|
63
|
+
const [activeEntry, setActiveEntry] = useState(emrData[0].daysSinceFirstResearchVisit);
|
|
64
|
+
|
|
65
|
+
const emrDataValues = Object.values(emrData);
|
|
66
|
+
const emrMap = emrDataValues.reduce((accum, emr) => ({
|
|
67
|
+
...accum, [emr.daysSinceFirstResearchVisit]: emr,
|
|
68
|
+
}), {});
|
|
69
|
+
|
|
70
|
+
const entries = Object.entries(emrMap[activeEntry].patientData);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<React.Fragment>
|
|
74
|
+
<EMRDropdown
|
|
75
|
+
activeEntry={activeEntry}
|
|
76
|
+
setActiveEntry={setActiveEntry}
|
|
77
|
+
emrData={emrDataValues}
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
<Table entries={entries} scheme={scheme} tab="emrData" />
|
|
81
|
+
</React.Fragment>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const MetadataTable = ({ metadata, scheme, activeTab }: {
|
|
86
|
+
metadata: { [key: string]: any },
|
|
87
|
+
scheme: { [key: string]: any },
|
|
88
|
+
activeTab: string,
|
|
89
|
+
}) => {
|
|
90
|
+
const tabMetadata = metadata[activeTab];
|
|
91
|
+
const tabScheme = scheme[activeTab];
|
|
92
|
+
|
|
93
|
+
const entries = Object.entries(tabMetadata || {});
|
|
94
|
+
|
|
95
|
+
if (activeTab === 'emrData') return <EMRTable emrData={tabMetadata} scheme={tabScheme} />;
|
|
96
|
+
|
|
97
|
+
return <Table entries={entries} scheme={tabScheme} tab={activeTab} />;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default MetadataTable;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as R from 'ramda';
|
|
2
|
+
import { Metadata, MetadataType } from '../../../../@types/CertificateTypes';
|
|
3
|
+
|
|
4
|
+
export const getHeader = (
|
|
5
|
+
label: string,
|
|
6
|
+
desc: {
|
|
7
|
+
[key: string]: string,
|
|
8
|
+
}) => {
|
|
9
|
+
if (label === 'Sample') return `${label} (${desc['Visit Name']})`;
|
|
10
|
+
return label;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const getTabs = (metadata: Metadata) => {
|
|
14
|
+
const tabs: string[] = [];
|
|
15
|
+
const keys = Object.keys(metadata);
|
|
16
|
+
|
|
17
|
+
if (!keys.length) return tabs;
|
|
18
|
+
|
|
19
|
+
keys.forEach((key) => {
|
|
20
|
+
const metadataKey: MetadataType = key as MetadataType;
|
|
21
|
+
const data = metadata[metadataKey];
|
|
22
|
+
|
|
23
|
+
if (!R.isEmpty(data)) tabs.push(key);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return tabs;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const hasMetadata = (
|
|
30
|
+
metadata: {
|
|
31
|
+
[key: string]: Metadata,
|
|
32
|
+
},
|
|
33
|
+
nodeId: string,
|
|
34
|
+
) => {
|
|
35
|
+
const nodeMetadata = metadata[nodeId];
|
|
36
|
+
|
|
37
|
+
if (R.isNil(nodeMetadata) || R.isEmpty(nodeMetadata)) return false;
|
|
38
|
+
|
|
39
|
+
const typeEntries = Object.values(nodeMetadata);
|
|
40
|
+
|
|
41
|
+
const areAllEntriesEmpty = typeEntries.every((entry) => R.isEmpty(entry));
|
|
42
|
+
|
|
43
|
+
if (areAllEntriesEmpty) return false;
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
};
|