Skip to content

Commit

Permalink
Mind 133 tela de pacientes pro terapeuta (#42)
Browse files Browse the repository at this point in the history
* add patientProfile method

* add Data component

* add 4xl BasicText

* export RecurrenceCard

* add patients PsychLayout

* add PatientsScreen

* add PatientProfileScreen

* add patients and move finances to ProfessionalOptions

* add patient link in professionals home page

* add NoteCard

* refactor LastNotes to use NoteCard

* add (psych)/patients routes to PsychLayout

* refactor PatientProfileScreen

* remove patients from /psych layout
  • Loading branch information
abdulhdr1 authored Feb 27, 2024
1 parent 218d925 commit f7713a2
Show file tree
Hide file tree
Showing 10 changed files with 625 additions and 63 deletions.
2 changes: 2 additions & 0 deletions apps/expo/src/app/(app)/(psych)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export default function PsychLayout() {
}}
/>
<Stack.Screen name="payments-setup" />
<Stack.Screen name="patients/index" />
<Stack.Screen name="patients/[patientId]" />
</Stack>
);
}
281 changes: 281 additions & 0 deletions apps/expo/src/app/(app)/(psych)/patients/[patientId].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
import { useState } from "react";
import {
LayoutAnimation,
RefreshControl,
ScrollView,
View,
} from "react-native";
import { useLocalSearchParams, useRouter } from "expo-router";
import { Trans, t } from "@lingui/macro";
import { FlashList } from "@shopify/flash-list";
import { format } from "date-fns";

import {
type Appointment,
type Note,
type Patient,
type Recurrence,
type Therapist,
} from "../../../../../../../packages/db";
import { BasicText } from "../../../../components/BasicText";
import { Data } from "../../../../components/Data";
import { FullScreenLoading } from "../../../../components/FullScreenLoading";
import { Header } from "../../../../components/Header";
import { NoteCard } from "../../../../components/NoteCard";
import { Refreshable } from "../../../../components/Refreshable";
import { ScreenWrapper } from "../../../../components/ScreenWrapper";
import { SmallButton } from "../../../../components/SmallButton";
import { TherapistAppointmentCard } from "../../../../components/TherapistAppointmentCard";
import { UserPhoto } from "../../../../components/UserPhotos";
import { api } from "../../../../utils/api";
import { RecurrenceCard } from "../../settings/recurrences";

export default function PatientProfileScreen() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);

const [refreshing, setRefreshing] = useState(false);
const params = useLocalSearchParams();
const patientProfile = api.therapists.patientProfile.useQuery({
patientId: String(params.patientId),
});

if (patientProfile.isLoading) return <FullScreenLoading />;

if (patientProfile.error || !patientProfile.data)
return (
<View
style={{
marginTop: 24,
marginHorizontal: 12,
}}
>
<BasicText color="red">
{t({
message: "Error fetching patient profile",
})}
</BasicText>
</View>
);

const onRefresh = async () => {
setRefreshing(true);
try {
await patientProfile.refetch();
} finally {
setRefreshing(false);
}
};

return (
<ScreenWrapper>
<Refreshable
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
>
<Header />
<ScrollView>
<View
style={{
flexDirection: "row",
alignItems: "center",
gap: 12,
}}
>
<UserPhoto
userId={patientProfile.data?.userId}
url={patientProfile.data?.profilePictureUrl}
alt={`${patientProfile.data?.name} profile picture`}
width={72}
height={72}
/>
<View
style={{
flex: 1,
}}
>
<BasicText size="2xl" fontWeight="bold">
{patientProfile.data?.name}
</BasicText>

<View
style={{
alignItems: "flex-start",
gap: 4,
flex: 1,
}}
>
<BasicText
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
flex: 1,
}}
>
<Trans>First appointment:</Trans>{" "}
<BasicText color="black">
{format(
patientProfile.data
?.firstAppointment,
"dd/MM/yyyy",
)}
</BasicText>
</BasicText>

<BasicText>
<Trans>Total value:</Trans>{" "}
<BasicText
color="primaryBlue"
fontWeight="bold"
>
R$ {patientProfile.data?.totalValue}
</BasicText>
</BasicText>
</View>
</View>
</View>
<View
style={{
marginTop: 16,
}}
>
<AppointmentsList
appointments={patientProfile.data.appointments}
unpaidAppointments={
patientProfile.data.unpaidAppointments
}
/>

<RecurrenceList
recurrences={patientProfile.data.Recurrence}
/>

<NotesList
patient={patientProfile.data}
notes={patientProfile.data.Note}
/>
</View>
</ScrollView>
</Refreshable>
</ScreenWrapper>
);
}

function SectionHeader({ title }: { title: string | React.ReactNode }) {
return typeof title === "string" ? (
<BasicText size="lg" fontWeight="bold">
{title}
</BasicText>
) : (
title
);
}

function AppointmentsList({
appointments,
unpaidAppointments,
}: {
appointments: (Appointment & { therapist: Therapist } & {
patient: Patient;
})[];
unpaidAppointments: number;
}) {
return (
<FlashList
ListHeaderComponent={() => (
<>
<SectionHeader title={t({ message: "Appointments" })} />
<View
style={{
display: "flex",
flexDirection: "row",
gap: 12,
}}
>
<Data
value={appointments.length}
label={t({ message: "Total" })}
/>

<Data
color="red"
value={unpaidAppointments}
label={t({ message: "Unpaid" })}
/>
</View>
</>
)}
data={appointments.slice(0, 3)}
renderItem={({ item }) => (
<TherapistAppointmentCard key={item.id} appointment={item} />
)}
estimatedItemSize={160}
keyExtractor={(item) => item.id}
/>
);
}

function RecurrenceList({
recurrences,
}: {
recurrences: (Recurrence & {
therapist: Therapist;
patient: Patient;
})[];
}) {
return (
<FlashList
ListHeaderComponent={() => (
<SectionHeader title={t({ message: "Recurrences" })} />
)}
data={recurrences.slice(0, 3)}
renderItem={({ item }) => <RecurrenceCard recurrence={item} />}
estimatedItemSize={200}
keyExtractor={(item) => item.id}
/>
);
}

function NotesList({ notes, patient }: { notes: Note[]; patient: Patient }) {
const router = useRouter();
return (
<FlashList
ListHeaderComponent={() => (
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
flex: 1,
}}
>
<BasicText size="lg" fontWeight="bold">
<Trans>Notes</Trans>
</BasicText>
<SmallButton
onPress={() =>
router.push({
pathname: "/notes/new",
params: {
patientId: patient.id,
patientUserId: patient.userId,
},
})
}
textSize="lg"
>
<Trans>New</Trans>
</SmallButton>
</View>
)}
estimatedItemSize={200}
data={notes}
renderItem={({ item }) => <NoteCard note={item} />}
keyExtractor={(item) => item.id}
/>
);
}
Loading

0 comments on commit f7713a2

Please sign in to comment.