Skip to content

Commit 3b40a05

Browse files
authored
feature:messaging appointment implementation (#135)
## Description Implement the functionality for making appointments using messaging chat. This includes capturing user details, selecting a doctor, scheduling the appointment, and providing confirmation. ## Checklist - [x] **Appointment Booking via Message Chat** - Implement the ability for users to book appointments using text messages. - [x] Capture user details (name, contact info). - [x] Allow selection of doctor and appointment time. - [x] Confirm booking and send confirmation message. ## Additional Notes - Test the appointment booking functionality thoroughly text message. - Follow best practices for coding standards and documentation. - Ensure data security and privacy, especially for sensitive user information. ## screenshoots of what i will be working on. <img width="226" alt="Screenshot 2024-06-26 at 17 00 11" src="https://github.com/atlp-rwanda/commanders-rn-medica/assets/86968559/a6edc780-b633-42d1-a81b-fd18eaf6c5c0"> <img width="223" alt="Screenshot 2024-06-26 at 17 00 32" src="https://github.com/atlp-rwanda/commanders-rn-medica/assets/86968559/18bff641-a043-4ab3-be95-63ffa38b336b"> ## Assignees - @Emmyfrank
2 parents 071a0ed + 9cb665e commit 3b40a05

File tree

15 files changed

+449
-231
lines changed

15 files changed

+449
-231
lines changed

app/Appointments/details.tsx

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
44
import { SvgXml } from "react-native-svg";
55
import { back } from "@/assets/icons/userprofile/icons";
66
import { NavigationHeader } from "@/components/NavigationHeader";
7-
import { router, useLocalSearchParams, useRouter } from "expo-router";
7+
import { router, useGlobalSearchParams, useLocalSearchParams, useRouter } from "expo-router";
88
import Touchable from "@/components/common/touchable";
99
import { heartFilledIcon } from "@/assets/icons/heart";
1010
import { videoIcon, videoIconWhite } from "@/assets/icons/video";
@@ -58,12 +58,15 @@ const pkgs = {
5858
};
5959

6060
const VideoCallAppointment = ({ route }: any) => {
61+
6162
const insets = useSafeAreaInsets();
6263

6364
const { typecall } = useLocalSearchParams<{
6465
typecall: "Voice call" | "Messaging" | "Video call";
6566
}>();
6667

68+
const appointment = useSelector((state: RootState) => state.appointment.selectedAppointment);
69+
6770
return (
6871
<View className={`flex-1 pt-[${insets.top}px] bg-white`}>
6972
<View className="px-6 mt-8">
@@ -89,20 +92,20 @@ const VideoCallAppointment = ({ route }: any) => {
8992
<View className="bg-white rounded-3xl p-4 mb-6" style={styles.card1}>
9093
<View className="flex-row justify-between w-full">
9194
<Image
92-
source={require("../../assets/doctors/doctor2.png")}
95+
source={{uri:appointment.doctor.image}}
9396
className="w-28 h-28"
9497
/>
9598
<View className="justify-evenly pl-1 w-[60%]">
9699
<Text className="text-[18px] font-[UrbanistBold] text-greyscale-900">
97-
Dr. Maria Foose
100+
{appointment.doctor.name}
98101
</Text>
99102
<View className="border-t border-t-[#EEEEEE] w-full" />
100103
<Text className="font-[UrbanistMedium] text-xs text-greyscale-800">
101-
Dermatologists
104+
{appointment.doctor.role}
102105
</Text>
103-
<Text className="font-[UrbanistMedium] text-xs text-greyscale-800">
106+
{/* <Text className="font-[UrbanistMedium] text-xs text-greyscale-800">
104107
The Venus Hospital in Paris, France
105-
</Text>
108+
</Text> */}
106109
</View>
107110
</View>
108111
</View>
@@ -112,23 +115,23 @@ const VideoCallAppointment = ({ route }: any) => {
112115
Scheduled Appointment
113116
</Text>
114117
<Text className="text-[16px] font-UrbanistRegular text-greyscale-800 mb-3">
115-
Today, December 22, 2022
118+
{appointment.appointment_date}
116119
</Text>
117120
<Text className="text-[16px] font-UrbanistRegular text-greyscale-800 mb-3">
118-
10:00 - 10:30 AM (30 minutes)
121+
{appointment.appointment_time}
119122
</Text>
120123
</View>
121124
<View className="mb-5 mx-6">
122125
<Text className="text-[20px] font-UrbanistBold text-greyscale-900 mb-4">
123126
Patient Information
124127
</Text>
125-
<Detail title={"Full Name"} text={"Andrew Ainsley"} />
126-
<Detail title={"Gender"} text={"Male"} />
127-
<Detail title={"Age"} text={"27"} />
128+
<Detail title={"Full Name"} text={appointment.patient.full_name} />
129+
<Detail title={"Gender"} text={appointment.patient.gender} />
130+
<Detail title={"Age"} text={appointment.patient.date_of_birth} />
128131
<Detail
129-
title={"Problem"}
132+
title={""}
130133
text={
131-
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor. view more"
134+
appointment.Reason_couse_toUpdated
132135
}
133136
/>
134137
</View>

app/Appointments/doctorcard/cardss.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import { getAppointment } from "@/redux/reducers/appointment";
12
import { router } from "expo-router";
23
import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native";
4+
import { useDispatch } from "react-redux";
35
function Cardscomponent(props: any) {
6+
const appointment = props.appointment;
7+
8+
const dispatch = useDispatch();
49
const handlePush = () => {
10+
dispatch(getAppointment(appointment))
511
if (props.action === "Upcoming") {
612
router.push({
713
pathname: "/Appointments/details/",
8-
params: { typecall: props.typecall },
14+
params: { typecall: props.typecall, appointment },
915
});
1016
}
1117
};
@@ -17,7 +23,7 @@ function Cardscomponent(props: any) {
1723
className="bg-white rounded-3xl px-4 py-4 w-full shadow-md mb-4"
1824
>
1925
<View className="flex-row justify-center items-center border-b-2 border-slate-100 pb-3">
20-
<Image source={{uri:props.imager}} className="w-24 h-24 rounded-xl mr-3" />
26+
<Image source={{uri:appointment.doctor.image}} className="w-24 h-24 rounded-xl mr-3" />
2127
<View className="flex-1">
2228
<Text className="text-xl font-UrbanistBold mb-3">{props.name}</Text>
2329
<View className="flex items-center flex-row mb-3">

app/Appointments/index.tsx

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ import {
1313
import { supabase } from "../supabase";
1414
import Cardcomponent from "./doctorcard/cards";
1515
import Cardscomponent from "./doctorcard/cardss";
16+
import { useDispatch, useSelector } from "react-redux";
17+
import { RootState } from "@/redux/store/store";
18+
import { getAppointments } from "@/redux/reducers/appointment";
1619

1720
function Screen() {
1821
const [upcoming, setUpcoming] = useState(true);
1922
const [cancels, setCancels] = useState(false);
2023
const [complete, setComlpete] = useState(false);
2124
const [notupcome, setNotupcome] = useState(false);
2225
const [isModalVisible, setIsModalVisible] = useState(false);
23-
const [appointmentData, setAppointmentData] = useState<any[]>([]);
2426
const { doctorId } = useLocalSearchParams<{ doctorId: string }>();
25-
const [appointId, setAppointId] = useState({});
27+
const appointments = useSelector(
28+
(state: RootState) => state.appointment.appointments
29+
);
2630
const [canceledData, setcanceledData] = useState<any[]>([]);
27-
31+
const [appointId, setAppointId] = useState({});
32+
const dispatch = useDispatch();
2833
const handleUpcoming = () => {
2934
setCancels(false);
3035
setUpcoming(true);
@@ -55,27 +60,39 @@ function Screen() {
5560

5661
const fetchAppointment = async () => {
5762
try {
58-
const { data: userData, error: userError } = await supabase.auth.getUser();
63+
const { data: userData, error: userError } =
64+
await supabase.auth.getUser();
5965
if (userError) throw userError;
6066
const userId = userData?.user?.id;
6167
const { data, error } = await supabase
6268
.from("appointment")
63-
.select(`
69+
.select(
70+
`
6471
*,
6572
doctor(
6673
id,
6774
name,
6875
role,
6976
image,
7077
hospital
71-
)
72-
`)
78+
),
79+
patient(
80+
id,
81+
email,
82+
phone,
83+
full_name,
84+
nickname,
85+
date_of_birth,
86+
gender
87+
)
88+
`
89+
)
7390
.eq("patient_id", userId);
7491

7592
if (error) {
7693
console.log("Error occurred while fetching appointments", error);
7794
} else {
78-
setAppointmentData(data);
95+
dispatch(getAppointments(data));
7996
if (data.length === 0) {
8097
setNotupcome(true);
8198
setUpcoming(false);
@@ -92,10 +109,10 @@ function Screen() {
92109
fetchAppointment();
93110
}, []);
94111

95-
96112
const cancelAppointment = async () => {
97113
try {
98-
const { data: userData, error: userError } = await supabase.auth.getUser();
114+
const { data: userData, error: userError } =
115+
await supabase.auth.getUser();
99116
if (userError) throw userError;
100117
const userId = userData?.user?.id;
101118
const { data, error } = await supabase
@@ -117,14 +134,13 @@ function Screen() {
117134
cancelAppointment();
118135
}, []);
119136

120-
121137
const getPackageIcon = (typecall: any) => {
122138
switch (typecall) {
123-
case 'Voice Call':
139+
case "Voice Call":
124140
return require("../../assets/appointmentIcon/voice.png");
125-
case 'Video Call':
141+
case "Video Call":
126142
return require("../../assets/appointmentIcon/video.png");
127-
case 'Messaging':
143+
case "Messaging":
128144
return require("../../assets/appointmentIcon/message.png");
129145
default:
130146
return null;
@@ -136,14 +152,18 @@ function Screen() {
136152
appointmentId: appointment.id,
137153
appointment_date: appointment.appointment_date,
138154
appointment_time: appointment.appointment_time,
139-
doctorId:appointment.doctor?.id,
140-
doctorname:appointment.doctor.name,
141-
doctimage:appointment.doctor.image,
142-
typecall:appointment.package
155+
doctorId: appointment.doctor?.id,
156+
doctorname: appointment.doctor.name,
157+
doctimage: appointment.doctor.image,
158+
typecall: appointment.package,
143159
});
144160
setIsModalVisible(true);
145161
};
146162

163+
useEffect(() => {
164+
fetchAppointment();
165+
}, []);
166+
147167
return (
148168
<>
149169
<View className="pb-6">
@@ -226,26 +246,27 @@ function Screen() {
226246
>
227247
{cancels && (
228248
<View style={styles.content}>
229-
{canceledData.map((canceled,index)=>(
230-
<Cardcomponent
231-
key={index}
232-
name={canceled.doctorname}
233-
imager={canceled.doctimage}
234-
typecall={canceled.package}
235-
action="Cancelled"
236-
date={canceled.appointment_date}
237-
time={canceled.appointment_time.slice(0, 5)}
238-
imagerr={getPackageIcon(canceled.package)}
239-
styles={styles.cancelStyles}
240-
/>
241-
))}
249+
{canceledData.map((canceled, index) => (
250+
<Cardcomponent
251+
key={index}
252+
name={canceled.doctorname}
253+
imager={canceled.doctimage}
254+
typecall={canceled.package}
255+
action="Cancelled"
256+
date={canceled.appointment_date}
257+
time={canceled.appointment_time.slice(0, 5)}
258+
imagerr={getPackageIcon(canceled.package)}
259+
styles={styles.cancelStyles}
260+
/>
261+
))}
242262
</View>
243263
)}
244264
{upcoming && (
245265
<View style={styles.content}>
246-
{appointmentData.map((appointment: any, index: any) => (
266+
{appointments.map((appointment: any, index: any) => (
247267
<Cardscomponent
248268
key={index}
269+
appointment={appointment}
249270
name={appointment.doctor.name}
250271
imager={appointment.doctor.image}
251272
typecall={appointment.package}
@@ -262,8 +283,8 @@ function Screen() {
262283
pathname: "/Appointments/reschedul",
263284
params: {
264285
appointmentId: appointment.id,
265-
appointmentdate:appointment.appointment_date,
266-
appointmentime:appointment.appointment_time,
286+
appointmentdate: appointment.appointment_date,
287+
appointmentime: appointment.appointment_time,
267288
},
268289
})
269290
}
@@ -275,7 +296,9 @@ function Screen() {
275296

276297
{notupcome && (
277298
<View style={styles.centerbar}>
278-
<Image source={require("../../assets/appointmentIcon/first.png")} />
299+
<Image
300+
source={require("../../assets/appointmentIcon/first.png")}
301+
/>
279302
<Text style={styles.centerbartitle}>
280303
You don't have an appointment yet
281304
</Text>
@@ -341,15 +364,14 @@ const styles = StyleSheet.create({
341364
flexGrow: 1,
342365
paddingBottom: 50,
343366
paddingTop: 20,
344-
height:"80%"
367+
height: "80%",
345368
},
346369
centerbar: {
347370
display: "flex",
348371
justifyContent: "center",
349372
alignItems: "center",
350373
height: "76%",
351374
gap: 20,
352-
353375
},
354376
centerbartitle: {
355377
fontSize: 20,
@@ -442,4 +464,4 @@ const styles = StyleSheet.create({
442464
mode: {
443465
color: "#f75555",
444466
},
445-
});
467+
});

0 commit comments

Comments
 (0)