@openmrs/esm-appointments-app 9.2.1-pre.6940 → 9.2.1-pre.6942

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.
@@ -6,6 +6,7 @@ import {
6
6
  Button,
7
7
  ButtonSet,
8
8
  Form,
9
+ FormGroup,
9
10
  InlineLoading,
10
11
  MultiSelect,
11
12
  NumberInput,
@@ -37,6 +38,7 @@ import {
37
38
  } from '@openmrs/esm-framework';
38
39
  import { z } from 'zod';
39
40
  import { type ConfigObject } from '../config-schema';
41
+ import type { Appointment, AppointmentPayload, RecurringPattern } from '../types';
40
42
  import {
41
43
  checkAppointmentConflict,
42
44
  saveAppointment,
@@ -44,17 +46,9 @@ import {
44
46
  useAppointmentService,
45
47
  useMutateAppointments,
46
48
  } from './appointments-form.resource';
47
- import {
48
- appointmentLocationTagName,
49
- dateFormat,
50
- datePickerFormat,
51
- datePickerPlaceHolder,
52
- moduleName,
53
- weekDays,
54
- } from '../constants';
55
- import { useProviders } from '../hooks/useProviders';
56
- import type { Appointment, AppointmentPayload, RecurringPattern } from '../types';
49
+ import { appointmentLocationTagName, dateFormat, moduleName, weekDays } from '../constants';
57
50
  import { useAppointmentsStore } from '../store';
51
+ import { useProviders } from '../hooks/useProviders';
58
52
  import Workload from '../workload/workload.component';
59
53
  import styles from './appointments-form.scss';
60
54
 
@@ -202,7 +196,7 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
202
196
  },
203
197
  )
204
198
  .superRefine((data, ctx) => {
205
- // If not all-day, duration must be > 0
199
+ // If not all-day, duration must be > 0 and <= 1440 minutes (24 hours)
206
200
  if (!data.isAllDayAppointment && (!data.duration || data.duration <= 0)) {
207
201
  ctx.addIssue({
208
202
  path: ['duration'],
@@ -210,6 +204,17 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
210
204
  message: translateFrom(moduleName, 'durationErrorMessage', 'Duration should be greater than zero'),
211
205
  });
212
206
  }
207
+ if (!data.isAllDayAppointment && data.duration && data.duration > 1440) {
208
+ ctx.addIssue({
209
+ path: ['duration'],
210
+ code: z.ZodIssueCode.custom,
211
+ message: translateFrom(
212
+ moduleName,
213
+ 'durationMaxErrorMessage',
214
+ 'Duration cannot exceed 1440 minutes (24 hours)',
215
+ ),
216
+ });
217
+ }
213
218
  });
214
219
 
215
220
  type AppointmentFormData = z.infer<typeof appointmentsFormSchema>;
@@ -476,19 +481,18 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
476
481
 
477
482
  return (
478
483
  <Form onSubmit={handleSubmit(handleSaveAppointment)}>
479
- <Stack gap={4}>
480
- {patient && (
481
- <ExtensionSlot
482
- name="patient-header-slot"
483
- state={{
484
- patient,
485
- patientUuid: patientUuid,
486
- hideActionsOverflow: true,
487
- }}
488
- />
489
- )}
490
- <section className={styles.formGroup}>
491
- <span className={styles.heading}>{t('location', 'Location')}</span>
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')}>
492
496
  <ResponsiveWrapper>
493
497
  <Controller
494
498
  name="location"
@@ -514,9 +518,8 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
514
518
  )}
515
519
  />
516
520
  </ResponsiveWrapper>
517
- </section>
518
- <section className={styles.formGroup}>
519
- <span className={styles.heading}>{t('service', 'Service')}</span>
521
+ </FormGroup>
522
+ <FormGroup className={styles.formGroup} legendText={t('service', 'Service')}>
520
523
  <ResponsiveWrapper>
521
524
  <Controller
522
525
  name="selectedService"
@@ -560,9 +563,8 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
560
563
  )}
561
564
  />
562
565
  </ResponsiveWrapper>
563
- </section>
564
- <section className={styles.formGroup}>
565
- <span className={styles.heading}>{t('appointmentType_title', 'Appointment Type')}</span>
566
+ </FormGroup>
567
+ <FormGroup className={styles.formGroup} legendText={t('appointmentType_title', 'Appointment Type')}>
566
568
  <ResponsiveWrapper>
567
569
  <Controller
568
570
  name="appointmentType"
@@ -589,21 +591,21 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
589
591
  )}
590
592
  />
591
593
  </ResponsiveWrapper>
592
- </section>
593
-
594
- <section className={styles.formGroup}>
595
- <span className={styles.heading}>{t('recurringAppointment', 'Recurring Appointment')}</span>
596
- <Toggle
597
- id="recurringToggle"
598
- labelB={t('yes', 'Yes')}
599
- labelA={t('no', 'No')}
600
- labelText={t('isRecurringAppointment', 'Is this a recurring appointment?')}
601
- onClick={() => setIsRecurringAppointment(!isRecurringAppointment)}
602
- />
603
- </section>
604
-
605
- <section className={styles.formGroup}>
606
- <span className={styles.heading}>{t('dateTime', 'Date & Time')}</span>
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')}>
607
609
  <div className={styles.dateTimeFields}>
608
610
  {isRecurringAppointment && (
609
611
  <div className={styles.inputContainer}>
@@ -673,7 +675,6 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
673
675
  max={356}
674
676
  label={t('repeatEvery', 'Repeat every')}
675
677
  invalidText={t('invalidNumber', 'Number is not valid')}
676
- size="md"
677
678
  value={value}
678
679
  onBlur={onBlur}
679
680
  onChange={(e) => {
@@ -782,10 +783,10 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
782
783
  </div>
783
784
  )}
784
785
  </div>
785
- </section>
786
+ </FormGroup>
786
787
 
787
788
  {getValues('selectedService') && (
788
- <section className={styles.formGroup}>
789
+ <FormGroup className={styles.formGroup} legendText="">
789
790
  <ResponsiveWrapper>
790
791
  <Workload
791
792
  appointmentDate={watch('appointmentDateTime').startDate}
@@ -793,12 +794,11 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
793
794
  selectedService={watch('selectedService')}
794
795
  />
795
796
  </ResponsiveWrapper>
796
- </section>
797
+ </FormGroup>
797
798
  )}
798
799
 
799
800
  {context !== 'creating' ? (
800
- <section className={styles.formGroup}>
801
- <span className={styles.heading}>{t('appointmentStatus', 'Appointment Status')}</span>
801
+ <FormGroup className={styles.formGroup} legendText={t('appointmentStatus', 'Appointment Status')}>
802
802
  <ResponsiveWrapper>
803
803
  <Controller
804
804
  name="appointmentStatus"
@@ -824,11 +824,10 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
824
824
  )}
825
825
  />
826
826
  </ResponsiveWrapper>
827
- </section>
827
+ </FormGroup>
828
828
  ) : null}
829
829
 
830
- <section className={styles.formGroup}>
831
- <span className={styles.heading}>{t('provider', 'Provider')}</span>
830
+ <FormGroup className={styles.formGroup} legendText={t('provider', 'Provider')}>
832
831
  <ResponsiveWrapper>
833
832
  <Controller
834
833
  name="provider"
@@ -853,9 +852,11 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
853
852
  )}
854
853
  />
855
854
  </ResponsiveWrapper>
856
- </section>
857
- <section className={styles.formGroup}>
858
- <span className={styles.heading}>{t('dateScheduled', 'Date appointment issued')}</span>
855
+ </FormGroup>
856
+
857
+ <FormGroup
858
+ className={styles.formGroup}
859
+ legendText={t('dateAppointmentScheduled', 'Date appointment scheduled')}>
859
860
  <ResponsiveWrapper>
860
861
  <Controller
861
862
  name="dateAppointmentScheduled"
@@ -869,19 +870,18 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
869
870
  invalidText={fieldState?.error?.message}
870
871
  labelText={t('dateAppointmentIssued', 'Date appointment issued')}
871
872
  maxDate={new Date()}
872
- style={{ width: '100%' }}
873
873
  onBlur={field.onBlur}
874
874
  onChange={field.onChange}
875
+ style={{ width: '100%' }}
875
876
  value={field.value}
876
877
  />
877
878
  </div>
878
879
  )}
879
880
  />
880
881
  </ResponsiveWrapper>
881
- </section>
882
+ </FormGroup>
882
883
 
883
- <section className={styles.formGroup}>
884
- <span className={styles.heading}>{t('note', 'Note')}</span>
884
+ <FormGroup className={styles.formGroup} legendText={t('note', 'Note')}>
885
885
  <ResponsiveWrapper>
886
886
  <Controller
887
887
  name="appointmentNote"
@@ -901,7 +901,7 @@ const AppointmentsForm: React.FC<AppointmentsFormProps & DefaultWorkspaceProps>
901
901
  )}
902
902
  />
903
903
  </ResponsiveWrapper>
904
- </section>
904
+ </FormGroup>
905
905
  </Stack>
906
906
  <ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
907
907
  <Button className={styles.button} onClick={closeWorkspace} kind="secondary">
@@ -960,19 +960,19 @@ function TimeAndDuration({ t, watch, control, services, errors }) {
960
960
  control={control}
961
961
  render={({ field: { onChange, onBlur, value, ref } }) => (
962
962
  <NumberInput
963
+ allowEmpty
963
964
  disableWheel
964
965
  hideSteppers
965
966
  id="duration"
966
967
  invalid={!!errors?.duration}
967
968
  invalidText={errors?.duration?.message}
968
969
  label={t('durationInMinutes', 'Duration (minutes)')}
969
- max={1440}
970
- min={0}
971
970
  onBlur={onBlur}
972
- onChange={(event) => onChange(event.target.value === '' ? null : Number(event.target.value))}
971
+ onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
972
+ onChange(event.target.value === '' ? null : Number(event.target.value))
973
+ }
973
974
  ref={ref}
974
- size="md"
975
- value={value}
975
+ value={value ?? ''}
976
976
  />
977
977
  )}
978
978
  />
@@ -61,9 +61,9 @@
61
61
  "date&Time": "Date & time",
62
62
  "dateAppointmentIssued": "Date appointment issued",
63
63
  "dateAppointmentIssuedCannotBeAfterAppointmentDate": "Date appointment issued cannot be after the appointment date",
64
+ "dateAppointmentScheduled": "Date appointment scheduled",
64
65
  "dateOfBirth": "Date of birth",
65
66
  "dateRange": "Set date range",
66
- "dateScheduled": "Date appointment issued",
67
67
  "dateTime": "Date & Time",
68
68
  "day": "Day",
69
69
  "daysOfWeek": "Days of the week",