From 7379e242fef6974581f51de513f8de66e502f07c Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Tue, 15 Apr 2025 11:05:00 -0400 Subject: [PATCH 01/15] feat:implemented the first experimental calendar navigation component that displays the current date and year (although it isn't the best navigation based component). --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 35 ++++--- .../core/calendarNavigation/index.tsx | 93 +++++++++++++++++++ 2 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 src/components/core/calendarNavigation/index.tsx diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index e70c490..bcbf07b 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -33,6 +33,7 @@ import { Dropdown } from 'react-native-element-dropdown'; import CalendarModeSwitcher, { CustomRecurrenceModal, } from '@/components/core/calendarModeSwitcher'; +import CalendarNavigation from '@/components/core/calendarNavigation'; // TODO : define the edit event and new event modal as seperate components and pass down data as a prop instead // TODO : add an interface referencing the event useState hook @@ -818,6 +819,18 @@ export default function Schedule() { const [showCustomRecurrenceModal, setShowCustomRecurrenceModal] = useState(false); const [customSelectedDays, setCustomSelectedDays] = useState([]); // stores the custom days the event should be repeated + // useState hook variable for currentCalendarDate + const [currentCalendarDate, setCurrentCalendarDate] = useState(new Date().toISOString()); + + const handleCalendarDateChange = useCallback((newDate: string | any) => { + setCurrentCalendarDate(newDate); + calendarRef.current?.goToDate({ + date: newDate, + animatedDate: true, + hourScroll: true, + }); + }, []); + // this is just an example of how to add hours to the current time // this variable is intended to be a reference, it is not being used const _four_hours_delay = new Date().getHours() + 4; @@ -938,17 +951,6 @@ export default function Schedule() { (current_event: any) => current_event.id === uniqueId ); - // console.log(`id_existence value : ${id_existence}`); - - // in the event that this conditional is true, that means the id doesn't exist - // that means we can attach the current event's data with the new id that has been found - // save it into the list (which will then be sent to the database based on the email of the user) - // treating this also as a form of base case - // if (id_existence === -1) { - // const updatedEvent = { - // ...currentEventData, - // id: uniqueId, - // }; const newEvent = { ...currentEventData, id: uniqueId, @@ -1083,10 +1085,15 @@ export default function Schedule() { position: 'relative', }} > - {/* - TODO : the view isn't entirely functional - *Calendar mode switcher is intended to be added at the top */} + {/**TODO : should handle navigating between different date, month and year? + * NOTE : this is one of the navigation controls present, a bit crude and unstructured, lots of room for improvement + * + */} + {/* * insert acitivity Indicator animation loading logic here diff --git a/src/components/core/calendarNavigation/index.tsx b/src/components/core/calendarNavigation/index.tsx new file mode 100644 index 0000000..5dfb50e --- /dev/null +++ b/src/components/core/calendarNavigation/index.tsx @@ -0,0 +1,93 @@ +import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; + +// component wrapper around Ionicons (refer to the definition of the component itself) +import { Ionicon } from '../icon'; +const CalendarNavigation = ({ currentDate, onDateChange }) => { + // Format the current date for display + const FormatDisplayDate = (date) => { + // @reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString + const options: any = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }; + return new Date(date).toLocaleDateString(undefined, options); + }; + + // 3 void functions to decrement current month, increment current month, and navigate to current date + // handle previous month navigation + // all the final dates needs to be converted to ISOString() using the built in method from Date + const goToPrevMonth = () => { + // retrieves the current date that has been selected + // backtrack by a single month + const date = new Date(currentDate); + date.setMonth(date.getMonth() - 1); + onDateChange(date.toISOString()); // update currentDate + }; + + const goToNextMonth = () => { + const date = new Date(currentDate); + date.setMonth(date.getMonth() + 1); + onDateChange(date.toISOString()); // update currentDate + }; + + const goToToday = () => { + onDateChange(new Date().toISOString()); + }; + return ( + + + + + + + Today + + + + + + {FormatDisplayDate(currentDate)} + + ); +}; + +// styles for navigation component +const navigationStyles = StyleSheet.create({ + container: { + paddingVertical: 10, + paddingHorizontal: 15, + backgroundColor: '#fff', + flexDirection: 'column', + borderBottomWidth: 1, + borderBottomColor: '#f0f0f0', + }, + dateControls: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginBottom: 5, + }, + navButton: { + padding: 5, + }, + todayButton: { + paddingHorizontal: 15, + paddingVertical: 5, + marginHorizontal: 10, + backgroundColor: '#e6f2ff', + }, + todayText: { + color: '#3498db', + fontWeight: 'bold', + }, + dateText: { + fontSize: 18, + fontWeight: 'bold', + textAlign: 'center', + color: '#333', + }, +}); + +export default CalendarNavigation; From 940e9a6c2c830f6c089e73754f5c15ade1d1bead Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Tue, 15 Apr 2025 13:30:37 -0400 Subject: [PATCH 02/15] feat:successful navigation logic for calendar. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 30 +++++----------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index bcbf07b..19616b5 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -1054,25 +1054,6 @@ export default function Schedule() { email: 'user email goes here', events_data: 'information regarding new events goes here', }; - // useEffect hook to test if sample event is working as intended - // TODO : delete this useState hooks (and console.log statements within it) - // useEffect(() => { - // console.log(`List of available events : ${JSON.stringify(eventsList)}`); - // // console.log('Detected changes to start date : ', startDate.toISOString()); - // // console.log('Detected changes to end date : ', endDate.toISOString()); - - // // console.log(`current selected event : ${JSON.stringify(selectedEvent)}`); - // // console.log(`current status of event modal display : ${showExistingEventModal}`); - // // console.log(currentEventData); - // // console.log(`current start and end date : \n${startDate}\n ${endDate}`); - // }, [ - // eventsList, - // // startDate, - // // endDate, - // // selectedEvent, - // // showExistingEventModal - // ]); - // useEffect hook to check if recurrence modal state is being updated useEffect(() => { console.log('showCustomRecurrencModal : ', showCustomRecurrenceModal); @@ -1086,10 +1067,7 @@ export default function Schedule() { }} > - {/**TODO : should handle navigating between different date, month and year? - * NOTE : this is one of the navigation controls present, a bit crude and unstructured, lots of room for improvement - * - */} + {showExistingEventModal && ( // TODO : fix the issue with text input not working and changing the current functions into reusable reference functions From be53be8c3d388736077ecc8d88d4a3d0dda4e669 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Wed, 16 Apr 2025 23:50:04 -0400 Subject: [PATCH 03/15] enhancement:calendar navigation component --- .../core/calendarNavigation/index.tsx | 175 ++++++++++++++++-- 1 file changed, 160 insertions(+), 15 deletions(-) diff --git a/src/components/core/calendarNavigation/index.tsx b/src/components/core/calendarNavigation/index.tsx index 5dfb50e..8524076 100644 --- a/src/components/core/calendarNavigation/index.tsx +++ b/src/components/core/calendarNavigation/index.tsx @@ -1,9 +1,28 @@ -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; - +import { View, Text, StyleSheet, TouchableOpacity, Modal, Platform } from 'react-native'; +import { useState } from 'react'; +import DateTimePicker from '@react-native-community/datetimepicker'; // component wrapper around Ionicons (refer to the definition of the component itself) import { Ionicon } from '../icon'; +import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; const CalendarNavigation = ({ currentDate, onDateChange }) => { - // Format the current date for display + // useState hook for DateTimePicker component enhancement + // reused logic from start and end date time from Schedule component + // determines whether datetimepicker component should be displayed or not + const [showDatePickerModal, setShowDatePickerModal] = useState(false); + const [datePickerValue, setDatePickerValue] = useState(new Date()); + + // Function to handle date picker changes + const handleDatePickerChange = (event, selectedDate) => { + const currentDate = selectedDate || datePickerValue; + setDatePickerValue(currentDate); + }; + + // functon to confirm date selection + const confirmDateSelection = () => { + onDateChange(datePickerValue.toISOString()); + setShowDatePickerModal(false); + }; + const FormatDisplayDate = (date) => { // @reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString const options: any = { @@ -36,20 +55,78 @@ const CalendarNavigation = ({ currentDate, onDateChange }) => { onDateChange(new Date().toISOString()); }; return ( - - - - - - - Today - - - + <> + + + + + + + Today + + + + + + + {/* + * fine-tune the navigation logic + */} + setShowDatePickerModal(true)} + style={navigationStyles.dateTextContainer} + > + {FormatDisplayDate(currentDate)} + - {FormatDisplayDate(currentDate)} - + + {/** Date time picker modal to navigate within different portion of the calendar */} + setShowDatePickerModal(false)} + > + {/**TouchableWithoutFeedback has the opposite behavior of TouchableOpacity and generally useful when something needs to be clicked but doesn't require any kind of animation present */} + setShowDatePickerModal(true)}> + + + + Select a Date + + + setShowDatePickerModal(false)} + > + Cancel + + + Save + + + + + + + + ); }; @@ -77,6 +154,7 @@ const navigationStyles = StyleSheet.create({ paddingVertical: 5, marginHorizontal: 10, backgroundColor: '#e6f2ff', + borderRadius: 15, }, todayText: { color: '#3498db', @@ -88,6 +166,73 @@ const navigationStyles = StyleSheet.create({ textAlign: 'center', color: '#333', }, + + // the styling here really doesn't make much of a difference to the text + // TODO : this styling might be unneccessary, so remove it + dateTextContainer: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + calendarIcon: { + marginLeft: 8, + }, +}); + +// define styles for the DateTimePicker modal for fine-tuned navigation +const dateTimePickerStyles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.5)', + }, + + // reused modal style code (refer to Schedule.tsx) + modalView: { + width: '80%', // adjust as needed + backgroundColor: 'white', + borderRadius: 10, + padding: 20, + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5, + }, + title: { + fontSize: 18, + fontWeight: 'bold', + marginBottom: 15, + color: '#333', + }, + buttonContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + width: '100%', + marginTop: 20, + }, + button: { + padding: 12, + borderRadius: 5, + width: '45%', + alignItems: 'center', + }, + buttonConfirm: { + backgroundColor: '#3498db', + }, + buttonCancel: { + backgroundColor: '#e74c3c', + }, + buttonText: { + color: 'white', + fontWeight: 'bold', + fontSize: 16, + }, }); export default CalendarNavigation; From 8270a23300b596cc02e11ea34b8d334318b84d18 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Thu, 17 Apr 2025 09:01:26 -0400 Subject: [PATCH 04/15] fix:modal rendering issue for date navigation fixed. --- .../core/calendarNavigation/index.tsx | 100 +++++++++++++++--- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/src/components/core/calendarNavigation/index.tsx b/src/components/core/calendarNavigation/index.tsx index 8524076..15fb888 100644 --- a/src/components/core/calendarNavigation/index.tsx +++ b/src/components/core/calendarNavigation/index.tsx @@ -1,9 +1,16 @@ -import { View, Text, StyleSheet, TouchableOpacity, Modal, Platform } from 'react-native'; -import { useState } from 'react'; +import { + View, + Text, + StyleSheet, + TouchableOpacity, + Modal, + Platform, + TouchableWithoutFeedback, +} from 'react-native'; +import { useEffect, useState } from 'react'; import DateTimePicker from '@react-native-community/datetimepicker'; // component wrapper around Ionicons (refer to the definition of the component itself) import { Ionicon } from '../icon'; -import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; const CalendarNavigation = ({ currentDate, onDateChange }) => { // useState hook for DateTimePicker component enhancement // reused logic from start and end date time from Schedule component @@ -11,6 +18,10 @@ const CalendarNavigation = ({ currentDate, onDateChange }) => { const [showDatePickerModal, setShowDatePickerModal] = useState(false); const [datePickerValue, setDatePickerValue] = useState(new Date()); + // For IOS, conditionally render the datetimepicker + const [showIOSDatePicker, setShowIOSDatePicker] = useState(false); + const [centerString, setCenterString] = useState('Today'); + // Function to handle date picker changes const handleDatePickerChange = (event, selectedDate) => { const currentDate = selectedDate || datePickerValue; @@ -21,6 +32,7 @@ const CalendarNavigation = ({ currentDate, onDateChange }) => { const confirmDateSelection = () => { onDateChange(datePickerValue.toISOString()); setShowDatePickerModal(false); + setCenterString('Jump To Today'); }; const FormatDisplayDate = (date) => { @@ -42,18 +54,37 @@ const CalendarNavigation = ({ currentDate, onDateChange }) => { // backtrack by a single month const date = new Date(currentDate); date.setMonth(date.getMonth() - 1); + setCenterString('Jump To Today'); onDateChange(date.toISOString()); // update currentDate }; const goToNextMonth = () => { const date = new Date(currentDate); date.setMonth(date.getMonth() + 1); + setCenterString('Jump To Today'); onDateChange(date.toISOString()); // update currentDate }; const goToToday = () => { onDateChange(new Date().toISOString()); + setCenterString('Today'); + }; + + // handle opening date picker + const openDatePicker = () => { + if (Platform.OS === 'ios') { + setShowIOSDatePicker(true); + setShowDatePickerModal(true); + } else { + // handle every other platform + setShowDatePickerModal(true); + } }; + useEffect(() => { + console.log( + `current date prop value : ${FormatDisplayDate(currentDate)}, \n actual current date (should be today's date) : ${new Date().toDateString()} ` + ); + }, [currentDate]); return ( <> @@ -62,7 +93,7 @@ const CalendarNavigation = ({ currentDate, onDateChange }) => { - Today + {centerString} @@ -91,26 +122,53 @@ const CalendarNavigation = ({ currentDate, onDateChange }) => { animationType="slide" transparent={true} visible={showDatePickerModal} - onRequestClose={() => setShowDatePickerModal(false)} + onRequestClose={() => { + setShowDatePickerModal(false); + setShowIOSDatePicker(false); + }} > {/**TouchableWithoutFeedback has the opposite behavior of TouchableOpacity and generally useful when something needs to be clicked but doesn't require any kind of animation present */} - setShowDatePickerModal(true)}> + { + // not entirely sure why the state should be set to false here instead of true + setShowDatePickerModal(false); + setShowIOSDatePicker(false); + // setShowDatePickerModal(true) + // setShowIOSDatePicker(true) + }} + > - Select a Date - + Select a Date + {/**Conditionally render datetime pciker for IOS */} + {(Platform.OS === 'android' || (Platform.OS === 'ios' && showIOSDatePicker)) && ( + + )} + + {/**on IOS, show an explicit button to render the datepicker */} + {Platform.OS === 'ios' && !showIOSDatePicker && ( + + Show Date Picker + + )} setShowDatePickerModal(false)} + onPress={() => { + setShowDatePickerModal(false); + setShowIOSDatePicker(false); + }} > Cancel @@ -233,6 +291,16 @@ const dateTimePickerStyles = StyleSheet.create({ fontWeight: 'bold', fontSize: 16, }, + showPickerButton: { + backgroundColor: '#3498db', + padding: 10, + borderRadius: 5, + marginVertical: 15, + }, + showPickerText: { + color: 'white', + fontWeight: 'bold', + }, }); export default CalendarNavigation; From 21a7d5aecb87c637b7324b4f95b19e95f6061420 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Thu, 17 Apr 2025 23:32:32 -0400 Subject: [PATCH 05/15] chore:some logic experimentation, nothing important. --- package.json | 2 +- src/app/(root)/(tabs)/(index)/Schedule.tsx | 49 ++++++++++++++++++---- src/app/onboarding3.tsx | 2 + yarn.lock | 8 ++-- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index ed5e627..a85b81e 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@howljs/calendar-kit": "^2.2.1", "@mobile-reality/react-native-select-pro": "^2.3.0", "@prisma/client": "^5.22.0", - "@react-native-async-storage/async-storage": "1.23.1", + "@react-native-async-storage/async-storage": "^2.1.2", "@react-native-community/datetimepicker": "8.2.0", "@react-native-picker/picker": "2.9.0", "@react-navigation/material-top-tabs": "^6.6.14", diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index 19616b5..e3fe040 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { useCallback, useEffect, useState, useRef } from 'react'; +// TODO : use react-native async storage instead to analyze the data +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useCallback, useEffect, useState, useRef, useMemo } from 'react'; import { View, Text, @@ -34,6 +36,7 @@ import CalendarModeSwitcher, { CustomRecurrenceModal, } from '@/components/core/calendarModeSwitcher'; import CalendarNavigation from '@/components/core/calendarNavigation'; +import { useLocalSearchParams } from 'expo-router'; // TODO : define the edit event and new event modal as seperate components and pass down data as a prop instead // TODO : add an interface referencing the event useState hook @@ -530,6 +533,33 @@ const ExistingEventModal = ({ }; export default function Schedule() { + // function to improve json syntax highlighting for debugging purpose + // copied from stack overflow + function syntaxHighlight(json: any) { + json = json.replace(/&/g, '&').replace(//g, '>'); + return json.replace( + /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, + function (match: any) { + let cls = 'number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'key'; + } else { + cls = 'string'; + } + } else if (/true|false/.test(match)) { + cls = 'boolean'; + } else if (/null/.test(match)) { + cls = 'null'; + } + return '' + match + ''; + } + ); + } + const route_params = useLocalSearchParams(); + + // extract email (for payload) + const { email } = route_params; // supporting variables for the calendar mode switcher // NOTE : useRef allows us to persist values between renders /** @@ -940,7 +970,6 @@ export default function Schedule() { } const uniqueId = `event_${Date.now()}_${Math.floor(Math.random() * 1000)}`; - console.log(`generated unique id : ${uniqueId}`); // replaced with string based value for easier identification // const random_generated_id = Math.floor(Math.random() * 100 + 1); // console.log(random_generated_id); @@ -960,7 +989,7 @@ export default function Schedule() { // if user wants an event to be recurring based on a specific selected frequency if (newEvent.isRecurring && newEvent.recurrence_frequency) { - eventsToAdd = calculateRecurringEvents(newEvent); + eventsToAdd = calculateRecurringEvents(newEvent); // append recurring events to eventsList } else { // otherwise, simply add a single instance copy of the particular event eventsToAdd = [newEvent]; @@ -1051,13 +1080,17 @@ export default function Schedule() { // prototype of the data that needs to be sent out to the datbase const events_payload = { - email: 'user email goes here', - events_data: 'information regarding new events goes here', + email: email, + events_data: eventsList, }; - // useEffect hook to check if recurrence modal state is being updated + + // console.log('Events Payload Data Retrieved : ', events_payload); + + // TODO : fix this implementation useEffect(() => { - console.log('showCustomRecurrencModal : ', showCustomRecurrenceModal); - }, [showCustomRecurrenceModal]); + // console.log(`List of events : ${syntaxHighlight(JSON.stringify(eventsList))}`); + console.log(`List of events : ${JSON.stringify(eventsList)}`); + }, [eventsList]); return ( { // old navigation route router.push({ pathname: '/(root)/(tabs)/(index)/Schedule', + + // router parameter data is being passed here, retrieve it within Schedule.tsx file params: { email: payload.email }, }); // point the user to the correct path } else { diff --git a/yarn.lock b/yarn.lock index b4fdcb8..b666936 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1887,10 +1887,10 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.0" -"@react-native-async-storage/async-storage@1.23.1": - version "1.23.1" - resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz#cad3cd4fab7dacfe9838dce6ecb352f79150c883" - integrity sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA== +"@react-native-async-storage/async-storage@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-2.1.2.tgz#8aae432adfc20800308e2ef3ce380864f0f9def8" + integrity sha512-dvlNq4AlGWC+ehtH12p65+17V0Dx7IecOWl6WanF2ja38O1Dcjjvn7jVzkUHJ5oWkQBlyASurTPlTHgKXyYiow== dependencies: merge-options "^3.0.4" From 280513234362a2131aae8d00e2b83bd7933279b8 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Sat, 19 Apr 2025 20:33:07 -0400 Subject: [PATCH 06/15] chore:some ongoing progress with deletion of recurring events, logic has been tested for the onPress behavior, all that remains is the rendering portion. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 78 ++++++++++++++++++- src/components/core/deleteModals/index.tsx | 89 ++++++++++++++++++++++ 2 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/components/core/deleteModals/index.tsx diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index e3fe040..d54cdbd 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -37,6 +37,7 @@ import CalendarModeSwitcher, { } from '@/components/core/calendarModeSwitcher'; import CalendarNavigation from '@/components/core/calendarNavigation'; import { useLocalSearchParams } from 'expo-router'; +import DeleteSingleEvent from '@/components/core/deleteModals'; // TODO : define the edit event and new event modal as seperate components and pass down data as a prop instead // TODO : add an interface referencing the event useState hook @@ -215,7 +216,80 @@ const ExistingEventModal = ({ onPress={onRequestDelete} > - + {current_event.isRecurring ? ( + + ) : ( + + + + + Delete Recurring Event? + + + + Yes + + + No + + + + + + )} + {/* - + */} diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx new file mode 100644 index 0000000..594badb --- /dev/null +++ b/src/components/core/deleteModals/index.tsx @@ -0,0 +1,89 @@ +// requires 2 seperate imports +import { Modal, View, Text, TouchableOpacity } from 'react-native'; + +// interface for deleting single event +interface DeleteSingleEventInterface { + visibillity: boolean; + onPressDeleteConfirmation: any; + onPressDeleteCancellation: any; + buttonStyling: any; +} +// basic delete modal to handle deletion of single events (that isn't recurring) +const DeleteSingleEvent = ({ + visibillity, + onPressDeleteConfirmation, + onPressDeleteCancellation, + buttonStyling, +}: DeleteSingleEventInterface) => { + return ( + + + + + Delete Recurring Events? + + + + Yes + + + No + + + + + + ); +}; + +export default DeleteSingleEvent; + +// TODO : implement this +export const DeleteRecurringEvents = ({ visibile }) => {}; From 794371c52ebf2ab6fa067217cf5248f3a42d7824 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Sat, 19 Apr 2025 21:09:43 -0400 Subject: [PATCH 07/15] feat:some additional progress with the triple choice of events selection process. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 161 ++++--- src/components/core/deleteModals/index.tsx | 500 ++++++++++++++++++++- 2 files changed, 592 insertions(+), 69 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index d54cdbd..e69615b 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -37,7 +37,8 @@ import CalendarModeSwitcher, { } from '@/components/core/calendarModeSwitcher'; import CalendarNavigation from '@/components/core/calendarNavigation'; import { useLocalSearchParams } from 'expo-router'; -import DeleteSingleEvent from '@/components/core/deleteModals'; +import DeleteSingleEvent, { DeleteRecurringEvents } from '@/components/core/deleteModals'; +import { Delete } from 'lucide-react-native'; // TODO : define the edit event and new event modal as seperate components and pass down data as a prop instead // TODO : add an interface referencing the event useState hook @@ -217,77 +218,90 @@ const ExistingEventModal = ({ > {current_event.isRecurring ? ( + + ) : ( + // + // + // + // + // + // Delete Recurring Event? + // + // + // + // Yes + // + // + // No + // + // + // + // + // - ) : ( - - - - - Delete Recurring Event? - - - - Yes - - - No - - - - - )} {/* - Delete Recurring Events? + Are Your Sure you want to delete this Event? {}; +export const DeleteRecurringEvents = ({ + visibile, + onPressDeleteConfirmation, + onPressDeleteCancellation, + buttonStyling, + recurrenceEventStyles, +}) => { + const [selectedValue, setSelectedValue] = useState(); + return ( + + + + + Delete This Recurring Events? + + + {/* + All Events + */} + { + console.log('selected all events : ', textInput); + }} + > + All Events + + { + console.log('selected subsequent events: ', textInput); + }} + > + Subsequent Events + + { + console.log('selected this event only : ', textInput); + }} + > + This Event Only + + + + + + ); +}; + +// NOTE : sample typescript code placed here for reference, this should be transferred over to Schedule.tsx: + +const sample_id_original = 'event_1744902168035_736'; +const sample_id_recurring = 'event_1744902168035_736_recurring_1'; + +// test to check resulting output converting string into array +const sample_id_original_array = sample_id_original.split('_'); +const sample_id_original_recurring_array = sample_id_recurring.split('_'); + +// Output : ["event", "1744902168035", "736"] +// console.log(sample_id_original_array); + +// Output : ["event", "1744902168035", "736", "recurring", "1"] +// console.log(sample_id_original_recurring_array); + +// then we can simply check if the original ID contains the particular val +// console.log(sample_id_original.includes(sample_id_original_array[1] + "_" + sample_id_original_array[2])); // output : true +// console.log(sample_id_recurring.includes(sample_id_original_array[1] + "_" + sample_id_original_array[2])); // output : true +// console.log(`ID match? : ${sample_id_original === sample_id_original_array[0] + "_" + sample_id_original_array[1] + "_" + sample_id_original_array[2]}`); + +const sample_data = [ + { + // this object should not be deleted when deleteAllEvents is called (since this is a different original event) + id: 'event_1744902168038_736', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-18T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-18T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + }, + { + id: 'event_1744902168035_736', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-18T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-18T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + }, + { + id: 'event_1744902168035_736_recurring_1', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-19T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-19T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_2', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-20T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-20T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_8', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-26T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-26T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_9', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-27T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-27T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_15', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-03T13:01:00.000Z', + }, + end: { + dateTime: '2025-05-03T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_16', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-04T13:01:00.000Z', + }, + end: { + dateTime: '2025-05-04T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_22', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-10T13:01:00.000Z', + }, + end: { + dateTime: '2025-05-10T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_23', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-11T13:01:00.000Z', + }, + end: { + dateTime: '2025-05-11T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_29', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-17T13:01:00.000Z', + }, + end: { + dateTime: '2025-05-17T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_30', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-18T13:01:00.000Z', + }, + end: { + dateTime: '2025-05-18T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, + { + id: 'event_1744902168035_736_recurring_36', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-05-24T13:01:00.000Z', + }, + + end: { + dateTime: '2025-05-24T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', + }, +]; +// we want to remove all indicators of recurring events +// NOTE : this is experimental as the intention is to build out additional data based on this + +// another way of deleting this would be to first determine all id of the particular events that needs to be deleted (which will be stored as an array of strings) +// which will then be iterated over +// this function assumes that we only want the original event to be retained +const deleteRecurringEvents = (originalEventsData: any[]) => { + // try removing the original event and retian all the recurring ones for now + // suppose we only want the original id + + // this will ensure that the original event is retained and recurring events are deleted. + const filtered_data = originalEventsData.filter( + (currentEvent) => !currentEvent.id.includes(sample_id_original + '_recurring') + ); + console.log(JSON.stringify(filtered_data)); +}; + +/** + * @originalEventsData : the array of objects containing information about the events themselves. + * @event : the event to be deleted, which will be passed in as a parameter + */ +const deleteSubsequentEvents = (originalEventsData: any[], event: any) => { + const event_id = event.id; + console.log('Provided Event ID : ', event_id); + + const event_id_array = event_id.split('_'); + console.log('Constructed array of event id : ', event_id_array); + + // construct the ID of the original event, since we also need to check if the event matches + // the 2nd and 3rd values wtihin the array will contain this information + const original_event_id = event_id_array[0] + '_' + event_id_array[1] + '_' + event_id_array[2]; + + // alternatively, the original event id can also be retrieved in the following method + if (event.id.includes('recurring')) { + const original_event_id = event.parentEventId; + console.log('retrieved parent event id : ', original_event_id); + } + console.log(`Retrieved original event id : ${original_event_id}`); + + // we want to check if the current event happens to be recurring + if (event_id.includes('recurring')) { + // NOTE : indexing by -1 doesn't seem to work in typescript arrays + // const currentRecurrenceUnit = event_id_array[-1]; // contains information about the particular recurrence number of the event + + // alternative + const currentRecurrenceUnit = parseInt(event_id_array[event_id_array.length - 1]); + console.log(`Current Requccurence Unit : ${currentRecurrenceUnit}`); + + // filter logic + // not entirely sure if this logic will work + // const filtered_events = originalEventsData.filter((currentEvent) => !(currentEvent.id.includes(original_event_id) && parseInt(currentEvent.id.split('_')[-1]) > currentRecurrenceUnit)); + + // first remove the subsequent events (feel free to mess around with it) + // NOTE : it should be "greater than", not "greater than or equal to" + const filtered_events = originalEventsData.filter( + (currentEvent) => + !( + parseInt(currentEvent.id.split('_')[currentEvent.id.split('_').length - 1]) > + currentRecurrenceUnit + ) + ); + + // experimental check to remove the original event + // const filtered_events_2 = filtered_events.filter((currentEvent) => currentEvent.id !== original_event_id); + console.log(filtered_events); + } else { + // assuming user chose the original event (which is the same as deleting all instances of the event) + + // otherwise, we simply delete all the events + console.log(deleteAllEvents(originalEventsData, event)); + } +}; + +// this function should execute if user wants to delete all instances of the original event and subsequent recurring events +// first we need the parent id corresponding to the event +const deleteAllEvents = (originalEvent: any[], eventToDelete: any) => { + // const parentEventID = eventToDelete.id; + // NOTE the negation operator within the filter predicate + return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); +}; +// deleteRecurringEvents(sample_data); + +const subsequent_event_to_delete = { + id: 'event_1744902168035_736_recurring_9', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-27T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-27T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', + isRecurringInstance: true, + parentEventId: 'event_1744902168035_736', +}; + +const sample_original_event = { + id: 'event_1744902168035_736', + title: 'Repeat During Wekeend', + description: 'This Event Will Repeat Every Weekend', + start: { + dateTime: '2025-04-18T13:01:00.000Z', + }, + end: { + dateTime: '2025-04-18T15:01:00.000Z', + }, + color: '#DB4437', + location: 'Not Specified', + isRecurring: true, + recurrence_frequency: 'Every Weekend', +}; +deleteSubsequentEvents(sample_data, sample_original_event); From 5f3a2ce3fa59c19632ee136dee56b975e5e1863f Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Sun, 20 Apr 2025 21:28:50 -0400 Subject: [PATCH 08/15] feat:some additional progress. --- package.json | 1 + src/app/(root)/(tabs)/(index)/Schedule.tsx | 8 +- src/components/core/deleteModals/index.tsx | 188 ++++++++++++++------- yarn.lock | 5 + 4 files changed, 143 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index a85b81e..037ffb7 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "expo-haptics": "~14.0.1", "expo-image": "~2.0.6", "expo-linking": "~7.0.5", + "expo-radio-button": "^1.0.8", "expo-router": "~4.0.17", "expo-splash-screen": "~0.29.22", "expo-status-bar": "~2.0.1", diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index e69615b..66c3f8b 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -42,7 +42,7 @@ import { Delete } from 'lucide-react-native'; // TODO : define the edit event and new event modal as seperate components and pass down data as a prop instead // TODO : add an interface referencing the event useState hook -interface CalendarEvent { +export interface CalendarEvent { id: string; title: string; description?: string; @@ -1955,4 +1955,10 @@ const recurrenceEventStyling = StyleSheet.create({ fontWeight: 'bold', fontSize: 16, }, + modifiedButtonStyling: { + padding: 10, + borderRadius: 5, + width: '42%', + alignItems: 'center', + }, }); diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index ab257ed..c50da62 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -1,6 +1,9 @@ // requires 2 seperate imports -import { Modal, View, Text, TouchableOpacity } from 'react-native'; -import { useState } from 'react'; +import { Modal, View, Text, TouchableOpacity, GestureResponderEvent } from 'react-native'; +import { useEffect, useState } from 'react'; +import RadioButtonGroup, { RadioButtonItem } from 'expo-radio-button'; +import { CalendarEvent } from '@/app/(root)/(tabs)/(index)/Schedule'; + // interface for deleting single event interface DeleteSingleEventInterface { visibillity: boolean; @@ -85,17 +88,59 @@ const DeleteSingleEvent = ({ export default DeleteSingleEvent; +interface DeleteRecurringEventsType { + visible: boolean; + onPressDeleteConfirmation: any; + onPressDeleteCancellation: any; + buttonStyling: any; + recurrenceEventStyles: any; + list_of_events: CalendarEvent[]; + handleOnRequestModalClose: any; + selectedEvent: CalendarEvent; +} + // TODO : implement this export const DeleteRecurringEvents = ({ - visibile, + visible, onPressDeleteConfirmation, onPressDeleteCancellation, buttonStyling, recurrenceEventStyles, -}) => { - const [selectedValue, setSelectedValue] = useState(); + list_of_events, + handleOnRequestModalClose, + selectedEvent, +}: DeleteRecurringEventsType) => { + // useState hook to handle what has been currently selected by a particular user. + const [currentRadioButton, setCurrentRadioButton] = useState('all-event'); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_radioButtonSelected, setRadioButtonSelected] = useState(false); + + // helper functions to handle different instances of events that needs to be deleted + const deleteAllEvents = (originalEvent: any[], eventToDelete: any) => { + // const parentEventID = eventToDelete.id; + // NOTE the negation operator within the filter predicate + // TODO : the logic for this isn't entirely correct, needs to be fixed + return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); + }; + + switch (currentRadioButton) { + // this means user wants to delete all subsequent events + // invoke the function to delete all the events corresponding to the id + case 'all-event': + deleteAllEvents(list_of_events, selectedEvent); + } + // check to see if radio button is being updated correctly + // TODO : delete later, this is primarily for debugging purposes + useEffect(() => { + console.log(`current selected radio-button : ${currentRadioButton}`); + }, [currentRadioButton]); return ( - + All Events */} - { - console.log('selected all events : ', textInput); - }} - > - All Events - - { - console.log('selected subsequent events: ', textInput); + containerStyle={{ marginBottom: 10 }} + selected={currentRadioButton} + onSelected={(value: string) => { + setCurrentRadioButton(value); }} + containerOptionStyle={{ margin: 5 }} + radioBackground="#3498db" > - Subsequent Events - - { + setRadioButtonSelected(true); + }} + /> + { + setRadioButtonSelected(true); + }} + label={This and the following events} + /> + { + setRadioButtonSelected(true); + }} + label={This Event Only} + /> + + { - console.log('selected this event only : ', textInput); + justifyContent: 'center', }} > - This Event Only - + { + console.log('selected this event only : ', textInput); + }} + > + Cancel + + { + console.log('selected all events : ', textInput); + }} + > + Ok + + @@ -488,10 +561,10 @@ const deleteRecurringEvents = (originalEventsData: any[]) => { */ const deleteSubsequentEvents = (originalEventsData: any[], event: any) => { const event_id = event.id; - console.log('Provided Event ID : ', event_id); + // console.log('Provided Event ID : ', event_id); const event_id_array = event_id.split('_'); - console.log('Constructed array of event id : ', event_id_array); + // console.log('Constructed array of event id : ', event_id_array); // construct the ID of the original event, since we also need to check if the event matches // the 2nd and 3rd values wtihin the array will contain this information @@ -500,9 +573,9 @@ const deleteSubsequentEvents = (originalEventsData: any[], event: any) => { // alternatively, the original event id can also be retrieved in the following method if (event.id.includes('recurring')) { const original_event_id = event.parentEventId; - console.log('retrieved parent event id : ', original_event_id); + // console.log('retrieved parent event id : ', original_event_id); } - console.log(`Retrieved original event id : ${original_event_id}`); + // console.log(`Retrieved original event id : ${original_event_id}`); // we want to check if the current event happens to be recurring if (event_id.includes('recurring')) { @@ -511,7 +584,7 @@ const deleteSubsequentEvents = (originalEventsData: any[], event: any) => { // alternative const currentRecurrenceUnit = parseInt(event_id_array[event_id_array.length - 1]); - console.log(`Current Requccurence Unit : ${currentRecurrenceUnit}`); + // console.log(`Current Requccurence Unit : ${currentRecurrenceUnit}`); // filter logic // not entirely sure if this logic will work @@ -529,12 +602,11 @@ const deleteSubsequentEvents = (originalEventsData: any[], event: any) => { // experimental check to remove the original event // const filtered_events_2 = filtered_events.filter((currentEvent) => currentEvent.id !== original_event_id); - console.log(filtered_events); + // console.log(filtered_events); } else { // assuming user chose the original event (which is the same as deleting all instances of the event) - // otherwise, we simply delete all the events - console.log(deleteAllEvents(originalEventsData, event)); + // console.log(deleteAllEvents(originalEventsData, event)); } }; diff --git a/yarn.lock b/yarn.lock index b666936..688bccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4788,6 +4788,11 @@ expo-modules-core@2.2.3: dependencies: invariant "^2.2.4" +expo-radio-button@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/expo-radio-button/-/expo-radio-button-1.0.8.tgz#5f5c411ae0a2da633ae4b56d87a59dfa23a47bae" + integrity sha512-sE+aCtq5/lL1Y24oVYMyGug0IPt0R3dXS6rSK2S+A+EFm6HYBj0ZKGA35ywSekS+/8lE9UwKS+ORwQlWsfXoUQ== + expo-router@~4.0.17: version "4.0.19" resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.19.tgz#b2c33c07f18c81af0692ee06b6f1e3dee50da979" From d526aa028e5935cc76fdaec2ba898905c795c074 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Wed, 23 Apr 2025 00:01:09 -0400 Subject: [PATCH 09/15] chore:some additional progress. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 105 +++++---------------- src/components/core/deleteModals/index.tsx | 54 ++++------- 2 files changed, 41 insertions(+), 118 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index 66c3f8b..c639126 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -64,23 +64,13 @@ interface ExistingEventModal { // useState variables that determines whether modal should be displayed or not visibillity_state: boolean; delete_event_modal: boolean; - // this is based on the docs, added any just in case the previous two types fail to work - // this prop is intended to handle what will happen when modal is selected to be closed - onRequestClose: ((event: NativeSyntheticEvent) => void) | undefined | any; - - // this prop should be a useState hook that will handle whether the text input is edtiable - // ideally the text input should be editable when the edit button has been selected + delete_event_modal_recurring: boolean; isEditable: boolean; - - // deletes the current event upon selecting the delete icon - // TODO : determine and filter out the event based on the id - // the parameter that needs to be passed in is the current_event prop + onRequestClose: ((event: NativeSyntheticEvent) => void) | undefined | any; onRequestDelete: ((event: NativeSyntheticEvent) => void) | undefined | any; start_time: any; end_time: any; - // This function will handle how the modal's data will be edited - // when the edit icon is selected onRequestEdit: ((event: NativeSyntheticEvent) => void) | undefined | any; handleOnChangeTitle: any; @@ -103,9 +93,13 @@ interface ExistingEventModal { | ((event: NativeSyntheticEvent) => void) | undefined | any; + handleCloseRecurringDeleteModal: + | ((event: NativeSyntheticEvent) => void) + | undefined + | any; } -// TODO : Integrate logic for event deletion of existng event. +// TODO : requires lots of refactoring, badly written composition code const ExistingEventModal = ({ current_event, visibillity_state, @@ -131,6 +125,8 @@ const ExistingEventModal = ({ handleOnPressDeleteConfirmation, handleOnPressDeleteCancellation, delete_event_modal, + delete_event_modal_recurring, + handleCloseRecurringDeleteModal, // this will handle the closing of the modal after recurring events have been chosen to be deleted or saved. }: ExistingEventModal) => { return ( {current_event.isRecurring ? ( ) : ( - // - // - // - // - // - // Delete Recurring Event? - // - // - // - // Yes - // - // - // No - // - // - // - // - // { setCurrentCalendarDate(newDate); calendarRef.current?.goToDate({ @@ -1277,7 +1210,10 @@ export default function Schedule() { setDeleteEventModal(false); setShowExistingEventModal(false); // close the event }} + // handles visibillity of single event modal delete_event_modal={deleteEventModal} + // handles visibillity of multiple event modal + delete_event_modal_recurring={recurrenceDeleteModal} end_time={endDate} start_time={startDate} // pass in the start and end date for the date time picker current_event={selectedEvent} @@ -1390,6 +1326,7 @@ export default function Schedule() { // setEventList(updatedEvents); // setShowExistingEventModal(false); // close the event }} + handleCloseRecurringDeleteModal={() => setRecurrenceDeleteModal(false)} /> )} diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index c50da62..1969123 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -123,12 +123,24 @@ export const DeleteRecurringEvents = ({ return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); }; - switch (currentRadioButton) { - // this means user wants to delete all subsequent events - // invoke the function to delete all the events corresponding to the id - case 'all-event': - deleteAllEvents(list_of_events, selectedEvent); - } + // TODO : needs wrapped around a function + const handleRecurringEventDeletion = async () => { + switch (currentRadioButton) { + // this means user wants to delete all subsequent events + // invoke the function to delete all the events corresponding to the id + case 'all-event': + deleteAllEvents(list_of_events, selectedEvent); + break; + case 'subsequent': + deleteSubsequentEvents(list_of_events, selectedEvent); + break; + case 'current': + deleteCurrentEvent(list_of_events, selectedEvent); + break; + default: + break; + } + }; // check to see if radio button is being updated correctly // TODO : delete later, this is primarily for debugging purposes useEffect(() => { @@ -181,30 +193,8 @@ export const DeleteRecurringEvents = ({ - {/* - All Events - */} { - console.log('selected this event only : ', textInput); - }} + onPress={onPressDeleteCancellation} > Cancel @@ -283,9 +271,7 @@ export const DeleteRecurringEvents = ({ marginRight: 10, }, ]} - onPress={(textInput) => { - console.log('selected all events : ', textInput); - }} + onPress={onPressDeleteConfirmation} > Ok From 7bd220d481d00daea6fe0aad56886036e7229b13 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Wed, 23 Apr 2025 06:00:26 -0400 Subject: [PATCH 10/15] fix:some minor logic fix with state updates. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 74 +++------------------- 1 file changed, 8 insertions(+), 66 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index c639126..0bb9fa8 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -210,6 +210,7 @@ const ExistingEventModal = ({ // marginRight: 15, } } + // triggers the corresponding conditional rendering of non-recurring and recurring modals that needs to be deleted onPress={onRequestDelete} > @@ -233,70 +234,6 @@ const ExistingEventModal = ({ buttonStyling={ButtonStyling} /> )} - {/* - - - - Are You Sure You Want to Delete This Event? - - - - Yes - - - No - - - - - */} @@ -1312,8 +1249,13 @@ export default function Schedule() { setIsModalEditable(false); setShowExistingEventModal(false); }} - onRequestDelete={() => { - setDeleteEventModal(true); + onRequestDelete={async () => { + // need to conditionally handle which modal to set true + if (await selectedEvent.isRecurring) { + setRecurrenceDeleteModal(true); + } else { + setDeleteEventModal(true); + } // correct logic below for deleting a particular event // // remove the event within the list whose current id matches the id of the currently selected event // // include all other events except the current event containing matching id From 8725b3ce490889a0f12a57951431df34a8118e93 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Wed, 23 Apr 2025 07:31:36 -0400 Subject: [PATCH 11/15] chore:additional progress with the recurring event deletion modal, further refactoring and logical fixes needed. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 132 ++++++++++++-- src/components/core/deleteModals/index.tsx | 199 +++++++++++---------- 2 files changed, 217 insertions(+), 114 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index 0bb9fa8..6130e10 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -81,6 +81,8 @@ interface ExistingEventModal { dropdown_list: any; handleDropdownFunction: any; renderDropdownItem: any; + radioButtonChangeHandler: (() => void) | any; + radioButtonSelectorhandler: (() => void) | any; handleChangeEventColor: ((event: NativeSyntheticEvent) => void) | undefined | any; handleSaveEditedEvent: ((event: NativeSyntheticEvent) => void) | undefined | any; @@ -97,9 +99,11 @@ interface ExistingEventModal { | ((event: NativeSyntheticEvent) => void) | undefined | any; + listOfEvents: any[]; } // TODO : requires lots of refactoring, badly written composition code +// TODO : see how you can replace prop drilling with context since there are quite a few repetitive props const ExistingEventModal = ({ current_event, visibillity_state, @@ -126,7 +130,12 @@ const ExistingEventModal = ({ handleOnPressDeleteCancellation, delete_event_modal, delete_event_modal_recurring, - handleCloseRecurringDeleteModal, // this will handle the closing of the modal after recurring events have been chosen to be deleted or saved. + handleCloseRecurringDeleteModal, + listOfEvents, + radioButtonChangeHandler, // prop drilling for handleRadioButtonOnChange + radioButtonSelectorhandler, // prop drilling for handleOnSelectedRadioButtons + + // this will handle the closing of the modal after recurring events have been chosen to be deleted or saved. }: ExistingEventModal) => { return ( ) : ( /g, '>'); return json.replace( /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, @@ -511,6 +523,85 @@ export default function Schedule() { } ); } + + // relevant functions to handle deletion of events + /** + * @listOfEvents : the array of objects containing information about the events themselves. + * @event : the event to be deleted, which will be passed in as a parameter + */ + + // useState hook to keep track of which radio button has been selected + const [currentRadioButton, setCurrentRadioButton] = useState('all-event'); + const [radioButtonSelected, setRadioButtonSelected] = useState(false); + + // // helper functions to handle different instances of events that needs to be deleted + const deleteAllEvents = async (originalEvent: any[], eventToDelete: any) => { + // const parentEventID = eventToDelete.id; + // NOTE the negation operator within the filter predicate + // TODO : the logic for this isn't entirely correct, needs to be fixed + if (eventToDelete.id.includes('recurring')) { + const parentEventID = eventToDelete.parentId; + return await originalEvent.filter((currentEvent) => !currentEvent.id.includes(parentEventID)); + } + + // otherwise, if it happens to be an original event itself rather than recurring one + return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); + }; + + const deleteCurrentEvent = async (listOfEvents: any[], eventToDelete: any) => { + return await listOfEvents.filter((event) => event.id !== eventToDelete.id); + }; + const deleteSubsequentEvents = async (listOfEvents: any[], event: any) => { + // check if current event happens to be the recurring event + // otherwise, it's an original event (in which case we can go ahead and delete all events) + if (event.id.includes('recurring')) { + const event_id_array = event.id.split('_'); + const recurrence_unit = parseInt(event_id_array[event_id_array.length - 1]); + return listOfEvents.filter( + (currentEvent) => + !( + currentEvent.id.includes(event.parentEventId) && + parseInt(currentEvent.id.split('_')[currentEvent.id.split('_').length - 1]) > + recurrence_unit + ) + ); + } else { + await deleteAllEvents(listOfEvents, event); + } + }; + // TODO : needs wrapped around a function + const handleRecurringEventDeletion = async () => { + switch (currentRadioButton) { + // this means user wants to delete all subsequent events + // invoke the function to delete all the events corresponding to the id + // all three function variation will + case 'all-event': + await deleteAllEvents(eventsList, selectedEvent); + break; + case 'subsequent': + await deleteSubsequentEvents(eventsList, selectedEvent); + break; + case 'current': + await deleteCurrentEvent(eventsList, selectedEvent); + break; + default: + break; + } + }; + + const handleRadioButtonOnChange = (value: string) => { + setCurrentRadioButton(value); + }; + + const radioButtonSelectorUpdate = () => { + setRadioButtonSelected(true); + }; + + // TODO : delete after + useEffect(() => { + console.log('current selected radio button value : ', currentRadioButton); + }, [currentRadioButton]); + const route_params = useLocalSearchParams(); // extract email (for payload) @@ -788,6 +879,7 @@ export default function Schedule() { // this hook will determine whether to show the current existing event in the form of a modal const [showExistingEventModal, setShowExistingEventModal] = useState(false); + const [deleteEventModal, setDeleteEventModal] = useState(false); const [isModalEditable, setIsModalEditable] = useState(false); // this will determine the event that has been currently selected @@ -1029,7 +1121,7 @@ export default function Schedule() { // TODO : this seems somewhat repetitive, find a fix for this setSelectedEvent(event); } - // console.log(`Pressed event : ${JSON.stringify(event)}`); // TODO : delete this statement, this is just to check if the event update is working as intended + // TODO : delete this statement, this is just to check if the event update is working as intended // setSelectedEvent(event); setShowExistingEventModal(true); }, @@ -1132,8 +1224,19 @@ export default function Schedule() { {showExistingEventModal && ( // TODO : fix the issue with text input not working and changing the current functions into reusable reference functions { - setDeleteEventModal(false); + // if (selectedEvent.isRecurring) { + // setRecurrenceDeleteModal(false); + // } else { + // setDeleteEventModal(false); + // } + + // same thing + selectedEvent.isRecurring + ? setRecurrenceDeleteModal(false) + : setDeleteEventModal(false); }} handleOnPressDeleteConfirmation={async () => { // logic for deleting a particular event @@ -1142,7 +1245,7 @@ export default function Schedule() { ); // TODO : delete later, this is to experiment to check if the current event has been deleted or not console.log(`The updated events are : ${updatedEvents}`); - // set the newly updated event + // close the relevant modals after update has taken place setEventList(updatedEvents); setDeleteEventModal(false); setShowExistingEventModal(false); // close the event @@ -1249,6 +1352,8 @@ export default function Schedule() { setIsModalEditable(false); setShowExistingEventModal(false); }} + // slight variation based on whether event is recurring or not + // due to the need for the modals that needs to be displayed being different from one another onRequestDelete={async () => { // need to conditionally handle which modal to set true if (await selectedEvent.isRecurring) { @@ -1256,19 +1361,12 @@ export default function Schedule() { } else { setDeleteEventModal(true); } - // correct logic below for deleting a particular event - // // remove the event within the list whose current id matches the id of the currently selected event - // // include all other events except the current event containing matching id - // const updatedEvents = await eventsList.filter( - // (event) => event.id !== selectedEvent.id - // ); - // // TODO : delete later, this is to experiment to check if the current event has been deleted or not - // console.log(`The updated events are : ${updatedEvents}`); - // // set the newly updated event - // setEventList(updatedEvents); - // setShowExistingEventModal(false); // close the event }} handleCloseRecurringDeleteModal={() => setRecurrenceDeleteModal(false)} + listOfEvents={eventsList} + // props for radio button selection + radioButtonChangeHandler={handleRadioButtonOnChange} + radioButtonSelectorhandler={radioButtonSelectorUpdate} /> )} diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index 1969123..f20af71 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -90,18 +90,22 @@ export default DeleteSingleEvent; interface DeleteRecurringEventsType { visible: boolean; - onPressDeleteConfirmation: any; - onPressDeleteCancellation: any; + onPressDeleteConfirmation?: any; + onPressDeleteCancellation?: any; buttonStyling: any; recurrenceEventStyles: any; list_of_events: CalendarEvent[]; handleOnRequestModalClose: any; selectedEvent: CalendarEvent; + handleRadioButtonOnChange: any; // updates test-id of different radio buttons + handleOnSelectedRadioButtons: any; // updates status of whether a specific radio button has been selected (not really relevant) + handleRecurringEventDeletion: any; // TODO : prop drilling within ExistingEventModal needed } // TODO : implement this export const DeleteRecurringEvents = ({ visible, + // eslint-disable-next-line @typescript-eslint/no-unused-vars onPressDeleteConfirmation, onPressDeleteCancellation, buttonStyling, @@ -109,43 +113,81 @@ export const DeleteRecurringEvents = ({ list_of_events, handleOnRequestModalClose, selectedEvent, + handleRadioButtonOnChange, + handleOnSelectedRadioButtons, + handleRecurringEventDeletion, }: DeleteRecurringEventsType) => { // useState hook to handle what has been currently selected by a particular user. const [currentRadioButton, setCurrentRadioButton] = useState('all-event'); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_radioButtonSelected, setRadioButtonSelected] = useState(false); + const [radioButtonSelected, setRadioButtonSelected] = useState(false); + + // // helper functions to handle different instances of events that needs to be deleted + // const deleteAllEvents = async (originalEvent: any[], eventToDelete: any) => { + // // const parentEventID = eventToDelete.id; + // // NOTE the negation operator within the filter predicate + // // TODO : the logic for this isn't entirely correct, needs to be fixed + // if (eventToDelete.id.includes('recurring')) { + // const parentEventID = eventToDelete.parentId; + // return await originalEvent.filter((currentEvent) => !currentEvent.id.includes(parentEventID)); + // } + + // // otherwise, if it happens to be an original event itself rather than recurring one + // return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); + // }; - // helper functions to handle different instances of events that needs to be deleted - const deleteAllEvents = (originalEvent: any[], eventToDelete: any) => { - // const parentEventID = eventToDelete.id; - // NOTE the negation operator within the filter predicate - // TODO : the logic for this isn't entirely correct, needs to be fixed - return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); + const deleteCurrentEvent = async (listOfEvents: any[], eventToDelete: any) => { + return await listOfEvents.filter((event) => event.id !== eventToDelete.id); }; - // TODO : needs wrapped around a function - const handleRecurringEventDeletion = async () => { - switch (currentRadioButton) { - // this means user wants to delete all subsequent events - // invoke the function to delete all the events corresponding to the id - case 'all-event': - deleteAllEvents(list_of_events, selectedEvent); - break; - case 'subsequent': - deleteSubsequentEvents(list_of_events, selectedEvent); - break; - case 'current': - deleteCurrentEvent(list_of_events, selectedEvent); - break; - default: - break; + /** + * @listOfEvents : the array of objects containing information about the events themselves. + * @event : the event to be deleted, which will be passed in as a parameter + */ + const deleteSubsequentEvents = async (listOfEvents: any[], event: any) => { + // check if current event happens to be the recurring event + // otherwise, it's an original event (in which case we can go ahead and delete all events) + if (event.id.includes('recurring')) { + const event_id_array = event.id.split('_'); + const recurrence_unit = parseInt(event_id_array[event_id_array.length - 1]); + return listOfEvents.filter( + (currentEvent) => + !( + currentEvent.id.includes(event.parentEventId) && + parseInt(currentEvent.id.split('_')[currentEvent.id.split('_').length - 1]) > + recurrence_unit + ) + ); + } else { + await deleteAllEvents(listOfEvents, event); } }; + + // TODO : needs wrapped around a function + // migrated to Schedule.tsx + // const handleRecurringEventDeletion = async () => { + // switch (currentRadioButton) { + // // this means user wants to delete all subsequent events + // // invoke the function to delete all the events corresponding to the id + // // all three function variation will + // case 'all-event': + // await deleteAllEvents(list_of_events, selectedEvent); + // break; + // case 'subsequent': + // await deleteSubsequentEvents(list_of_events, selectedEvent); + // break; + // case 'current': + // await deleteCurrentEvent(list_of_events, selectedEvent); + // break; + // default: + // break; + // } + // }; // check to see if radio button is being updated correctly // TODO : delete later, this is primarily for debugging purposes - useEffect(() => { - console.log(`current selected radio-button : ${currentRadioButton}`); - }, [currentRadioButton]); + // useEffect(() => { + // console.log(`current selected radio-button : ${currentRadioButton}`); + // }, [currentRadioButton]); return ( { - setCurrentRadioButton(value); - }} + // old handler logic for selecting radio button + // has been migrated to parent component + // onSelected={(value: string) => { + // setCurrentRadioButton(value); + // }} + onSelected={handleRadioButtonOnChange} containerOptionStyle={{ margin: 5 }} radioBackground="#3498db" > { - setRadioButtonSelected(true); - }} + // old handler code logic + // onSelected={() => { + // setRadioButtonSelected(true); + // }} + + onSelected={handleOnSelectedRadioButtons} /> { - setRadioButtonSelected(true); - }} + onSelected={handleOnSelectedRadioButtons} label={This and the following events} /> { - setRadioButtonSelected(true); - }} + onSelected={handleOnSelectedRadioButtons} label={This Event Only} /> @@ -271,7 +315,8 @@ export const DeleteRecurringEvents = ({ marginRight: 10, }, ]} - onPress={onPressDeleteConfirmation} + // onPress={async () => await handleRecurringEventDeletion()} + onPress={handleRecurringEventDeletion} > Ok @@ -541,60 +586,21 @@ const deleteRecurringEvents = (originalEventsData: any[]) => { console.log(JSON.stringify(filtered_data)); }; -/** - * @originalEventsData : the array of objects containing information about the events themselves. - * @event : the event to be deleted, which will be passed in as a parameter - */ -const deleteSubsequentEvents = (originalEventsData: any[], event: any) => { - const event_id = event.id; - // console.log('Provided Event ID : ', event_id); - - const event_id_array = event_id.split('_'); - // console.log('Constructed array of event id : ', event_id_array); - - // construct the ID of the original event, since we also need to check if the event matches - // the 2nd and 3rd values wtihin the array will contain this information - const original_event_id = event_id_array[0] + '_' + event_id_array[1] + '_' + event_id_array[2]; - - // alternatively, the original event id can also be retrieved in the following method - if (event.id.includes('recurring')) { - const original_event_id = event.parentEventId; - // console.log('retrieved parent event id : ', original_event_id); - } - // console.log(`Retrieved original event id : ${original_event_id}`); - - // we want to check if the current event happens to be recurring - if (event_id.includes('recurring')) { - // NOTE : indexing by -1 doesn't seem to work in typescript arrays - // const currentRecurrenceUnit = event_id_array[-1]; // contains information about the particular recurrence number of the event - - // alternative - const currentRecurrenceUnit = parseInt(event_id_array[event_id_array.length - 1]); - // console.log(`Current Requccurence Unit : ${currentRecurrenceUnit}`); - - // filter logic - // not entirely sure if this logic will work - // const filtered_events = originalEventsData.filter((currentEvent) => !(currentEvent.id.includes(original_event_id) && parseInt(currentEvent.id.split('_')[-1]) > currentRecurrenceUnit)); - - // first remove the subsequent events (feel free to mess around with it) - // NOTE : it should be "greater than", not "greater than or equal to" - const filtered_events = originalEventsData.filter( - (currentEvent) => - !( - parseInt(currentEvent.id.split('_')[currentEvent.id.split('_').length - 1]) > - currentRecurrenceUnit - ) - ); - - // experimental check to remove the original event - // const filtered_events_2 = filtered_events.filter((currentEvent) => currentEvent.id !== original_event_id); - // console.log(filtered_events); - } else { - // assuming user chose the original event (which is the same as deleting all instances of the event) - // otherwise, we simply delete all the events - // console.log(deleteAllEvents(originalEventsData, event)); - } -}; +// /** +// * @originalEventsData : the array of objects containing information about the events themselves. +// * @event : the event to be deleted, which will be passed in as a parameter +// */ +// const deleteSubsequentEvents = (listOfEvents: any[], event: any) => { +// // check if current event happens to be the recurring event +// // otherwise, it's an original event (in which case we can go ahead and delete all events) +// if (event.id.includes("recurring")) { +// const event_id_array = event.id.split("_"); +// const recurrence_unit = parseInt(event_id_array[event_id_array.length - 1]); +// return listOfEvents.filter((currentEvent) => !(currentEvent.id.includes(event.parentEventId) && parseInt(currentEvent.id.split('_')[-1]) > recurrence_unit)); +// } else { +// deleteAllEvents(listOfEvents, event); +// } +// }; // this function should execute if user wants to delete all instances of the original event and subsequent recurring events // first we need the parent id corresponding to the event @@ -603,7 +609,6 @@ const deleteAllEvents = (originalEvent: any[], eventToDelete: any) => { // NOTE the negation operator within the filter predicate return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); }; -// deleteRecurringEvents(sample_data); const subsequent_event_to_delete = { id: 'event_1744902168035_736_recurring_9', @@ -638,4 +643,4 @@ const sample_original_event = { isRecurring: true, recurrence_frequency: 'Every Weekend', }; -deleteSubsequentEvents(sample_data, sample_original_event); +// deleteSubsequentEvents(sample_data, sample_original_event); From f917b2183b1806895ec4c45f5e0bf866e594ae0d Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Fri, 25 Apr 2025 21:26:42 -0400 Subject: [PATCH 12/15] chore:fixed event related issues. --- src/components/core/deleteModals/index.tsx | 74 +++++++--------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index f20af71..9c4851c 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -110,8 +110,10 @@ export const DeleteRecurringEvents = ({ onPressDeleteCancellation, buttonStyling, recurrenceEventStyles, + // eslint-disable-next-line @typescript-eslint/no-unused-vars list_of_events, handleOnRequestModalClose, + // eslint-disable-next-line @typescript-eslint/no-unused-vars selectedEvent, handleRadioButtonOnChange, handleOnSelectedRadioButtons, @@ -123,71 +125,48 @@ export const DeleteRecurringEvents = ({ const [radioButtonSelected, setRadioButtonSelected] = useState(false); // // helper functions to handle different instances of events that needs to be deleted - // const deleteAllEvents = async (originalEvent: any[], eventToDelete: any) => { - // // const parentEventID = eventToDelete.id; - // // NOTE the negation operator within the filter predicate - // // TODO : the logic for this isn't entirely correct, needs to be fixed - // if (eventToDelete.id.includes('recurring')) { - // const parentEventID = eventToDelete.parentId; - // return await originalEvent.filter((currentEvent) => !currentEvent.id.includes(parentEventID)); - // } + const deleteAllEvents = async () => { + // const parentEventID = eventToDelete.id; + // NOTE the negation operator within the filter predicate + // TODO : the logic for this isn't entirely correct, needs to be fixed + if (selectedEvent.id.includes('recurring')) { + const parentEventID = selectedEvent.parentEventId; + return await list_of_events.filter( + (currentEvent) => !currentEvent.id.includes(parentEventID) + ); + } - // // otherwise, if it happens to be an original event itself rather than recurring one - // return originalEvent.filter((currentEvent) => !currentEvent.id.includes(eventToDelete.id)); - // }; + // otherwise, if it happens to be an original event itself rather than recurring one + return list_of_events.filter((currentEvent) => !currentEvent.id.includes(selectedEvent.id)); + }; - const deleteCurrentEvent = async (listOfEvents: any[], eventToDelete: any) => { - return await listOfEvents.filter((event) => event.id !== eventToDelete.id); + // remove params, replace with the id corresponding to the event itself + const deleteCurrentEvent = async () => { + return await list_of_events.filter((event) => event.id !== selectedEvent.id); }; /** * @listOfEvents : the array of objects containing information about the events themselves. * @event : the event to be deleted, which will be passed in as a parameter */ - const deleteSubsequentEvents = async (listOfEvents: any[], event: any) => { + const deleteSubsequentEvents = async () => { // check if current event happens to be the recurring event // otherwise, it's an original event (in which case we can go ahead and delete all events) - if (event.id.includes('recurring')) { - const event_id_array = event.id.split('_'); + if (selectedEvent.id.includes('recurring')) { + const event_id_array = selectedEvent.id.split('_'); const recurrence_unit = parseInt(event_id_array[event_id_array.length - 1]); - return listOfEvents.filter( + return list_of_events.filter( (currentEvent) => !( - currentEvent.id.includes(event.parentEventId) && + currentEvent.id.includes(selectedEvent.parentEventId) && parseInt(currentEvent.id.split('_')[currentEvent.id.split('_').length - 1]) > recurrence_unit ) ); } else { - await deleteAllEvents(listOfEvents, event); + await deleteAllEvents(); } }; - - // TODO : needs wrapped around a function - // migrated to Schedule.tsx - // const handleRecurringEventDeletion = async () => { - // switch (currentRadioButton) { - // // this means user wants to delete all subsequent events - // // invoke the function to delete all the events corresponding to the id - // // all three function variation will - // case 'all-event': - // await deleteAllEvents(list_of_events, selectedEvent); - // break; - // case 'subsequent': - // await deleteSubsequentEvents(list_of_events, selectedEvent); - // break; - // case 'current': - // await deleteCurrentEvent(list_of_events, selectedEvent); - // break; - // default: - // break; - // } - // }; - // check to see if radio button is being updated correctly - // TODO : delete later, this is primarily for debugging purposes - // useEffect(() => { - // console.log(`current selected radio-button : ${currentRadioButton}`); - // }, [currentRadioButton]); return ( { - // setCurrentRadioButton(value); - // }} onSelected={handleRadioButtonOnChange} containerOptionStyle={{ margin: 5 }} radioBackground="#3498db" From 045c99f82db700732b284610732be5fc762f1974 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Sat, 26 Apr 2025 20:08:42 -0400 Subject: [PATCH 13/15] chore:working callback function logic --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 23 +++++---- src/components/core/deleteModals/index.tsx | 60 +++++++++++++++++----- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index 6130e10..ecd3d14 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -82,7 +82,6 @@ interface ExistingEventModal { handleDropdownFunction: any; renderDropdownItem: any; radioButtonChangeHandler: (() => void) | any; - radioButtonSelectorhandler: (() => void) | any; handleChangeEventColor: ((event: NativeSyntheticEvent) => void) | undefined | any; handleSaveEditedEvent: ((event: NativeSyntheticEvent) => void) | undefined | any; @@ -100,6 +99,7 @@ interface ExistingEventModal { | undefined | any; listOfEvents: any[]; + currentSelectedRadioButton: string; } // TODO : requires lots of refactoring, badly written composition code @@ -133,10 +133,15 @@ const ExistingEventModal = ({ handleCloseRecurringDeleteModal, listOfEvents, radioButtonChangeHandler, // prop drilling for handleRadioButtonOnChange - radioButtonSelectorhandler, // prop drilling for handleOnSelectedRadioButtons + + currentSelectedRadioButton, // this will handle the closing of the modal after recurring events have been chosen to be deleted or saved. }: ExistingEventModal) => { + const sample_event_data: string[] = ['This', 'is', 'an', 'array']; + const handleRecurringEventDeletionCallbackExperimental = async (updatedEventList: any[]) => { + console.log('asynchronous Callback function has been triggered'); + }; return ( + await handleRecurringEventDeletionCallbackExperimental(sample_event_data) + } + currentRadioButton={currentSelectedRadioButton} /> ) : ( { setCurrentRadioButton(value); }; + // determines if current radio button has been selected const radioButtonSelectorUpdate = () => { setRadioButtonSelected(true); }; - - // TODO : delete after - useEffect(() => { - console.log('current selected radio button value : ', currentRadioButton); - }, [currentRadioButton]); - const route_params = useLocalSearchParams(); // extract email (for payload) @@ -1365,6 +1369,7 @@ export default function Schedule() { handleCloseRecurringDeleteModal={() => setRecurrenceDeleteModal(false)} listOfEvents={eventsList} // props for radio button selection + currentSelectedRadioButton={currentRadioButton} radioButtonChangeHandler={handleRadioButtonOnChange} radioButtonSelectorhandler={radioButtonSelectorUpdate} /> diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index 9c4851c..633fb2e 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -98,8 +98,8 @@ interface DeleteRecurringEventsType { handleOnRequestModalClose: any; selectedEvent: CalendarEvent; handleRadioButtonOnChange: any; // updates test-id of different radio buttons - handleOnSelectedRadioButtons: any; // updates status of whether a specific radio button has been selected (not really relevant) - handleRecurringEventDeletion: any; // TODO : prop drilling within ExistingEventModal needed + handleRecurringEventDeletionCallback: any | undefined; // TODO : prop drilling within ExistingEventModal needed + currentRadioButton: string; } // TODO : implement this @@ -116,14 +116,9 @@ export const DeleteRecurringEvents = ({ // eslint-disable-next-line @typescript-eslint/no-unused-vars selectedEvent, handleRadioButtonOnChange, - handleOnSelectedRadioButtons, - handleRecurringEventDeletion, + currentRadioButton, + handleRecurringEventDeletionCallback, }: DeleteRecurringEventsType) => { - // useState hook to handle what has been currently selected by a particular user. - const [currentRadioButton, setCurrentRadioButton] = useState('all-event'); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [radioButtonSelected, setRadioButtonSelected] = useState(false); - // // helper functions to handle different instances of events that needs to be deleted const deleteAllEvents = async () => { // const parentEventID = eventToDelete.id; @@ -167,6 +162,32 @@ export const DeleteRecurringEvents = ({ await deleteAllEvents(); } }; + + const handleRecurringEventDeletionInternal = async () => { + try { + switch (currentRadioButton) { + case 'all-event': + await deleteAllEvents(); + console.log('Interval function has been trieggered!'); + break; + + case 'subsequent': + await deleteSubsequentEvents(); + console.log('Interval function has been trieggered!'); + break; + + case 'current': + await deleteCurrentEvent(); + console.log('Interval function has been trieggered!'); + break; + default: + break; + } + } catch (error) { + console.error('Error : ', error); + return error; + } + }; return ( { + // setRadioButtonSelected(true); + // }} + // onSelected={handleOnSelectedRadioButtons} label={This and the following events} /> { + // setRadioButtonSelected(true); + // }} + // onSelected={handleOnSelectedRadioButtons} label={This Event Only} /> @@ -290,7 +317,14 @@ export const DeleteRecurringEvents = ({ }, ]} // onPress={async () => await handleRecurringEventDeletion()} - onPress={handleRecurringEventDeletion} + // onPress={handleRecurringEventDeletion} + onPress={async () => { + const mutated_event = await handleRecurringEventDeletionInternal(); + + // take in a parameter, and then return the update + // mutated_event will be the output that is passed in as a parameter to the prop + await handleRecurringEventDeletionCallback(mutated_event); + }} > Ok From 0f182e75291666629f61620fea516d3075651d45 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Mon, 28 Apr 2025 21:08:31 -0400 Subject: [PATCH 14/15] feat:can pass data from child to parent components now, requires additional props. TODO : refactor prop drilling with useContext hooks instead. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 29 ++++++--- src/components/core/deleteModals/index.tsx | 72 ++++++++++------------ 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index ecd3d14..9bee7eb 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -100,6 +100,8 @@ interface ExistingEventModal { | any; listOfEvents: any[]; currentSelectedRadioButton: string; + setListOfEventsSecondChild: any; + set_delete_event_modal_recurring: any; } // TODO : requires lots of refactoring, badly written composition code @@ -130,8 +132,10 @@ const ExistingEventModal = ({ handleOnPressDeleteCancellation, delete_event_modal, delete_event_modal_recurring, + set_delete_event_modal_recurring, handleCloseRecurringDeleteModal, listOfEvents, + setListOfEventsSecondChild, radioButtonChangeHandler, // prop drilling for handleRadioButtonOnChange currentSelectedRadioButton, @@ -140,7 +144,8 @@ const ExistingEventModal = ({ }: ExistingEventModal) => { const sample_event_data: string[] = ['This', 'is', 'an', 'array']; const handleRecurringEventDeletionCallbackExperimental = async (updatedEventList: any[]) => { - console.log('asynchronous Callback function has been triggered'); + console.log('Retrieved updated event:\n'); + console.log(updatedEventList); }; return ( {current_event.isRecurring ? ( + // NOTE : child component, passing data from child to parent component back up - await handleRecurringEventDeletionCallbackExperimental(sample_event_data) - } + // TODO : Fix the logic here + // handleRecurringEventDeletionCallback={async () => + // // await handleRecurringEventDeletionCallbackExperimental(sample_event_data) + // } + + handleRecurringEventDeletionCallback={undefined} currentRadioButton={currentSelectedRadioButton} + setEventsList={setListOfEventsSecondChild} /> ) : ( { // console.log(`List of events : ${syntaxHighlight(JSON.stringify(eventsList))}`); - console.log(`List of events : ${JSON.stringify(eventsList)}`); - }, [eventsList]); + console.log(`Changes to Event List Detected : ${JSON.stringify(eventsList)}`); + console.log(`Detected changes to recurrence event modal : ${recurrenceDeleteModal}`); + }, [eventsList, recurrenceDeleteModal]); return ( setRecurrenceDeleteModal(false)} listOfEvents={eventsList} + setListOfEventsSecondChild={setEventList} // props for radio button selection currentSelectedRadioButton={currentRadioButton} radioButtonChangeHandler={handleRadioButtonOnChange} - radioButtonSelectorhandler={radioButtonSelectorUpdate} + // radioButtonSelectorhandler={radioButtonSelectorUpdate as any} /> )} diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index 633fb2e..f2a6129 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -1,8 +1,9 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ // requires 2 seperate imports -import { Modal, View, Text, TouchableOpacity, GestureResponderEvent } from 'react-native'; -import { useEffect, useState } from 'react'; +import { Modal, View, Text, TouchableOpacity } from 'react-native'; import RadioButtonGroup, { RadioButtonItem } from 'expo-radio-button'; import { CalendarEvent } from '@/app/(root)/(tabs)/(index)/Schedule'; +import VisibleDateProvider from '@howljs/calendar-kit/lib/typescript/context/VisibleDateProvider'; // interface for deleting single event interface DeleteSingleEventInterface { @@ -23,8 +24,6 @@ const DeleteSingleEvent = ({ { // // helper functions to handle different instances of events that needs to be deleted const deleteAllEvents = async () => { @@ -145,8 +148,6 @@ export const DeleteRecurringEvents = ({ * @event : the event to be deleted, which will be passed in as a parameter */ const deleteSubsequentEvents = async () => { - // check if current event happens to be the recurring event - // otherwise, it's an original event (in which case we can go ahead and delete all events) if (selectedEvent.id.includes('recurring')) { const event_id_array = selectedEvent.id.split('_'); const recurrence_unit = parseInt(event_id_array[event_id_array.length - 1]); @@ -159,26 +160,31 @@ export const DeleteRecurringEvents = ({ ) ); } else { + // NOTE : logical error here potentially await deleteAllEvents(); } }; const handleRecurringEventDeletionInternal = async () => { + let updated_event: any = []; try { switch (currentRadioButton) { case 'all-event': - await deleteAllEvents(); - console.log('Interval function has been trieggered!'); + updated_event = await deleteAllEvents(); + console.log('all-event case function has been trieggered!'); + console.log(`Updated Event Data : ${JSON.stringify(updated_event)}`); break; case 'subsequent': - await deleteSubsequentEvents(); - console.log('Interval function has been trieggered!'); + updated_event = await deleteSubsequentEvents(); + console.log('subsequent case function has been trieggered!'); + console.log(`Updated Event Data : ${JSON.stringify(updated_event)}`); break; case 'current': - await deleteCurrentEvent(); - console.log('Interval function has been trieggered!'); + updated_event = await deleteCurrentEvent(); + console.log('current case function has been trieggered!'); + console.log(`Updated Event Data : ${JSON.stringify(updated_event)}`); break; default: break; @@ -187,6 +193,9 @@ export const DeleteRecurringEvents = ({ console.error('Error : ', error); return error; } + + // return the updated event at the end + return updated_event; }; return ( - { - // setRadioButtonSelected(true); - // }} - - // onSelected={handleOnSelectedRadioButtons} - /> + { @@ -287,8 +284,6 @@ export const DeleteRecurringEvents = ({ }} > Cancel await handleRecurringEventDeletion()} - // onPress={handleRecurringEventDeletion} onPress={async () => { + // passing in + // internal component function const mutated_event = await handleRecurringEventDeletionInternal(); - // take in a parameter, and then return the update - // mutated_event will be the output that is passed in as a parameter to the prop - await handleRecurringEventDeletionCallback(mutated_event); + // NOTE : mutated event is returning undefined, something wrong with the code logic + console.log('Retrieved mutated event is : ', JSON.stringify(mutated_event)); + // upadting the event list after update to the event has been made + setEventsList(mutated_event); + console.log(`Toggling modal visibillity within child component.`); + setModalVisibillity(!visible); }} > Ok From ad5c687f8852db50d04626b853e93a5dd9b42a86 Mon Sep 17 00:00:00 2001 From: DeveloperMindset123 Date: Wed, 30 Apr 2025 16:32:38 -0400 Subject: [PATCH 15/15] fix: "This and subsequent" events logic for event deletion now working fully. --- src/app/(root)/(tabs)/(index)/Schedule.tsx | 215 ++++++--------------- src/components/core/deleteModals/index.tsx | 69 +++---- 2 files changed, 89 insertions(+), 195 deletions(-) diff --git a/src/app/(root)/(tabs)/(index)/Schedule.tsx b/src/app/(root)/(tabs)/(index)/Schedule.tsx index 9bee7eb..802c3c3 100644 --- a/src/app/(root)/(tabs)/(index)/Schedule.tsx +++ b/src/app/(root)/(tabs)/(index)/Schedule.tsx @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -// TODO : use react-native async storage instead to analyze the data -import AsyncStorage from '@react-native-async-storage/async-storage'; import { useCallback, useEffect, useState, useRef, useMemo } from 'react'; import { View, @@ -10,10 +8,8 @@ import { StyleSheet, TextInput, Alert, - // this is a wrapper around the modal, will ensure that modal is closed when the external area has been pressed TouchableWithoutFeedback, Platform, - Button, NativeSyntheticEvent, ActivityIndicator, } from 'react-native'; @@ -21,12 +17,9 @@ import { CalendarBody, CalendarContainer, CalendarHeader, - DraggingEvent, - DraggingEventProps, PackedEvent, LocaleConfigsProps, CalendarKitHandle, - EventItem, } from '@howljs/calendar-kit'; import { Ionicons, AntDesign } from '@expo/vector-icons'; import { Ionicon } from '@/components/core/icon'; @@ -38,7 +31,6 @@ import CalendarModeSwitcher, { import CalendarNavigation from '@/components/core/calendarNavigation'; import { useLocalSearchParams } from 'expo-router'; import DeleteSingleEvent, { DeleteRecurringEvents } from '@/components/core/deleteModals'; -import { Delete } from 'lucide-react-native'; // TODO : define the edit event and new event modal as seperate components and pass down data as a prop instead // TODO : add an interface referencing the event useState hook @@ -51,28 +43,23 @@ export interface CalendarEvent { color: string; location: string; isRecurring: boolean; - recurrence_frequency: any | string | undefined; // not entirely sure of the type + recurrence_frequency: any | string | undefined; isRecurringInstance?: boolean; parentEventId?: string | any; } -// experiment with the extension logic alongside a seperate independent interface to see which raises errors interface ExistingEventModal { - // input data for the current event related information the modal should render current_event: any; - - // useState variables that determines whether modal should be displayed or not + setEventListUseStateSetter: any; visibillity_state: boolean; delete_event_modal: boolean; delete_event_modal_recurring: boolean; isEditable: boolean; onRequestClose: ((event: NativeSyntheticEvent) => void) | undefined | any; onRequestDelete: ((event: NativeSyntheticEvent) => void) | undefined | any; - start_time: any; end_time: any; onRequestEdit: ((event: NativeSyntheticEvent) => void) | undefined | any; - handleOnChangeTitle: any; handleOnChangeDescription: any; handleOnChangeStart: any; @@ -82,7 +69,6 @@ interface ExistingEventModal { handleDropdownFunction: any; renderDropdownItem: any; radioButtonChangeHandler: (() => void) | any; - handleChangeEventColor: ((event: NativeSyntheticEvent) => void) | undefined | any; handleSaveEditedEvent: ((event: NativeSyntheticEvent) => void) | undefined | any; handleCancelEditedEvent: any; @@ -108,6 +94,7 @@ interface ExistingEventModal { // TODO : see how you can replace prop drilling with context since there are quite a few repetitive props const ExistingEventModal = ({ current_event, + setEventListUseStateSetter, visibillity_state, onRequestClose, isEditable, @@ -126,8 +113,6 @@ const ExistingEventModal = ({ handleCancelEditedEvent, start_time, end_time, - - // additional props to handle the confirmation/deletion of a particular event handleOnPressDeleteConfirmation, handleOnPressDeleteCancellation, delete_event_modal, @@ -136,17 +121,9 @@ const ExistingEventModal = ({ handleCloseRecurringDeleteModal, listOfEvents, setListOfEventsSecondChild, - radioButtonChangeHandler, // prop drilling for handleRadioButtonOnChange - + radioButtonChangeHandler, currentSelectedRadioButton, - - // this will handle the closing of the modal after recurring events have been chosen to be deleted or saved. }: ExistingEventModal) => { - const sample_event_data: string[] = ['This', 'is', 'an', 'array']; - const handleRecurringEventDeletionCallbackExperimental = async (updatedEventList: any[]) => { - console.log('Retrieved updated event:\n'); - console.log(updatedEventList); - }; return ( @@ -210,33 +182,23 @@ const ExistingEventModal = ({ position: 'absolute', right: 0, top: -2, - flexDirection: 'row', // so that 2 items can be placed side by side + flexDirection: 'row', }} > - {/** Change it such that instead of delete icon, there's instead edit icon available */} - + {current_event.isRecurring ? ( - // NOTE : child component, passing data from child to parent component back up - // // await handleRecurringEventDeletionCallbackExperimental(sample_event_data) - // } - handleRecurringEventDeletionCallback={undefined} currentRadioButton={currentSelectedRadioButton} setEventsList={setListOfEventsSecondChild} /> ) : ( @@ -394,13 +338,10 @@ const ExistingEventModal = ({ @@ -428,9 +369,6 @@ const ExistingEventModal = ({ )} @@ -484,7 +422,6 @@ const ExistingEventModal = ({ { backgroundColor: color }, current_event.color === color && eventColorStyling.selectedColor, ]} - // onFocus={} onPress={handleChangeEventColor} /> ))} @@ -610,6 +547,7 @@ export default function Schedule() { // reference to determine currently selected radio button const handleRadioButtonOnChange = (value: string) => { + console.log(`Selected radio button : ${value}`); setCurrentRadioButton(value); }; @@ -1035,10 +973,6 @@ export default function Schedule() { } const uniqueId = `event_${Date.now()}_${Math.floor(Math.random() * 1000)}`; - // replaced with string based value for easier identification - // const random_generated_id = Math.floor(Math.random() * 100 + 1); - // console.log(random_generated_id); - // check if the newly generated id happens to exist within the current event list // TODO : Handle issue regarding existence of potential duplicate generated events as well const id_existence = eventsList.findIndex( @@ -1051,22 +985,13 @@ export default function Schedule() { }; let eventsToAdd = []; - - // if user wants an event to be recurring based on a specific selected frequency if (newEvent.isRecurring && newEvent.recurrence_frequency) { - eventsToAdd = calculateRecurringEvents(newEvent); // append recurring events to eventsList + eventsToAdd = calculateRecurringEvents(newEvent); } else { - // otherwise, simply add a single instance copy of the particular event eventsToAdd = [newEvent]; } - - // double spread opearator - // since eventsToAdd can be more than one depending on recurrence frequency setEventList([...eventsList, ...eventsToAdd]); setNewEventModal(false); - - // reset current state data so we don't have to worry about it later - // NOTE : this would replace the logic for the handleCreateNewEvent function setCurrentEventData({ id: -1, title: '', @@ -1117,27 +1042,29 @@ export default function Schedule() { ); }; - // useCallback hook is being used as a wrapper around this reference function - // simple logic for handling recurring ev + // set regardless const handlePressEvent = useCallback( (event: CalendarEvent | any) => { // NOTE : the properties isRecurringInstance and parentEventId comes from the calculateRecurringEvents function // refer to the defintition of calculateRecurringEvents definition for reference - if (event.isRecurringInstance && event.parentEventId) { - // parentEvent : simply the original event set to be recurring - const parentEvent = eventsList.find((e: CalendarEvent) => e.id === event.parentEventId); - - if (parentEvent) { - setSelectedEvent(parentEvent); - } else { - setSelectedEvent(event); // otherwise, the original event should be set to selected - } - } else { - // TODO : this seems somewhat repetitive, find a fix for this - setSelectedEvent(event); - } - // TODO : delete this statement, this is just to check if the event update is working as intended - // setSelectedEvent(event); + // if (event.isRecurringInstance && event.parentEventId) { + // // parentEvent : simply the original event set to be recurring + + // + // // const parentEvent = eventsList.find((e: CalendarEvent) => e.id === event.parentEventId); + + // // if (parentEvent) { + // // setSelectedEvent(parentEvent); + // // } else { + // // setSelectedEvent(event); + // // } + + // } else { + // // TODO : this seems somewhat repetitive, find a fix for this + // setSelectedEvent(event); + // } + // console.log(`Selected Event : ${JSON.stringify(event)}`); + setSelectedEvent(event); setShowExistingEventModal(true); }, [eventsList] @@ -1149,14 +1076,14 @@ export default function Schedule() { events_data: eventsList, }; - // console.log('Events Payload Data Retrieved : ', events_payload); - - // TODO : fix this implementation useEffect(() => { - // console.log(`List of events : ${syntaxHighlight(JSON.stringify(eventsList))}`); - console.log(`Changes to Event List Detected : ${JSON.stringify(eventsList)}`); - console.log(`Detected changes to recurrence event modal : ${recurrenceDeleteModal}`); - }, [eventsList, recurrenceDeleteModal]); + console.log(`Selected Event : ${JSON.stringify(selectedEvent)}`); + }); + // useEffect(() => { + // console.log('Detected changes to event lists.'); + // console.log(eventsList); + // }, [eventsList]); + return ( {showExistingEventModal && ( - // TODO : fix the issue with text input not working and changing the current functions into reusable reference functions { - // if (selectedEvent.isRecurring) { - // setRecurrenceDeleteModal(false); - // } else { - // setDeleteEventModal(false); - // } - - // same thing selectedEvent.isRecurring ? setRecurrenceDeleteModal(false) : setDeleteEventModal(false); @@ -1275,6 +1183,7 @@ export default function Schedule() { end_time={endDate} start_time={startDate} // pass in the start and end date for the date time picker current_event={selectedEvent} + setEventListUseStateSetter={setEventList} visibillity_state={showExistingEventModal} onRequestClose={() => setShowExistingEventModal(false)} isEditable={isModalEditable} @@ -1288,14 +1197,8 @@ export default function Schedule() { ...prev, title: newUserInputTitle, })); - - // this wouldn't work due to asynchronous nature of the code - // setSelectedEvent(currentEventData); } }} - // TODO : change to a reference function for reusabillity - // TODO : fix this, the incorrect state is being updated here - // change from setCurrentEventData -> setSelectedEvent(prev => ...prev, { title : newUserInputTitle}) instead handleOnChangeDescription={(newUserInputDescription: any) => { if (isModalEditable) { setSelectedEvent((prevData: CalendarEvent) => ({ diff --git a/src/components/core/deleteModals/index.tsx b/src/components/core/deleteModals/index.tsx index f2a6129..5002d0c 100644 --- a/src/components/core/deleteModals/index.tsx +++ b/src/components/core/deleteModals/index.tsx @@ -90,6 +90,7 @@ export default DeleteSingleEvent; interface DeleteRecurringEventsType { visible: boolean; setModalVisibillity: any; + setEventsLists: any; onPressDeleteConfirmation?: any; onPressDeleteCancellation?: any; buttonStyling: any; @@ -97,8 +98,10 @@ interface DeleteRecurringEventsType { list_of_events: CalendarEvent[] | any[]; handleOnRequestModalClose: any; selectedEvent: CalendarEvent; - handleRadioButtonOnChange: any; // updates test-id of different radio buttons - handleRecurringEventDeletionCallback: any | undefined; // TODO : prop drilling within ExistingEventModal needed + handleRadioButtonOnChange: any; + + // TODO : prop drilling within ExistingEventModal needed + handleRecurringEventDeletionCallback: any | undefined; currentRadioButton: string; setEventsList: any; } @@ -107,6 +110,7 @@ interface DeleteRecurringEventsType { export const DeleteRecurringEvents = ({ visible, setModalVisibillity, + setEventsLists, // eslint-disable-next-line @typescript-eslint/no-unused-vars onPressDeleteConfirmation, onPressDeleteCancellation, @@ -122,36 +126,26 @@ export const DeleteRecurringEvents = ({ handleRecurringEventDeletionCallback, setEventsList, }: DeleteRecurringEventsType) => { - // // helper functions to handle different instances of events that needs to be deleted - const deleteAllEvents = async () => { - // const parentEventID = eventToDelete.id; - // NOTE the negation operator within the filter predicate + const deleteAllEvents = () => { // TODO : the logic for this isn't entirely correct, needs to be fixed if (selectedEvent.id.includes('recurring')) { const parentEventID = selectedEvent.parentEventId; - return await list_of_events.filter( - (currentEvent) => !currentEvent.id.includes(parentEventID) - ); + return list_of_events.filter((currentEvent) => !currentEvent.id.includes(parentEventID)); } - // otherwise, if it happens to be an original event itself rather than recurring one return list_of_events.filter((currentEvent) => !currentEvent.id.includes(selectedEvent.id)); }; - // remove params, replace with the id corresponding to the event itself const deleteCurrentEvent = async () => { return await list_of_events.filter((event) => event.id !== selectedEvent.id); }; - /** - * @listOfEvents : the array of objects containing information about the events themselves. - * @event : the event to be deleted, which will be passed in as a parameter - */ const deleteSubsequentEvents = async () => { + console.log(`Selected Event : ${JSON.stringify(selectedEvent)}`); if (selectedEvent.id.includes('recurring')) { const event_id_array = selectedEvent.id.split('_'); const recurrence_unit = parseInt(event_id_array[event_id_array.length - 1]); - return list_of_events.filter( + const filtered_events = list_of_events.filter( (currentEvent) => !( currentEvent.id.includes(selectedEvent.parentEventId) && @@ -159,43 +153,43 @@ export const DeleteRecurringEvents = ({ recurrence_unit ) ); + console.log(`Data from filtered events : ${JSON.stringify(filtered_events)}`); + return filtered_events; } else { - // NOTE : logical error here potentially - await deleteAllEvents(); + return await deleteAllEvents(); } }; const handleRecurringEventDeletionInternal = async () => { - let updated_event: any = []; + let updated_event_list: any = []; try { switch (currentRadioButton) { case 'all-event': - updated_event = await deleteAllEvents(); + updated_event_list = await deleteAllEvents(); console.log('all-event case function has been trieggered!'); - console.log(`Updated Event Data : ${JSON.stringify(updated_event)}`); break; case 'subsequent': - updated_event = await deleteSubsequentEvents(); + updated_event_list = await deleteSubsequentEvents(); console.log('subsequent case function has been trieggered!'); - console.log(`Updated Event Data : ${JSON.stringify(updated_event)}`); break; case 'current': - updated_event = await deleteCurrentEvent(); + updated_event_list = await deleteCurrentEvent(); console.log('current case function has been trieggered!'); - console.log(`Updated Event Data : ${JSON.stringify(updated_event)}`); break; default: break; } + + console.info(`Updated list of events : ${updated_event_list}`); + setEventsList(updated_event_list); + setModalVisibillity(!visible); } catch (error) { console.error('Error : ', error); return error; } - - // return the updated event at the end - return updated_event; + return updated_event_list; }; return ( { - // passing in - // internal component function - const mutated_event = await handleRecurringEventDeletionInternal(); + // onPress={() => { + // // const mutated_event = handleRecurringEventDeletionInternal(); + // // console.log('Retrieved mutated event is : ', JSON.stringify(mutated_event)); + // // setEventsList(mutated_event); + // // console.log(`Toggling modal visibillity within child component.`); + // // setModalVisibillity(!visible); - // NOTE : mutated event is returning undefined, something wrong with the code logic - console.log('Retrieved mutated event is : ', JSON.stringify(mutated_event)); - // upadting the event list after update to the event has been made - setEventsList(mutated_event); - console.log(`Toggling modal visibillity within child component.`); - setModalVisibillity(!visible); - }} + // }} + onPress={handleRecurringEventDeletionInternal} > Ok