From f9ab76a57da8f56b51f3fd0fb96b72d015db38cf Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Thu, 14 Nov 2024 19:30:04 +0100 Subject: [PATCH 01/22] Revert "Demo button upgrades" --- .../src/components/inputs/ProvidersSelect.tsx | 1 - .../src/helpers/create-sample-appointments.ts | 47 ++++--------------- .../app/src/hooks/useOttehrUser.tsx | 2 + .../app/src/pages/Appointments.tsx | 2 +- .../tracking-board/CreateDemoVisits.tsx | 11 +---- .../features/tracking-board/StateSelect.tsx | 3 -- .../tracking-board/TrackingBoardFilters.tsx | 2 + .../tracking-board/TrackingBoardTable.tsx | 8 ++-- .../tracking-board/TrackingBoardTabs.tsx | 2 +- packages/telemed-ehr/app/src/types/types.ts | 2 - .../zambdas/src/get-appointments/index.ts | 4 ++ .../zambdas/src/demo/example.spec.ts | 1 + .../zambdas/src/get-schedule/index.ts | 9 ++++ .../app/src/api/zapehrApi.ts | 2 + .../zambdas/src/shared/dateUtils.ts | 2 + 15 files changed, 39 insertions(+), 59 deletions(-) diff --git a/packages/telemed-ehr/app/src/components/inputs/ProvidersSelect.tsx b/packages/telemed-ehr/app/src/components/inputs/ProvidersSelect.tsx index 7c3402b6..f342c31d 100644 --- a/packages/telemed-ehr/app/src/components/inputs/ProvidersSelect.tsx +++ b/packages/telemed-ehr/app/src/components/inputs/ProvidersSelect.tsx @@ -18,7 +18,6 @@ export default function ProvidersSelect({ providers, practitioners, handleSubmit practitionerIDToName[practitioner.id] = formatHumanName(practitioner.name[0]); } }); - return ( => { try { if (!fhirClient) { @@ -63,7 +58,7 @@ export const createSampleAppointments = async ( for (let i = 0; i < 10; i++) { const visitService = appointmentTypes[i % 2]; - const randomPatientInfo = await generateRandomPatientInfo(fhirClient, visitService, user, phoneNumber); + const randomPatientInfo = await generateRandomPatientInfo(fhirClient, visitService, phoneNumber); const inputBody = JSON.stringify(randomPatientInfo); const response = await fetch(`${intakeZambdaUrl}/zambda/${createAppointmentZambdaId}/execute`, { @@ -88,7 +83,6 @@ export const createSampleAppointments = async ( const generateRandomPatientInfo = async ( fhirClient: FhirClient, visitService: 'in-person' | 'telemedicine', - user: User | undefined, phoneNumber?: string, ): Promise => { const firstNames = ['Alice', 'Bob', 'Charlie', 'Diana', 'Ethan', 'Fatima', 'Gabriel', 'Hannah', 'Ibrahim', 'Jake']; @@ -96,14 +90,11 @@ const generateRandomPatientInfo = async ( const sexes: PersonSex[] = [PersonSex.Male, PersonSex.Female, PersonSex.Intersex]; const searchParams: SearchParam[] = [{ name: 'status', value: 'active' }]; - - const allOffices: any[] = await fhirClient?.searchResources({ + const availableLocations: any[] = await fhirClient?.searchResources({ resourceType: 'Location', - searchParams: [{ name: '_count', value: '1000' }], + searchParams: searchParams, }); - const activeOffices = allOffices.filter((item) => item.status === 'active'); - const practitionersTemp: Practitioner[] = await fhirClient.searchResources({ resourceType: 'Practitioner', searchParams: [ @@ -119,26 +110,11 @@ const generateRandomPatientInfo = async ( .minus({ years: 7 + Math.floor(Math.random() * 16) }) .toISODate(); const randomSex = sexes[Math.floor(Math.random() * sexes.length)]; - const randomLocationIndex = Math.floor(Math.random() * activeOffices.length); - const randomLocationId = activeOffices[randomLocationIndex].id; + const randomLocationIndex = Math.floor(Math.random() * availableLocations.length); + const randomLocationId = availableLocations[randomLocationIndex].id; const randomProviderId = practitionersTemp[Math.floor(Math.random() * practitionersTemp.length)].id; - const selectedInPersonLocationID = localStorage.getItem('selectedLocationID'); - const selectedState = localStorage.getItem('selectedState'); - - const availableStates = - user?.profileResource && - allLicensesForPractitioner(user.profileResource).map((item) => { - return item.state; - }); - - const randomState = availableStates?.[Math.floor(Math.random() * availableStates.length)] || ''; - - const locationId = getLocationIdFromState(selectedState || randomState, allOffices); - - const isLocationActive = activeOffices.some((office) => office.id === locationId); - - const telemedLocationId = isLocationActive ? locationId : randomLocationId; + const selectedLocationID = localStorage.getItem('selectedLocationID'); if (visitService === 'telemedicine') { return { @@ -151,14 +127,14 @@ const generateRandomPatientInfo = async ( email: randomEmail, emailUser: 'Patient', }, - scheduleType: 'location', + scheduleType: 'provider', visitType: 'now', visitService: visitService, providerID: randomProviderId, timezone: 'UTC', isDemo: true, phoneNumber: phoneNumber, - locationID: telemedLocationId, + locationID: randomLocationId, }; } @@ -179,14 +155,9 @@ const generateRandomPatientInfo = async ( scheduleType: 'location', visitType: 'now', visitService: visitService, - locationID: selectedInPersonLocationID || randomLocationId, + locationID: selectedLocationID || randomLocationId, timezone: 'UTC', isDemo: true, phoneNumber: phoneNumber, }; }; - -const getLocationIdFromState = (state: string, allLocations: any[]): string | undefined => { - const location = allLocations.find((item) => item.address?.state === state); - return location?.id; -}; diff --git a/packages/telemed-ehr/app/src/hooks/useOttehrUser.tsx b/packages/telemed-ehr/app/src/hooks/useOttehrUser.tsx index ba6706c7..1cf4419b 100644 --- a/packages/telemed-ehr/app/src/hooks/useOttehrUser.tsx +++ b/packages/telemed-ehr/app/src/hooks/useOttehrUser.tsx @@ -125,6 +125,7 @@ export default function useOttehrUser(): OttehrUser | undefined { if (resourceType && resourceId && resourceType === 'Practitioner') { const practitioner = await client.readResource({ resourceType, resourceId }); useOttehrUserStore.setState({ profile: practitioner }); + console.log('practitioner', practitioner); } _profileLoadingState = LoadingState.idle; } catch (e) { @@ -346,6 +347,7 @@ const useGetProfile = () => { if (resourceType && resourceId && resourceType === 'Practitioner') { const practitioner = await fhirClient?.readResource({ resourceType, resourceId }); useOttehrUserStore.setState({ profile: practitioner }); + console.log('practitioner', practitioner); } } catch (e) { console.error(`error fetching user's fhir profile: ${JSON.stringify(e)}`); diff --git a/packages/telemed-ehr/app/src/pages/Appointments.tsx b/packages/telemed-ehr/app/src/pages/Appointments.tsx index ae77c996..32c614de 100644 --- a/packages/telemed-ehr/app/src/pages/Appointments.tsx +++ b/packages/telemed-ehr/app/src/pages/Appointments.tsx @@ -437,7 +437,7 @@ function AppointmentsBody(props: AppointmentsBodyProps): ReactElement { updateAppointments={updateAppointments} setEditingComment={setEditingComment} /> - + diff --git a/packages/telemed-ehr/app/src/telemed/features/tracking-board/CreateDemoVisits.tsx b/packages/telemed-ehr/app/src/telemed/features/tracking-board/CreateDemoVisits.tsx index cea7e252..8a1b12b9 100644 --- a/packages/telemed-ehr/app/src/telemed/features/tracking-board/CreateDemoVisits.tsx +++ b/packages/telemed-ehr/app/src/telemed/features/tracking-board/CreateDemoVisits.tsx @@ -8,13 +8,8 @@ import { useAuth0 } from '@auth0/auth0-react'; import { otherColors } from '../../../CustomThemeProvider'; import { LoadingButton } from '@mui/lab'; import { Box } from '@mui/system'; -import useOttehrUser from '../../../hooks/useOttehrUser'; -interface CreateDemoVisitsProps { - schedulePage?: 'telemedicine' | 'in-person'; -} - -const CreateDemoVisits = ({ schedulePage }: CreateDemoVisitsProps): ReactElement => { +const CreateDemoVisits = (): ReactElement => { const [phoneNumber, setPhoneNumber] = useState(''); const [inputError, setInputError] = useState(false); const [loading, setLoading] = useState(false); @@ -29,7 +24,6 @@ const CreateDemoVisits = ({ schedulePage }: CreateDemoVisitsProps): ReactElement }); const { fhirClient } = useApiClients(); const { getAccessTokenSilently } = useAuth0(); - const user = useOttehrUser(); const handleCreateSampleAppointments = async ( event: React.MouseEvent | React.FormEvent, @@ -46,13 +40,12 @@ const CreateDemoVisits = ({ schedulePage }: CreateDemoVisitsProps): ReactElement setLoading(true); setInputError(false); const authToken = await getAccessTokenSilently(); - const response = await createSampleAppointments(fhirClient, authToken, formattedPhoneNumber, user); + const response = await createSampleAppointments(fhirClient, authToken, formattedPhoneNumber); setSnackbar({ open: true, message: 'Appointments created successfully!', severity: 'success', }); - return response; } catch (error) { setSnackbar({ open: true, diff --git a/packages/telemed-ehr/app/src/telemed/features/tracking-board/StateSelect.tsx b/packages/telemed-ehr/app/src/telemed/features/tracking-board/StateSelect.tsx index 403bf8d1..9404558d 100644 --- a/packages/telemed-ehr/app/src/telemed/features/tracking-board/StateSelect.tsx +++ b/packages/telemed-ehr/app/src/telemed/features/tracking-board/StateSelect.tsx @@ -9,10 +9,7 @@ export function StateSelect(): ReactElement { const { availableStates, state } = getSelectors(useTrackingBoardStore, ['availableStates', 'state']); const options = [EMPTY_STATE, ...availableStates.map((state) => ({ label: state, value: state }))]; - const randomState = availableStates[Math.floor(Math.random() * availableStates.length)]; - const handleStateChange = (_e: any, { value }: { label: string | null; value: string | null }): void => { - localStorage.setItem('selectedState', value || ''); useTrackingBoardStore.setState((prevState) => ({ ...prevState, state: value })); }; diff --git a/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardFilters.tsx b/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardFilters.tsx index 3bbdc83a..a41a29c8 100644 --- a/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardFilters.tsx +++ b/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardFilters.tsx @@ -85,10 +85,12 @@ export const TrackingBoardFilters: FC<{ tab: ApptTab }> = (props) => { const useDate = tab === ApptTab.complete; const useUnsigned = tab === ApptTab['not-signed']; const handleProviderChange = (_e: any, value: string[]): void => { + console.log(10, value); setProviders(value); useTrackingBoardStore.setState({ providers: value }); }; const handleGroupChange = (_e: any, value: string[]): void => { + console.log(10, value); setGroups(value); useTrackingBoardStore.setState({ groups: value }); }; diff --git a/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTable.tsx b/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTable.tsx index e7e3e60c..cb7e9272 100644 --- a/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTable.tsx +++ b/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTable.tsx @@ -42,11 +42,11 @@ export function TrackingBoardTable({ tab }: AppointmentTableProps): ReactElement return {}; } return filteredAppointments.reduce>((accumulator, appointment) => { - if (appointment.location.state) { - if (!accumulator[appointment.location.state]) { - accumulator[appointment.location.state] = []; + if (appointment.location.locationID) { + if (!accumulator[appointment.location.locationID]) { + accumulator[appointment.location.locationID] = []; } - accumulator[appointment.location.state].push(appointment); + accumulator[appointment.location.locationID].push(appointment); return accumulator; } else if (appointment.provider) { if (!accumulator[appointment.provider.join(',')]) { diff --git a/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTabs.tsx b/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTabs.tsx index 4219c20b..fabb750b 100644 --- a/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTabs.tsx +++ b/packages/telemed-ehr/app/src/telemed/features/tracking-board/TrackingBoardTabs.tsx @@ -63,7 +63,7 @@ export function TrackingBoardTabs(): ReactElement { - + ); diff --git a/packages/telemed-ehr/app/src/types/types.ts b/packages/telemed-ehr/app/src/types/types.ts index 16af1f72..bbc1d83f 100644 --- a/packages/telemed-ehr/app/src/types/types.ts +++ b/packages/telemed-ehr/app/src/types/types.ts @@ -112,8 +112,6 @@ export const AllStates = [ export type StateType = (typeof AllStates extends readonly (infer TElementType)[] ? TElementType : never)['value']; -export const AllStatesValues: StateType[] = AllStates.map(({ value }) => value); - export const AllStatesToNames: { [value in StateType]: string; } = { diff --git a/packages/telemed-ehr/zambdas/src/get-appointments/index.ts b/packages/telemed-ehr/zambdas/src/get-appointments/index.ts index c121f855..a09bfca7 100644 --- a/packages/telemed-ehr/zambdas/src/get-appointments/index.ts +++ b/packages/telemed-ehr/zambdas/src/get-appointments/index.ts @@ -208,6 +208,9 @@ export const index = async (input: ZambdaInput): Promise }); const [activeEncounters, searchResultsForSelectedDate] = await Promise.all([encounterSearch, appointmentSearch]); console.timeEnd('get_active_encounters + get_appointment_data'); + // console.log(searchResultsForSelectedDate); + // console.log(appointmentSearchParams); + // console.log(1, searchResultsForSelectedDate); const encounterIds: string[] = []; const tempAppointmentDates = activeEncounters @@ -416,6 +419,7 @@ export const index = async (input: ZambdaInput): Promise console.time('structure_appointment_data'); let appointments: Appointment[] = []; if (visitType?.length > 0) { + console.log(1, allAppointments.length); appointments = allAppointments?.filter((appointment) => { return visitType?.includes(appointment.appointmentType?.text || ''); }); diff --git a/packages/telemed-intake/zambdas/src/demo/example.spec.ts b/packages/telemed-intake/zambdas/src/demo/example.spec.ts index 18e174c0..99537e68 100644 --- a/packages/telemed-intake/zambdas/src/demo/example.spec.ts +++ b/packages/telemed-intake/zambdas/src/demo/example.spec.ts @@ -150,6 +150,7 @@ export async function getUser( await page.waitForTimeout(3000); expect(await page.getByText('Code is invalid').isVisible()).toBeFalsy(); await page.waitForTimeout(500); + console.log(1, token); return token; } diff --git a/packages/telemed-intake/zambdas/src/get-schedule/index.ts b/packages/telemed-intake/zambdas/src/get-schedule/index.ts index 930b6482..48f72720 100644 --- a/packages/telemed-intake/zambdas/src/get-schedule/index.ts +++ b/packages/telemed-intake/zambdas/src/get-schedule/index.ts @@ -261,6 +261,7 @@ async function getSchedule( currentDayTemp = currentDayTemp.plus({ days: 1 }); } }); + console.log(1, slots); return { message: 'Successful reply', // reminder to fix item adress @@ -396,6 +397,13 @@ export const distributeTimeSlots = ( currentAppointments: Appointment[], slotLength: number, ): string[] => { + // console.log(1, startTime, capacity, openingTime, closingTime); + // const minutesToDistributeInHour = Math.min( + // 60, + // startTime.diff(openingTime, 'minutes').minutes, + // startTime.diff(closingTime, 'minutes').minutes + // ); + // adjust startTime if minutes are not 00 to get an accurate minutesToDistributeInHour const adjustedStart: DateTime = startTime.minute !== 0 ? startTime.minus({ minutes: startTime.minute }) : startTime; @@ -422,6 +430,7 @@ export const distributeTimeSlots = ( const tempRoundedTime = tempTime.set({ minute: tempUpdatedRoundedMinute, second: 0, millisecond: 0 }); tempTime = tempTime.plus({ minutes: minutesPerSlot }); const timesSlotIndex = tempRoundedTime.toISO() || ''; + // console.log(1, tempRoundedTime.toISO()); // Appointments are bookable an hour away from the current time if (tempRoundedTime < DateTime.now().setZone('UTC').plus({ hours: 1 })) { diff --git a/packages/urgent-care-intake/app/src/api/zapehrApi.ts b/packages/urgent-care-intake/app/src/api/zapehrApi.ts index ab2832a8..d80a2c14 100644 --- a/packages/urgent-care-intake/app/src/api/zapehrApi.ts +++ b/packages/urgent-care-intake/app/src/api/zapehrApi.ts @@ -311,6 +311,7 @@ class API { if (GET_PRESIGNED_FILE_URL == null || REACT_APP_IS_LOCAL == null) { throw new Error('get presigned file url environment variable could not be loaded'); } + console.log(1); const response = await zambdaClient?.invokePublicZambda({ zambdaId: GET_PRESIGNED_FILE_URL, payload: { @@ -319,6 +320,7 @@ class API { fileFormat, }, }); + console.log(1); const jsonToUse = chooseJson(response, REACT_APP_IS_LOCAL); return jsonToUse; diff --git a/packages/urgent-care-intake/zambdas/src/shared/dateUtils.ts b/packages/urgent-care-intake/zambdas/src/shared/dateUtils.ts index 3ede8fb7..2a674f77 100644 --- a/packages/urgent-care-intake/zambdas/src/shared/dateUtils.ts +++ b/packages/urgent-care-intake/zambdas/src/shared/dateUtils.ts @@ -79,6 +79,7 @@ export const distributeTimeSlots = ( closingTime: DateTime, currentAppointments: Appointment[], ): string[] => { + // console.log(1, startTime, capacity, openingTime, closingTime); const ROUND_MINUTES = 15; // const minutesToDistributeInHour = Math.min( @@ -113,6 +114,7 @@ export const distributeTimeSlots = ( const tempRoundedTime = tempTime.set({ minute: tempUpdatedRoundedMinute, second: 0, millisecond: 0 }); tempTime = tempTime.plus({ minutes: minutesPerSlot }); const timesSlotIndex = tempRoundedTime.toISO() || ''; + // console.log(1, tempRoundedTime.toISO()); // Appointments are bookable an hour away from the current time if (tempRoundedTime < DateTime.now().setZone('UTC').plus({ hours: 1 })) { From 8044495411592b266fea909218ce5fdf7c46f366 Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Thu, 14 Nov 2024 19:30:33 +0100 Subject: [PATCH 02/22] Revert "Videochat invite list error" --- packages/telemed-intake/zambdas/scripts/setup.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/telemed-intake/zambdas/scripts/setup.ts b/packages/telemed-intake/zambdas/scripts/setup.ts index cf5101d1..49a01fde 100644 --- a/packages/telemed-intake/zambdas/scripts/setup.ts +++ b/packages/telemed-intake/zambdas/scripts/setup.ts @@ -26,8 +26,6 @@ async function createApplication( 'Zambda:Function:telemed-create-paperwork', 'Zambda:Function:telemed-cancel-telemed-appointment', 'Zambda:Function:telemed-cancel-in-person-appointment', - 'Zambda:Function:telemed-video-chat-invites-list', - 'Zambda:Function:telemed-video-chat-invites-create', ], action: ['Zambda:InvokeFunction'], effect: 'Allow', From 0ebd6db68638c23bbd1a504d4d283b88f9d93a3f Mon Sep 17 00:00:00 2001 From: aykhanahmadli Date: Wed, 20 Nov 2024 15:05:55 +0100 Subject: [PATCH 03/22] configure redirect issue for videocall --- packages/telemed-intake/app/src/components/VideoControls.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/telemed-intake/app/src/components/VideoControls.tsx b/packages/telemed-intake/app/src/components/VideoControls.tsx index 10d94a7a..95a07443 100644 --- a/packages/telemed-intake/app/src/components/VideoControls.tsx +++ b/packages/telemed-intake/app/src/components/VideoControls.tsx @@ -11,10 +11,13 @@ import { CallSettingsTooltip } from './CallSettingsTooltip'; import { useVideoCallStore } from '../features/video-call'; import { otherColors } from '../IntakeThemeProvider'; import { CallSettings } from './CallSettingsDialog'; +import { useNavigate } from 'react-router-dom'; +import { IntakeFlowPageRoute } from 'src/App'; export const VideoControls: FC = () => { const { toggleVideo, isVideoEnabled } = useLocalVideo(); const { muted, toggleMute } = useToggleLocalMute(); + const navigate = useNavigate(); const meetingManager = useMeetingManager(); const [isSettingsOpen, setIsSettingsOpen] = useState(false); @@ -46,6 +49,7 @@ export const VideoControls: FC = () => { const disconnect = async (): Promise => { await cleanup(); useVideoCallStore.setState({ meetingData: null }); + navigate(IntakeFlowPageRoute.PatientPortal.path); }; return ( From a4d9f38b4d198ffaff5f6c3dde385eb158264139 Mon Sep 17 00:00:00 2001 From: aykhanahmadli Date: Wed, 20 Nov 2024 16:26:43 +0100 Subject: [PATCH 04/22] feat: add past slot error dialog and redirect logic --- .../appointments/appointment.store.ts | 1 + .../app/src/pages/ConfirmDateOfBirth.tsx | 14 +++++++ .../app/src/pages/PatientInformation.tsx | 9 ++++ .../telemed-intake/app/src/pages/Welcome.tsx | 9 +++- .../app/src/theme/ottehr/i18n-en.json | 5 +++ .../app/src/utils/checkSlotTime.ts | 41 +++++++++++++++++++ 6 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 packages/telemed-intake/app/src/utils/checkSlotTime.ts 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..1c54aa33 100644 --- a/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx +++ b/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx @@ -17,6 +17,7 @@ import { useGetPaperwork, usePaperworkStore } from '../features/paperwork'; import { usePatientInfoStore } from '../features/patient-info'; import { useZapEHRAPIClient } from '../utils'; import { decode } from 'html-entities'; +import { handleClosePastTimeErrorDialog, isSlotTimePassed } from 'src/utils/checkSlotTime'; const ConfirmDateOfBirth = (): JSX.Element => { const navigate = useNavigate(); @@ -27,6 +28,7 @@ const ConfirmDateOfBirth = (): JSX.Element => { const createAppointment = useCreateAppointmentMutation(); const [getPaperworkEnabled, setGetPaperworkEnabled] = useState(false); const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); + const [isPastTimeErrorDialogOpen, setIsPastTimeErrorDialogOpen] = useState(false); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -116,6 +118,11 @@ const ConfirmDateOfBirth = (): JSX.Element => { }, [challengeDay.name, challengeMonth.name, challengeYear.name, formValuesCopy]); const createOrUpdateAppointment = (unconfirmedDateOfBirth = false): void => { + if (isSlotTimePassed()) { + setIsPastTimeErrorDialogOpen(true); + return; + } + if (!apiClient) { throw new Error('apiClient is not defined'); } @@ -261,6 +268,13 @@ const ConfirmDateOfBirth = (): JSX.Element => { closeButtonText={t('general.button.close')} handleClose={() => setRequestErrorDialogOpen(false)} /> + handleClosePastTimeErrorDialog(setIsPastTimeErrorDialogOpen, navigate)} + /> ); diff --git a/packages/telemed-intake/app/src/pages/PatientInformation.tsx b/packages/telemed-intake/app/src/pages/PatientInformation.tsx index f364e827..b361cf4b 100644 --- a/packages/telemed-intake/app/src/pages/PatientInformation.tsx +++ b/packages/telemed-intake/app/src/pages/PatientInformation.tsx @@ -27,6 +27,7 @@ import { useFilesStore } from '../features/files'; import { useGetPaperwork, usePaperworkStore } from '../features/paperwork'; import { usePatientInfoStore } from '../features/patient-info'; import { useZapEHRAPIClient } from '../utils'; +import { handleClosePastTimeErrorDialog } from 'src/utils/checkSlotTime'; const UPDATEABLE_PATIENT_INFO_FIELDS: (keyof Omit)[] = [ 'firstName', @@ -69,6 +70,7 @@ 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', @@ -397,6 +399,13 @@ const PatientInformation = (): JSX.Element => { closeButtonText={t('general.button.close')} handleClose={() => setRequestErrorDialogOpen(false)} /> + handleClosePastTimeErrorDialog(setIsPastTimeErrorDialogOpen, navigate)} + /> ); }; 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..a64f0401 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 is in the past. 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..8f7e9dce --- /dev/null +++ b/packages/telemed-intake/app/src/utils/checkSlotTime.ts @@ -0,0 +1,41 @@ +import { DateTime } from 'luxon'; +import { getSelectors } from 'ottehr-utils'; +import { NavigateFunction } from 'react-router-dom'; +import { IntakeFlowPageRoute } from 'src/App'; +import { useAppointmentStore } from 'src/features/appointments'; + +export const isSlotTimePassed = (): boolean => { + const selectedSlot = getSelectors(useAppointmentStore, ['selectedSlot']) as string; + if (!selectedSlot) return false; + const slotDateTime = DateTime.fromISO(selectedSlot); + const now = DateTime.now(); + return slotDateTime < now; +}; + +export const handleClosePastTimeErrorDialog = ( + setIsPastTimeErrorDialogOpen: (value: boolean) => void, + navigate: NavigateFunction, +): void => { + const { visitType, visitService, scheduleType, slug } = getSelectors(useAppointmentStore, [ + 'visitType', + 'visitService', + 'scheduleType', + 'slug', + ]); + + 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)}`, + ); +}; From ab74bf97f911e29bb4e2d215a78ae31007134044 Mon Sep 17 00:00:00 2001 From: aykhanahmadli Date: Wed, 20 Nov 2024 17:03:59 +0100 Subject: [PATCH 05/22] fix hook handling --- .../app/src/pages/ConfirmDateOfBirth.tsx | 21 ++++++++++++---- .../app/src/pages/PatientInformation.tsx | 24 +++++++++++++++---- .../app/src/utils/checkSlotTime.ts | 21 ++++++++-------- .../telemed-intake/app/src/utils/index.ts | 1 + 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx b/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx index 1c54aa33..c837b907 100644 --- a/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx +++ b/packages/telemed-intake/app/src/pages/ConfirmDateOfBirth.tsx @@ -15,9 +15,8 @@ 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'; -import { handleClosePastTimeErrorDialog, isSlotTimePassed } from 'src/utils/checkSlotTime'; const ConfirmDateOfBirth = (): JSX.Element => { const navigate = useNavigate(); @@ -27,7 +26,10 @@ 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', @@ -118,7 +120,7 @@ const ConfirmDateOfBirth = (): JSX.Element => { }, [challengeDay.name, challengeMonth.name, challengeYear.name, formValuesCopy]); const createOrUpdateAppointment = (unconfirmedDateOfBirth = false): void => { - if (isSlotTimePassed()) { + if (isSlotTimePassed(selectedSlot, visitType)) { setIsPastTimeErrorDialogOpen(true); return; } @@ -273,7 +275,16 @@ const ConfirmDateOfBirth = (): JSX.Element => { title={t('patientInfo.pastTimeError.title')} description={t('patientInfo.pastTimeError.description')} closeButtonText={t('patientInfo.pastTimeError.button')} - handleClose={() => handleClosePastTimeErrorDialog(setIsPastTimeErrorDialogOpen, navigate)} + handleClose={() => + 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 b361cf4b..a7ae9438 100644 --- a/packages/telemed-intake/app/src/pages/PatientInformation.tsx +++ b/packages/telemed-intake/app/src/pages/PatientInformation.tsx @@ -26,8 +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 } from 'src/utils/checkSlotTime'; +import { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; const UPDATEABLE_PATIENT_INFO_FIELDS: (keyof Omit)[] = [ 'firstName', @@ -77,7 +76,10 @@ const PatientInformation = (): JSX.Element => { ]); 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', @@ -167,6 +169,11 @@ const PatientInformation = (): JSX.Element => { return; } + if (isSlotTimePassed(selectedSlot, visitType)) { + setIsPastTimeErrorDialogOpen(true); + return; + } + if (!apiClient) { throw new Error('apiClient is not defined'); } @@ -404,7 +411,16 @@ const PatientInformation = (): JSX.Element => { title={t('patientInfo.pastTimeError.title')} description={t('patientInfo.pastTimeError.description')} closeButtonText={t('patientInfo.pastTimeError.button')} - handleClose={() => handleClosePastTimeErrorDialog(setIsPastTimeErrorDialogOpen, navigate)} + handleClose={() => + handleClosePastTimeErrorDialog( + setIsPastTimeErrorDialogOpen, + navigate, + visitType, + visitService, + scheduleType, + slug, + ) + } /> ); diff --git a/packages/telemed-intake/app/src/utils/checkSlotTime.ts b/packages/telemed-intake/app/src/utils/checkSlotTime.ts index 8f7e9dce..c1be29d0 100644 --- a/packages/telemed-intake/app/src/utils/checkSlotTime.ts +++ b/packages/telemed-intake/app/src/utils/checkSlotTime.ts @@ -1,28 +1,27 @@ import { DateTime } from 'luxon'; -import { getSelectors } from 'ottehr-utils'; import { NavigateFunction } from 'react-router-dom'; import { IntakeFlowPageRoute } from 'src/App'; -import { useAppointmentStore } from 'src/features/appointments'; -export const isSlotTimePassed = (): boolean => { - const selectedSlot = getSelectors(useAppointmentStore, ['selectedSlot']) as string; +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 => { - const { visitType, visitService, scheduleType, slug } = getSelectors(useAppointmentStore, [ - 'visitType', - 'visitService', - 'scheduleType', - 'slug', - ]); - if (!visitService || !visitType || !scheduleType || !slug) { console.error('One or more required parameters are missing'); navigate(IntakeFlowPageRoute.PatientPortal.path); 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'; From 5faf2b711b2d0b2bdd9ccafa65b5d325eda77fb5 Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Wed, 20 Nov 2024 19:59:26 +0100 Subject: [PATCH 06/22] update description text Co-authored-by: Gilad Schneider <73044725+GiladSchneider@users.noreply.github.com> Signed-off-by: Aykhan Ahmadli --- packages/telemed-intake/app/src/theme/ottehr/i18n-en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a64f0401..599bdf2c 100644 --- a/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json +++ b/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json @@ -176,7 +176,7 @@ }, "pastTimeError": { "title": "Error booking visit", - "description": "We are sorry, your selected check-in time is in the past. Please click below to select a different time slot.", + "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" } }, From 69e3ca6ecfebe5e92206fda157549230240670ff Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Thu, 21 Nov 2024 15:40:13 +0100 Subject: [PATCH 07/22] Revert "Past slot time selection handling" --- .../appointments/appointment.store.ts | 1 - .../app/src/pages/ConfirmDateOfBirth.tsx | 29 +------------- .../app/src/pages/PatientInformation.tsx | 29 +------------- .../telemed-intake/app/src/pages/Welcome.tsx | 9 +---- .../app/src/theme/ottehr/i18n-en.json | 5 --- .../app/src/utils/checkSlotTime.ts | 40 ------------------- .../telemed-intake/app/src/utils/index.ts | 1 - 7 files changed, 6 insertions(+), 108 deletions(-) delete mode 100644 packages/telemed-intake/app/src/utils/checkSlotTime.ts 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 eea75ac7..a199d1cb 100644 --- a/packages/telemed-intake/app/src/features/appointments/appointment.store.ts +++ b/packages/telemed-intake/app/src/features/appointments/appointment.store.ts @@ -11,7 +11,6 @@ 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 c837b907..7dd80b40 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 { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; +import { useZapEHRAPIClient } from '../utils'; import { decode } from 'html-entities'; const ConfirmDateOfBirth = (): JSX.Element => { @@ -26,11 +26,7 @@ const ConfirmDateOfBirth = (): JSX.Element => { const [requestErrorDialogOpen, setRequestErrorDialogOpen] = useState(false); const createAppointment = useCreateAppointmentMutation(); const [getPaperworkEnabled, setGetPaperworkEnabled] = useState(false); - const { appointmentID, selectedSlot, visitType, visitService, scheduleType, slug } = getSelectors( - useAppointmentStore, - ['appointmentID', 'selectedSlot', 'visitType', 'visitService', 'scheduleType', 'slug'], - ); - const [isPastTimeErrorDialogOpen, setIsPastTimeErrorDialogOpen] = useState(false); + const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -120,11 +116,6 @@ 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'); } @@ -270,22 +261,6 @@ 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 a7ae9438..f364e827 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 { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; +import { useZapEHRAPIClient } from '../utils'; const UPDATEABLE_PATIENT_INFO_FIELDS: (keyof Omit)[] = [ 'firstName', @@ -69,17 +69,13 @@ 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, visitType, visitService, scheduleType, slug, selectedSlot } = getSelectors( - useAppointmentStore, - ['appointmentID', 'visitType', 'visitService', 'scheduleType', 'slug', 'selectedSlot'], - ); + const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -169,11 +165,6 @@ const PatientInformation = (): JSX.Element => { return; } - if (isSlotTimePassed(selectedSlot, visitType)) { - setIsPastTimeErrorDialogOpen(true); - return; - } - if (!apiClient) { throw new Error('apiClient is not defined'); } @@ -406,22 +397,6 @@ 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 39deeb76..3433226a 100644 --- a/packages/telemed-intake/app/src/pages/Welcome.tsx +++ b/packages/telemed-intake/app/src/pages/Welcome.tsx @@ -53,13 +53,8 @@ const Welcome = (): JSX.Element => { }, [visitService, setAppointment, visitType, scheduleType]); useEffect(() => { - setAppointment({ - locationID: schedule?.locationID, - providerID: schedule?.providerID, - groupID: schedule?.groupID, - slug, - }); - }, [schedule?.groupID, schedule?.locationID, schedule?.providerID, setAppointment, slug]); + setAppointment({ locationID: schedule?.locationID, providerID: schedule?.providerID, groupID: schedule?.groupID }); + }, [schedule?.groupID, schedule?.locationID, schedule?.providerID, setAppointment]); 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 599bdf2c..c19a3f0f 100644 --- a/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json +++ b/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json @@ -173,11 +173,6 @@ "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 deleted file mode 100644 index c1be29d0..00000000 --- a/packages/telemed-intake/app/src/utils/checkSlotTime.ts +++ /dev/null @@ -1,40 +0,0 @@ -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 3bc9a3bd..011cb1f4 100644 --- a/packages/telemed-intake/app/src/utils/index.ts +++ b/packages/telemed-intake/app/src/utils/index.ts @@ -2,4 +2,3 @@ export * from './getZapEHRAPI'; export * from './getPaperworkPageInfo'; export * from './mapQuestionsToFormInputFields'; export * from './zustandDevtools'; -export * from './checkSlotTime'; From be0af8ef3e3558e9f77209a46eb68ad8f03aae94 Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Thu, 21 Nov 2024 15:41:42 +0100 Subject: [PATCH 08/22] Revert "End call redirect issue" --- packages/telemed-intake/app/src/components/VideoControls.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/telemed-intake/app/src/components/VideoControls.tsx b/packages/telemed-intake/app/src/components/VideoControls.tsx index 95a07443..10d94a7a 100644 --- a/packages/telemed-intake/app/src/components/VideoControls.tsx +++ b/packages/telemed-intake/app/src/components/VideoControls.tsx @@ -11,13 +11,10 @@ import { CallSettingsTooltip } from './CallSettingsTooltip'; import { useVideoCallStore } from '../features/video-call'; import { otherColors } from '../IntakeThemeProvider'; import { CallSettings } from './CallSettingsDialog'; -import { useNavigate } from 'react-router-dom'; -import { IntakeFlowPageRoute } from 'src/App'; export const VideoControls: FC = () => { const { toggleVideo, isVideoEnabled } = useLocalVideo(); const { muted, toggleMute } = useToggleLocalMute(); - const navigate = useNavigate(); const meetingManager = useMeetingManager(); const [isSettingsOpen, setIsSettingsOpen] = useState(false); @@ -49,7 +46,6 @@ export const VideoControls: FC = () => { const disconnect = async (): Promise => { await cleanup(); useVideoCallStore.setState({ meetingData: null }); - navigate(IntakeFlowPageRoute.PatientPortal.path); }; return ( From d1cd9bdf751ff1ddda364eb04ef84074d2e22d24 Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Thu, 21 Nov 2024 15:43:36 +0100 Subject: [PATCH 09/22] Revert "Revert "End call redirect issue"" --- packages/telemed-intake/app/src/components/VideoControls.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/telemed-intake/app/src/components/VideoControls.tsx b/packages/telemed-intake/app/src/components/VideoControls.tsx index 10d94a7a..95a07443 100644 --- a/packages/telemed-intake/app/src/components/VideoControls.tsx +++ b/packages/telemed-intake/app/src/components/VideoControls.tsx @@ -11,10 +11,13 @@ import { CallSettingsTooltip } from './CallSettingsTooltip'; import { useVideoCallStore } from '../features/video-call'; import { otherColors } from '../IntakeThemeProvider'; import { CallSettings } from './CallSettingsDialog'; +import { useNavigate } from 'react-router-dom'; +import { IntakeFlowPageRoute } from 'src/App'; export const VideoControls: FC = () => { const { toggleVideo, isVideoEnabled } = useLocalVideo(); const { muted, toggleMute } = useToggleLocalMute(); + const navigate = useNavigate(); const meetingManager = useMeetingManager(); const [isSettingsOpen, setIsSettingsOpen] = useState(false); @@ -46,6 +49,7 @@ export const VideoControls: FC = () => { const disconnect = async (): Promise => { await cleanup(); useVideoCallStore.setState({ meetingData: null }); + navigate(IntakeFlowPageRoute.PatientPortal.path); }; return ( From 76f1a7d72423d573d68da96f0ba86fb1b7ce6026 Mon Sep 17 00:00:00 2001 From: aykhanahmadli Date: Thu, 21 Nov 2024 19:12:35 +0100 Subject: [PATCH 10/22] filter in-person appointments --- packages/telemed-ehr/zambdas/src/get-appointments/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/telemed-ehr/zambdas/src/get-appointments/index.ts b/packages/telemed-ehr/zambdas/src/get-appointments/index.ts index c121f855..43eee98b 100644 --- a/packages/telemed-ehr/zambdas/src/get-appointments/index.ts +++ b/packages/telemed-ehr/zambdas/src/get-appointments/index.ts @@ -296,7 +296,7 @@ export const index = async (input: ZambdaInput): Promise const healthcareServiceIdToResourceMap: Record = {}; searchResultsForSelectedDate.forEach((resource) => { - if (resource.resourceType === 'Appointment') { + if (resource.resourceType === 'Appointment' && (resource as Appointment).serviceType?.[0]?.text === 'in-person') { allAppointments.push(resource as Appointment); } else if (resource.resourceType === 'Patient' && resource.id) { patientIdMap[resource.id] = resource as Patient; From 1162fdd245753d14b236d95910b58cb11d53896e Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 15:48:02 -0500 Subject: [PATCH 11/22] some changes --- .github/workflows/build-and-deploy-telemed-ehr.yml | 4 +--- .github/workflows/build-and-deploy-telemed-intake.yml | 2 -- packages/telemed-ehr/app/package.json | 3 ++- packages/telemed-intake/app/package.json | 3 ++- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-deploy-telemed-ehr.yml b/.github/workflows/build-and-deploy-telemed-ehr.yml index 9d679d40..f694cabd 100644 --- a/.github/workflows/build-and-deploy-telemed-ehr.yml +++ b/.github/workflows/build-and-deploy-telemed-ehr.yml @@ -14,10 +14,8 @@ on: type: choice default: 'development' options: - - development - - dev2 + - development - testing - - staging incrementVersion: description: 'Bump Version?' type: boolean diff --git a/.github/workflows/build-and-deploy-telemed-intake.yml b/.github/workflows/build-and-deploy-telemed-intake.yml index 47057651..4c97c28a 100644 --- a/.github/workflows/build-and-deploy-telemed-intake.yml +++ b/.github/workflows/build-and-deploy-telemed-intake.yml @@ -15,9 +15,7 @@ on: default: 'development' options: - development - - dev2 - testing - - staging incrementVersion: description: 'Bump Version?' type: boolean diff --git a/packages/telemed-ehr/app/package.json b/packages/telemed-ehr/app/package.json index d35328d8..4158bb0f 100644 --- a/packages/telemed-ehr/app/package.json +++ b/packages/telemed-ehr/app/package.json @@ -30,6 +30,7 @@ "test": "react-scripts test", "eject": "react-scripts eject", "deploy:development": " PREFIX=development CLOUDFRONT_ID=E10TA6FN58D1OS ENV=development pnpm run ci-deploy-skeleton", + "deploy:testing": " PREFIX=testing CLOUDFRONT_ID=ETFAEVC5V0490 ENV=testing pnpm run ci-deploy-skeleton", "ci-deploy-skeleton": "ENV=${ENV} VITE_APP_SHA=${GIT_HEAD:-$(git rev-parse --short HEAD)} VITE_APP_VERSION=$(node -pe 'require(\"./package.json\").version') pnpm run build:${ENV} && aws s3 sync build/ s3://ehr.ottehr.com --region us-east-1 --delete && aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_ID} --paths '/*' --region us-east-1" }, "dependencies": { @@ -62,4 +63,4 @@ "@types/styled-components": "^5.1.34", "@types/react-page-visibility": "^6.4.4" } -} +} \ No newline at end of file diff --git a/packages/telemed-intake/app/package.json b/packages/telemed-intake/app/package.json index 3c3c7252..e410c1ff 100644 --- a/packages/telemed-intake/app/package.json +++ b/packages/telemed-intake/app/package.json @@ -8,6 +8,7 @@ "build:development": "tsc && vite build --mode development", "build": "tsc && vite build", "deploy:development": "ENV=development PREFIX=development CLOUDFRONT_ID=EIYX001DGGQK8 pnpm run ci-deploy-skeleton", + "deploy:development": "ENV=testing PREFIX=testing CLOUDFRONT_ID=E2IMNWB48TKHJO pnpm run ci-deploy-skeleton", "ci-deploy-skeleton": "ENV=${ENV} VITE_APP_SHA=${GIT_HEAD:-$(git rev-parse --short HEAD)} VITE_APP_VERSION=$(node -pe 'require(\"./package.json\").version') pnpm run build:${ENV} && aws s3 sync build/ s3://telemed.ottehr.com --region us-east-1 --delete && aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_ID} --paths '/*' --region us-east-1" }, "dependencies": { @@ -23,4 +24,4 @@ "devDependencies": { "@types/styled-components": "^5.1.34" } -} +} \ No newline at end of file From 0d993ab16070bdc09528b80c00fdaffdf37c74b3 Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 15:54:07 -0500 Subject: [PATCH 12/22] more fixes --- .../zambdas/scripts/configure-zapehr-secrets.ts | 8 ++++---- packages/telemed-intake/app/package.json | 2 +- packages/telemed-intake/zambdas/scripts/common.ts | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/telemed-ehr/zambdas/scripts/configure-zapehr-secrets.ts b/packages/telemed-ehr/zambdas/scripts/configure-zapehr-secrets.ts index e9e2a238..8b37d2b6 100644 --- a/packages/telemed-ehr/zambdas/scripts/configure-zapehr-secrets.ts +++ b/packages/telemed-ehr/zambdas/scripts/configure-zapehr-secrets.ts @@ -1,5 +1,5 @@ import devConfig from '../.env/development.json'; -// import testingConfig from '../.env/testing.json'; +import testingConfig from '../.env/testing.json'; import { getAuth0Token } from '../src/shared'; import { ZambdaClient } from '@zapehr/sdk'; @@ -37,9 +37,9 @@ const main = async (): Promise => { case 'development': await setupSecrets(devConfig); break; - // case 'testing': - // await setupSecrets(testingConfig); - // break; + case 'testing': + await setupSecrets(testingConfig); + break; default: throw new Error('¯\\_(ツ)_/¯ environment must match a valid zapEHR environment.'); } diff --git a/packages/telemed-intake/app/package.json b/packages/telemed-intake/app/package.json index e410c1ff..1eba652b 100644 --- a/packages/telemed-intake/app/package.json +++ b/packages/telemed-intake/app/package.json @@ -8,7 +8,7 @@ "build:development": "tsc && vite build --mode development", "build": "tsc && vite build", "deploy:development": "ENV=development PREFIX=development CLOUDFRONT_ID=EIYX001DGGQK8 pnpm run ci-deploy-skeleton", - "deploy:development": "ENV=testing PREFIX=testing CLOUDFRONT_ID=E2IMNWB48TKHJO pnpm run ci-deploy-skeleton", + "deploy:testing": "ENV=testing PREFIX=testing CLOUDFRONT_ID=E2IMNWB48TKHJO pnpm run ci-deploy-skeleton", "ci-deploy-skeleton": "ENV=${ENV} VITE_APP_SHA=${GIT_HEAD:-$(git rev-parse --short HEAD)} VITE_APP_VERSION=$(node -pe 'require(\"./package.json\").version') pnpm run build:${ENV} && aws s3 sync build/ s3://telemed.ottehr.com --region us-east-1 --delete && aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_ID} --paths '/*' --region us-east-1" }, "dependencies": { diff --git a/packages/telemed-intake/zambdas/scripts/common.ts b/packages/telemed-intake/zambdas/scripts/common.ts index ceeb258d..7ebf906e 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.'); } From 2b1085eaf6deb3f2171b21cb6149be8c381e5daa Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 15:58:16 -0500 Subject: [PATCH 13/22] adds build:testing to intake app? --- packages/telemed-intake/app/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/telemed-intake/app/package.json b/packages/telemed-intake/app/package.json index 1eba652b..19ccf9d5 100644 --- a/packages/telemed-intake/app/package.json +++ b/packages/telemed-intake/app/package.json @@ -6,6 +6,7 @@ "scripts": { "start:local": "vite", "build:development": "tsc && vite build --mode development", + "build:testing": "tsc && vite build --mode testing", "build": "tsc && vite build", "deploy:development": "ENV=development PREFIX=development CLOUDFRONT_ID=EIYX001DGGQK8 pnpm run ci-deploy-skeleton", "deploy:testing": "ENV=testing PREFIX=testing CLOUDFRONT_ID=E2IMNWB48TKHJO pnpm run ci-deploy-skeleton", From 578b65a23468b6be8bce0dba9cb9267eb46984ed Mon Sep 17 00:00:00 2001 From: Aykhan Ahmadli Date: Thu, 21 Nov 2024 15:40:13 +0100 Subject: [PATCH 14/22] Revert "Past slot time selection handling" --- .../appointments/appointment.store.ts | 1 - .../app/src/pages/ConfirmDateOfBirth.tsx | 29 +------------- .../app/src/pages/PatientInformation.tsx | 29 +------------- .../telemed-intake/app/src/pages/Welcome.tsx | 9 +---- .../app/src/theme/ottehr/i18n-en.json | 5 --- .../app/src/utils/checkSlotTime.ts | 40 ------------------- .../telemed-intake/app/src/utils/index.ts | 1 - 7 files changed, 6 insertions(+), 108 deletions(-) delete mode 100644 packages/telemed-intake/app/src/utils/checkSlotTime.ts 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 eea75ac7..a199d1cb 100644 --- a/packages/telemed-intake/app/src/features/appointments/appointment.store.ts +++ b/packages/telemed-intake/app/src/features/appointments/appointment.store.ts @@ -11,7 +11,6 @@ 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 c837b907..7dd80b40 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 { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; +import { useZapEHRAPIClient } from '../utils'; import { decode } from 'html-entities'; const ConfirmDateOfBirth = (): JSX.Element => { @@ -26,11 +26,7 @@ const ConfirmDateOfBirth = (): JSX.Element => { const [requestErrorDialogOpen, setRequestErrorDialogOpen] = useState(false); const createAppointment = useCreateAppointmentMutation(); const [getPaperworkEnabled, setGetPaperworkEnabled] = useState(false); - const { appointmentID, selectedSlot, visitType, visitService, scheduleType, slug } = getSelectors( - useAppointmentStore, - ['appointmentID', 'selectedSlot', 'visitType', 'visitService', 'scheduleType', 'slug'], - ); - const [isPastTimeErrorDialogOpen, setIsPastTimeErrorDialogOpen] = useState(false); + const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -120,11 +116,6 @@ 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'); } @@ -270,22 +261,6 @@ 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 a7ae9438..f364e827 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 { handleClosePastTimeErrorDialog, isSlotTimePassed, useZapEHRAPIClient } from '../utils'; +import { useZapEHRAPIClient } from '../utils'; const UPDATEABLE_PATIENT_INFO_FIELDS: (keyof Omit)[] = [ 'firstName', @@ -69,17 +69,13 @@ 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, visitType, visitService, scheduleType, slug, selectedSlot } = getSelectors( - useAppointmentStore, - ['appointmentID', 'visitType', 'visitService', 'scheduleType', 'slug', 'selectedSlot'], - ); + const { appointmentID } = getSelectors(useAppointmentStore, ['appointmentID']); const { patchCompletedPaperwork, setQuestions } = getSelectors(usePaperworkStore, [ 'patchCompletedPaperwork', 'setQuestions', @@ -169,11 +165,6 @@ const PatientInformation = (): JSX.Element => { return; } - if (isSlotTimePassed(selectedSlot, visitType)) { - setIsPastTimeErrorDialogOpen(true); - return; - } - if (!apiClient) { throw new Error('apiClient is not defined'); } @@ -406,22 +397,6 @@ 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 39deeb76..3433226a 100644 --- a/packages/telemed-intake/app/src/pages/Welcome.tsx +++ b/packages/telemed-intake/app/src/pages/Welcome.tsx @@ -53,13 +53,8 @@ const Welcome = (): JSX.Element => { }, [visitService, setAppointment, visitType, scheduleType]); useEffect(() => { - setAppointment({ - locationID: schedule?.locationID, - providerID: schedule?.providerID, - groupID: schedule?.groupID, - slug, - }); - }, [schedule?.groupID, schedule?.locationID, schedule?.providerID, setAppointment, slug]); + setAppointment({ locationID: schedule?.locationID, providerID: schedule?.providerID, groupID: schedule?.groupID }); + }, [schedule?.groupID, schedule?.locationID, schedule?.providerID, setAppointment]); 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 599bdf2c..c19a3f0f 100644 --- a/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json +++ b/packages/telemed-intake/app/src/theme/ottehr/i18n-en.json @@ -173,11 +173,6 @@ "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 deleted file mode 100644 index c1be29d0..00000000 --- a/packages/telemed-intake/app/src/utils/checkSlotTime.ts +++ /dev/null @@ -1,40 +0,0 @@ -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 3bc9a3bd..011cb1f4 100644 --- a/packages/telemed-intake/app/src/utils/index.ts +++ b/packages/telemed-intake/app/src/utils/index.ts @@ -2,4 +2,3 @@ export * from './getZapEHRAPI'; export * from './getPaperworkPageInfo'; export * from './mapQuestionsToFormInputFields'; export * from './zustandDevtools'; -export * from './checkSlotTime'; From ea5e5c00767417f0021a39d17c4cefe260f24299 Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 14:55:26 -0500 Subject: [PATCH 15/22] everything but addresses #499 --- .../components/EmployeeInformationForm.tsx | 131 +++++++++++------- packages/telemed-ehr/app/src/types/types.ts | 2 + .../zambdas/src/update-user/index.ts | 32 ++++- .../update-user/validateRequestParameters.ts | 6 +- 4 files changed, 115 insertions(+), 56 deletions(-) diff --git a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx index f070ff19..a7442a76 100644 --- a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx +++ b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx @@ -55,6 +55,8 @@ interface EmployeeForm { lastName: string; nameSuffix: string; roles: string[]; + phoneNumber: string; + npi: string; } const AVAILABLE_ROLES: { @@ -62,32 +64,32 @@ const AVAILABLE_ROLES: { label: string; hint: string; }[] = [ - { - value: RoleType.Administrator, - label: 'Administrator', - hint: `Adjust full settings for entire system`, - }, - { - value: RoleType.Manager, - label: 'Manager', - hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, - }, - { - value: RoleType.Staff, - label: 'Staff', - hint: `No settings changes; essentially read-only`, - }, - { - value: RoleType.Provider, - label: 'Provider', - hint: `A clinician, such as a doctor, a PA or an NP`, - }, - { - value: RoleType.Prescriber, - label: 'Prescriber', - hint: `A clinician that is allowed to prescribe`, - }, -]; + { + value: RoleType.Administrator, + label: 'Administrator', + hint: `Adjust full settings for entire system`, + }, + { + value: RoleType.Manager, + label: 'Manager', + hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, + }, + { + value: RoleType.Staff, + label: 'Staff', + hint: `No settings changes; essentially read-only`, + }, + { + value: RoleType.Provider, + label: 'Provider', + hint: `A clinician, such as a doctor, a PA or an NP`, + }, + { + value: RoleType.Prescriber, + label: 'Prescriber', + hint: `A clinician that is allowed to prescribe`, + }, + ]; if (import.meta.env.MODE === 'default' || import.meta.env.MODE === 'development') { AVAILABLE_ROLES.push( @@ -150,22 +152,6 @@ export default function EmployeeInformationForm({ console.log('existingUser', existingUser); - let npiText = 'n/a'; - if (existingUser?.profileResource?.identifier) { - const npi = existingUser.profileResource.identifier.find((identifier) => identifier.system === FHIR_IDENTIFIER_NPI); - if (npi && npi.value) { - npiText = npi.value; - } - } - - let phoneText = ''; - if (existingUser?.profileResource?.telecom) { - const phone = existingUser.profileResource.telecom.find((tel) => tel.system === 'sms')?.value; - if (phone) { - phoneText = phone; - } - } - let photoSrc = ''; if (existingUser?.profileResource?.photo) { const photo = existingUser.profileResource.photo[0]; @@ -201,6 +187,26 @@ export default function EmployeeInformationForm({ setValue('middleName', middleName); setValue('lastName', lastName); setValue('nameSuffix', nameSuffix); + + let phoneText = ''; + if (existingUser?.profileResource?.telecom) { + const phone = existingUser.profileResource.telecom.find((tel) => tel.system === 'sms')?.value; + if (phone) { + phoneText = phone; + } + } + setValue('phoneNumber', phoneText); + + let npiText = 'n/a'; + if (existingUser?.profileResource?.identifier) { + const npi = existingUser.profileResource.identifier.find( + (identifier) => identifier.system === FHIR_IDENTIFIER_NPI, + ); + if (npi && npi.value) { + npiText = npi.value; + } + } + setValue('npi', npiText); } }, [existingUser, setValue]); @@ -229,6 +235,8 @@ export default function EmployeeInformationForm({ nameSuffix: data.nameSuffix, selectedRoles: data.roles, licenses: newLicenses, + phoneNumber: data.phoneNumber, + npi: data.npi, }); } catch (error) { console.log(`Failed to update user: ${error}`); @@ -334,16 +342,19 @@ export default function EmployeeInformationForm({ disabled: true, }} /> - ( + + )} /> @@ -420,7 +431,21 @@ export default function EmployeeInformationForm({ /> )} /> - + ( + + )} + /> {isProviderRoleSelected && ( <> diff --git a/packages/telemed-ehr/app/src/types/types.ts b/packages/telemed-ehr/app/src/types/types.ts index 16af1f72..84eae09a 100644 --- a/packages/telemed-ehr/app/src/types/types.ts +++ b/packages/telemed-ehr/app/src/types/types.ts @@ -38,6 +38,8 @@ export interface UpdateUserParameters { nameSuffix?: string; selectedRoles?: string[] | undefined; licenses?: PractitionerLicense[]; + phoneNumber?: string; + npi?: string; // locations: Location[]; } diff --git a/packages/telemed-ehr/zambdas/src/update-user/index.ts b/packages/telemed-ehr/zambdas/src/update-user/index.ts index 665c2064..9dfb2cc2 100644 --- a/packages/telemed-ehr/zambdas/src/update-user/index.ts +++ b/packages/telemed-ehr/zambdas/src/update-user/index.ts @@ -1,6 +1,6 @@ import { APIGatewayProxyResult } from 'aws-lambda'; import { Practitioner, HumanName } from 'fhir/r4'; -import { PractitionerLicense, Secrets } from 'ehr-utils'; +import { FHIR_IDENTIFIER_NPI, PractitionerLicense, Secrets } from 'ehr-utils'; import { RoleType } from '../../../app/src/types/types'; import { getSecret } from '../shared'; import { topLevelCatch } from '../shared/errors'; @@ -19,6 +19,8 @@ export interface UpdateUserInput { nameSuffix?: string; selectedRoles?: RoleType[]; licenses?: PractitionerLicense[]; + phoneNumber?: string; + npi?: string; } let m2mtoken: string; @@ -27,7 +29,7 @@ export const index = async (input: ZambdaInput): Promise console.group('validateRequestParameters'); const validatedParameters = validateRequestParameters(input); console.log('validatedParameters:', JSON.stringify(validatedParameters, null, 4)); - const { secrets, userId, firstName, middleName, lastName, nameSuffix, selectedRoles, licenses } = + const { secrets, userId, firstName, middleName, lastName, nameSuffix, selectedRoles, licenses, phoneNumber, npi } = validatedParameters; console.groupEnd(); console.debug('validateRequestParameters success'); @@ -98,14 +100,40 @@ export const index = async (input: ZambdaInput): Promise id: practitionerId, name: name ? [name] : undefined, qualification: practitionerQualificationExtension, + telecom: phoneNumber ? [{ system: 'sms', value: phoneNumber }] : undefined, }); } else { + const existingTelecom = existingPractitionerResource.telecom || []; + const smsIndex = existingTelecom.findIndex((tel) => tel.system === 'sms'); + const updatedTelecom = [...existingTelecom]; + if (phoneNumber) { + if (smsIndex >= 0) { + updatedTelecom[smsIndex] = { system: 'sms', value: phoneNumber }; + } else { + updatedTelecom.push({ system: 'sms', value: phoneNumber }); + } + } + if (npi) { + if (!existingPractitionerResource.identifier) { + existingPractitionerResource.identifier = []; + } + const npiIndex = existingPractitionerResource.identifier.findIndex((id) => id.system === FHIR_IDENTIFIER_NPI); + if (npiIndex >= 0) { + existingPractitionerResource.identifier[npiIndex].value = npi; + } else { + existingPractitionerResource.identifier.push({ + system: FHIR_IDENTIFIER_NPI, + value: npi, + }); + } + } await fhirClient.updateResource({ ...existingPractitionerResource, identifier: existingPractitionerResource.identifier, photo: existingPractitionerResource.photo, name: name ? [name] : undefined, qualification: practitionerQualificationExtension, + telecom: updatedTelecom, }); } } catch (error: unknown) { diff --git a/packages/telemed-ehr/zambdas/src/update-user/validateRequestParameters.ts b/packages/telemed-ehr/zambdas/src/update-user/validateRequestParameters.ts index ab490ff3..d3873e24 100644 --- a/packages/telemed-ehr/zambdas/src/update-user/validateRequestParameters.ts +++ b/packages/telemed-ehr/zambdas/src/update-user/validateRequestParameters.ts @@ -7,7 +7,9 @@ export function validateRequestParameters(input: ZambdaInput): UpdateUserInput { throw new Error('No request body provided'); } - const { userId, firstName, middleName, lastName, nameSuffix, selectedRoles, licenses } = JSON.parse(input.body); + const { userId, firstName, middleName, lastName, nameSuffix, selectedRoles, licenses, phoneNumber, npi } = JSON.parse( + input.body, + ); if ( userId === undefined @@ -36,5 +38,7 @@ export function validateRequestParameters(input: ZambdaInput): UpdateUserInput { licenses, // locations, secrets: input.secrets, + phoneNumber: phoneNumber ? phoneNumber.trim() : phoneNumber, + npi: npi ? npi.trim() : npi, }; } From 4c56269f4030e6ad8972598f4aace8d65f81514f Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 16:09:25 -0500 Subject: [PATCH 16/22] fix build --- .../components/EmployeeInformationForm.tsx | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx index a7442a76..cb6b6a58 100644 --- a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx +++ b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx @@ -64,32 +64,32 @@ const AVAILABLE_ROLES: { label: string; hint: string; }[] = [ - { - value: RoleType.Administrator, - label: 'Administrator', - hint: `Adjust full settings for entire system`, - }, - { - value: RoleType.Manager, - label: 'Manager', - hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, - }, - { - value: RoleType.Staff, - label: 'Staff', - hint: `No settings changes; essentially read-only`, - }, - { - value: RoleType.Provider, - label: 'Provider', - hint: `A clinician, such as a doctor, a PA or an NP`, - }, - { - value: RoleType.Prescriber, - label: 'Prescriber', - hint: `A clinician that is allowed to prescribe`, - }, - ]; + { + value: RoleType.Administrator, + label: 'Administrator', + hint: `Adjust full settings for entire system`, + }, + { + value: RoleType.Manager, + label: 'Manager', + hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, + }, + { + value: RoleType.Staff, + label: 'Staff', + hint: `No settings changes; essentially read-only`, + }, + { + value: RoleType.Provider, + label: 'Provider', + hint: `A clinician, such as a doctor, a PA or an NP`, + }, + { + value: RoleType.Prescriber, + label: 'Prescriber', + hint: `A clinician that is allowed to prescribe`, + }, +]; if (import.meta.env.MODE === 'default' || import.meta.env.MODE === 'development') { AVAILABLE_ROLES.push( From 966a2f66ea2e2fd3e017325f9033c3734d6a6103 Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 14:55:26 -0500 Subject: [PATCH 17/22] everything but addresses #499 --- .../components/EmployeeInformationForm.tsx | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx index cb6b6a58..a7442a76 100644 --- a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx +++ b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx @@ -64,32 +64,32 @@ const AVAILABLE_ROLES: { label: string; hint: string; }[] = [ - { - value: RoleType.Administrator, - label: 'Administrator', - hint: `Adjust full settings for entire system`, - }, - { - value: RoleType.Manager, - label: 'Manager', - hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, - }, - { - value: RoleType.Staff, - label: 'Staff', - hint: `No settings changes; essentially read-only`, - }, - { - value: RoleType.Provider, - label: 'Provider', - hint: `A clinician, such as a doctor, a PA or an NP`, - }, - { - value: RoleType.Prescriber, - label: 'Prescriber', - hint: `A clinician that is allowed to prescribe`, - }, -]; + { + value: RoleType.Administrator, + label: 'Administrator', + hint: `Adjust full settings for entire system`, + }, + { + value: RoleType.Manager, + label: 'Manager', + hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, + }, + { + value: RoleType.Staff, + label: 'Staff', + hint: `No settings changes; essentially read-only`, + }, + { + value: RoleType.Provider, + label: 'Provider', + hint: `A clinician, such as a doctor, a PA or an NP`, + }, + { + value: RoleType.Prescriber, + label: 'Prescriber', + hint: `A clinician that is allowed to prescribe`, + }, + ]; if (import.meta.env.MODE === 'default' || import.meta.env.MODE === 'development') { AVAILABLE_ROLES.push( From 53033e64221276cd85350afb5f7c6e1577339c2c Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 16:09:25 -0500 Subject: [PATCH 18/22] fix build --- .../components/EmployeeInformationForm.tsx | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx index a7442a76..cb6b6a58 100644 --- a/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx +++ b/packages/telemed-ehr/app/src/components/EmployeeInformationForm.tsx @@ -64,32 +64,32 @@ const AVAILABLE_ROLES: { label: string; hint: string; }[] = [ - { - value: RoleType.Administrator, - label: 'Administrator', - hint: `Adjust full settings for entire system`, - }, - { - value: RoleType.Manager, - label: 'Manager', - hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, - }, - { - value: RoleType.Staff, - label: 'Staff', - hint: `No settings changes; essentially read-only`, - }, - { - value: RoleType.Provider, - label: 'Provider', - hint: `A clinician, such as a doctor, a PA or an NP`, - }, - { - value: RoleType.Prescriber, - label: 'Prescriber', - hint: `A clinician that is allowed to prescribe`, - }, - ]; + { + value: RoleType.Administrator, + label: 'Administrator', + hint: `Adjust full settings for entire system`, + }, + { + value: RoleType.Manager, + label: 'Manager', + hint: `Adjust operating hours or schedule overrides; adjust pre-booked visits per hour`, + }, + { + value: RoleType.Staff, + label: 'Staff', + hint: `No settings changes; essentially read-only`, + }, + { + value: RoleType.Provider, + label: 'Provider', + hint: `A clinician, such as a doctor, a PA or an NP`, + }, + { + value: RoleType.Prescriber, + label: 'Prescriber', + hint: `A clinician that is allowed to prescribe`, + }, +]; if (import.meta.env.MODE === 'default' || import.meta.env.MODE === 'development') { AVAILABLE_ROLES.push( From f2b2c88341a459f9a2c297e6ded1ee08733bb6e1 Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Thu, 21 Nov 2024 16:24:17 -0500 Subject: [PATCH 19/22] undo changes --- .../appointments/appointment.store.ts | 1 + .../app/src/pages/ConfirmDateOfBirth.tsx | 29 +++++++++++++- .../app/src/pages/PatientInformation.tsx | 29 +++++++++++++- .../telemed-intake/app/src/pages/Welcome.tsx | 9 ++++- .../app/src/theme/ottehr/i18n-en.json | 5 +++ .../app/src/utils/checkSlotTime.ts | 40 +++++++++++++++++++ .../telemed-intake/app/src/utils/index.ts | 1 + 7 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 packages/telemed-intake/app/src/utils/checkSlotTime.ts 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'; From 1582c4d1bc712a18ef680ac6f1b9b68f7164a07b Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Fri, 22 Nov 2024 12:28:57 -0500 Subject: [PATCH 20/22] set up testing.json --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1ce2d59..e3d59a8d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,12 @@ jobs: - name: Set the urgent-care-intake/zambdas/.env/development.json run: cp packages/urgent-care-intake/zambdas/.env/local.template.json packages/urgent-care-intake/zambdas/.env/development.json + - name: Set the telemed-ehr/zambdas/.env/testing.json + run: cp packages/telemed-ehr/zambdas/.env/local.template.json packages/telemed-ehr/zambdas/.env/testing.json + + - name: Set the telemed-intake/zambdas/.env/testing.json + run: cp packages/telemed-intake/zambdas/.env/local.template.json packages/telemed-intake/zambdas/.env/testing.json + - id: get-store-path run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT From 63bb3279b6a0ce42959cdd1b061098282dbc9b7e Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Mon, 25 Nov 2024 11:14:12 -0500 Subject: [PATCH 21/22] update version --- package.json | 2 +- packages/ehr-utils/package.json | 2 +- packages/ottehr-components/package.json | 2 +- packages/telemed-ehr/app/package.json | 2 +- packages/telemed-ehr/zambdas/package.json | 2 +- packages/telemed-intake/app/package.json | 2 +- packages/telemed-intake/zambdas/package.json | 2 +- packages/utils/package.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 89bc37b4..6b18986a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ottehr", - "version": "0.17.0", + "version": "0.18.0", "private": true, "scripts": { "test": "pnpm recursive run test", diff --git a/packages/ehr-utils/package.json b/packages/ehr-utils/package.json index 275c6d23..6d6c1fdc 100644 --- a/packages/ehr-utils/package.json +++ b/packages/ehr-utils/package.json @@ -1,7 +1,7 @@ { "name": "ehr-utils", "private": true, - "version": "0.17.0", + "version": "0.18.0", "main": "lib/main.ts", "types": "lib/main.ts", "scripts": { diff --git a/packages/ottehr-components/package.json b/packages/ottehr-components/package.json index 091a5620..93ab427f 100644 --- a/packages/ottehr-components/package.json +++ b/packages/ottehr-components/package.json @@ -1,7 +1,7 @@ { "name": "ottehr-components", "private": true, - "version": "0.17.0", + "version": "0.18.0", "main": "lib/main.ts", "types": "lib/main.ts", "scripts": { diff --git a/packages/telemed-ehr/app/package.json b/packages/telemed-ehr/app/package.json index 4158bb0f..6cd59280 100644 --- a/packages/telemed-ehr/app/package.json +++ b/packages/telemed-ehr/app/package.json @@ -1,6 +1,6 @@ { "name": "telemed-ehr-app", - "version": "0.17.0", + "version": "0.18.0", "private": true, "browserslist": { "production": [ diff --git a/packages/telemed-ehr/zambdas/package.json b/packages/telemed-ehr/zambdas/package.json index b26627fe..0c4ea8a6 100644 --- a/packages/telemed-ehr/zambdas/package.json +++ b/packages/telemed-ehr/zambdas/package.json @@ -1,6 +1,6 @@ { "name": "telemed-ehrzambdas", - "version": "0.17.0", + "version": "0.18.0", "private": true, "scripts": { "start": "npm run start:local", diff --git a/packages/telemed-intake/app/package.json b/packages/telemed-intake/app/package.json index 19ccf9d5..98ad67d9 100644 --- a/packages/telemed-intake/app/package.json +++ b/packages/telemed-intake/app/package.json @@ -1,7 +1,7 @@ { "name": "telemed-intake-app", "private": true, - "version": "0.17.0", + "version": "0.18.0", "type": "module", "scripts": { "start:local": "vite", diff --git a/packages/telemed-intake/zambdas/package.json b/packages/telemed-intake/zambdas/package.json index f0247122..06c921e6 100644 --- a/packages/telemed-intake/zambdas/package.json +++ b/packages/telemed-intake/zambdas/package.json @@ -1,6 +1,6 @@ { "name": "telemed-intake-zambdas", - "version": "0.17.0", + "version": "0.18.0", "private": true, "scripts": { "start": "npm run start:local", diff --git a/packages/utils/package.json b/packages/utils/package.json index f28e701f..8cd9cb0e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,7 +1,7 @@ { "name": "ottehr-utils", "private": true, - "version": "0.17.0", + "version": "0.18.0", "main": "lib/main.ts", "types": "lib/main.ts", "scripts": { From 02398351910d9d39a213348e4277f982c85b19f2 Mon Sep 17 00:00:00 2001 From: Gilad Schneider Date: Mon, 25 Nov 2024 11:22:11 -0500 Subject: [PATCH 22/22] reverts unnecesarry changes --- .../appointments/appointment.store.ts | 1 + .../app/src/pages/ConfirmDateOfBirth.tsx | 29 +++++++++++++- .../app/src/pages/PatientInformation.tsx | 29 +++++++++++++- .../telemed-intake/app/src/pages/Welcome.tsx | 9 ++++- .../app/src/theme/ottehr/i18n-en.json | 5 +++ .../app/src/utils/checkSlotTime.ts | 40 +++++++++++++++++++ .../telemed-intake/app/src/utils/index.ts | 1 + .../telemed-intake/zambdas/scripts/common.ts | 8 ++-- 8 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 packages/telemed-intake/app/src/utils/checkSlotTime.ts 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.'); }