@openmrs/esm-appointments-app 9.2.1-pre.7279 → 9.2.1-pre.7288

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.
@@ -33,8 +33,9 @@ import {
33
33
  useLocations,
34
34
  usePatient,
35
35
  useSession,
36
- type DefaultWorkspaceProps,
36
+ Workspace2,
37
37
  type FetchResponse,
38
+ type Workspace2DefinitionProps,
38
39
  } from '@openmrs/esm-framework';
39
40
  import { z } from 'zod';
40
41
  import { type ConfigObject } from '../config-schema';
@@ -52,24 +53,21 @@ import { useProviders } from '../hooks/useProviders';
52
53
  import Workload from '../workload/workload.component';
53
54
  import styles from './appointments-form.scss';
54
55
 
55
- interface AppointmentsFormProps {
56
+ export interface AppointmentsFormProps {
56
57
  appointment?: Appointment;
57
58
  recurringPattern?: RecurringPattern;
58
59
  patientUuid?: string;
59
- context: string;
60
+ context: 'creating' | 'editing';
60
61
  }
61
62
 
62
63
  const time12HourFormatRegexPattern = '^(1[0-2]|0?[1-9]):[0-5][0-9]$';
64
+ const time12HourFormatRegex = /^(1[0-2]|0?[1-9]):[0-5][0-9]$/;
63
65
 
64
- const isValidTime = (timeStr: string) => timeStr.match(new RegExp(time12HourFormatRegexPattern));
66
+ const isValidTime = (timeStr: string) => time12HourFormatRegex.test(timeStr);
65
67
 
66
- const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps> = ({
67
- appointment,
68
- recurringPattern,
69
- patientUuid,
70
- context,
68
+ const AppointmentsForm: React.FC<Workspace2DefinitionProps<AppointmentsFormProps>> = ({
69
+ workspaceProps: { appointment, recurringPattern, patientUuid, context },
71
70
  closeWorkspace,
72
- promptBeforeClosing,
73
71
  }) => {
74
72
  const { patient } = usePatient(patientUuid);
75
73
  const { mutateAppointments } = useMutateAppointments();
@@ -283,19 +281,16 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
283
281
  useEffect(() => {
284
282
  if (isSuccessful) {
285
283
  reset();
286
- promptBeforeClosing(() => false);
287
- closeWorkspace();
288
- return;
284
+ closeWorkspace({ closeWindow: true, discardUnsavedChanges: true });
289
285
  }
290
- promptBeforeClosing(() => isDirty);
291
- }, [isDirty, promptBeforeClosing, isSuccessful, reset, closeWorkspace]);
286
+ }, [isSuccessful, reset, closeWorkspace]);
292
287
 
293
288
  const handleWorkloadDateChange = (date: Date) => {
294
289
  const appointmentDate = getValues('appointmentDateTime');
295
290
  setValue('appointmentDateTime', { ...appointmentDate, startDate: date });
296
291
  };
297
292
 
298
- const handleSelectChange = (e) => {
293
+ const handleSelectChange = (e: { selectedItems: Array<{ id: number; label: string }> }) => {
299
294
  setValue(
300
295
  'selectedDaysOfWeekText',
301
296
  (() => {
@@ -312,8 +307,8 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
312
307
  );
313
308
  setValue(
314
309
  'recurringPatternDaysOfWeek',
315
- e.selectedItems.map((s) => {
316
- return s.id;
310
+ e.selectedItems.map((s: { id: number }) => {
311
+ return String(s.id);
317
312
  }),
318
313
  );
319
314
  };
@@ -450,7 +445,7 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
450
445
  providers: [{ uuid: provider }],
451
446
  patientUuid: patientUuid,
452
447
  comments: appointmentNote,
453
- uuid: context === 'editing' ? appointment.uuid : undefined,
448
+ uuid: context === 'editing' ? appointment?.uuid : undefined,
454
449
  dateAppointmentScheduled: dayjs(dateAppointmentScheduled).format(),
455
450
  };
456
451
  };
@@ -480,340 +475,375 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
480
475
  );
481
476
 
482
477
  return (
483
- <Form onSubmit={handleSubmit(handleSaveAppointment)}>
484
- {patient && (
485
- <ExtensionSlot
486
- name="patient-header-slot"
487
- state={{
488
- patient,
489
- patientUuid: patientUuid,
490
- hideActionsOverflow: true,
491
- }}
492
- />
493
- )}
494
- <Stack className={styles.formWrapper} gap={6}>
495
- <FormGroup className={styles.formGroup} legendText={t('location', 'Location')}>
496
- <ResponsiveWrapper>
497
- <Controller
498
- name="location"
499
- control={control}
500
- render={({ field: { onChange, value, onBlur, ref } }) => (
501
- <Select
502
- id="location"
503
- invalid={!!errors?.location}
504
- invalidText={errors?.location?.message}
505
- labelText={t('selectALocation', 'Select a location')}
506
- onChange={onChange}
507
- onBlur={onBlur}
508
- ref={ref}
509
- value={value}>
510
- <SelectItem text={t('chooseLocation', 'Choose a location')} value="" />
511
- {locations?.length > 0 &&
512
- locations.map((location) => (
513
- <SelectItem key={location.uuid} text={location.display} value={location.uuid}>
514
- {location.display}
515
- </SelectItem>
516
- ))}
517
- </Select>
518
- )}
519
- />
520
- </ResponsiveWrapper>
521
- </FormGroup>
522
- <FormGroup className={styles.formGroup} legendText={t('service', 'Service')}>
523
- <ResponsiveWrapper>
524
- <Controller
525
- name="selectedService"
526
- control={control}
527
- render={({ field: { onBlur, onChange, value, ref } }) => (
528
- <Select
529
- id="service"
530
- invalid={!!errors?.selectedService}
531
- invalidText={errors?.selectedService?.message}
532
- labelText={t('selectService', 'Select a service')}
533
- onBlur={onBlur}
534
- onChange={(event) => {
535
- if (context === 'creating') {
536
- setValue(
537
- 'duration',
538
- services?.find((service) => service.name === event.target.value)?.durationMins,
539
- );
540
- } else if (context === 'editing') {
541
- const previousServiceDuration = services?.find(
542
- (service) => service.name === getValues('selectedService'),
543
- )?.durationMins;
544
- const selectedServiceDuration = services?.find(
545
- (service) => service.name === event.target.value,
546
- )?.durationMins;
547
- if (selectedServiceDuration && previousServiceDuration === getValues('duration')) {
548
- setValue('duration', selectedServiceDuration);
549
- }
550
- }
551
- onChange(event);
552
- }}
553
- ref={ref}
554
- value={value}>
555
- <SelectItem text={t('chooseService', 'Select service')} value="" />
556
- {services?.length > 0 &&
557
- services.map((service) => (
558
- <SelectItem key={service.uuid} text={service.name} value={service.name}>
559
- {service.name}
560
- </SelectItem>
561
- ))}
562
- </Select>
563
- )}
564
- />
565
- </ResponsiveWrapper>
566
- </FormGroup>
567
- <FormGroup className={styles.formGroup} legendText={t('appointmentType_title', 'Appointment Type')}>
568
- <ResponsiveWrapper>
569
- <Controller
570
- name="appointmentType"
571
- control={control}
572
- render={({ field: { onBlur, onChange, value, ref } }) => (
573
- <Select
574
- disabled={!appointmentTypes?.length}
575
- id="appointmentType"
576
- invalid={!!errors?.appointmentType}
577
- invalidText={errors?.appointmentType?.message}
578
- labelText={t('selectAppointmentType', 'Select the type of appointment')}
579
- onBlur={onBlur}
580
- onChange={onChange}
581
- ref={ref}
582
- value={value}>
583
- <SelectItem text={t('chooseAppointmentType', 'Choose appointment type')} value="" />
584
- {appointmentTypes?.length > 0 &&
585
- appointmentTypes.map((appointmentType, index) => (
586
- <SelectItem key={index} text={appointmentType} value={appointmentType}>
587
- {appointmentType}
588
- </SelectItem>
589
- ))}
590
- </Select>
591
- )}
592
- />
593
- </ResponsiveWrapper>
594
- </FormGroup>
595
-
596
- <FormGroup className={styles.formGroup} legendText={t('recurringAppointment', 'Recurring Appointment')}>
597
- <div>
598
- <Toggle
599
- id="recurringToggle"
600
- labelB={t('yes', 'Yes')}
601
- labelA={t('no', 'No')}
602
- labelText={t('isRecurringAppointment', 'Is this a recurring appointment?')}
603
- onClick={() => setIsRecurringAppointment(!isRecurringAppointment)}
604
- />
605
- </div>
606
- </FormGroup>
607
-
608
- <FormGroup className={styles.formGroup} legendText={t('dateTime', 'Date & Time')}>
609
- <div className={styles.dateTimeFields}>
610
- {isRecurringAppointment && (
611
- <div className={styles.inputContainer}>
612
- {allowAllDayAppointments && (
613
- <Controller
614
- name="isAllDayAppointment"
615
- control={control}
616
- render={({ field: { value, onChange } }) => (
617
- <Toggle
618
- id="allDayToggle"
619
- labelA={t('no', 'No')}
620
- labelB={t('yes', 'Yes')}
621
- labelText={t('allDay', 'All day')}
622
- toggled={value}
623
- onToggle={onChange}
624
- />
625
- )}
626
- />
478
+ <Workspace2
479
+ title={
480
+ context === 'editing'
481
+ ? t('editAppointment', 'Edit appointment')
482
+ : t('createNewAppointment', 'Create new appointment')
483
+ }
484
+ hasUnsavedChanges={isDirty}>
485
+ <Form onSubmit={handleSubmit(handleSaveAppointment)}>
486
+ {patient && (
487
+ <ExtensionSlot
488
+ name="patient-header-slot"
489
+ state={{
490
+ patient,
491
+ patientUuid: patientUuid,
492
+ hideActionsOverflow: true,
493
+ }}
494
+ />
495
+ )}
496
+ <Stack className={styles.formWrapper} gap={6}>
497
+ <FormGroup className={styles.formGroup} legendText={t('location', 'Location')}>
498
+ <ResponsiveWrapper>
499
+ <Controller
500
+ name="location"
501
+ control={control}
502
+ render={({ field: { onChange, value, onBlur, ref } }) => (
503
+ <Select
504
+ id="location"
505
+ invalid={!!errors?.location}
506
+ invalidText={errors?.location?.message}
507
+ labelText={t('selectALocation', 'Select a location')}
508
+ onChange={onChange}
509
+ onBlur={onBlur}
510
+ ref={ref}
511
+ value={value}>
512
+ <SelectItem text={t('chooseLocation', 'Choose a location')} value="" />
513
+ {locations?.length > 0 &&
514
+ locations.map((location) => (
515
+ <SelectItem key={location.uuid} text={location.display} value={location.uuid}>
516
+ {location.display}
517
+ </SelectItem>
518
+ ))}
519
+ </Select>
627
520
  )}
628
- <ResponsiveWrapper>
629
- <Controller
630
- name="appointmentDateTime"
631
- control={control}
632
- render={({ field: { onChange, value }, fieldState }) => (
633
- <OpenmrsDateRangePicker
634
- value={
635
- value.startDate && value.recurringPatternEndDate
636
- ? [value.startDate, value.recurringPatternEndDate]
637
- : null
521
+ />
522
+ </ResponsiveWrapper>
523
+ </FormGroup>
524
+ <FormGroup className={styles.formGroup} legendText={t('service', 'Service')}>
525
+ <ResponsiveWrapper>
526
+ <Controller
527
+ name="selectedService"
528
+ control={control}
529
+ render={({ field: { onBlur, onChange, value, ref } }) => (
530
+ <Select
531
+ id="service"
532
+ invalid={!!errors?.selectedService}
533
+ invalidText={errors?.selectedService?.message}
534
+ labelText={t('selectService', 'Select a service')}
535
+ onBlur={onBlur}
536
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) => {
537
+ if (context === 'creating') {
538
+ setValue(
539
+ 'duration',
540
+ services?.find((service) => service.name === event.target.value)?.durationMins,
541
+ );
542
+ } else if (context === 'editing') {
543
+ const previousServiceDuration = services?.find(
544
+ (service) => service.name === getValues('selectedService'),
545
+ )?.durationMins;
546
+ const selectedServiceDuration = services?.find(
547
+ (service) => service.name === event.target.value,
548
+ )?.durationMins;
549
+ if (selectedServiceDuration && previousServiceDuration === getValues('duration')) {
550
+ setValue('duration', selectedServiceDuration);
638
551
  }
639
- onChange={(dateRange) => {
640
- const [startDate, endDate] = dateRange;
641
- onChange({
642
- ...value,
643
- startDate,
644
- startDateText: startDate ? dayjs(startDate).format(dateFormat) : '',
645
- recurringPatternEndDate: endDate,
646
- recurringPatternEndDateText: endDate ? dayjs(endDate).format(dateFormat) : '',
647
- });
648
- }}
649
- startName="start"
650
- endName="end"
651
- id="appointmentRecurringDateRangePicker"
652
- data-testid="appointmentRecurringDateRangePicker"
653
- labelText={t('dateRange', 'Set date range')}
654
- invalid={!!fieldState?.error?.message}
655
- invalidText={fieldState?.error?.message}
656
- isRequired
657
- />
658
- )}
659
- />
660
- </ResponsiveWrapper>
661
-
662
- {!watch('isAllDayAppointment') && <TimeAndDuration t={t} control={control} errors={errors} />}
663
-
664
- <ResponsiveWrapper>
665
- <Controller
666
- name="recurringPatternPeriod"
667
- control={control}
668
- render={({ field: { onBlur, onChange, value } }) => (
669
- <NumberInput
670
- hideSteppers
671
- id="repeatNumber"
672
- min={1}
673
- max={356}
674
- label={t('repeatEvery', 'Repeat every')}
675
- invalidText={t('invalidNumber', 'Number is not valid')}
676
- value={value}
677
- onBlur={onBlur}
678
- onChange={(e) => {
679
- onChange(Number(e.target.value));
680
- }}
681
- />
682
- )}
683
- />
684
- </ResponsiveWrapper>
685
-
686
- <ResponsiveWrapper>
687
- <Controller
688
- name="recurringPatternType"
689
- control={control}
690
- render={({ field: { onChange, value } }) => (
691
- <RadioButtonGroup
692
- legendText={t('period', 'Period')}
693
- name="radio-button-group"
694
- onChange={(type) => onChange(type)}
695
- valueSelected={value}>
696
- <RadioButton labelText={t('day', 'Day')} value="DAY" id="radioDay" />
697
- <RadioButton labelText={t('week', 'Week')} value="WEEK" id="radioWeek" />
698
- </RadioButtonGroup>
699
- )}
700
- />
701
- </ResponsiveWrapper>
552
+ }
553
+ onChange(event);
554
+ }}
555
+ ref={ref}
556
+ value={value}>
557
+ <SelectItem text={t('chooseService', 'Select service')} value="" />
558
+ {services?.length > 0 &&
559
+ services.map((service) => (
560
+ <SelectItem key={service.uuid} text={service.name} value={service.name}>
561
+ {service.name}
562
+ </SelectItem>
563
+ ))}
564
+ </Select>
565
+ )}
566
+ />
567
+ </ResponsiveWrapper>
568
+ </FormGroup>
569
+ <FormGroup className={styles.formGroup} legendText={t('appointmentType_title', 'Appointment Type')}>
570
+ <ResponsiveWrapper>
571
+ <Controller
572
+ name="appointmentType"
573
+ control={control}
574
+ render={({ field: { onBlur, onChange, value, ref } }) => (
575
+ <Select
576
+ disabled={!appointmentTypes?.length}
577
+ id="appointmentType"
578
+ invalid={!!errors?.appointmentType}
579
+ invalidText={errors?.appointmentType?.message}
580
+ labelText={t('selectAppointmentType', 'Select the type of appointment')}
581
+ onBlur={onBlur}
582
+ onChange={onChange}
583
+ ref={ref}
584
+ value={value}>
585
+ <SelectItem text={t('chooseAppointmentType', 'Choose appointment type')} value="" />
586
+ {appointmentTypes?.length > 0 &&
587
+ appointmentTypes.map((appointmentType, index) => (
588
+ <SelectItem key={index} text={appointmentType} value={appointmentType}>
589
+ {appointmentType}
590
+ </SelectItem>
591
+ ))}
592
+ </Select>
593
+ )}
594
+ />
595
+ </ResponsiveWrapper>
596
+ </FormGroup>
702
597
 
703
- {watch('recurringPatternType') === 'WEEK' && (
704
- <div>
598
+ <FormGroup className={styles.formGroup} legendText={t('recurringAppointment', 'Recurring Appointment')}>
599
+ <div>
600
+ <Toggle
601
+ id="recurringToggle"
602
+ labelB={t('yes', 'Yes')}
603
+ labelA={t('no', 'No')}
604
+ labelText={t('isRecurringAppointment', 'Is this a recurring appointment?')}
605
+ onClick={() => setIsRecurringAppointment(!isRecurringAppointment)}
606
+ />
607
+ </div>
608
+ </FormGroup>
609
+
610
+ <FormGroup className={styles.formGroup} legendText={t('dateTime', 'Date & Time')}>
611
+ <div className={styles.dateTimeFields}>
612
+ {isRecurringAppointment && (
613
+ <div className={styles.inputContainer}>
614
+ {allowAllDayAppointments && (
705
615
  <Controller
706
- name="selectedDaysOfWeekText"
616
+ name="isAllDayAppointment"
707
617
  control={control}
708
- defaultValue={defaultSelectedDaysOfWeekText}
709
- render={({ field: { onChange } }) => (
710
- <MultiSelect
711
- className={styles.weekSelect}
712
- id="daysOfWeek"
713
- initialSelectedItems={weekDays.filter((i) =>
714
- getValues('recurringPatternDaysOfWeek').includes(i.id),
715
- )}
716
- items={weekDays}
717
- itemToString={(item) => (item ? t(item.labelCode, item.label) : '')}
718
- label={getValues('selectedDaysOfWeekText')}
719
- onChange={(e) => {
720
- onChange(e);
721
- handleSelectChange(e);
618
+ render={({ field: { value, onChange } }) => (
619
+ <Toggle
620
+ id="allDayToggle"
621
+ labelA={t('no', 'No')}
622
+ labelB={t('yes', 'Yes')}
623
+ labelText={t('allDay', 'All day')}
624
+ toggled={value}
625
+ onToggle={onChange}
626
+ />
627
+ )}
628
+ />
629
+ )}
630
+ <ResponsiveWrapper>
631
+ <Controller
632
+ name="appointmentDateTime"
633
+ control={control}
634
+ render={({ field: { onChange, value }, fieldState }) => (
635
+ <OpenmrsDateRangePicker
636
+ value={
637
+ value.startDate && value.recurringPatternEndDate
638
+ ? [value.startDate, value.recurringPatternEndDate]
639
+ : null
640
+ }
641
+ onChange={(dateRange) => {
642
+ const [startDate, endDate] = dateRange;
643
+ onChange({
644
+ ...value,
645
+ startDate,
646
+ startDateText: startDate ? dayjs(startDate).format(dateFormat) : '',
647
+ recurringPatternEndDate: endDate,
648
+ recurringPatternEndDateText: endDate ? dayjs(endDate).format(dateFormat) : '',
649
+ });
722
650
  }}
723
- selectionFeedback="top-after-reopen"
724
- sortItems={(items) => {
725
- return items.sort((a, b) => a.order > b.order);
651
+ startName="start"
652
+ endName="end"
653
+ id="appointmentRecurringDateRangePicker"
654
+ data-testid="appointmentRecurringDateRangePicker"
655
+ labelText={t('dateRange', 'Set date range')}
656
+ invalid={!!fieldState?.error?.message}
657
+ invalidText={fieldState?.error?.message}
658
+ isRequired
659
+ />
660
+ )}
661
+ />
662
+ </ResponsiveWrapper>
663
+
664
+ {!watch('isAllDayAppointment') && <TimeAndDuration t={t} control={control} errors={errors} />}
665
+
666
+ <ResponsiveWrapper>
667
+ <Controller
668
+ name="recurringPatternPeriod"
669
+ control={control}
670
+ render={({ field: { onBlur, onChange, value } }) => (
671
+ <NumberInput
672
+ hideSteppers
673
+ id="repeatNumber"
674
+ min={1}
675
+ max={356}
676
+ label={t('repeatEvery', 'Repeat every')}
677
+ invalidText={t('invalidNumber', 'Number is not valid')}
678
+ value={value}
679
+ onBlur={onBlur}
680
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
681
+ onChange(Number(e.target.value));
726
682
  }}
727
683
  />
728
684
  )}
729
685
  />
730
- </div>
731
- )}
732
- </div>
733
- )}
734
-
735
- {!isRecurringAppointment && (
736
- <div className={styles.inputContainer}>
737
- {allowAllDayAppointments && (
738
- <Controller
739
- name="isAllDayAppointment"
740
- control={control}
741
- render={({ field: { value, onChange } }) => (
742
- <Toggle
743
- id="allDayToggle"
744
- labelA={t('no', 'No')}
745
- labelB={t('yes', 'Yes')}
746
- labelText={t('allDay', 'All day')}
747
- toggled={value}
748
- onToggle={onChange}
749
- />
750
- )}
751
- />
752
- )}
753
- <ResponsiveWrapper>
754
- <Controller
755
- name="appointmentDateTime"
756
- control={control}
757
- render={({ field, fieldState }) => (
758
- <OpenmrsDatePicker
759
- data-testid="datePickerInput"
760
- id="datePickerInput"
761
- invalid={!!fieldState?.error?.message}
762
- invalidText={fieldState?.error?.message}
763
- labelText={t('date', 'Date')}
764
- onBlur={field.onBlur}
765
- onChange={(date) => {
766
- field.onChange({
767
- ...field.value,
768
- startDate: date,
769
- });
770
- }}
771
- style={{ width: '100%' }}
772
- value={field.value.startDate}
686
+ </ResponsiveWrapper>
687
+
688
+ <ResponsiveWrapper>
689
+ <Controller
690
+ name="recurringPatternType"
691
+ control={control}
692
+ render={({ field: { onChange, value } }) => (
693
+ <RadioButtonGroup
694
+ legendText={t('period', 'Period')}
695
+ name="radio-button-group"
696
+ onChange={(type: string) => onChange(type)}
697
+ valueSelected={value}>
698
+ <RadioButton labelText={t('day', 'Day')} value="DAY" id="radioDay" />
699
+ <RadioButton labelText={t('week', 'Week')} value="WEEK" id="radioWeek" />
700
+ </RadioButtonGroup>
701
+ )}
702
+ />
703
+ </ResponsiveWrapper>
704
+
705
+ {watch('recurringPatternType') === 'WEEK' && (
706
+ <div>
707
+ <Controller
708
+ name="selectedDaysOfWeekText"
709
+ control={control}
710
+ defaultValue={defaultSelectedDaysOfWeekText}
711
+ render={({ field: { onChange } }) => (
712
+ <MultiSelect
713
+ className={styles.weekSelect}
714
+ id="daysOfWeek"
715
+ initialSelectedItems={weekDays.filter((i) =>
716
+ getValues('recurringPatternDaysOfWeek').includes(i.id),
717
+ )}
718
+ items={weekDays}
719
+ itemToString={(item: any) => (item ? t(item.labelCode, item.label) : '')}
720
+ label={getValues('selectedDaysOfWeekText')}
721
+ onChange={(e: any) => {
722
+ onChange(e);
723
+ handleSelectChange(e);
724
+ }}
725
+ selectionFeedback="top-after-reopen"
726
+ sortItems={(items: any[]) => {
727
+ return items.sort((a: any, b: any) => a.order - b.order);
728
+ }}
729
+ />
730
+ )}
773
731
  />
774
- )}
775
- />
776
- </ResponsiveWrapper>
732
+ </div>
733
+ )}
734
+ </div>
735
+ )}
777
736
 
778
- {!watch('isAllDayAppointment') && <TimeAndDuration t={t} control={control} errors={errors} />}
779
- </div>
780
- )}
781
- </div>
782
- </FormGroup>
737
+ {!isRecurringAppointment && (
738
+ <div className={styles.inputContainer}>
739
+ {allowAllDayAppointments && (
740
+ <Controller
741
+ name="isAllDayAppointment"
742
+ control={control}
743
+ render={({ field: { value, onChange } }) => (
744
+ <Toggle
745
+ id="allDayToggle"
746
+ labelA={t('no', 'No')}
747
+ labelB={t('yes', 'Yes')}
748
+ labelText={t('allDay', 'All day')}
749
+ toggled={value}
750
+ onToggle={onChange}
751
+ />
752
+ )}
753
+ />
754
+ )}
755
+ <ResponsiveWrapper>
756
+ <Controller
757
+ name="appointmentDateTime"
758
+ control={control}
759
+ render={({ field, fieldState }) => (
760
+ <OpenmrsDatePicker
761
+ data-testid="datePickerInput"
762
+ id="datePickerInput"
763
+ invalid={!!fieldState?.error?.message}
764
+ invalidText={fieldState?.error?.message}
765
+ labelText={t('date', 'Date')}
766
+ onBlur={field.onBlur}
767
+ onChange={(date) => {
768
+ field.onChange({
769
+ ...field.value,
770
+ startDate: date,
771
+ });
772
+ }}
773
+ style={{ width: '100%' }}
774
+ value={field.value.startDate}
775
+ />
776
+ )}
777
+ />
778
+ </ResponsiveWrapper>
783
779
 
784
- {getValues('selectedService') && (
785
- <FormGroup className={styles.formGroup} legendText="">
786
- <ResponsiveWrapper>
787
- <Workload
788
- appointmentDate={watch('appointmentDateTime').startDate}
789
- onWorkloadDateChange={handleWorkloadDateChange}
790
- selectedService={watch('selectedService')}
791
- />
792
- </ResponsiveWrapper>
780
+ {!watch('isAllDayAppointment') && <TimeAndDuration t={t} control={control} errors={errors} />}
781
+ </div>
782
+ )}
783
+ </div>
793
784
  </FormGroup>
794
- )}
795
785
 
796
- {context !== 'creating' ? (
797
- <FormGroup className={styles.formGroup} legendText={t('appointmentStatus', 'Appointment Status')}>
786
+ {getValues('selectedService') && (
787
+ <FormGroup className={styles.formGroup} legendText="">
788
+ <ResponsiveWrapper>
789
+ <Workload
790
+ appointmentDate={watch('appointmentDateTime').startDate}
791
+ onWorkloadDateChange={handleWorkloadDateChange}
792
+ selectedService={watch('selectedService')}
793
+ />
794
+ </ResponsiveWrapper>
795
+ </FormGroup>
796
+ )}
797
+
798
+ {context !== 'creating' ? (
799
+ <FormGroup className={styles.formGroup} legendText={t('appointmentStatus', 'Appointment Status')}>
800
+ <ResponsiveWrapper>
801
+ <Controller
802
+ name="appointmentStatus"
803
+ control={control}
804
+ render={({ field: { onBlur, onChange, value, ref } }) => (
805
+ <Select
806
+ id="appointmentStatus"
807
+ invalid={!!errors?.appointmentStatus}
808
+ invalidText={errors?.appointmentStatus?.message}
809
+ labelText={t('selectAppointmentStatus', 'Select status')}
810
+ onBlur={onBlur}
811
+ onChange={onChange}
812
+ ref={ref}
813
+ value={value}>
814
+ <SelectItem text={t('selectAppointmentStatus', 'Select status')} value="" />
815
+ {appointmentStatuses?.length > 0 &&
816
+ appointmentStatuses.map((appointmentStatus, index) => (
817
+ <SelectItem key={index} text={appointmentStatus} value={appointmentStatus}>
818
+ {appointmentStatus}
819
+ </SelectItem>
820
+ ))}
821
+ </Select>
822
+ )}
823
+ />
824
+ </ResponsiveWrapper>
825
+ </FormGroup>
826
+ ) : null}
827
+
828
+ <FormGroup className={styles.formGroup} legendText={t('provider', 'Provider')}>
798
829
  <ResponsiveWrapper>
799
830
  <Controller
800
- name="appointmentStatus"
831
+ name="provider"
801
832
  control={control}
802
- render={({ field: { onBlur, onChange, value, ref } }) => (
833
+ render={({ field: { onChange, value, onBlur, ref } }) => (
803
834
  <Select
804
- id="appointmentStatus"
805
- invalid={!!errors?.appointmentStatus}
806
- invalidText={errors?.appointmentStatus?.message}
807
- labelText={t('selectAppointmentStatus', 'Select status')}
808
- onBlur={onBlur}
835
+ id="provider"
836
+ invalidText="Required"
837
+ labelText={t('selectProvider', 'Select a provider')}
809
838
  onChange={onChange}
839
+ onBlur={onBlur}
810
840
  ref={ref}
811
841
  value={value}>
812
- <SelectItem text={t('selectAppointmentStatus', 'Select status')} value="" />
813
- {appointmentStatuses?.length > 0 &&
814
- appointmentStatuses.map((appointmentStatus, index) => (
815
- <SelectItem key={index} text={appointmentStatus} value={appointmentStatus}>
816
- {appointmentStatus}
842
+ <SelectItem text={t('chooseProvider', 'Choose a provider')} value="" />
843
+ {providers?.providers?.length > 0 &&
844
+ providers?.providers?.map((provider) => (
845
+ <SelectItem key={provider.uuid} text={provider.display} value={provider.uuid}>
846
+ {provider.display}
817
847
  </SelectItem>
818
848
  ))}
819
849
  </Select>
@@ -821,93 +851,69 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
821
851
  />
822
852
  </ResponsiveWrapper>
823
853
  </FormGroup>
824
- ) : null}
825
-
826
- <FormGroup className={styles.formGroup} legendText={t('provider', 'Provider')}>
827
- <ResponsiveWrapper>
828
- <Controller
829
- name="provider"
830
- control={control}
831
- render={({ field: { onChange, value, onBlur, ref } }) => (
832
- <Select
833
- id="provider"
834
- invalidText="Required"
835
- labelText={t('selectProvider', 'Select a provider')}
836
- onChange={onChange}
837
- onBlur={onBlur}
838
- ref={ref}
839
- value={value}>
840
- <SelectItem text={t('chooseProvider', 'Choose a provider')} value="" />
841
- {providers?.providers?.length > 0 &&
842
- providers?.providers?.map((provider) => (
843
- <SelectItem key={provider.uuid} text={provider.display} value={provider.uuid}>
844
- {provider.display}
845
- </SelectItem>
846
- ))}
847
- </Select>
848
- )}
849
- />
850
- </ResponsiveWrapper>
851
- </FormGroup>
852
-
853
- <FormGroup
854
- className={styles.formGroup}
855
- legendText={t('dateAppointmentScheduled', 'Date appointment scheduled')}>
856
- <ResponsiveWrapper>
857
- <Controller
858
- name="dateAppointmentScheduled"
859
- control={control}
860
- render={({ field, fieldState }) => (
861
- <div style={{ width: '100%' }}>
862
- <OpenmrsDatePicker
863
- data-testid="dateAppointmentScheduledPickerInput"
864
- id="dateAppointmentScheduledPickerInput"
865
- invalid={!!fieldState?.error?.message}
866
- invalidText={fieldState?.error?.message}
867
- labelText={t('dateAppointmentIssued', 'Date appointment issued')}
868
- maxDate={new Date()}
869
- onBlur={field.onBlur}
870
- onChange={field.onChange}
871
- style={{ width: '100%' }}
872
- value={field.value}
854
+
855
+ <FormGroup
856
+ className={styles.formGroup}
857
+ legendText={t('dateAppointmentScheduled', 'Date appointment scheduled')}>
858
+ <ResponsiveWrapper>
859
+ <Controller
860
+ name="dateAppointmentScheduled"
861
+ control={control}
862
+ render={({ field, fieldState }) => (
863
+ <div style={{ width: '100%' }}>
864
+ <OpenmrsDatePicker
865
+ data-testid="dateAppointmentScheduledPickerInput"
866
+ id="dateAppointmentScheduledPickerInput"
867
+ invalid={!!fieldState?.error?.message}
868
+ invalidText={fieldState?.error?.message}
869
+ labelText={t('dateAppointmentIssued', 'Date appointment issued')}
870
+ maxDate={new Date()}
871
+ onBlur={field.onBlur}
872
+ onChange={field.onChange}
873
+ style={{ width: '100%' }}
874
+ value={field.value}
875
+ />
876
+ </div>
877
+ )}
878
+ />
879
+ </ResponsiveWrapper>
880
+ </FormGroup>
881
+
882
+ <FormGroup className={styles.formGroup} legendText={t('note', 'Note')}>
883
+ <ResponsiveWrapper>
884
+ <Controller
885
+ name="appointmentNote"
886
+ control={control}
887
+ render={({ field: { onChange, onBlur, value, ref } }) => (
888
+ <TextArea
889
+ enableCounter
890
+ id="appointmentNote"
891
+ value={value}
892
+ labelText={t('appointmentNoteLabel', 'Write an additional note')}
893
+ placeholder={t('appointmentNotePlaceholder', 'Write any additional points here')}
894
+ maxCount={255}
895
+ onChange={onChange}
896
+ onBlur={onBlur}
897
+ ref={ref}
873
898
  />
874
- </div>
875
- )}
876
- />
877
- </ResponsiveWrapper>
878
- </FormGroup>
879
-
880
- <FormGroup className={styles.formGroup} legendText={t('note', 'Note')}>
881
- <ResponsiveWrapper>
882
- <Controller
883
- name="appointmentNote"
884
- control={control}
885
- render={({ field: { onChange, onBlur, value, ref } }) => (
886
- <TextArea
887
- enableCounter
888
- id="appointmentNote"
889
- value={value}
890
- labelText={t('appointmentNoteLabel', 'Write an additional note')}
891
- placeholder={t('appointmentNotePlaceholder', 'Write any additional points here')}
892
- maxCount={255}
893
- onChange={onChange}
894
- onBlur={onBlur}
895
- ref={ref}
896
- />
897
- )}
898
- />
899
- </ResponsiveWrapper>
900
- </FormGroup>
901
- </Stack>
902
- <ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
903
- <Button className={styles.button} onClick={closeWorkspace} kind="secondary">
904
- {t('discard', 'Discard')}
905
- </Button>
906
- <Button className={styles.button} disabled={isSubmitting} type="submit">
907
- {t('saveAndClose', 'Save and close')}
908
- </Button>
909
- </ButtonSet>
910
- </Form>
899
+ )}
900
+ />
901
+ </ResponsiveWrapper>
902
+ </FormGroup>
903
+ </Stack>
904
+ <ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
905
+ <Button
906
+ className={styles.button}
907
+ onClick={() => closeWorkspace({ discardUnsavedChanges: false })}
908
+ kind="secondary">
909
+ {t('discard', 'Discard')}
910
+ </Button>
911
+ <Button className={styles.button} disabled={isSubmitting} type="submit">
912
+ {t('saveAndClose', 'Save and close')}
913
+ </Button>
914
+ </ButtonSet>
915
+ </Form>
916
+ </Workspace2>
911
917
  );
912
918
  };
913
919
 
@@ -937,7 +943,7 @@ function TimeAndDuration({ t, control, errors }: TimeAndDurationProps) {
937
943
  invalid={!!errors?.startTime}
938
944
  invalidText={errors?.startTime?.message}
939
945
  labelText={t('time', 'Time')}
940
- onChange={(event) => {
946
+ onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
941
947
  onChange(event.target.value);
942
948
  }}
943
949
  style={{ marginLeft: '0.125rem', flex: 'none' }}
@@ -948,7 +954,9 @@ function TimeAndDuration({ t, control, errors }: TimeAndDurationProps) {
948
954
  render={({ field: { value, onChange } }) => (
949
955
  <TimePickerSelect
950
956
  id="time-picker-select-1"
951
- onChange={(event) => onChange(event.target.value as 'AM' | 'PM')}
957
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
958
+ onChange(event.target.value as 'AM' | 'PM')
959
+ }
952
960
  value={value}
953
961
  aria-label={t('time', 'Time')}>
954
962
  <SelectItem value="AM" text="AM" />