@@ -61,8 +61,8 @@ const csvConfig = mkConfig({
6161 useKeysAsHeaders : true ,
6262 filename : `Testing_Activity_Search_${ new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] } `
6363} ) ;
64-
6564const LOT_INPUT_KEYS = [ 'lot-input-1' , 'lot-input-2' , 'lot-input-3' , 'lot-input-4' , 'lot-input-5' ] as const ;
65+ const toDate = ( value ?: string ) => ( value ? new Date ( `${ value } T00:00:00` ) : undefined ) ;
6666
6767const TestSearch = ( ) => {
6868 const [ hasSearched , setHasSearched ] = useState ( false ) ;
@@ -357,9 +357,9 @@ const TestSearch = () => {
357357 resetAlert ( ) ;
358358 } ;
359359
360- const handleWithdrawalDateChange = ( dates : ( string | Date ) [ ] , type : 'start' | 'end' ) => {
360+ const handleWithdrawalDateChange = ( dates : Date [ ] , type : 'start' | 'end' ) => {
361361 const raw = dates ?. [ 0 ] ;
362- const value = typeof raw === 'string' ? raw : raw ? .toISOString ( ) . slice ( 0 , 10 ) ;
362+ const value = raw instanceof Date ? raw . toISOString ( ) . slice ( 0 , 10 ) : undefined ;
363363
364364 setSearchParams ( ( prev ) => {
365365 const currentStart = prev . seedWithdrawalStartDate ;
@@ -472,140 +472,150 @@ const TestSearch = () => {
472472 < Row className = "consep-test-search-title" >
473473 < PageTitle title = "Testing activities" />
474474 </ Row >
475- < Row className = "consep-test-search-lot-numbers-filter" >
476- < FormLabel className = "lot-inputs-label" > Lot #</ FormLabel >
477- < div className = "lot-inputs" >
478- { rawLotInput . map ( ( value , index ) => (
479- < TextInput
480- key = { `${ LOT_INPUT_KEYS [ index ] } ` }
481- id = { `lot-input-${ index } ` }
482- value = { value }
483- labelText = { `Lot # ${ index + 1 } ` }
484- hideLabel
485- onChange = { ( e : ChangeEvent < HTMLInputElement > ) => {
486- handleLotInputChange ( index , e . target . value ) ;
475+ < form
476+ noValidate
477+ onSubmit = { ( e ) => {
478+ e . preventDefault ( ) ;
479+ if ( ! hasValidationErrors ( ) ) {
480+ handleSearchClick ( ) ;
481+ }
482+ } }
483+ >
484+ < Row className = "consep-test-search-lot-numbers-filter" >
485+ < FormLabel className = "lot-inputs-label" > Lot #</ FormLabel >
486+ < div className = "lot-inputs" >
487+ { rawLotInput . map ( ( value , index ) => (
488+ < TextInput
489+ key = { `${ LOT_INPUT_KEYS [ index ] } ` }
490+ id = { `lot-input-${ index } ` }
491+ value = { value }
492+ labelText = { `Lot # ${ index + 1 } ` }
493+ hideLabel
494+ onChange = { ( e : ChangeEvent < HTMLInputElement > ) => {
495+ handleLotInputChange ( index , e . target . value ) ;
496+ } }
497+ invalid = { validateSearch . lotNumbers [ index ] ?. error }
498+ invalidText = { validateSearch . lotNumbers [ index ] ?. errorMessage }
499+ />
500+ ) ) }
501+ </ div >
502+ </ Row >
503+ < Row className = "consep-test-search-filters" >
504+ < Column className = "filters-row" >
505+ < FilterableMultiSelect
506+ id = "test-type-input"
507+ className = "test-type-input"
508+ titleText = "Test type"
509+ items = {
510+ testTypeQuery . data
511+ ? testTypeQuery . data . map ( ( type : string ) => ( {
512+ id : type ,
513+ text : type
514+ } ) )
515+ : [ ]
516+ }
517+ itemToString = { ( item : { id : string ; text : string } | null ) => ( item ? item . text : '' ) }
518+ onChange = { ( event : { selectedItems : Array < { id : string } > } ) => {
519+ handleMultiSelectChanges ( 'testTypes' , event . selectedItems . map ( ( it : { id : string } ) => it . id ) ) ;
487520 } }
488- invalid = { validateSearch . lotNumbers [ index ] ?. error }
489- invalidText = { validateSearch . lotNumbers [ index ] ?. errorMessage }
521+ selectionFeedback = "top-after-reopen"
490522 />
491- ) ) }
492- </ div >
493- </ Row >
494- < Row className = "consep-test-search-filters" >
495- < Column className = "filters-row" >
496- < FilterableMultiSelect
497- id = "test-type-input"
498- className = "test-type-input"
499- titleText = "Test type"
500- items = {
501- testTypeQuery . data
502- ? testTypeQuery . data . map ( ( type : string ) => ( {
503- id : type ,
504- text : type
505- } ) )
506- : [ ]
507- }
508- itemToString = { ( item : { id : string ; text : string } | null ) => ( item ? item . text : '' ) }
509- onChange = { ( event : { selectedItems : Array < { id : string } > } ) => {
510- handleMultiSelectChanges ( 'testTypes' , event . selectedItems . map ( ( it : { id : string } ) => it . id ) ) ;
511- } }
512- selectionFeedback = "top-after-reopen"
513- />
514- < FilterableMultiSelect
515- id = "activity-type-input"
516- className = "activity-type-input"
517- titleText = "Choose activity"
518- items = {
519- activityIdQuery . data
520- ? activityIdQuery . data . map ( ( id ) => ( { id, text : id } ) )
521- : [ ]
522- }
523- itemToString = { ( item : { id : string ; text : string } | null ) => ( item ? item . text : '' ) }
524- onChange = { ( event : { selectedItems : Array < { id : string } > } ) => {
525- handleMultiSelectChanges ( 'activityIds' , event . selectedItems . map ( ( it : { id : string } ) => it . id ) ) ;
526- } }
527- selectionFeedback = "top-after-reopen"
528- />
529- < TextInput
530- id = "germ-tray-input"
531- className = "germ-tray-input"
532- labelText = "Germ tray ID"
533- type = "number"
534- onChange = { ( e : ChangeEvent < HTMLInputElement > ) => {
535- handleGermTrayIdChange ( e ) ;
536- } }
537- value = { searchParams . germinatorTrayId }
538- invalid = { validateSearch . germinatorTray . error }
539- invalidText = { validateSearch . germinatorTray . errorMessage }
540- />
541- < DatePicker
542- datePickerType = "single"
543- className = "withdrawal-date-input"
544- dateFormat = { DATE_FORMAT }
545- onChange = { ( e : Array < Date > ) => {
546- handleWithdrawalDateChange ( e , 'start' ) ;
547- } }
548- value = {
549- searchParams . seedWithdrawalStartDate !== minStartDate
550- ? searchParams . seedWithdrawalStartDate
551- : undefined
552- }
553- style = { { minWidth : '9rem' } }
554- >
555- < DatePickerInput
556- id = "withdrawal-start-date-input"
557- labelText = "Withdrawal start"
558- autoComplete = "off"
523+ < FilterableMultiSelect
524+ id = "activity-type-input"
525+ className = "activity-type-input"
526+ titleText = "Choose activity"
527+ items = {
528+ activityIdQuery . data
529+ ? activityIdQuery . data . map ( ( id ) => ( { id, text : id } ) )
530+ : [ ]
531+ }
532+ itemToString = { ( item : { id : string ; text : string } | null ) => ( item ? item . text : '' ) }
533+ onChange = { ( event : { selectedItems : Array < { id : string } > } ) => {
534+ handleMultiSelectChanges ( 'activityIds' , event . selectedItems . map ( ( it : { id : string } ) => it . id ) ) ;
535+ } }
536+ selectionFeedback = "top-after-reopen"
559537 />
560- </ DatePicker >
561- < DatePicker
562- datePickerType = "single"
563- className = "withdrawal-date-input"
564- dateFormat = { DATE_FORMAT }
565- onChange = { ( e : Array < Date > ) => {
566- handleWithdrawalDateChange ( e , 'end' ) ;
567- } }
568- minDate = { searchParams . seedWithdrawalStartDate ?? undefined }
569- value = {
570- searchParams . seedWithdrawalEndDate !== maxEndDate
571- ? searchParams . seedWithdrawalEndDate
572- : undefined
573- }
574- style = { { minWidth : '9rem' } }
575- >
576- < DatePickerInput
577- id = "withdrawal-end-date-input"
578- labelText = "Withdrawal end"
579- autoComplete = "off"
538+ < TextInput
539+ id = "germ-tray-input"
540+ className = "germ-tray-input"
541+ labelText = "Germ tray ID"
542+ type = "number"
543+ onChange = { ( e : ChangeEvent < HTMLInputElement > ) => {
544+ handleGermTrayIdChange ( e ) ;
545+ } }
546+ value = { searchParams . germinatorTrayId }
547+ invalid = { validateSearch . germinatorTray . error }
548+ invalidText = { validateSearch . germinatorTray . errorMessage }
580549 />
581- </ DatePicker >
582- < div className = "filters-row-buttons" >
583- < Button ref = { advSearchRef } size = "md" kind = "tertiary" onClick = { toggleAdvSearch } >
584- Filters
585- </ Button >
586- < Button
587- renderIcon = { Search }
588- iconDescription = "Search activity"
589- size = "md"
590- onClick = { handleSearchClick }
591- disabled = { hasValidationErrors ( ) }
550+ < DatePicker
551+ datePickerType = "single"
552+ className = "withdrawal-date-input"
553+ dateFormat = { DATE_FORMAT }
554+ onChange = { ( dates : Date [ ] ) => {
555+ handleWithdrawalDateChange ( dates , 'start' ) ;
556+ } }
557+ value = {
558+ searchParams . seedWithdrawalStartDate !== minStartDate
559+ ? toDate ( searchParams . seedWithdrawalStartDate )
560+ : undefined
561+ }
562+ style = { { minWidth : '9rem' } }
592563 >
593- Search activity
594- </ Button >
595- </ div >
596- </ Column >
597- </ Row >
598- { openAdvSearch && modalAnchor && (
599- < AdvancedFilters
600- searchParams = { searchParams }
601- setSearchParams = { setSearchParams }
602- validateSearch = { validateSearch }
603- setValidateSearch = { setValidateSearch }
604- alignTo = { modalAnchor }
605- onClose = { handleCloseAdvSearch }
606- anchorRef = { advSearchRef }
607- />
608- ) }
564+ < DatePickerInput
565+ id = "withdrawal-start-date-input"
566+ labelText = "Withdrawal start"
567+ autoComplete = "off"
568+ />
569+ </ DatePicker >
570+ < DatePicker
571+ datePickerType = "single"
572+ className = "withdrawal-date-input"
573+ dateFormat = { DATE_FORMAT }
574+ onChange = { ( dates : Date [ ] ) => {
575+ handleWithdrawalDateChange ( dates , 'end' ) ;
576+ } }
577+ minDate = { searchParams . seedWithdrawalStartDate || undefined }
578+ value = {
579+ searchParams . seedWithdrawalEndDate !== maxEndDate
580+ ? toDate ( searchParams . seedWithdrawalEndDate )
581+ : undefined
582+ }
583+ style = { { minWidth : '9rem' } }
584+ >
585+ < DatePickerInput
586+ id = "withdrawal-end-date-input"
587+ labelText = "Withdrawal end"
588+ autoComplete = "off"
589+ />
590+ </ DatePicker >
591+ < div className = "filters-row-buttons" >
592+ < Button ref = { advSearchRef } size = "md" kind = "tertiary" onClick = { toggleAdvSearch } >
593+ Filters
594+ </ Button >
595+ < Button
596+ type = "submit"
597+ renderIcon = { Search }
598+ iconDescription = "Search activity"
599+ size = "md"
600+ disabled = { hasValidationErrors ( ) }
601+ >
602+ Search activity
603+ </ Button >
604+ </ div >
605+ </ Column >
606+ </ Row >
607+ { openAdvSearch && modalAnchor && (
608+ < AdvancedFilters
609+ searchParams = { searchParams }
610+ setSearchParams = { setSearchParams }
611+ validateSearch = { validateSearch }
612+ setValidateSearch = { setValidateSearch }
613+ alignTo = { modalAnchor }
614+ onClose = { handleCloseAdvSearch }
615+ anchorRef = { advSearchRef }
616+ />
617+ ) }
618+ </ form >
609619 </ FlexGrid >
610620 < FlexGrid >
611621 < Row className = "consep-test-search-alert" >
0 commit comments