From 7ba7aacd20c935957b9e1a6872be4480ca8852ac Mon Sep 17 00:00:00 2001 From: Arkadiusz Bachorski <60391032+arkadiuszbachorski@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:45:36 +0200 Subject: [PATCH 1/4] Add extension to FHIRAppointment --- modules/firebase/models/medication.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/firebase/models/medication.ts b/modules/firebase/models/medication.ts index 33e5f864..a3baafda 100644 --- a/modules/firebase/models/medication.ts +++ b/modules/firebase/models/medication.ts @@ -5,8 +5,6 @@ // // SPDX-License-Identifier: MIT // - -import { startCase } from 'es-toolkit' import { type FHIRSimpleQuantity, type FHIRCodeableConcept, @@ -15,6 +13,7 @@ import { type FHIRReference, type FHIRPeriod, type FHIRResource, + type FHIRExtension, } from './baseTypes.js' export interface FHIRMedication extends FHIRElement { @@ -125,10 +124,8 @@ export enum FHIRAppointmentStatus { waitlist = 'waitlist', } -export const stringifyAppointmentStatus = (status: FHIRAppointmentStatus) => - startCase(status) - export interface FHIRAppointment { + extension?: FHIRExtension[] status: FHIRAppointmentStatus created: string start: string From 9036c43fbd0894377092ccfe30184240f389b912 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bachorski <60391032+arkadiuszbachorski@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:45:51 +0200 Subject: [PATCH 2/4] Make FHIRReference field nullable --- modules/firebase/models/baseTypes.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/firebase/models/baseTypes.ts b/modules/firebase/models/baseTypes.ts index fadd7bec..697bd81f 100644 --- a/modules/firebase/models/baseTypes.ts +++ b/modules/firebase/models/baseTypes.ts @@ -53,10 +53,10 @@ export interface FHIRRatio { // // eslint-disable-next-line @typescript-eslint/no-unused-vars export interface FHIRReference { - reference?: string - type?: string - identifier?: string - display?: string + reference?: string | null + type?: string | null + identifier?: string | null + display?: string | null } export interface FHIRSimpleQuantity { From 7b248a2dfa8bc5e2b8accda43461e4d04861e281 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bachorski <60391032+arkadiuszbachorski@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:45:58 +0200 Subject: [PATCH 3/4] Add ExtensionURL --- modules/firebase/models/baseTypes.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/firebase/models/baseTypes.ts b/modules/firebase/models/baseTypes.ts index 697bd81f..97ea2519 100644 --- a/modules/firebase/models/baseTypes.ts +++ b/modules/firebase/models/baseTypes.ts @@ -31,6 +31,11 @@ export interface FHIRExtension { valueQuantities?: FHIRSimpleQuantity[] valueReference?: FHIRReference valueMedicationRequest?: FHIRMedicationRequest + valueString?: string +} + +export enum ExtensionURL { + providerName = 'http://engagehf.bdh.stanford.edu/fhir/StructureDefinition/Appointment/extension/providerName', } export interface FHIRPeriod { From 7c2a4c675e6d59a1938a35619f8730ac1dd48175 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bachorski <60391032+arkadiuszbachorski@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:46:18 +0200 Subject: [PATCH 4/4] Update appointments according to the meeting notes --- .../patients/[id]/AppointmentForm.tsx | 52 +++---------------- .../patients/[id]/Appointments.tsx | 11 ++-- app/(dashboard)/patients/actions.tsx | 28 ++++++++-- app/(dashboard)/patients/utils.ts | 9 +++- 4 files changed, 40 insertions(+), 60 deletions(-) diff --git a/app/(dashboard)/patients/[id]/AppointmentForm.tsx b/app/(dashboard)/patients/[id]/AppointmentForm.tsx index 13bcd157..01aeb1fd 100644 --- a/app/(dashboard)/patients/[id]/AppointmentForm.tsx +++ b/app/(dashboard)/patients/[id]/AppointmentForm.tsx @@ -9,10 +9,6 @@ import { type ComponentProps } from 'react' import { z } from 'zod' import { type Appointment } from '@/app/(dashboard)/patients/utils' -import { - FHIRAppointmentStatus, - stringifyAppointmentStatus, -} from '@/modules/firebase/models/medication' import { Button } from '@/packages/design-system/src/components/Button' import { DatePicker } from '@/packages/design-system/src/components/DatePicker' import { @@ -21,23 +17,16 @@ import { DialogHeader, DialogTitle, } from '@/packages/design-system/src/components/Dialog' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/packages/design-system/src/components/Select' +import { Input } from '@/packages/design-system/src/components/Input' import { Textarea } from '@/packages/design-system/src/components/Textarea' import { Field } from '@/packages/design-system/src/forms/Field' import { useForm } from '@/packages/design-system/src/forms/useForm' export const appointmentFormSchema = z.object({ - status: z.nativeEnum(FHIRAppointmentStatus), start: z.date(), - end: z.date(), comment: z.string().nullable(), patientInstruction: z.string().nullable(), + providerName: z.string(), }) export type AppointmentFormSchema = z.infer @@ -55,11 +44,10 @@ export const AppointmentForm = ({ const form = useForm({ formSchema: appointmentFormSchema, defaultValues: { - status: appointment?.status, start: appointment ? new Date(appointment.start) : undefined, - end: appointment ? new Date(appointment.end) : undefined, comment: appointment?.comment ?? null, patientInstruction: appointment?.patientInstruction ?? null, + providerName: appointment?.providerName ?? '', }, }) @@ -69,25 +57,6 @@ export const AppointmentForm = ({ return (
- ( - - )} - /> ( - field.onChange(date)} - defaultMonth={field.value} - fromDate={new Date()} - showTimePicker - /> - )} + name="providerName" + label="Provider" + render={({ field }) => } /> stringifyAppointmentStatus(props.getValue()), + columnHelper.accessor('providerName', { + header: 'Provider name', + cell: (props) => props.getValue(), }), columnHelper.accessor('start', { header: 'Start', cell: dateTimeColumn, }), - columnHelper.accessor('end', { - header: 'End', - cell: dateTimeColumn, - }), columnHelper.display({ id: 'actions', cell: (props) => ( diff --git a/app/(dashboard)/patients/actions.tsx b/app/(dashboard)/patients/actions.tsx index 8a6b5d73..f749c589 100644 --- a/app/(dashboard)/patients/actions.tsx +++ b/app/(dashboard)/patients/actions.tsx @@ -7,6 +7,7 @@ // 'use server' import { addDoc, deleteDoc, setDoc } from '@firebase/firestore' +import { addHours } from 'date-fns' import { revalidatePath } from 'next/cache' import { type AllergyFormSchema } from '@/app/(dashboard)/patients/[id]/AllergyForm' import { type AppointmentFormSchema } from '@/app/(dashboard)/patients/[id]/AppointmentForm' @@ -14,9 +15,11 @@ import { type LabFormSchema } from '@/app/(dashboard)/patients/[id]/LabForm' import { getUnitOfObservationType } from '@/app/(dashboard)/patients/clientUtils' import { getAuthenticatedOnlyApp } from '@/modules/firebase/guards' import { AllergyType } from '@/modules/firebase/models/allergy' +import { ExtensionURL } from '@/modules/firebase/models/baseTypes' import { FHIRAllergyIntoleranceCriticality, FHIRAllergyIntoleranceType, + FHIRAppointmentStatus, FHIRObservationStatus, } from '@/modules/firebase/models/medication' import { @@ -193,13 +196,28 @@ export const updateAllergy = async ( return 'success' } -const getAppointmentData = (payload: AppointmentFormSchema) => ({ - status: payload.status, +const getAppointmentData = ( + payload: AppointmentFormSchema & { userId: string }, +) => ({ + status: FHIRAppointmentStatus.booked, start: payload.start.toISOString(), - end: payload.end.toISOString(), + end: addHours(payload.start, 1).toISOString(), comment: payload.comment, patientInstruction: payload.patientInstruction, - participant: [], + extension: [ + { url: ExtensionURL.providerName, valueString: payload.providerName }, + ], + participant: [ + { + actor: { + display: null, + type: null, + identifier: null, + reference: `users/${payload.userId}`, + }, + type: null, + }, + ], }) export const createAppointment = async ( @@ -238,7 +256,7 @@ export const updateAppointment = async ( appointmentId: payload.appointmentId, }), getAppointmentData(payload), - { mergeFields: ['created'] }, + { merge: true }, ) revalidatePath(routes.patients.patient(payload.userId)) return 'success' diff --git a/app/(dashboard)/patients/utils.ts b/app/(dashboard)/patients/utils.ts index c8ef1530..a85f3f13 100644 --- a/app/(dashboard)/patients/utils.ts +++ b/app/(dashboard)/patients/utils.ts @@ -9,6 +9,7 @@ import { groupBy } from 'es-toolkit' import { query, where } from 'firebase/firestore' import { getAuthenticatedOnlyApp } from '@/modules/firebase/guards' import { AllergyType } from '@/modules/firebase/models/allergy' +import { ExtensionURL } from '@/modules/firebase/models/baseTypes' import { type FHIRAllergyIntolerance, FHIRAllergyIntoleranceCriticality, @@ -205,9 +206,15 @@ export const getAppointmentsData = async ({ resourceType: ResourceType }) => { const { refs } = await getAuthenticatedOnlyApp() - const appointments = await getDocsData( + const rawAppointments = await getDocsData( refs.appointments({ userId, resourceType }), ) + const appointments = rawAppointments.map((appointment) => ({ + ...appointment, + providerName: appointment.extension?.find( + (extension) => extension.url === (ExtensionURL.providerName as string), + )?.valueString, + })) return { appointments, userId, resourceType } }