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
package/package.json
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hise-flow-graphs",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "This package manages ReactFlow graphs for HISE.",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"repository": "git@github.com:aifimmunology/hise-flow-graphs.git",
|
|
7
7
|
"author": "Allen Institute for Immunology",
|
|
8
8
|
"license": "ISC",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "webpack"
|
|
10
|
+
"build": "webpack --config webpack.config.js --env ENVIRONMENT=dev",
|
|
11
|
+
"build:dev": "webpack --config webpack.config.js --env ENVIRONMENT=dev",
|
|
12
|
+
"build:stage": "webpack --config webpack.config.js --env ENVIRONMENT=stage",
|
|
13
|
+
"build:prod": "webpack --config webpack.config.js --env ENVIRONMENT=prod"
|
|
11
14
|
},
|
|
12
15
|
"dependencies": {
|
|
13
16
|
"bootstrap": "5.1.3",
|
|
14
17
|
"dagre": "^0.8.5",
|
|
18
|
+
"dotenv": "^16.4.5",
|
|
15
19
|
"hise-components": "^1.0.0",
|
|
20
|
+
"process": "^0.11.10",
|
|
16
21
|
"ramda": "0.27.1",
|
|
17
22
|
"react-toastify": "^9.1.3",
|
|
18
23
|
"reactflow": "^11.8.1"
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
import { Edge, Node } from 'reactflow';
|
|
2
2
|
import { StringMap } from './GeneralTypes';
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
Approved = "approved",
|
|
6
|
-
Rejected = "rejected",
|
|
7
|
-
Restricted = "restricted",
|
|
8
|
-
Unset = "unset"
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type AssetReview = {
|
|
12
|
-
vertex: string,
|
|
13
|
-
reviewStatus: AssetReviewStatus,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export type Certificate = {
|
|
4
|
+
export interface Certificate {
|
|
17
5
|
id: string,
|
|
18
6
|
isExecutable: boolean,
|
|
19
7
|
vertices: Array<string>,
|
|
@@ -22,8 +10,12 @@ export type Certificate = {
|
|
|
22
10
|
customLabels: StringMap,
|
|
23
11
|
serviceLabels: StringMap,
|
|
24
12
|
schemes: StringMap,
|
|
25
|
-
metadata:
|
|
26
|
-
|
|
13
|
+
metadata: {
|
|
14
|
+
[key: string]: Metadata,
|
|
15
|
+
},
|
|
16
|
+
restrictedAssets: {
|
|
17
|
+
[vertex: string]: RestrictedAssetObject,
|
|
18
|
+
},
|
|
27
19
|
subgraphs: {
|
|
28
20
|
[key: string]: {
|
|
29
21
|
edges: { [key: string]: Array<string> }
|
|
@@ -32,6 +24,24 @@ export type Certificate = {
|
|
|
32
24
|
},
|
|
33
25
|
};
|
|
34
26
|
|
|
27
|
+
export interface RestrictedAssetObject {
|
|
28
|
+
restrictionInstructions: string,
|
|
29
|
+
restrictionNotes: string,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type MetadataMap = { [key: string]: any };
|
|
33
|
+
|
|
34
|
+
export enum MetadataType {
|
|
35
|
+
labData = 'labData',
|
|
36
|
+
surveyData = 'surveyData',
|
|
37
|
+
demographicsData = 'demographicsData',
|
|
38
|
+
emrData = 'emrData'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type Metadata = {
|
|
42
|
+
[key in MetadataType]?: MetadataMap;
|
|
43
|
+
};
|
|
44
|
+
|
|
35
45
|
export type Replica = {
|
|
36
46
|
matches: StringMap,
|
|
37
47
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { getNodeDescription, getNodeLabel } from './helpers';
|
|
3
3
|
|
|
4
|
-
export const Description = (
|
|
4
|
+
export const Description = ({ label, description }: {
|
|
5
5
|
label: string,
|
|
6
|
-
description:
|
|
6
|
+
description: any,
|
|
7
7
|
}) => {
|
|
8
|
-
const nodeDescription = getNodeDescription(
|
|
8
|
+
const nodeDescription = getNodeDescription(label, description);
|
|
9
9
|
|
|
10
|
-
if (!Object.keys(
|
|
10
|
+
if (!nodeDescription || !Object.keys(description).length) return null;
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
13
|
<div className="node-description text-center text-break">
|
|
@@ -16,24 +16,24 @@ export const Description = (props: {
|
|
|
16
16
|
);
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export const Label = (
|
|
19
|
+
export const Label = ({ label, description, link }: {
|
|
20
20
|
label: string,
|
|
21
|
-
description:
|
|
21
|
+
description: any,
|
|
22
22
|
link?: string | null,
|
|
23
23
|
}) => {
|
|
24
|
-
const
|
|
24
|
+
const nodeLabel = getNodeLabel(label, description);
|
|
25
25
|
|
|
26
|
-
if (!
|
|
26
|
+
if (!link) {
|
|
27
27
|
return (
|
|
28
28
|
<div className="node-label text-break flex-fill">
|
|
29
|
-
{
|
|
29
|
+
{nodeLabel}
|
|
30
30
|
</div>
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<a
|
|
36
|
-
href={`#${
|
|
36
|
+
href={`#${link}`}
|
|
37
37
|
target="_blank"
|
|
38
38
|
rel="noreferrer"
|
|
39
39
|
className="node-label text-break flex-fill"
|
|
@@ -4,12 +4,12 @@ import dagre from 'dagre';
|
|
|
4
4
|
import * as R from 'ramda';
|
|
5
5
|
import { getStyle as getReviewNodeStyle } from '../ReviewCertificate/helpers';
|
|
6
6
|
import { StringMap } from '../../../@types/GeneralTypes';
|
|
7
|
-
import {
|
|
7
|
+
import { Certificate } from '../../../@types/CertificateTypes';
|
|
8
8
|
|
|
9
|
-
const getNodeStyle = (type: string, label: string,
|
|
9
|
+
const getNodeStyle = (type: string, label: string, isAssetRestricted: boolean) => {
|
|
10
10
|
switch (type) {
|
|
11
11
|
case 'reviewNode':
|
|
12
|
-
return getReviewNodeStyle(label,
|
|
12
|
+
return getReviewNodeStyle(label, isAssetRestricted);
|
|
13
13
|
|
|
14
14
|
case 'readNode':
|
|
15
15
|
case 'editNode':
|
|
@@ -21,11 +21,11 @@ const getNodeStyle = (type: string, label: string, assetReview: AssetReview | nu
|
|
|
21
21
|
export const getNodesAndEdges = (
|
|
22
22
|
vertices: Certificate['vertices'],
|
|
23
23
|
edges: Certificate['edges'],
|
|
24
|
-
|
|
24
|
+
restrictedAssets: Certificate['restrictedAssets'],
|
|
25
25
|
type: string,
|
|
26
|
-
data: { [key: string]:
|
|
26
|
+
data: { [key: string]: Function },
|
|
27
27
|
) => {
|
|
28
|
-
const initialNodes = getNodes(vertices,
|
|
28
|
+
const initialNodes = getNodes(vertices, restrictedAssets, type, data);
|
|
29
29
|
const initialEdges = getEdges(edges);
|
|
30
30
|
|
|
31
31
|
return getLayoutedElements(initialNodes, initialEdges);
|
|
@@ -33,9 +33,9 @@ export const getNodesAndEdges = (
|
|
|
33
33
|
|
|
34
34
|
const getNodes = (
|
|
35
35
|
vertices: Certificate['vertices'],
|
|
36
|
-
|
|
36
|
+
restrictedAssets: Certificate['restrictedAssets'],
|
|
37
37
|
type: string,
|
|
38
|
-
data: { [key: string]:
|
|
38
|
+
data: { [key: string]: Function },
|
|
39
39
|
) => {
|
|
40
40
|
const position = { x: 0, y: 0 };
|
|
41
41
|
|
|
@@ -45,18 +45,13 @@ const getNodes = (
|
|
|
45
45
|
|
|
46
46
|
vertices.forEach((vertex: string) => {
|
|
47
47
|
const label = vertex.split('/')[0];
|
|
48
|
-
let assetReview: any;
|
|
49
|
-
|
|
50
|
-
if (type === 'reviewNode' && ['File', 'IDE'].includes(label)) {
|
|
51
|
-
assetReview = R.find(R.propEq('vertex', vertex))(assetReviews) ?? null;
|
|
52
|
-
}
|
|
53
48
|
|
|
54
49
|
nodes.push({
|
|
55
50
|
id: vertex,
|
|
56
51
|
position,
|
|
57
52
|
type: label !== 'FileLayer' ? type : 'fileLayerNode',
|
|
58
|
-
data
|
|
59
|
-
style: getNodeStyle(type, label,
|
|
53
|
+
data,
|
|
54
|
+
style: getNodeStyle(type, label, !!restrictedAssets[vertex]),
|
|
60
55
|
});
|
|
61
56
|
});
|
|
62
57
|
|
|
@@ -181,13 +176,21 @@ export const getStyle = (type: string) => {
|
|
|
181
176
|
return style;
|
|
182
177
|
};
|
|
183
178
|
|
|
184
|
-
export const getSchemes = (
|
|
179
|
+
export const getSchemes = (
|
|
180
|
+
label: string,
|
|
181
|
+
vertex: string,
|
|
182
|
+
labels: Certificate['serviceLabels'],
|
|
183
|
+
schemes: Certificate['schemes'],
|
|
184
|
+
) => {
|
|
185
185
|
const nodeSchemes: { [key: string]: object } = {};
|
|
186
186
|
|
|
187
|
+
if (!['Sample', 'Subject'].includes(label)) return nodeSchemes;
|
|
187
188
|
if (!labels || !schemes) return nodeSchemes;
|
|
188
189
|
|
|
189
190
|
const schemeLabels = labels[vertex];
|
|
190
191
|
|
|
192
|
+
if (!schemeLabels) return nodeSchemes;
|
|
193
|
+
|
|
191
194
|
Object.entries(schemeLabels).forEach(([key, val]: any) => {
|
|
192
195
|
if (key === 'Survey Data Scheme Key') {
|
|
193
196
|
nodeSchemes.surveyData = schemes[val];
|
|
@@ -206,6 +209,8 @@ export const getSchemes = (vertex: string, labels: Certificate['serviceLabels'],
|
|
|
206
209
|
};
|
|
207
210
|
|
|
208
211
|
export const getNodeLabel = (type: string, desc: any) => {
|
|
212
|
+
if (!desc) return type;
|
|
213
|
+
|
|
209
214
|
switch (type) {
|
|
210
215
|
case 'FileSet':
|
|
211
216
|
return (
|
|
@@ -254,6 +259,8 @@ export const getNodeLabel = (type: string, desc: any) => {
|
|
|
254
259
|
};
|
|
255
260
|
|
|
256
261
|
export const getNodeDescription = (type: string, desc: any) => {
|
|
262
|
+
if (!desc) return null;
|
|
263
|
+
|
|
257
264
|
switch (type) {
|
|
258
265
|
case 'Cohort':
|
|
259
266
|
case 'FileSet':
|
|
@@ -267,7 +274,7 @@ export const getNodeDescription = (type: string, desc: any) => {
|
|
|
267
274
|
return desc.Description;
|
|
268
275
|
|
|
269
276
|
case 'Sample':
|
|
270
|
-
return
|
|
277
|
+
return desc['Visit Name'];
|
|
271
278
|
|
|
272
279
|
case 'Specimen':
|
|
273
280
|
return desc['Specimen Type'];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo } from 'react';
|
|
2
2
|
import dagre from 'dagre';
|
|
3
3
|
import * as R from 'ramda';
|
|
4
4
|
import ReactFlow, {
|
|
@@ -24,42 +24,37 @@ import 'react-toastify/dist/ReactToastify.css';
|
|
|
24
24
|
import 'reactflow/dist/style.css';
|
|
25
25
|
import './style.css';
|
|
26
26
|
|
|
27
|
-
const nodeTypes = {
|
|
28
|
-
readNode: ReadNode,
|
|
29
|
-
fileLayerNode: FileLayerNode,
|
|
30
|
-
fileNode: ReadNode,
|
|
31
|
-
pipelineNode: ReadNode,
|
|
32
|
-
IDENode: ReadNode,
|
|
33
|
-
editNode: EditNode,
|
|
34
|
-
reviewNode: ReviewNode,
|
|
35
|
-
workspaceFileNode: WorkspaceFileNode,
|
|
36
|
-
workspacePipelineNode: WorkspacePipelineNode,
|
|
37
|
-
workspaceIDENode: WorkspaceIDENode,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
27
|
const dagreGraph = new dagre.graphlib.Graph();
|
|
41
28
|
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
42
29
|
|
|
43
|
-
const Graph = ({
|
|
44
|
-
type: string,
|
|
30
|
+
const Graph = ({ certificate, graph, children }: {
|
|
45
31
|
certificate: Certificate,
|
|
46
32
|
graph: {
|
|
47
33
|
nodes: Array<Node>,
|
|
48
34
|
edges: Array<Edge>,
|
|
49
35
|
},
|
|
50
|
-
replica?: Replica,
|
|
51
36
|
children: any,
|
|
52
37
|
}) => {
|
|
53
38
|
const {
|
|
54
|
-
setCertificate,
|
|
55
|
-
setReplica,
|
|
56
|
-
setType,
|
|
57
39
|
nodesState,
|
|
58
40
|
edgesState,
|
|
59
41
|
nodeLayers,
|
|
60
42
|
setNodeLayers,
|
|
61
43
|
} = useGraph();
|
|
62
44
|
|
|
45
|
+
const nodeTypes = useMemo(() => ({
|
|
46
|
+
readNode: ReadNode,
|
|
47
|
+
fileLayerNode: FileLayerNode,
|
|
48
|
+
fileNode: ReadNode,
|
|
49
|
+
pipelineNode: ReadNode,
|
|
50
|
+
IDENode: ReadNode,
|
|
51
|
+
editNode: EditNode,
|
|
52
|
+
reviewNode: ReviewNode,
|
|
53
|
+
workspaceFileNode: WorkspaceFileNode,
|
|
54
|
+
workspacePipelineNode: WorkspacePipelineNode,
|
|
55
|
+
workspaceIDENode: WorkspaceIDENode,
|
|
56
|
+
}), []);
|
|
57
|
+
|
|
63
58
|
const [nodes, setNodes, onNodesChange] = nodesState;
|
|
64
59
|
const [edges, setEdges, onEdgesChange] = edgesState;
|
|
65
60
|
|
|
@@ -76,10 +71,6 @@ const Graph = ({ type, certificate, replica, graph, children }: {
|
|
|
76
71
|
edges: graph.edges,
|
|
77
72
|
},
|
|
78
73
|
});
|
|
79
|
-
|
|
80
|
-
setType(type);
|
|
81
|
-
setCertificate(certificate);
|
|
82
|
-
if (replica) setReplica(replica);
|
|
83
74
|
}, [certificate]);
|
|
84
75
|
|
|
85
76
|
return (
|
|
@@ -95,7 +86,9 @@ const Graph = ({ type, certificate, replica, graph, children }: {
|
|
|
95
86
|
onEdgesChange={onEdgesChange}
|
|
96
87
|
connectionLineType={ConnectionLineType.SmoothStep}
|
|
97
88
|
nodeTypes={nodeTypes}
|
|
98
|
-
|
|
89
|
+
onlyRenderVisibleElements
|
|
90
|
+
edgesUpdatable={false}
|
|
91
|
+
defaultViewport={{ x: 0, y: 0, zoom: 0.6 }}
|
|
99
92
|
>
|
|
100
93
|
<Controls />
|
|
101
94
|
<Background />
|
|
@@ -115,12 +108,14 @@ const CertificateGraph = ({ type, certificate, replica, graph, children }: {
|
|
|
115
108
|
replica?: Replica,
|
|
116
109
|
children?: any,
|
|
117
110
|
}) => (
|
|
118
|
-
<GraphProvider
|
|
111
|
+
<GraphProvider
|
|
112
|
+
type={type}
|
|
113
|
+
certificate={certificate}
|
|
114
|
+
replica={replica}
|
|
115
|
+
>
|
|
119
116
|
<div style={{ height: '75vh' }}>
|
|
120
117
|
<Graph
|
|
121
|
-
type={type}
|
|
122
118
|
certificate={certificate}
|
|
123
|
-
replica={replica}
|
|
124
119
|
graph={graph}
|
|
125
120
|
>
|
|
126
121
|
{children}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Handle, Position, NodeProps } from 'reactflow';
|
|
3
|
+
import { useGraph } from '../../../state/GraphContext';
|
|
3
4
|
import { MaskMetadataButton } from './MaskMetadataModal';
|
|
4
5
|
import { Description, Label } from '../CertificateGraph/NodeAttributes';
|
|
5
6
|
import { EditNodeModalBtn } from './EditNodeModal';
|
|
6
7
|
import { getLabelLink } from '../CertificateGraph/helpers';
|
|
7
8
|
|
|
8
|
-
const EditNode = (
|
|
9
|
-
const {
|
|
9
|
+
const EditNode = (props: NodeProps) => {
|
|
10
|
+
const { customLabels, generatedLabels, isExecutable } = useGraph().certificate;
|
|
11
|
+
const { id } = props;
|
|
10
12
|
|
|
11
|
-
const [label, guid] =
|
|
13
|
+
const [label, guid] = id.split('/');
|
|
14
|
+
const description = customLabels[id] || generatedLabels[id];
|
|
12
15
|
const link = getLabelLink(label, guid, isExecutable);
|
|
13
16
|
|
|
14
17
|
return (
|
|
@@ -21,15 +24,8 @@ const EditNode = ({ data }: NodeProps) => {
|
|
|
21
24
|
<div className="p-2" style={{ width: '250px' }}>
|
|
22
25
|
<div className="label">
|
|
23
26
|
<Label link={link} label={label} description={description} />
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
node={data}
|
|
27
|
-
/>
|
|
28
|
-
|
|
29
|
-
<MaskMetadataButton
|
|
30
|
-
show={metadata[vertex]}
|
|
31
|
-
node={data}
|
|
32
|
-
/>
|
|
27
|
+
<EditNodeModalBtn node={props} />
|
|
28
|
+
<MaskMetadataButton node={props} />
|
|
33
29
|
</div>
|
|
34
30
|
|
|
35
31
|
<Description label={label} description={description} />
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { NodeProps } from 'reactflow';
|
|
2
3
|
import { Button, ConfirmationModal, ResourceEditor, MessageStripe } from 'hise-components';
|
|
3
4
|
import { useGraph } from '../../../state/GraphContext';
|
|
4
5
|
import { getFormControls } from './helpers';
|
|
5
6
|
|
|
6
|
-
export const EditNodeModalBtn = ({ node }
|
|
7
|
+
export const EditNodeModalBtn = ({ node }: {
|
|
8
|
+
node: NodeProps,
|
|
9
|
+
}) => {
|
|
7
10
|
const { setNode } = useGraph();
|
|
8
11
|
|
|
9
12
|
return (
|
|
@@ -12,13 +15,13 @@ export const EditNodeModalBtn = ({ node }) => {
|
|
|
12
15
|
key="edit-btn"
|
|
13
16
|
color="link"
|
|
14
17
|
className="text-info px-1 pt-0 ml-2"
|
|
15
|
-
icon="fas fa-edit"
|
|
16
|
-
noText
|
|
17
18
|
onClick={() => {
|
|
18
|
-
node.toggleEditModal();
|
|
19
|
+
node.data.toggleEditModal();
|
|
19
20
|
setNode(node);
|
|
20
21
|
}}
|
|
21
|
-
|
|
22
|
+
>
|
|
23
|
+
EDIT
|
|
24
|
+
</Button>
|
|
22
25
|
);
|
|
23
26
|
};
|
|
24
27
|
|
|
@@ -28,21 +31,60 @@ const EditNodeModal = ({
|
|
|
28
31
|
toggle,
|
|
29
32
|
handleUpdate,
|
|
30
33
|
refresh,
|
|
34
|
+
}: {
|
|
35
|
+
isOpen: boolean,
|
|
36
|
+
loading: boolean,
|
|
37
|
+
toggle: Function,
|
|
38
|
+
handleUpdate: Function,
|
|
39
|
+
refresh: Function,
|
|
31
40
|
}) => {
|
|
32
|
-
const { node } = useGraph();
|
|
33
|
-
|
|
34
|
-
const {
|
|
35
|
-
link, vertex, label, description, customLabels, metadata,
|
|
36
|
-
} = node;
|
|
41
|
+
const { node, certificate } = useGraph();
|
|
42
|
+
const { customLabels, generatedLabels, metadata } = certificate;
|
|
37
43
|
|
|
38
44
|
const [error, setError] = useState('');
|
|
39
|
-
|
|
40
45
|
const [form, setForm] = useState({});
|
|
41
46
|
const clear = () => setForm({});
|
|
42
47
|
|
|
43
48
|
useEffect(() => {
|
|
49
|
+
if (!node?.id) return;
|
|
50
|
+
|
|
51
|
+
const description = customLabels[node.id] || generatedLabels[node.id];
|
|
52
|
+
|
|
44
53
|
if (description) setForm(description);
|
|
45
|
-
}, [
|
|
54
|
+
}, [node.id]);
|
|
55
|
+
|
|
56
|
+
if (!node?.id) return null;
|
|
57
|
+
|
|
58
|
+
const label = node?.id.split('/')[0];
|
|
59
|
+
|
|
60
|
+
const onFieldChange = (fields: Map<string, string>) => setForm(fields);
|
|
61
|
+
|
|
62
|
+
const onUpdate = () => {
|
|
63
|
+
const body = {
|
|
64
|
+
metadata,
|
|
65
|
+
customLabels: {
|
|
66
|
+
...customLabels,
|
|
67
|
+
[node.id]: form,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
handleUpdate(body).then((response: any) => {
|
|
72
|
+
if (response.status === 200) {
|
|
73
|
+
toggle();
|
|
74
|
+
clear();
|
|
75
|
+
refresh();
|
|
76
|
+
setError('');
|
|
77
|
+
} else {
|
|
78
|
+
setError(response.data.Message);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const onCancel = () => {
|
|
84
|
+
toggle();
|
|
85
|
+
clear();
|
|
86
|
+
setError('');
|
|
87
|
+
};
|
|
46
88
|
|
|
47
89
|
return (
|
|
48
90
|
<ConfirmationModal
|
|
@@ -54,24 +96,13 @@ const EditNodeModal = ({
|
|
|
54
96
|
noCancel
|
|
55
97
|
content={(
|
|
56
98
|
<>
|
|
57
|
-
{link && (
|
|
58
|
-
<Button
|
|
59
|
-
to={link}
|
|
60
|
-
color="link"
|
|
61
|
-
icon="fas fa-long-arrow-alt-right"
|
|
62
|
-
target="_blank"
|
|
63
|
-
>
|
|
64
|
-
{vertex}
|
|
65
|
-
</Button>
|
|
66
|
-
)}
|
|
67
|
-
|
|
68
99
|
{(error && !loading) && (
|
|
69
100
|
<MessageStripe message={error} color="danger" dismissible={false} />
|
|
70
101
|
)}
|
|
71
102
|
|
|
72
103
|
<ResourceEditor
|
|
73
104
|
className=""
|
|
74
|
-
onChange={
|
|
105
|
+
onChange={onFieldChange}
|
|
75
106
|
editedResource={form}
|
|
76
107
|
controls={getFormControls(label, form)}
|
|
77
108
|
/>
|
|
@@ -88,11 +119,7 @@ const EditNodeModal = ({
|
|
|
88
119
|
icon="fas fa-times"
|
|
89
120
|
className="mx-2"
|
|
90
121
|
noText
|
|
91
|
-
onClick={
|
|
92
|
-
toggle();
|
|
93
|
-
clear();
|
|
94
|
-
setError('');
|
|
95
|
-
}}
|
|
122
|
+
onClick={onCancel}
|
|
96
123
|
/>
|
|
97
124
|
|
|
98
125
|
<Button
|
|
@@ -100,27 +127,7 @@ const EditNodeModal = ({
|
|
|
100
127
|
icon="fas fa-check"
|
|
101
128
|
noText
|
|
102
129
|
color="primary"
|
|
103
|
-
onClick={
|
|
104
|
-
const body = {
|
|
105
|
-
metadata,
|
|
106
|
-
customLabels: {
|
|
107
|
-
...customLabels,
|
|
108
|
-
[vertex]: form,
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
handleUpdate(body)
|
|
113
|
-
.then((response) => {
|
|
114
|
-
if (response.status === 200) {
|
|
115
|
-
toggle();
|
|
116
|
-
clear();
|
|
117
|
-
refresh();
|
|
118
|
-
setError('');
|
|
119
|
-
} else {
|
|
120
|
-
setError(response.data.Message);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}}
|
|
130
|
+
onClick={onUpdate}
|
|
124
131
|
/>
|
|
125
132
|
</div>
|
|
126
133
|
)}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import MaskMetadataTable from './MaskMetadataTable';
|
|
3
|
+
import { Metadata, MetadataType } from '../../../../@types/CertificateTypes';
|
|
4
|
+
|
|
5
|
+
const EMRDropdown = ({ data, entry, setEntry }: {
|
|
6
|
+
data: { [key: string]: any }
|
|
7
|
+
entry: {
|
|
8
|
+
daysSinceFirstResearchVisit: string,
|
|
9
|
+
},
|
|
10
|
+
setEntry: Function,
|
|
11
|
+
}) => (
|
|
12
|
+
<div className="text-center mt-3">
|
|
13
|
+
<div className="dropdown">
|
|
14
|
+
<button className="btn btn-secondary dropdown-toggle" type="button" id="emrDropdownBtn" data-bs-toggle="dropdown" aria-expanded="false">
|
|
15
|
+
Visit Day
|
|
16
|
+
{' '}
|
|
17
|
+
{entry.daysSinceFirstResearchVisit}
|
|
18
|
+
</button>
|
|
19
|
+
<ul className="dropdown-menu" aria-labelledby="emrDropdownBtn">
|
|
20
|
+
{Object.entries(data).map(([i, emr]) => (
|
|
21
|
+
<li key={i}>
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
className="dropdown-item"
|
|
25
|
+
onClick={() => setEntry(i)}
|
|
26
|
+
>
|
|
27
|
+
Visit Day
|
|
28
|
+
{' '}
|
|
29
|
+
{emr.daysSinceFirstResearchVisit}
|
|
30
|
+
</button>
|
|
31
|
+
</li>
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const EMRMaskingTables = ({ type, metadata, setMetadata }: {
|
|
39
|
+
type: MetadataType,
|
|
40
|
+
metadata: Metadata,
|
|
41
|
+
setMetadata: Function,
|
|
42
|
+
}) => {
|
|
43
|
+
const emrData = metadata[type];
|
|
44
|
+
|
|
45
|
+
const [activeEntry, setActiveEntry] = useState(0);
|
|
46
|
+
|
|
47
|
+
if (!metadata) return null;
|
|
48
|
+
if (!emrData) return null;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<EMRDropdown
|
|
53
|
+
data={emrData}
|
|
54
|
+
entry={emrData[activeEntry]}
|
|
55
|
+
setEntry={setActiveEntry}
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
<MaskMetadataTable
|
|
59
|
+
type={type}
|
|
60
|
+
metadata={metadata}
|
|
61
|
+
setMetadata={setMetadata}
|
|
62
|
+
emrEntry={emrData[activeEntry]}
|
|
63
|
+
emrIndex={activeEntry}
|
|
64
|
+
/>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default EMRMaskingTables;
|