@@ -5,12 +5,14 @@ import {
55 Drawer ,
66 Grid ,
77 Group ,
8+ Menu ,
89 Modal ,
910 Radio ,
1011 Select ,
1112 Space ,
1213 Text ,
13- Tooltip
14+ Tooltip ,
15+ useMantineTheme
1416} from '@mantine/core' ;
1517import CodeMirror from '@uiw/react-codemirror' ;
1618import { useRecoilValue } from 'recoil' ;
@@ -20,7 +22,14 @@ import { measureBundleState } from '../../state/atoms/measureBundle';
2022import { useState } from 'react' ;
2123import { measurementPeriodFormattedState } from '../../state/atoms/measurementPeriod' ;
2224import { showNotification } from '@mantine/notifications' ;
23- import { IconAlertCircle , IconCircleCheck , IconCopy } from '@tabler/icons' ;
25+ import {
26+ IconAlertCircle ,
27+ IconChevronDown ,
28+ IconCircleCheck ,
29+ IconCopy ,
30+ IconPackage ,
31+ IconSquareCheck
32+ } from '@tabler/icons' ;
2433import { getPatientInfoString , getPatientNameString } from '../../util/fhir/patient' ;
2534import { createDataExchangeMeasureReport , createPatientBundle } from '../../util/fhir/resourceCreation' ;
2635import PopulationResultTable , { LabeledDetailedResult } from './PopulationResultsTable' ;
@@ -53,6 +62,7 @@ export default function PopulationCalculation() {
5362 const [ subjectValue , setSubjectValue ] = useState < string | null > ( '' ) ;
5463 const [ subjectData , setSubjectData ] = useState < { value : string ; label : string } [ ] > ( [ ] ) ;
5564 const [ evaluateText , setEvaluateText ] = useState < string > ( '' ) ;
65+ const theme = useMantineTheme ( ) ;
5666
5767 /**
5868 * Creates object that maps patient ids to their name/DOB info strings.
@@ -71,58 +81,102 @@ export default function PopulationCalculation() {
7181 * without using Measure/$deqm-submit-data endpoint
7282 * Each transaction bundle should contain DEQM Data Exchange MeasureReports with data-of-interest
7383 * and should be for a single subject (will do a separate POST for each patient)
84+ * @param proto: true if we should do a proto submit data that simply POSTs transaction bundles
7485 * @returns { {postedId: string, testCaseInfo: testCaseInfo}|undefined[] } objects with the evaluation service ids for POSTed patients (may be different than sent IDs or undefined if send was unsuccessful) and the corresponding test case information
7586 */
76- const submitDataToEvaluationService = async ( ) : Promise <
77- ( { postedId : string ; testCaseInfo : TestCaseInfo } | undefined ) [ ]
78- > => {
79- // collect data and POST to evaluation service (TODO: should be $submit-data when available)
80-
87+ const submitDataToEvaluationService = async (
88+ proto : boolean
89+ ) : Promise < ( { postedId : string ; testCaseInfo : TestCaseInfo } | undefined ) [ ] > => {
8190 const measure = measureBundle . content ?. entry ?. find ( e => e . resource ?. resourceType === 'Measure' )
8291 ?. resource as fhir4 . Measure ;
8392
84- const postedIds : Promise < { postedId : string ; testCaseInfo : TestCaseInfo } | undefined > [ ] = Object . keys (
85- currentPatients
86- ) . map ( async id => {
87- const bundle = createPatientBundle (
93+ const patientIds = Object . keys ( currentPatients ) ;
94+ const bundles = patientIds . map ( id => {
95+ return createPatientBundle (
8896 currentPatients [ id ] . patient ,
8997 minimizeTestCaseResources ( currentPatients [ id ] , measureBundle . content , drLookupByType ) ,
9098 currentPatients [ id ] . fullUrl ,
9199 createDataExchangeMeasureReport ( measure , measurementPeriodFormatted as fhir4 . Period , id )
92100 ) ;
93- const response = await fetch ( `${ evaluationServiceUrl } /` , {
101+ } ) ;
102+
103+ let responseBundles : ( fhir4 . Bundle | undefined ) [ ] = [ ] ;
104+ if ( proto ) {
105+ // Simple POST
106+ const bundlePromises = bundles . map ( async ( bundle , idx ) => {
107+ const response = await fetch ( `${ evaluationServiceUrl } /` , {
108+ method : 'POST' ,
109+ body : JSON . stringify ( bundle ) ,
110+ headers : { 'Content-Type' : 'application/json+fhir' }
111+ } ) ;
112+ const responseBody : fhir4 . Bundle | fhir4 . OperationOutcome = await response . json ( ) ;
113+ if ( responseBody . resourceType === 'OperationOutcome' ) {
114+ showNotification ( {
115+ icon : < IconAlertCircle /> ,
116+ title : 'Patient data submission failed' ,
117+ message : `Submitting data for Patient: ${ getPatientNameString (
118+ currentPatients [ patientIds [ idx ] ] . patient
119+ ) } failed with details: "${ responseBody . issue [ 0 ] . details ?. text ?? response . status } "`,
120+ color : 'red'
121+ } ) ;
122+ return undefined ;
123+ }
124+ return responseBody ;
125+ } ) ;
126+ responseBundles = await Promise . all ( bundlePromises ) ;
127+ } else {
128+ // full $submit-data
129+ const parameters = {
130+ resourceType : 'Parameters' ,
131+ parameter : bundles . map ( bundle => {
132+ return {
133+ name : 'bundle' ,
134+ resource : bundle
135+ } ;
136+ } )
137+ } ;
138+ const response = await fetch ( `${ evaluationServiceUrl } /Measure/$submit-data` , {
94139 method : 'POST' ,
95- body : JSON . stringify ( bundle ) ,
140+ body : JSON . stringify ( parameters ) ,
96141 headers : { 'Content-Type' : 'application/json+fhir' }
97142 } ) ;
98- const responseBody : fhir4 . Bundle | fhir4 . OperationOutcome = await response . json ( ) ;
143+ const responseBody : fhir4 . Parameters | fhir4 . OperationOutcome = await response . json ( ) ;
99144 if ( responseBody . resourceType === 'OperationOutcome' ) {
100145 showNotification ( {
101146 icon : < IconAlertCircle /> ,
102- title : 'Patient data submission failed ' ,
103- message : `Submitting data for Patient: ${ getPatientNameString (
104- currentPatients [ id ] . patient
105- ) } failed with details: " ${ responseBody . issue [ 0 ] . details ?. text ?? response . status } "`,
147+ title : '$submit- data failure ' ,
148+ message : `$submit- data operation failed with details: " ${
149+ responseBody . issue [ 0 ] . details ?. text ?? response . status
150+ } "`,
106151 color : 'red'
107152 } ) ;
108- return ;
153+ } else if ( ! responseBody . parameter ) {
154+ showNotification ( {
155+ icon : < IconAlertCircle /> ,
156+ title : '$submit-data no parameters' ,
157+ message : `$submit-data operation received a response with no parameters` ,
158+ color : 'red'
159+ } ) ;
160+ } else {
161+ responseBundles = responseBody . parameter . map ( p => p . resource as fhir4 . Bundle ) ;
109162 }
163+ }
110164
111- // should return transaction response bundles from which we can pull the posted patient id
112- const locationId = responseBody . entry
165+ const postedIds = responseBundles . map ( ( bundle , idx ) => {
166+ // should be transaction response bundles from which we can pull the posted patient id
167+ const locationId = bundle ?. entry
113168 ?. find ( e => e . response ?. location ?. includes ( 'Patient/' ) )
114169 ?. response ?. location ?. split ( 'Patient/' ) [ 1 ] ;
115- return locationId ? { postedId : locationId , testCaseInfo : currentPatients [ id ] } : undefined ;
170+ return locationId ? { postedId : locationId , testCaseInfo : currentPatients [ patientIds [ idx ] ] } : undefined ;
116171 } ) ;
117-
118- return Promise . all ( postedIds ) ;
172+ return postedIds ;
119173 } ;
120174
121175 /**
122176 * Wrapper function that calls submitDataToEvaluationService() and resolves patient data for future evaluation
123177 */
124- const submitData = ( ) => {
125- submitDataToEvaluationService ( )
178+ const submitData = ( proto = false ) => {
179+ submitDataToEvaluationService ( proto )
126180 . then ( postedIds => {
127181 const resolvedIds = postedIds ?. filter ( idObj => idObj !== undefined ) ;
128182 if ( resolvedIds . length > 0 ) {
@@ -367,15 +421,31 @@ export default function PopulationCalculation() {
367421 Show Clause Coverage
368422 </ Button >
369423 </ Tooltip >
370- < Button
371- data-testid = "submit-data-button"
372- aria-label = "Submit Data"
373- styles = { { root : { marginTop : 20 } } }
374- onClick = { ( ) => submitData ( ) }
375- variant = "outline"
376- >
377- Submit Data
378- </ Button >
424+ < Menu >
425+ < Menu . Target >
426+ < Button
427+ styles = { { root : { marginTop : 20 } } }
428+ variant = "outline"
429+ rightIcon = { < IconChevronDown size = { 18 } /> }
430+ >
431+ Submit Data
432+ </ Button >
433+ </ Menu . Target >
434+ < Menu . Dropdown >
435+ < Menu . Item
436+ onClick = { ( ) => submitData ( ) }
437+ icon = { < IconPackage color = { theme . colors . green [ 6 ] } stroke = { 1.5 } /> }
438+ >
439+ $submit-data
440+ </ Menu . Item >
441+ < Menu . Item
442+ onClick = { ( ) => submitData ( true ) }
443+ icon = { < IconSquareCheck color = { theme . colors . yellow [ 6 ] } stroke = { 1.5 } /> }
444+ >
445+ POST data
446+ </ Menu . Item >
447+ </ Menu . Dropdown >
448+ </ Menu >
379449 < Tooltip
380450 label = "Disabled until data submission has succeeeded"
381451 openDelay = { 1000 }
0 commit comments