@@ -12,13 +12,16 @@ import {
1212 Switch ,
1313 InlineLoading ,
1414 TextArea ,
15+ Layer ,
16+ InlineNotification ,
1517} from '@carbon/react' ;
1618import {
1719 navigate ,
1820 parseDate ,
1921 showNotification ,
2022 showSnackbar ,
2123 showToast ,
24+ useLayoutType ,
2225 useSession ,
2326 useVisit ,
2427} from '@openmrs/esm-framework' ;
@@ -30,6 +33,9 @@ import { ArrowUp, ArrowDown } from '@carbon/react/icons';
3033import styles from './change-status-dialog.scss' ;
3134import { QueueStatus , extractErrorMessagesFromResponse } from '../utils/utils' ;
3235import { updateVisit , useProviders } from './patient-queues.resource' ;
36+ import { Controller , useForm } from 'react-hook-form' ;
37+ import { zodResolver } from '@hookform/resolvers/zod' ;
38+ import { CreateQueueEntryFormData , createQueueEntrySchema } from './patient-queue-validation-schema.resource' ;
3339
3440interface ChangeStatusDialogProps {
3541 queueEntry : MappedQueueEntry ;
@@ -40,7 +46,9 @@ interface ChangeStatusDialogProps {
4046const ChangeStatus : React . FC < ChangeStatusDialogProps > = ( { queueEntry, currentEntry, closeModal } ) => {
4147 const { t } = useTranslation ( ) ;
4248
43- const { providers } = useProviders ( ) ;
49+ const isTablet = useLayoutType ( ) === 'tablet' ;
50+
51+ const { providers, error : errorLoadingProviders } = useProviders ( ) ;
4452
4553 const [ contentSwitcherIndex , setContentSwitcherIndex ] = useState ( 1 ) ;
4654
@@ -50,7 +58,11 @@ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, currentEn
5058
5159 const sessionUser = useSession ( ) ;
5260
53- const { queueRoomLocations, mutate } = useQueueRoomLocations ( sessionUser ?. sessionLocation ?. uuid ) ;
61+ const {
62+ queueRoomLocations,
63+ mutate,
64+ error : errorLoadingQueueRooms ,
65+ } = useQueueRoomLocations ( sessionUser ?. sessionLocation ?. uuid ) ;
5466
5567 const [ selectedNextQueueLocation , setSelectedNextQueueLocation ] = useState ( queueRoomLocations [ 0 ] ?. uuid ) ;
5668
@@ -102,6 +114,13 @@ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, currentEn
102114 [ ] ,
103115 ) ;
104116
117+ const { handleSubmit, control, formState } = useForm < CreateQueueEntryFormData > ( {
118+ mode : 'all' ,
119+ resolver : zodResolver ( createQueueEntrySchema ) ,
120+ } ) ;
121+
122+ const { errors } = formState ;
123+
105124 useEffect ( ( ) => {
106125 setPriorityComment ( priorityLabels [ contentSwitcherIndex ] ) ;
107126 } , [ contentSwitcherIndex , priorityLabels ] ) ;
@@ -176,8 +195,8 @@ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, currentEn
176195 }
177196 } ;
178197
179- const changeQueueStatus = useCallback (
180- async ( event : { preventDefault : ( ) => void ; target : { [ x : string ] : { value : string } } } ) => {
198+ const onSubmit = useCallback (
199+ async ( event ) => {
181200 event . preventDefault ( ) ;
182201
183202 try {
@@ -293,7 +312,7 @@ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, currentEn
293312 if ( queueEntry && Object . keys ( queueEntry ) ?. length > 0 ) {
294313 return (
295314 < div >
296- < Form onSubmit = { changeQueueStatus } >
315+ < Form onSubmit = { handleSubmit ( onSubmit ) } >
297316 < ModalHeader closeModal = { closeModal } />
298317 < ModalBody >
299318 < div className = { styles . modalBody } >
@@ -335,86 +354,172 @@ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, currentEn
335354 </ div >
336355 < section className = { styles . section } >
337356 < div className = { styles . sectionTitle } > { t ( 'priority' , 'Priority' ) } </ div >
338- < ContentSwitcher
339- selectedIndex = { contentSwitcherIndex }
340- className = { styles . contentSwitcher }
341- onChange = { ( { index } ) => setContentSwitcherIndex ( index ) }
342- >
343- { priorityLabels . map ( ( label , index ) => (
344- < Switch
345- key = { index }
346- name = { label . toLowerCase ( ) . replace ( ' ' , '' ) }
347- text = { t ( label . toLowerCase ( ) , label ) }
348- />
349- ) ) }
350- </ ContentSwitcher >
357+ < Controller
358+ name = "priorityComment"
359+ control = { control }
360+ render = { ( { field } ) => (
361+ < ContentSwitcher
362+ { ...field }
363+ selectedIndex = { contentSwitcherIndex }
364+ className = { styles . contentSwitcher }
365+ onChange = { ( { index } ) => {
366+ field . onChange ( index ) ;
367+ setContentSwitcherIndex ( index ) ;
368+ } }
369+ >
370+ { priorityLabels . map ( ( label , index ) => (
371+ < Switch
372+ key = { index }
373+ name = { label . toLowerCase ( ) . replace ( / \s + / g, '' ) }
374+ text = { t ( label . toLowerCase ( ) , label ) }
375+ />
376+ ) ) }
377+ </ ContentSwitcher >
378+ ) }
379+ />
351380 </ section >
352381
353382 < section className = { styles . section } >
354383 < div className = { styles . sectionTitle } > { t ( 'status' , 'Status' ) } </ div >
355- < ContentSwitcher
356- selectedIndex = { statusSwitcherIndex }
357- className = { styles . contentSwitcher }
358- onChange = { ( { index } ) => setStatusSwitcherIndex ( index ) }
359- >
360- { statusLabels . map ( ( status , index ) => (
361- < Switch
362- key = { index }
363- name = { status . label . toLowerCase ( ) . replace ( ' ' , '' ) }
364- text = { t ( status . label . toLowerCase ( ) , status . label ) }
365- />
366- ) ) }
367- </ ContentSwitcher >
384+ < Controller
385+ name = "status"
386+ control = { control }
387+ render = { ( { field } ) => (
388+ < ContentSwitcher
389+ { ...field }
390+ selectedIndex = { statusSwitcherIndex }
391+ className = { styles . contentSwitcher }
392+ onChange = { ( { index } ) => {
393+ field . onChange ( index ) ;
394+ setStatusSwitcherIndex ( index ) ;
395+ } }
396+ >
397+ { statusLabels . map ( ( status , index ) => (
398+ < Switch
399+ key = { index }
400+ name = { status . label . toLowerCase ( ) . replace ( ' ' , '' ) }
401+ text = { t ( status . label . toLowerCase ( ) , status . label ) }
402+ />
403+ ) ) }
404+ </ ContentSwitcher >
405+ ) }
406+ />
368407 </ section >
369408
370409 { status === QueueStatus . Completed && (
371410 < >
372411 < section className = { styles . section } >
373- < Select
374- labelText = { t ( 'selectNextQueueRoom' , 'Select next queue room ' ) }
375- id = "nextQueueLocation"
376- name = "nextQueueLocation"
377- invalidText = "Required"
378- value = { selectedNextQueueLocation }
379- onChange = { ( event ) => setSelectedNextQueueLocation ( event . target . value ) }
380- >
381- { ! selectedNextQueueLocation ? (
382- < SelectItem text = { t ( 'selectNextServicePoint' , 'Select next service point' ) } value = "" />
383- ) : null }
384- { filteredlocations . map ( ( location ) => (
385- < SelectItem key = { location . uuid } text = { location . display } value = { location . uuid } >
386- { location . display }
387- </ SelectItem >
388- ) ) }
389- </ Select >
412+ < div className = { styles . sectionTitle } > { t ( 'nextServicePoint' , 'Next service point' ) } </ div >
413+ < ResponsiveWrapper isTablet = { isTablet } >
414+ < Controller
415+ name = "locationTo"
416+ control = { control }
417+ defaultValue = { filteredlocations . length > 0 ? filteredlocations [ 0 ] . uuid : '' }
418+ render = { ( { field } ) => (
419+ < Select
420+ { ...field }
421+ labelText = { '' }
422+ id = "nextQueueLocation"
423+ name = "nextQueueLocation"
424+ disabled = { errorLoadingQueueRooms }
425+ invalid = { ! ! errors . locationTo }
426+ invalidText = { errors . locationTo ?. message }
427+ value = { field . value }
428+ onChange = { ( event ) => {
429+ field . onChange ( event . target . value ) ;
430+ setSelectedNextQueueLocation ( event . target . value ) ;
431+ } }
432+ >
433+ { ! field . value ? (
434+ < SelectItem text = { t ( 'selectNextServicePoint' , 'Choose next service point' ) } value = "" />
435+ ) : null }
436+ { filteredlocations . map ( ( location ) => (
437+ < SelectItem key = { location . uuid } text = { location . display } value = { location . uuid } >
438+ { location . display }
439+ </ SelectItem >
440+ ) ) }
441+ </ Select >
442+ ) }
443+ />
444+
445+ { errorLoadingQueueRooms && (
446+ < InlineNotification
447+ className = { styles . errorNotification }
448+ kind = "error"
449+ onClick = { ( ) => { } }
450+ subtitle = { errorLoadingQueueRooms }
451+ title = { t ( 'errorFetchingQueueRooms' , 'Error fetching queue rooms' ) }
452+ />
453+ ) }
454+ </ ResponsiveWrapper >
390455 </ section >
391456 < section className = { styles . section } >
392- < Select
393- labelText = { t ( 'selectProvider' , 'Select a provider' ) }
394- id = "providers-list"
395- name = "providers-list"
396- invalidText = "Required"
397- value = { selectedProvider }
398- onChange = { ( event ) => setSelectedProvider ( event . target . value ) }
399- >
400- { ! selectedProvider ? < SelectItem text = { t ( 'selectProvider' , 'Select a provider' ) } value = "" /> : null }
401- { filteredProviders . map ( ( provider ) => (
402- < SelectItem key = { provider . uuid } text = { provider . display } value = { provider . uuid } >
403- { provider . display }
404- </ SelectItem >
405- ) ) }
406- </ Select >
457+ < div className = { styles . sectionTitle } > { t ( 'selectAProvider' , 'Select a provider' ) } </ div >
458+ < ResponsiveWrapper isTablet = { isTablet } >
459+ < Controller
460+ name = "provider"
461+ control = { control }
462+ defaultValue = { filteredProviders . length > 0 ? filteredProviders [ 0 ] . uuid : '' }
463+ render = { ( { field } ) => (
464+ < Select
465+ { ...field }
466+ labelText = { '' }
467+ id = "providers-list"
468+ name = "providers-list"
469+ disabled = { errorLoadingProviders }
470+ invalid = { ! ! errors . provider }
471+ invalidText = { errors . provider ?. message }
472+ value = { field . value }
473+ onChange = { ( event ) => {
474+ field . onChange ( event . target . value ) ;
475+ setSelectedProvider ( event . target . value ) ;
476+ } }
477+ >
478+ { ! field . value ? (
479+ < SelectItem text = { t ( 'selectProvider' , 'choose a provider' ) } value = "" />
480+ ) : null }
481+ { filteredProviders . map ( ( provider ) => (
482+ < SelectItem key = { provider . uuid } text = { provider . display } value = { provider . uuid } >
483+ { provider . display }
484+ </ SelectItem >
485+ ) ) }
486+ </ Select >
487+ ) }
488+ />
489+
490+ { errorLoadingProviders && (
491+ < InlineNotification
492+ className = { styles . errorNotification }
493+ kind = "error"
494+ onClick = { ( ) => { } }
495+ subtitle = { errorLoadingProviders }
496+ title = { t ( 'errorFetchingQueueRooms' , 'Error fetching providers' ) }
497+ />
498+ ) }
499+ </ ResponsiveWrapper >
407500 </ section >
408501 < section className = { styles . section } >
409- < TextArea
410- labelText = { t ( 'notes' , 'Enter notes ' ) }
411- id = "nextNotes"
412- name = "nextNotes"
413- invalidText = "Required"
414- helperText = "Please enter notes"
415- maxCount = { 500 }
416- enableCounter
417- />
502+ < div className = { styles . sectionTitle } > { t ( 'notes' , 'Notes' ) } </ div >
503+ < ResponsiveWrapper isTablet = { isTablet } >
504+ < Controller
505+ name = "comment"
506+ control = { control }
507+ defaultValue = "NA"
508+ render = { ( { field } ) => (
509+ < TextArea
510+ { ...field }
511+ aria-label = { t ( 'comment' , 'Comment' ) }
512+ invalid = { ! ! errors . comment }
513+ invalidText = { errors . comment ?. message }
514+ labelText = ""
515+ id = "comment"
516+ name = "comment"
517+ maxCount = { 500 }
518+ enableCounter
519+ />
520+ ) }
521+ />
522+ </ ResponsiveWrapper >
418523 </ section >
419524 </ >
420525 ) }
@@ -438,4 +543,8 @@ const ChangeStatus: React.FC<ChangeStatusDialogProps> = ({ queueEntry, currentEn
438543 }
439544} ;
440545
546+ function ResponsiveWrapper ( { children, isTablet } ) {
547+ return isTablet ? < Layer > { children } </ Layer > : < div > { children } </ div > ;
548+ }
549+
441550export default ChangeStatus ;
0 commit comments