@@ -3,6 +3,13 @@ import type { PACSqueryCore } from "../../../api/pfdcm";
33import { Select , Input , Row , Col , Grid , Segmented } from "antd" ;
44import { useSearchParams } from "react-router-dom" ;
55import type { ReadonlyNonEmptyArray } from "fp-ts/ReadonlyNonEmptyArray" ;
6+ import {
7+ type PacsStudyState ,
8+ PacsSeriesCSVKeys ,
9+ PacsStudyCSVKeys ,
10+ } from "../types" ;
11+ import { DownloadIcon } from "@patternfly/react-icons" ;
12+ import OperationButton from "../../NewLibrary/components/operations/OperationButton" ;
613
714/** ------------------ Shared Types ------------------ **/
815type InputFieldProps = {
@@ -12,6 +19,7 @@ type InputFieldProps = {
1219type PacsInputProps = {
1320 services : ReadonlyNonEmptyArray < string > ;
1421 service : string ;
22+ studies : PacsStudyState [ ] | null ;
1523 setService : ( service : string ) => void ;
1624 onSubmit : ( service : string , query : PACSqueryCore ) => void ;
1725} ;
@@ -115,12 +123,16 @@ const PacsInput: React.FC<PacsInputProps> = ({
115123 service,
116124 setService,
117125 services,
126+
127+ studies : propsStudies ,
118128} ) => {
119129 const [ searchParams , setSearchParams ] = useSearchParams ( ) ;
120130
121131 // "searchMode" can be "mrn" or "accno"
122132 const searchMode = searchParams . get ( "searchMode" ) || "mrn" ;
123133
134+ const studies = propsStudies || [ ] ;
135+
124136 // Helper to update the search mode in the URL
125137 const setSearchMode = React . useCallback (
126138 ( mode : string ) => {
@@ -185,23 +197,86 @@ const PacsInput: React.FC<PacsInputProps> = ({
185197 </ div >
186198 ) ;
187199
200+ const csvLine = ( data : string [ ] ) =>
201+ data
202+ . map ( ( v ) => v . replaceAll ( '"' , '""' ) ) // escape double quotes
203+ . map ( ( v ) => `"${ v } "` ) // quote it
204+ . join ( "," ) ; // comma-separated
205+
206+ const downloadStudiesToCSV = ( ) => {
207+ const csvKeys = csvLine ( PacsStudyCSVKeys . concat ( PacsSeriesCSVKeys ) ) ;
208+ const studyCSV = studies . map ( ( { info, series } ) =>
209+ series
210+ . map ( ( eachSeries ) => {
211+ const studyList : string [ ] = PacsStudyCSVKeys . map ( ( each ) =>
212+ // @ts -expect-error
213+ String ( info [ each ] || "" ) ,
214+ ) ;
215+ const seriesList = PacsSeriesCSVKeys . map ( ( each ) =>
216+ // @ts -expect-error
217+ String ( eachSeries . info [ each ] || "" ) ,
218+ ) ;
219+
220+ return csvLine ( studyList . concat ( seriesList ) ) ;
221+ } )
222+ . join ( "\r\n" ) ,
223+ ) ;
224+
225+ const theCSV = [ csvKeys ] . concat ( studyCSV ) . join ( "\r\n" ) ;
226+
227+ const blob = new File ( [ theCSV ] , "PACS.csv" , { type : "text/csv" } ) ;
228+
229+ const downloadLink = document . createElement ( "a" ) ;
230+ const dataUrl = URL . createObjectURL ( blob ) ;
231+ downloadLink . href = dataUrl ;
232+
233+ const filenameNumber =
234+ searchMode === "mrn"
235+ ? studies [ 0 ] . info . PatientID
236+ : studies [ 0 ] . info . AccessionNumber ;
237+
238+ const filename = `PACS-${ searchMode } -${ filenameNumber } .csv` ;
239+ downloadLink . download = filename ;
240+
241+ document . body . appendChild ( downloadLink ) ;
242+ downloadLink . click ( ) ;
243+ document . body . removeChild ( downloadLink ) ;
244+ } ;
245+
246+ const ariaLabel = "Download queried results as csv" ;
247+
188248 return (
189- < Row gutter = { 2 } >
249+ < Row gutter = { 4 } >
190250 < Col xs = { 24 } sm = { 12 } md = { 8 } lg = { 6 } xl = { 5 } >
191251 { searchModeToggle }
192252 </ Col >
193253 < Col
194- xs = { { span : 24 , order : 1 } }
195- sm = { { span : 24 , order : 1 } }
196- md = { { span : 11 , order : 0 } }
197- lg = { { span : 13 , order : 0 } }
198- xl = { { span : 15 , order : 0 } }
254+ xs = { { span : 20 , order : 1 } }
255+ sm = { { span : 20 , order : 1 } }
256+ md = { { span : 6 , order : 0 } }
257+ lg = { { span : 9 , order : 0 } }
258+ xl = { { span : 11 , order : 0 } }
199259 >
200260 { input }
201261 </ Col >
202262 < Col xs = { 24 } sm = { 12 } md = { 5 } lg = { 5 } xl = { 4 } >
203263 { serviceDropdown }
204264 </ Col >
265+ < Col
266+ xs = { { span : 4 , order : 1 } }
267+ sm = { { span : 4 , order : 1 } }
268+ md = { { span : 4 , order : 0 } }
269+ lg = { { span : 4 , order : 0 } }
270+ xl = { { span : 4 , order : 0 } }
271+ >
272+ < OperationButton
273+ handleOperations = { downloadStudiesToCSV }
274+ count = { studies ?. length }
275+ icon = { < DownloadIcon /> }
276+ ariaLabel = { ariaLabel }
277+ operationKey = "download"
278+ />
279+ </ Col >
205280 </ Row >
206281 ) ;
207282} ;
0 commit comments