Skip to content

Commit 480a2ad

Browse files
authored
Feature write your review implemetation (#140)
**How does this pr do?** In this practice, the patient should be able to see their appointment be completed for some reason, like when the appointment passes or when the time of the appointment is up. and they should be able to write a review for the doctor. **How can you review this pr?** Visit your appointment After logging in to the appointment tab, you can see an upcoming, completed and canceled appointment Choose the tab called completed and see your appointment as completed. According to your choice, you can go to book appointment again by clicking on the book button again and to write a review by clicking on button called leave review
2 parents 1780d55 + 95e62fe commit 480a2ad

File tree

9 files changed

+377
-83
lines changed

9 files changed

+377
-83
lines changed

app/(tabs)/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useDispatch, useSelector } from "react-redux";
1515
import { MenuIcons } from "../../assets/icons";
1616
import { heart } from "../../assets/icons/heart";
1717
import { notification } from "../../assets/icons/notification";
18-
import DoctorCard from "../../components/cards/doctorCard";
18+
import DoctorCard from "../../components/cards/doctCard";
1919
import CarouselComponent from "../../components/carousel";
2020
import { SearchInput } from "../../components/searchinput2";
2121
import { supabase } from "../supabase";

app/Appointments/doctorcard/cardss.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function Cardscomponent(props: any) {
2323
className="bg-white rounded-3xl px-4 py-4 w-full shadow-md mb-4"
2424
>
2525
<View className="flex-row justify-center items-center border-b-2 border-slate-100 pb-3">
26-
<Image source={{uri:appointment.doctor.image}} className="w-24 h-24 rounded-xl mr-3" />
26+
<Image source={{uri:props.imager}} className="w-24 h-24 rounded-xl mr-3" />
2727
<View className="flex-1">
2828
<Text className="text-xl font-UrbanistBold mb-3">{props.name}</Text>
2929
<View className="flex items-center flex-row mb-3">

app/Appointments/doctorcard/star.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
2-
import React, { useState } from 'react';
1+
import React from 'react';
32
import { View, TouchableOpacity, StyleSheet } from 'react-native';
43
import Icon from 'react-native-vector-icons/FontAwesome';
54

@@ -16,20 +15,19 @@ const StarCheckbox: React.FC<StarCheckboxProps> = ({ checked, onPress }) => {
1615
);
1716
};
1817

19-
const FiveStarRating: React.FC = () => {
20-
const [selectedStars, setSelectedStars] = useState(0);
21-
22-
const handleStarPress = (index: number) => {
23-
setSelectedStars(index + 1);
24-
};
18+
interface FiveStarRatingProps {
19+
selectedStars: number;
20+
onStarPress: (index: number) => void;
21+
}
2522

23+
const FiveStarRating: React.FC<FiveStarRatingProps> = ({ selectedStars, onStarPress }) => {
2624
return (
2725
<View style={styles.container}>
2826
{[0, 1, 2, 3, 4].map((index) => (
2927
<StarCheckbox
3028
key={index}
3129
checked={index < selectedStars}
32-
onPress={() => handleStarPress(index)}
30+
onPress={() => onStarPress(index)}
3331
/>
3432
))}
3533
</View>
@@ -48,4 +46,3 @@ const styles = StyleSheet.create({
4846
});
4947

5048
export default FiveStarRating;
51-

app/Appointments/index.tsx

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ function Screen() {
2929
);
3030
const [canceledData, setcanceledData] = useState<any[]>([]);
3131
const [appointId, setAppointId] = useState({});
32+
const [completAppointment, setCompletedAppointment] = useState<any[]>([]);
33+
3234
const dispatch = useDispatch();
3335
const handleUpcoming = () => {
3436
setCancels(false);
@@ -87,11 +89,14 @@ function Screen() {
8789
)
8890
`
8991
)
90-
.eq("patient_id", userId);
92+
.eq("patient_id", userId)
93+
.eq("status", "Booked").order("appointment_date", { ascending: true })
94+
.order("appointment_time", { ascending: true })
9195

9296
if (error) {
9397
console.log("Error occurred while fetching appointments", error);
9498
} else {
99+
console.log(data)
95100
dispatch(getAppointments(data));
96101
if (data.length === 0) {
97102
setNotupcome(true);
@@ -109,6 +114,9 @@ function Screen() {
109114
fetchAppointment();
110115
}, []);
111116

117+
118+
119+
112120
const cancelAppointment = async () => {
113121
try {
114122
const { data: userData, error: userError } =
@@ -134,6 +142,43 @@ function Screen() {
134142
cancelAppointment();
135143
}, []);
136144

145+
const fetchCompletedAppointment=async()=>{
146+
try{
147+
const { data: userData, error: userError } = await supabase.auth.getUser();
148+
if (userError) throw userError;
149+
const userId = userData?.user?.id;
150+
const{data, error}=await supabase
151+
.from("appointment")
152+
.select(`
153+
*,
154+
doctor(
155+
id,
156+
name,
157+
role,
158+
image,
159+
hospital
160+
)
161+
`)
162+
.eq("patient_id", userId)
163+
.eq("status", "Completed").order("appointment_date", { ascending: false })
164+
.order("appointment_time", { ascending: false })
165+
166+
if(error){
167+
console.log("Error occured while fetching appointments", error)
168+
}else{
169+
console.log(data)
170+
setCompletedAppointment(data);
171+
172+
}}
173+
catch(error){
174+
console.log(error);
175+
}
176+
}
177+
useEffect(()=>{
178+
179+
fetchCompletedAppointment();
180+
},[])
181+
137182
const getPackageIcon = (typecall: any) => {
138183
switch (typecall) {
139184
case "Voice Call":
@@ -261,6 +306,38 @@ function Screen() {
261306
))}
262307
</View>
263308
)}
309+
310+
{complete && (
311+
<View style={styles.content}>
312+
{completAppointment.map((appointment:any, index:any) => (
313+
<Cardscomponent
314+
key={index}
315+
name={appointment.doctor.name}
316+
imager={appointment.doctor.image}
317+
typecall={appointment.package}
318+
action="Complete"
319+
date={appointment.appointment_date}
320+
time={appointment.appointment_time.slice(0,5)}
321+
imagerr={getPackageIcon(appointment.package)}
322+
styles={styles.completeStyles}
323+
chance="Book Again"
324+
cantchance="Leave a Review"
325+
backcad="bg-white rounded-xl flex-row p-4 w-400 items-center gap-7"
326+
fact={() => router.push({pathname:"/Appointments/voice-call/writeReview",
327+
params:{
328+
appointmentId:appointment.doctor.id,
329+
doctorImage:appointment.doctor.image,
330+
role:appointment.doctor.role,
331+
hospital:appointment.doctor.hospital,
332+
name:appointment.doctor.name
333+
}})}
334+
cancle={()=>router.push({pathname:"/doctor-appointments/book-appointment",
335+
params:{doctorId:appointment.doctor.id, }})}
336+
/>
337+
))}
338+
</View>
339+
)}
340+
264341
{upcoming && (
265342
<View style={styles.content}>
266343
{appointments.map((appointment: any, index: any) => (

app/Appointments/voice-call/writeReview.tsx

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React, { useState, useEffect } from "react";
22
import { View, Text, TouchableOpacity, Image, TextInput, StyleSheet, Modal, ScrollView, SafeAreaView, Pressable } from "react-native";
3-
import { router } from "expo-router";
3+
import { router, useGlobalSearchParams } from "expo-router";
44
import { useFonts } from 'expo-font';
55
import FiveStarRating from "../doctorcard/star";
6+
import { supabase } from "../../supabase";
67

78
interface CustomCheckBoxProps {
89
selected: boolean;
@@ -21,25 +22,107 @@ export default function Writereview() {
2122
const [isModalVisible, setIsModalVisible] = useState(false);
2223
const [isoption, setIsoption] = useState("");
2324
const [text, setText] = useState('');
25+
const [selectedStars, setSelectedStars] = useState(0);
26+
const [patientImageUrl, setPatientImageUrl] = useState("");
2427
const [fontLoaded] = useFonts({
2528
'UrbanistBold': require('../../../assets/fonts/Urbanist-Bold.ttf'),
2629
'UrbanistRegular': require("../../../assets/fonts/Urbanist-Regular.ttf"),
2730
'Urbanist-SemiBold': require("../../../assets/fonts/Urbanist-SemiBold.ttf"),
2831
'UrbanistMedium': require("../../../assets/fonts/Urbanist-Medium.ttf")
2932
});
3033

34+
35+
useEffect(() => {
36+
const fetchUserProfile = async () => {
37+
const { data, error } = await supabase.auth.getUser();
38+
if (error) {
39+
console.log(error);
40+
return;
41+
}
42+
const userId = data.user?.id;
43+
44+
const { data: patientData, error: errordata } = await supabase
45+
.from("patient")
46+
.select("profile_picture,full_name")
47+
.eq("id", userId);
48+
49+
if (errordata) {
50+
console.log(errordata);
51+
} else {
52+
const patient = patientData?.[0];
53+
if (patient?.profile_picture) {
54+
const { data: publicUrlData, error: publicUrlError } = await supabase
55+
.storage
56+
.from("files")
57+
.createSignedUrl(`${patient.profile_picture}`, 63072000);
58+
59+
if (publicUrlError) {
60+
console.error("Error fetching signed URL:", publicUrlError.message);
61+
return;
62+
}
63+
64+
setPatientImageUrl(publicUrlData.signedUrl || "");
65+
}
66+
}
67+
};
68+
fetchUserProfile();
69+
}, []);
70+
3171
const handleChangeText = (value: string) => {
3272
setText(value);
3373
};
3474

75+
const handleStarPress = (index: number) => {
76+
setSelectedStars(index + 1);
77+
};
3578
const isSubmitEnabled = text.trim().length > 0 && isoption.length > 0;
3679

80+
const Options = ["Yes", "No"];
81+
const { appointmentId, doctorImage, role, hospital, name } = useGlobalSearchParams<{ appointmentId: string, doctorImage: string, role: string, hospital: string, name: string }>();
82+
83+
const writeReview = async () => {
84+
const { data, error } = await supabase.auth.getUser();
85+
if (error) {
86+
console.log(error);
87+
return;
88+
}
89+
const userId = data.user?.id;
90+
91+
const { data: patientData, error: errordata } = await supabase
92+
.from("patient")
93+
.select("profile_picture,full_name")
94+
.eq("id", userId);
95+
96+
if (errordata) {
97+
console.log(errordata);
98+
} else {
99+
const patient = patientData?.[0];
100+
101+
const { data: reviewData, error: reviewDataError } = await supabase
102+
.from("reviews")
103+
.insert({
104+
doctorId: appointmentId,
105+
stars: selectedStars,
106+
content: text,
107+
image: patientImageUrl,
108+
role: role,
109+
hospital: hospital,
110+
name: patient?.full_name,
111+
liked: "false",
112+
})
113+
114+
if (reviewDataError) {
115+
console.log(reviewDataError);
116+
} else {
117+
console.log(reviewData);
118+
setIsModalVisible(true)
119+
}
120+
}
121+
};
122+
37123
if (!fontLoaded) {
38124
return null;
39125
}
40-
41-
const Options = ["Yes", "No"];
42-
43126
return (
44127
<>
45128
<Modal
@@ -52,7 +135,7 @@ export default function Writereview() {
52135
<Image source={require("../../../assets/appointmentIcon/review.png")} />
53136
<Text className="text-[#246bfd] font-UrbanistBold text-[20px]">Review successful!</Text>
54137
<Text className="font-UrbanistRegular text-center w-[300px] text-[16px]">Your review has been successfully submitted. thank you very much!</Text>
55-
<TouchableOpacity onPress={() => { setIsModalVisible(false) }}
138+
<TouchableOpacity onPress={() => router.push("/(tabs)/")}
56139
className='w-[276px] bg-blue-600 rounded-[100px] h-[58px] justify-center mt-5'
57140
>
58141
<Text className='text-white text-center font-UrbanistBold'>OK</Text>
@@ -62,7 +145,6 @@ export default function Writereview() {
62145
</Modal>
63146
<ScrollView contentContainerStyle={styles.scrollViewContent}>
64147
<View style={styles.container}>
65-
66148
<View className="w-[370px]">
67149
<View className="w-[370px] flex flex-row items-center pb-10 pt-5">
68150
<TouchableOpacity onPress={() => router.back()}>
@@ -71,14 +153,14 @@ export default function Writereview() {
71153
<Text className="text-[#212121] font-UrbanistBold text-[24px] pl-5">Write a Review</Text>
72154
</View>
73155
<View className="w-[370px] flex justify-center items-center gap-5 mb-2">
74-
<Image source={require("../../../assets/doctors/Ellipse.png")} />
156+
<Image source={{ uri: doctorImage }} style={{ width: 100, height: 100, borderRadius: 50 }} />
75157
<View>
76158
<Text className="text-[#212121] font-UrbanistBold text-[20px] text-center">How was your experience </Text>
77-
<Text className="text-[#212121] font-UrbanistBold text-[20px] text-center">with Dr. Drake Boeson?</Text>
159+
<Text className="text-[#212121] font-UrbanistBold text-[20px] text-center">with <Text>{name}</Text>?</Text>
78160
</View>
79161
<View className=" w-[350px] flex flex-row justify-center pb-3">
80162
<SafeAreaView>
81-
<FiveStarRating />
163+
<FiveStarRating selectedStars={selectedStars} onStarPress={handleStarPress} />
82164
</SafeAreaView>
83165
</View>
84166
</View>
@@ -92,7 +174,7 @@ export default function Writereview() {
92174
placeholder="Your review here..."
93175
style={styles.textInput}
94176
/>
95-
<Text className="text-[#212121] font-UrbanistBold text-[20px] pb-5 pt-3">Would you recommend Dr. Drake Boeson to your friends?</Text>
177+
<Text className="text-[#212121] font-UrbanistBold text-[20px] pb-5 pt-3">Would you recommend <Text>{name}</Text> to your friends?</Text>
96178
<View className="flex flex-row items-center pl-2 justify-between w-[130px]">
97179
{Options.map((option, index) => (
98180
<View key={index} className="flex flex-row items-center justify-center gap-2">
@@ -106,7 +188,6 @@ export default function Writereview() {
106188
</View>
107189
</View>
108190
</View>
109-
110191
<View className="flex flex-row w-[370px] justify-between items-center pt-3">
111192
<TouchableOpacity onPress={() => router.back()}>
112193
<View className=" rounded-3xl pb-3 pt-3 pl-14 pr-14 bg-slate-100 ">
@@ -117,7 +198,7 @@ export default function Writereview() {
117198
</TouchableOpacity>
118199
<View style={styles.container}>
119200
<Pressable
120-
onPress={() => setIsModalVisible(true)}
201+
onPress={() => writeReview()}
121202
style={[
122203
styles.button,
123204
{ backgroundColor: isSubmitEnabled ? '#246bfd' : '#3062cb' }
@@ -133,7 +214,6 @@ export default function Writereview() {
133214
</>
134215
)
135216
}
136-
137217
const styles = StyleSheet.create({
138218
container: {
139219
flex: 1,

0 commit comments

Comments
 (0)