diff --git a/packages/telemed-intake/app/src/features/appointments/appointment.store.ts b/packages/telemed-intake/app/src/features/appointments/appointment.store.ts index a199d1cb..eea75ac7 100644 --- a/packages/telemed-intake/app/src/features/appointments/appointment.store.ts +++ b/packages/telemed-intake/app/src/features/appointments/appointment.store.ts @@ -11,6 +11,7 @@ interface AppointmentState { providerID?: string; groupID?: string; scheduleType?: 'location' | 'provider'; + slug?: string; } const APOINTMENT_STATE_INITIAL: AppointmentState = {}; diff --git a/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx b/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx index 7dd80b40..c837b907 100644 --- a/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx +++ b/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx @@ -15,7 +15,7 @@ import { import { CustomContainer } from '../features/common'; import { useGetPaperwork, usePaperworkStore } from '../features/paperwork'; import { usePatientInfoStore } from '../features/patient-info'; -import { useZapEHRAPIClient } from '../utils'; +import { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; import { decode } from 'html-entities'; const ConfirmDateOfBirth = (): JSX.Element => { @@ -26,7 +26,11 @@ const ConfirmDateOfBirth = (): JSX.Element => { const [requestErrorDialogOpen, setRequestErrorDialogOpen] = useState(false); const createAppointment = useCreateAppointmentMutation(); const [getPaperworkEnabled, setGetPaperworkEnabled] = useState(false); - const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); + const { appointmentID, selectedSlot, visitType, visitService, scheduleType, slug } = getSelectors( + useAppointmentStore, + ['appointmentID', 'selectedSlot', 'visitType', 'visitService', 'scheduleType', 'slug'], + ); + const [isPastTimeErrorDialogOpen, setIsPastTimeErrorDialogOpen] = useState(false); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -116,6 +120,11 @@ const ConfirmDateOfBirth = (): JSX.Element => { }, [challengeDay.name, challengeMonth.name, challengeYear.name, formValuesCopy]); const createOrUpdateAppointment = (unconfirmedDateOfBirth = false): void => { + if (isSlotTimePassed(selectedSlot, visitType)) { + setIsPastTimeErrorDialogOpen(true); + return; + } + if (!apiClient) { throw new Error('apiClient is not defined'); } @@ -261,6 +270,22 @@ const ConfirmDateOfBirth = (): JSX.Element => { closeButtonText={t('general.button.close')} handleClose={() => setRequestErrorDialogOpen(false)} /> + + handleClosePastTimeErrorDialog( + setIsPastTimeErrorDialogOpen, + navigate, + visitType, + visitService, + scheduleType, + slug, + ) + } + /> ); diff --git a/packages/telemed-intake/app/src/pages/PatientInformation.tsx b/packages/telemed-intake/app/src/pages/PatientInformation.tsx index f364e827..a7ae9438 100644 --- a/packages/telemed-intake/app/src/pages/PatientInformation.tsx +++ b/packages/telemed-intake/app/src/pages/PatientInformation.tsx @@ -26,7 +26,7 @@ import { CustomContainer } from '../features/common'; import { useFilesStore } from '../features/files'; import { useGetPaperwork, usePaperworkStore } from '../features/paperwork'; import { usePatientInfoStore } from '../features/patient-info'; -import { useZapEHRAPIClient } from '../utils'; +import { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; const UPDATEABLE_PATIENT_INFO_FIELDS: (keyof Omit)[] = [ 'firstName', @@ -69,13 +69,17 @@ const PatientInformation = (): JSX.Element => { const { t } = useTranslation(); const [ageErrorDialogOpen, setAgeErrorDialogOpen] = useState(false); const [requestErrorDialogOpen, setRequestErrorDialogOpen] = useState(false); + const [isPastTimeErrorDialogOpen, setIsPastTimeErrorDialogOpen] = useState(false); const { patientInfo: currentPatientInfo, pendingPatientInfoUpdates } = getSelectors(usePatientInfoStore, [ 'patientInfo', 'pendingPatientInfoUpdates', ]); const patientInfo = { ...currentPatientInfo, ...pendingPatientInfoUpdates, id: currentPatientInfo.id }; const initialPatientInfoRef = useRef(currentPatientInfo); - const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); + const { appointmentID, visitType, visitService, scheduleType, slug, selectedSlot } = getSelectors( + useAppointmentStore, + ['appointmentID', 'visitType', 'visitService', 'scheduleType', 'slug', 'selectedSlot'], + ); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -165,6 +169,11 @@ const PatientInformation = (): JSX.Element => { return; } + if (isSlotTimePassed(selectedSlot, visitType)) { + setIsPastTimeErrorDialogOpen(true); + return; + } + if (!apiClient) { throw new Error('apiClient is not defined'); } @@ -397,6 +406,22 @@ const PatientInformation = (): JSX.Element => { closeButtonText={t('general.button.close')} handleClose={() => setRequestErrorDialogOpen(false)} /> + + handleClosePastTimeErrorDialog( + setIsPastTimeErrorDialogOpen, + navigate, + visitType, + visitService, + scheduleType, + slug, + ) + } + /> ); }; diff --git a/packages/telemed-intake/app/src/pages/Welcome.tsx b/packages/telemed-intake/app/src/pages/Welcome.tsx index 3433226a..39deeb76 100644 --- a/packages/telemed-intake/app/src/pages/Welcome.tsx +++ b/packages/telemed-intake/app/src/pages/Welcome.tsx @@ -53,8 +53,13 @@ const Welcome = (): JSX.Element => { }, [visitService, setAppointment, visitType, scheduleType]); useEffect(() => { - setAppointment({ locationID: schedule?.locationID, providerID: schedule?.providerID, groupID: schedule?.groupID }); - }, [schedule?.groupID, schedule?.locationID, schedule?.providerID, setAppointment]); + setAppointment({ + locationID: schedule?.locationID, + providerID: schedule?.providerID, + groupID: schedule?.groupID, + slug, + }); + }, [schedule?.groupID, schedule?.locationID, schedule?.providerID, setAppointment, slug]); const clearState = (): void => { useAppointmentStore.setState({ appointmentID: undefined, appointmentDate: undefined }); diff --git a/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json b/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json index c19a3f0f..599bdf2c 100644 --- a/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json +++ b/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json @@ -173,6 +173,11 @@ "requestError": { "title": "Error", "description": "An error occurred. Please, try again in a moment." + }, + "pastTimeError": { + "title": "Error booking visit", + "description": "We are sorry, your selected check-in time has passed. Please click below to select a different time slot.", + "button": "Select a time slot" } }, "patientPortal": { diff --git a/packages/telemed-intake/app/src/utils/checkSlotTime.ts b/packages/telemed-intake/app/src/utils/checkSlotTime.ts new file mode 100644 index 00000000..c1be29d0 --- /dev/null +++ b/packages/telemed-intake/app/src/utils/checkSlotTime.ts @@ -0,0 +1,40 @@ +import { DateTime } from 'luxon'; +import { NavigateFunction } from 'react-router-dom'; +import { IntakeFlowPageRoute } from 'src/App'; + +export const isSlotTimePassed = (selectedSlot: string | undefined, visitType: string | undefined): boolean => { + if (visitType === 'now') { + return false; + } + if (!selectedSlot) return false; + const slotDateTime = DateTime.fromISO(selectedSlot); + console.log('slotDateTime', slotDateTime); + const now = DateTime.now(); + console.log('now', now); + return slotDateTime < now; +}; + +export const handleClosePastTimeErrorDialog = ( + setIsPastTimeErrorDialogOpen: (value: boolean) => void, + navigate: NavigateFunction, + visitType: string | undefined, + visitService: string | undefined, + scheduleType: string | undefined, + slug: string | undefined, +): void => { + if (!visitService || !visitType || !scheduleType || !slug) { + console.error('One or more required parameters are missing'); + navigate(IntakeFlowPageRoute.PatientPortal.path); + return; + } + + setIsPastTimeErrorDialogOpen(false); + + navigate( + `${IntakeFlowPageRoute.Welcome.path + .replace(':schedule-type', scheduleType) + .replace(':slug', slug) + .replace(':visit-service', visitService) + .replace(':visit-type', visitType)}`, + ); +}; diff --git a/packages/telemed-intake/app/src/utils/index.ts b/packages/telemed-intake/app/src/utils/index.ts index 011cb1f4..3bc9a3bd 100644 --- a/packages/telemed-intake/app/src/utils/index.ts +++ b/packages/telemed-intake/app/src/utils/index.ts @@ -2,3 +2,4 @@ export * from './getZapEHRAPI'; export * from './getPaperworkPageInfo'; export * from './mapQuestionsToFormInputFields'; export * from './zustandDevtools'; +export * from './checkSlotTime'; diff --git a/packages/telemed-intake/zambdas/scripts/common.ts b/packages/telemed-intake/zambdas/scripts/common.ts index 7ebf906e..ceeb258d 100644 --- a/packages/telemed-intake/zambdas/scripts/common.ts +++ b/packages/telemed-intake/zambdas/scripts/common.ts @@ -1,5 +1,5 @@ import devConfig from '../.env/development.json'; -import testingConfig from '../.env/testing.json'; +// import testingConfig from '../.env/testing.json'; import { FhirClient, ZambdaClient } from '@zapehr/sdk'; import { getM2MClientToken } from '../src/shared'; @@ -10,9 +10,9 @@ export const performEffectWithEnvFile = async (callback: (config: any) => void) case 'development': await callback(devConfig); break; - case 'testing': - await callback(testingConfig); - break; + // case 'testing': + // await callback(testingConfig); + // break; default: throw new Error('¯\\_(ツ)_/¯ environment must match a valid zapEHR environment.'); }