I am using react-native-calendar's Agenda component to render the appointments for a user, like on which day he has appointments.
The built logic is here
React native component: -
/* eslint-disable no-nested-ternary */
import { useLazyQuery, useQuery } from "@apollo/client";
import dayjs from "dayjs";
import React, { useContext, useState } from "react";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
import { Agenda } from "react-native-calendars";
import { useDispatch } from "react-redux";
import { UserContext } from "../context/AuthContextProvider";
import { GET_USER_APPOINTMENTS } from "../graphql/query";
import testIDs from "../testIDs";
import {
addMissingDates,
parseAppointmentData,
} from "../utils/calendarDataParser";
import { leadSelected } from "../reducers/leadSlice";
import { appointmentSelected } from "../reducers/appointmentSlice";
function AppointmentCalendar({ route, navigation }) {
const [items, setItems] = useState({});
const { authUser } = useContext(UserContext);
const [variables, setVariables] = useState({
userIds: [authUser?.id],
startDate: dayjs()?.startOf("month")?.format("YYYY-MM-DD"),
endDate: dayjs()?.endOf("month")?.format("YYYY-MM-DD"),
});
const dispatch = useDispatch();
const { loading: appointmentLoader } = useQuery(GET_USER_APPOINTMENTS, {
variables: { ...variables },
onCompleted: (res) => {
setItems((prev) => ({
...prev,
...addMissingDates(
parseAppointmentData(res?.getUserAppointment?.data),
dayjs().format("YYYY-MM-DD")
),
}));
},
});
const loadItems = (day) => {
setVariables({
...variables,
startDate: dayjs(day.dateString)?.startOf("month")?.format("YYYY-MM-DD"),
endDate: dayjs(day.dateString)?.endOf("month")?.format("YYYY-MM-DD"),
});
};
const renderEmptyDate = () => (
<View style={styles?.emptyDate}>
<Text>No appointment scheduled!</Text>
</View>
);
const RenderItem = React.memo((props) => {
const { reservation, isFirst, dispatch, navigation, route, styles } = props;
const fontSize = isFirst ? 16 : 14;
const color = isFirst ? "black" : "#43515c";
const appointmentColor =
reservation.appointmentStatus === "SCHEDULED"
? "#1890ff"
: reservation.appointmentStatus === "RESCHEDULED"
? "#13c2c2"
: reservation.appointmentStatus === "CANCELLED"
? "#f5222d"
: "#52c41a";
return (
<TouchableOpacity
testID={testIDs.agenda.ITEM}
style={[
styles.item,
{ height: reservation.height, backgroundColor: appointmentColor },
]}
onPress={() => {
navigation.navigate("EditAppointment", {
...route?.params,
mode: "EDIT",
});
dispatch(appointmentSelected(reservation?.appointment));
}}
>
<Text style={{ fontSize, color: "#fff" }}>{reservation?.name}</Text>
</TouchableOpacity>
);
}, []);
const rowHasChanged = (r1, r2) => r1?.appointment?.id !== r2?.appointment?.id;
const timeToDate = (time) => {
const date = new Date(time);
return date.toISOString().split("T")[0];
};
const timeToString = (time) => {
const date = new Date(time);
return dayjs(date)?.format("hh:mm A");
};
return (
<View style={styles?.container}>
<Agenda
testID={testIDs?.agenda?.CONTAINER}
items={items}
loadItemsForMonth={loadItems}
selected={new Date()}
onDayChange={(day) => console.log(day.dateString)}
renderItem={(reservation, isFirst) => (
<RenderItem
isFirst={isFirst}
key={reservation?.appointment?.id}
reservation={reservation}
dispatch={dispatch}
navigation={navigation}
route={route}
styles={styles}
/>
)}
renderEmptyDate={renderEmptyDate}
rowHasChanged={rowHasChanged}
showClosingKnob
/>
</View>
);
}
export default AppointmentCalendar;
const styles = StyleSheet.create({
container: {
flex: 1,
// backgroundColor: "#a9addf",
},
boxContainer: {
backgroundColor: "#a9addf",
paddingTop: 10,
paddingBottom: 10,
},
listItem: {
marginTop: 5,
padding: 10,
backgroundColor: "#FFF",
width: "98%",
flex: 1,
alignSelf: "center",
flexDirection: "row",
borderRadius: 5,
},
viewRow: {
flex: 1,
flexDirection: "row",
},
textGray: {
color: "gray",
fontSize: 14,
marginBottom: 2,
},
textCapitalGray: {
color: "gray",
fontSize: 16,
marginBottom: 2,
},
item: {
backgroundColor: "white",
flex: 1,
borderRadius: 5,
padding: 10,
marginRight: 10,
marginTop: 17,
},
emptyDate: {
height: 15,
flex: 1,
paddingTop: 30,
},
});
Here are the helper functions as required for the Agenda component: -
export const parseAppointmentData = (input) => {
try {
const systemTimezone = dayjs?.tz?.guess();
const output = {};
input?.forEach((item) => {
const date = dayjs(item?.startDatetime)
?.tz(systemTimezone)
?.format("YYYY-MM-DD");
const time = dayjs(item?.startDatetime)
?.tz(systemTimezone)
?.format("HH:mm A");
const name = `${time} - ${item?.summary}`;
if (!output[date]) {
output[date] = [];
}
output[date]?.push({
name,
height: 50,
day: date,
appointmentStatus: item?.appointmentStatus,
appointment: item,
});
});
return output;
} catch (err) {
console?.log(err);
}
};
export function addMissingDates(json, date) {
const systemTimezone = dayjs?.tz?.guess();
const missingDates = [];
// Get the starting date of the current month
const startingDate = dayjs(date)?.tz(systemTimezone)?.startOf("month");
// Get the ending date of the current month
const endingDate = dayjs(date)?.tz(systemTimezone)?.endOf("month");
// Initialize the current date with the starting date
let currentDate = startingDate;
const datesWithAppointments = Object?.keys(json);
// Loop until the current date reaches the ending date
while (currentDate?.isSameOrBefore(endingDate, "day")) {
// Output the current date
if (!datesWithAppointments?.includes(currentDate?.format("YYYY-MM-DD"))) {
missingDates?.push(currentDate?.format("YYYY-MM-DD"));
}
// Increment the current date by one day
currentDate = currentDate?.add(1, "day");
}
missingDates?.forEach((missedDate) => {
const formattedDate = missedDate;
json[formattedDate] = [];
});
return json;
}
Now, the main problem that i am facing is when the user switches to the previous date from the date that has multiple appointments, the data changes abnormally
Please check the video link that i shared https://screenrec.com/share/YndIusWQgC
I have been scratching my head from the last 1 week, but couldn't find the solution to it, kindly help me if anyone find some issue here.
Thankyou in advance