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
@@ -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 { getEditNodeData } from './helpers';
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 = getEditNodeData(certificate.vertices, certificate, toggleEditModal, toggleMaskModal)
21
+ const data = { toggleEditModal, toggleMaskModal };
17
22
  const graph = getNodesAndEdges(
18
23
  certificate.vertices,
19
24
  certificate.edges,
20
- certificate.assetReviews,
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 { getWorkspaceData, getWorkspaceNodesAndEdges } from '../WorkspaceCertificate/helpers';
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.assetReviews,
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
- subgraphData,
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
+ };