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.
Files changed (50) hide show
  1. package/.env.dev +1 -0
  2. package/.env.prod +1 -0
  3. package/.env.stage +1 -0
  4. package/dist/main.js +1 -1
  5. package/package.json +7 -2
  6. package/src/@types/CertificateTypes.ts +25 -15
  7. package/src/components/Certificates/CertificateGraph/LayerBackButton.tsx +1 -1
  8. package/src/components/Certificates/CertificateGraph/NodeAttributes.tsx +10 -10
  9. package/src/components/Certificates/CertificateGraph/helpers.tsx +24 -17
  10. package/src/components/Certificates/CertificateGraph/index.tsx +23 -28
  11. package/src/components/Certificates/EditCertificate/EditNode.tsx +8 -12
  12. package/src/components/Certificates/EditCertificate/{EditNodeModal.jsx → EditNodeModal.tsx} +57 -50
  13. package/src/components/Certificates/EditCertificate/MaskMetadataModal/EMRMaskingTables.tsx +69 -0
  14. package/src/components/Certificates/EditCertificate/MaskMetadataModal/MaskMetadataTable.tsx +105 -0
  15. package/src/components/Certificates/EditCertificate/MaskMetadataModal/helpers.ts +41 -0
  16. package/src/components/Certificates/EditCertificate/MaskMetadataModal/index.tsx +167 -0
  17. package/src/components/Certificates/EditCertificate/helpers.ts +2 -44
  18. package/src/components/Certificates/EditCertificate/{index.jsx → index.tsx} +9 -4
  19. package/src/components/Certificates/FileLayerNode/index.tsx +12 -23
  20. package/src/components/Certificates/ReadCertificate/MetadataModal/MetadataTable.tsx +100 -0
  21. package/src/components/Certificates/ReadCertificate/MetadataModal/helpers.tsx +46 -0
  22. package/src/components/Certificates/ReadCertificate/MetadataModal/index.tsx +142 -0
  23. package/src/components/Certificates/ReadCertificate/ReadNode.tsx +7 -2
  24. package/src/components/Certificates/ReadCertificate/index.tsx +2 -3
  25. package/src/components/Certificates/ReviewCertificate/RestrictNode/index.tsx +161 -0
  26. package/src/components/Certificates/ReviewCertificate/ReviewNode.tsx +8 -5
  27. package/src/components/Certificates/ReviewCertificate/ReviewNodes/index.tsx +5 -4
  28. package/src/components/Certificates/ReviewCertificate/helpers.ts +18 -60
  29. package/src/components/Certificates/ReviewCertificate/{index.jsx → index.tsx} +14 -10
  30. package/src/components/Certificates/WorkspaceCertificate/ReplicaIDENode/index.tsx +5 -2
  31. package/src/components/Certificates/WorkspaceCertificate/ReplicaPipelineNode/index.tsx +4 -2
  32. package/src/components/Certificates/WorkspaceCertificate/RestrictedNode/index.tsx +106 -0
  33. package/src/components/Certificates/WorkspaceCertificate/WorkspaceFileNode/index.tsx +6 -2
  34. package/src/components/Certificates/WorkspaceCertificate/WorkspaceIDENode/index.tsx +10 -5
  35. package/src/components/Certificates/WorkspaceCertificate/WorkspaceNode/index.tsx +9 -3
  36. package/src/components/Certificates/WorkspaceCertificate/WorkspacePipelineNode/index.tsx +5 -5
  37. package/src/components/Certificates/WorkspaceCertificate/helpers.ts +7 -41
  38. package/src/components/Certificates/WorkspaceCertificate/index.tsx +10 -11
  39. package/src/components/Certificates/WorkspaceCertificate/style.css +0 -10
  40. package/src/index.ts +2 -2
  41. package/src/state/GraphContext.tsx +23 -22
  42. package/src/utils/getCertificateBody.ts +89 -0
  43. package/webpack.config.js +71 -54
  44. package/src/components/Certificates/EditCertificate/MaskMetadataModal.jsx +0 -299
  45. package/src/components/Certificates/FileLayerNode/helpers.ts +0 -31
  46. package/src/components/Certificates/ReadCertificate/MetadataModal/helpers.jsx +0 -22
  47. package/src/components/Certificates/ReadCertificate/MetadataModal/index.jsx +0 -169
  48. package/src/components/Certificates/ReadCertificate/helpers.ts +0 -38
  49. package/src/components/Certificates/ReviewCertificate/ReviewNodes/AllNodesModal.jsx +0 -63
  50. 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.21",
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 enum AssetReviewStatus {
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: StringMap,
26
- assetReviews: Array<AssetReview>
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
  };
@@ -48,7 +48,7 @@ const LayerBackButton = () => {
48
48
  vertexId={node.id}
49
49
  certificateId={certificate.id}
50
50
  type={type}
51
- refresh={node.data.refresh}
51
+ refresh={node.data?.refresh}
52
52
  />
53
53
  </div>
54
54
  );
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { getNodeDescription, getNodeLabel } from './helpers';
3
3
 
4
- export const Description = (props: {
4
+ export const Description = ({ label, description }: {
5
5
  label: string,
6
- description: string,
6
+ description: any,
7
7
  }) => {
8
- const nodeDescription = getNodeDescription(props.label, props.description);
8
+ const nodeDescription = getNodeDescription(label, description);
9
9
 
10
- if (!Object.keys(props.description).length || !nodeDescription) return null;
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 = (props: {
19
+ export const Label = ({ label, description, link }: {
20
20
  label: string,
21
- description: string,
21
+ description: any,
22
22
  link?: string | null,
23
23
  }) => {
24
- const label = getNodeLabel(props.label, props.description);
24
+ const nodeLabel = getNodeLabel(label, description);
25
25
 
26
- if (!props.link) {
26
+ if (!link) {
27
27
  return (
28
28
  <div className="node-label text-break flex-fill">
29
- {label}
29
+ {nodeLabel}
30
30
  </div>
31
31
  );
32
32
  }
33
33
 
34
34
  return (
35
35
  <a
36
- href={`#${props.link}`}
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 { AssetReview, Certificate } from '../../../@types/CertificateTypes';
7
+ import { Certificate } from '../../../@types/CertificateTypes';
8
8
 
9
- const getNodeStyle = (type: string, label: string, assetReview: AssetReview | null) => {
9
+ const getNodeStyle = (type: string, label: string, isAssetRestricted: boolean) => {
10
10
  switch (type) {
11
11
  case 'reviewNode':
12
- return getReviewNodeStyle(label, assetReview);
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
- assetReviews: Certificate['assetReviews'],
24
+ restrictedAssets: Certificate['restrictedAssets'],
25
25
  type: string,
26
- data: { [key: string]: Node['data'] },
26
+ data: { [key: string]: Function },
27
27
  ) => {
28
- const initialNodes = getNodes(vertices, assetReviews, type, data);
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
- assetReviews: Certificate['assetReviews'],
36
+ restrictedAssets: Certificate['restrictedAssets'],
37
37
  type: string,
38
- data: { [key: string]: Node['data'] },
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: data[vertex],
59
- style: getNodeStyle(type, label, assetReview),
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 = (vertex: string, labels: Certificate['serviceLabels'], schemes: Certificate['schemes']) => {
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 `${desc['Visit Name']} (${desc['Visit Details']})`;
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 = ({ type, certificate, replica, graph, children }: {
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
- fitView
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 = ({ data }: NodeProps) => {
9
- const { vertex, description, metadata, isExecutable } = data;
9
+ const EditNode = (props: NodeProps) => {
10
+ const { customLabels, generatedLabels, isExecutable } = useGraph().certificate;
11
+ const { id } = props;
10
12
 
11
- const [label, guid] = vertex.split('/');
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
- <EditNodeModalBtn
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
- }, [isOpen, node, description]);
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={(l) => setForm(l)}
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;