From f7c121772ad4651313a38b739e4a1bdb09abf1e1 Mon Sep 17 00:00:00 2001 From: Dejan Date: Wed, 30 Oct 2024 23:37:43 +0100 Subject: [PATCH 1/2] - Reverted check if start time and end time overlap, caused an issue --- components/pages/calendar/ManuallyAddEventModal.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/components/pages/calendar/ManuallyAddEventModal.tsx b/components/pages/calendar/ManuallyAddEventModal.tsx index 888ec2f..326f4e2 100644 --- a/components/pages/calendar/ManuallyAddEventModal.tsx +++ b/components/pages/calendar/ManuallyAddEventModal.tsx @@ -383,10 +383,6 @@ export const ManuallyAddEventModal = () => { onChange={(time) => { if (time <= endTime) { setStartTime(time); - } else { - const currentTime = new Date(); - currentTime.setSeconds(0, 0); - setStartTime(currentTime); } }} minuteInterval={5} @@ -428,10 +424,6 @@ export const ManuallyAddEventModal = () => { onChange={(time) => { if (time >= endTime) { setEndTime(time); - } else { - const currentTime = new Date(); - currentTime.setSeconds(0, 0); - setEndTime(currentTime); } }} minimumDate={startTime} From 1417ec8f9bfadfeef5cb4b62d6aef5159000525c Mon Sep 17 00:00:00 2001 From: ivic00 <102467664+ivic00@users.noreply.github.com> Date: Wed, 30 Oct 2024 23:44:37 +0100 Subject: [PATCH 2/2] ui fixes --- assets/svgs/PlusIcon.tsx | 19 + components/pages/brain_dump/BrainDumpPage.tsx | 5 +- components/pages/calendar/AddEventDialog.tsx | 5 +- components/pages/grocery/AddGroceryItem.tsx | 10 +- components/pages/grocery/EditGroceryItem.tsx | 329 ++++++++++-------- components/pages/grocery/GroceryItem.tsx | 154 ++++---- components/pages/grocery/GroceryList.tsx | 5 +- components/pages/grocery/GroceryWrapper.tsx | 79 +++-- components/pages/todos/AddChore.tsx | 31 +- components/pages/todos/ToDoItem.tsx | 11 +- components/pages/todos/ToDosList.tsx | 287 ++++++++++----- components/pages/todos/ToDosPage.tsx | 1 - 12 files changed, 585 insertions(+), 351 deletions(-) create mode 100644 assets/svgs/PlusIcon.tsx diff --git a/assets/svgs/PlusIcon.tsx b/assets/svgs/PlusIcon.tsx new file mode 100644 index 0000000..5e9af64 --- /dev/null +++ b/assets/svgs/PlusIcon.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; +import Svg, { SvgProps, Path } from "react-native-svg"; +const PlusIcon = (props: SvgProps) => ( + + + +); +export default PlusIcon; diff --git a/components/pages/brain_dump/BrainDumpPage.tsx b/components/pages/brain_dump/BrainDumpPage.tsx index 979e9c9..98782c5 100644 --- a/components/pages/brain_dump/BrainDumpPage.tsx +++ b/components/pages/brain_dump/BrainDumpPage.tsx @@ -6,6 +6,7 @@ import HeaderTemplate from "@/components/shared/HeaderTemplate"; import {Feather, MaterialIcons} from "@expo/vector-icons"; import AddBrainDump from "./AddBrainDump"; import LinearGradient from "react-native-linear-gradient"; +import PlusIcon from "@/assets/svgs/PlusIcon"; const BrainDumpPage = () => { const [searchText, setSearchText] = useState(""); @@ -85,10 +86,10 @@ const BrainDumpPage = () => { }} > - + New diff --git a/components/pages/calendar/AddEventDialog.tsx b/components/pages/calendar/AddEventDialog.tsx index def5322..1eab83d 100644 --- a/components/pages/calendar/AddEventDialog.tsx +++ b/components/pages/calendar/AddEventDialog.tsx @@ -10,6 +10,7 @@ import CalendarIcon from "@/assets/svgs/CalendarIcon"; import NavToDosIcon from "@/assets/svgs/NavToDosIcon"; import {useSetAtom} from "jotai"; import {selectedNewEventDateAtom} from "@/components/pages/calendar/atoms"; +import PlusIcon from "@/assets/svgs/PlusIcon"; export const AddEventDialog = () => { const [show, setShow] = useState(false); @@ -50,8 +51,8 @@ export const AddEventDialog = () => { onPress={() => setShow(true)} > - - + + New diff --git a/components/pages/grocery/AddGroceryItem.tsx b/components/pages/grocery/AddGroceryItem.tsx index 5304a7a..ec5c7f2 100644 --- a/components/pages/grocery/AddGroceryItem.tsx +++ b/components/pages/grocery/AddGroceryItem.tsx @@ -3,6 +3,7 @@ import React from "react"; import {Button, View,} from "react-native-ui-lib"; import {useGroceryContext} from "@/contexts/GroceryContext"; import {FontAwesome6} from "@expo/vector-icons"; +import PlusIcon from "@/assets/svgs/PlusIcon"; const AddGroceryItem = () => { const {setIsAddingGrocery} = useGroceryContext(); @@ -11,12 +12,12 @@ const AddGroceryItem = () => { @@ -25,7 +26,7 @@ const AddGroceryItem = () => { backgroundColor="#fd1775" label="Add item" text70L - iconSource={() => } + iconSource={() => } style={styles.finishShopBtn} labelStyle={styles.addBtnLbl} enableShadow @@ -69,6 +70,7 @@ const styles = StyleSheet.create({ }, finishShopBtn: { width: "100%", + height: 53.26, }, shoppingBtn: { flex: 1, diff --git a/components/pages/grocery/EditGroceryItem.tsx b/components/pages/grocery/EditGroceryItem.tsx index 0aecb91..812e98d 100644 --- a/components/pages/grocery/EditGroceryItem.tsx +++ b/components/pages/grocery/EditGroceryItem.tsx @@ -1,167 +1,204 @@ -import {Text, TextField, TextFieldRef, View} from "react-native-ui-lib"; -import React, {useEffect, useRef} from "react"; -import {GroceryCategory, useGroceryContext} from "@/contexts/GroceryContext"; -import {Dropdown} from "react-native-element-dropdown"; +import { Text, TextField, TextFieldRef, View } from "react-native-ui-lib"; +import React, { useEffect, useRef } from "react"; +import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext"; +import { Dropdown } from "react-native-element-dropdown"; import CloseXIcon from "@/assets/svgs/CloseXIcon"; -import {StyleSheet} from "react-native"; +import { findNodeHandle, StyleSheet, UIManager } from "react-native"; import DropdownIcon from "@/assets/svgs/DropdownIcon"; -import {AntDesign} from "@expo/vector-icons"; +import { AntDesign } from "@expo/vector-icons"; interface IEditGrocery { - id?: string; - title: string; - category: GroceryCategory; - setTitle: (value: string) => void; - setCategory?: (category: GroceryCategory) => void; - setSubmit?: (value: boolean) => void; - closeEdit?: () => void; - handleEditSubmit?: Function; + id?: string; + title: string; + category: GroceryCategory; + setTitle: (value: string) => void; + setCategory?: (category: GroceryCategory) => void; + setSubmit?: (value: boolean) => void; + closeEdit?: () => void; + handleEditSubmit?: Function; } +const EditGroceryItem = ({ + editGrocery, + onInputFocus, +}: { + editGrocery: IEditGrocery; + onInputFocus: (y: number) => void; +}) => { + const { fuzzyMatchGroceryCategory } = useGroceryContext(); + const inputRef = useRef(null); + const containerRef = useRef(null); -const EditGroceryItem = ({editGrocery}: { editGrocery: IEditGrocery }) => { - const {fuzzyMatchGroceryCategory} = useGroceryContext(); - const inputRef = useRef(null); + const handleFocus = () => { + if (containerRef.current) { + const handle = findNodeHandle(containerRef.current); + if (handle) { + UIManager.measure( + handle, + ( + x: number, + y: number, + width: number, + height: number, + pageX: number, + pageY: number + ) => { + onInputFocus(pageY); + } + ); + } + } + }; - const groceryCategoryOptions = Object.values(GroceryCategory).map( - (category) => ({ - label: category, - value: category, - }) - ); + const groceryCategoryOptions = Object.values(GroceryCategory).map( + (category) => ({ + label: category, + value: category, + }) + ); - const handleSubmit = () => { - inputRef?.current?.blur() - console.log("CALLLLLL") - if (editGrocery.setSubmit) { - editGrocery.setSubmit(true); - } - if (editGrocery.handleEditSubmit) { - editGrocery.handleEditSubmit({ - id: editGrocery.id, - title: editGrocery.title, - category: editGrocery.category, - }); - } - if (editGrocery.closeEdit) { - editGrocery.closeEdit(); - } + const handleSubmit = () => { + inputRef?.current?.blur(); + console.log("CALLLLLL"); + if (editGrocery.setSubmit) { + editGrocery.setSubmit(true); + } + if (editGrocery.handleEditSubmit) { + editGrocery.handleEditSubmit({ + id: editGrocery.id, + title: editGrocery.title, + category: editGrocery.category, + }); + } + if (editGrocery.closeEdit) { + editGrocery.closeEdit(); + } + }; + + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); } - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } + console.log(editGrocery.category); + }, []); - console.log(editGrocery.category); - }, []); - - return ( - + + { + editGrocery.setTitle(value); + let groceryCategory = fuzzyMatchGroceryCategory(value); + if (editGrocery.setCategory) { + editGrocery.setCategory(groceryCategory); + } + }} + maxLength={25} + /> + + - - { - editGrocery.setTitle(value); - let groceryCategory = fuzzyMatchGroceryCategory(value); - if (editGrocery.setCategory) { - editGrocery.setCategory(groceryCategory); - } - }} - maxLength={25} - /> - - - { - if (editGrocery.closeEdit) { - editGrocery.closeEdit(); - } - }} - /> - - - ( - - )} - renderItem={(item) => { - return ( - - {item.label} - - ); - }} - onChange={(item) => { - if (editGrocery.handleEditSubmit) { - editGrocery.handleEditSubmit({ - id: editGrocery.id, - category: item.value, - }); - if (editGrocery.closeEdit) editGrocery.closeEdit(); - } else { - if (editGrocery.setCategory) { - editGrocery.setCategory(item.value); - } - } - }} - /> + onPress={handleSubmit} + /> + { + if (editGrocery.closeEdit) { + editGrocery.closeEdit(); + } + }} + /> - ); + + ( + + )} + renderItem={(item) => { + return ( + + {item.label} + + ); + }} + onChange={(item) => { + if (editGrocery.handleEditSubmit) { + editGrocery.handleEditSubmit({ + id: editGrocery.id, + category: item.value, + }); + if (editGrocery.closeEdit) editGrocery.closeEdit(); + } else { + if (editGrocery.setCategory) { + editGrocery.setCategory(item.value); + } + } + }} + /> + + ); }; const styles = StyleSheet.create({ - itemText: { - fontFamily: "Manrope_400Regular", - fontSize: 15.42, - paddingLeft: 15, - }, - selectedText: { - fontFamily: "Manrope_500Medium", - fontSize: 13.2, - color: "#fd1775", - }, - dropdownStyle: {borderRadius: 6.61, height: 115.34, width: 187}, - itemStyle: {padding: 0, margin: 0}, + itemText: { + fontFamily: "Manrope_400Regular", + fontSize: 15.42, + paddingLeft: 15, + }, + selectedText: { + fontFamily: "Manrope_500Medium", + fontSize: 13.2, + color: "#fd1775", + }, + dropdownStyle: { borderRadius: 6.61, height: 115.34, width: 187 }, + itemStyle: { padding: 0, margin: 0 }, }); export default EditGroceryItem; diff --git a/components/pages/grocery/GroceryItem.tsx b/components/pages/grocery/GroceryItem.tsx index e521e44..45427f6 100644 --- a/components/pages/grocery/GroceryItem.tsx +++ b/components/pages/grocery/GroceryItem.tsx @@ -1,21 +1,23 @@ -import {Checkbox, Text, TouchableOpacity, View} from "react-native-ui-lib"; -import React, {useEffect, useState} from "react"; -import {AntDesign} from "@expo/vector-icons"; -import {GroceryCategory, useGroceryContext} from "@/contexts/GroceryContext"; +import { Checkbox, Text, TouchableOpacity, View } from "react-native-ui-lib"; +import React, { useEffect, useState } from "react"; +import { AntDesign } from "@expo/vector-icons"; +import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext"; import EditGroceryFrequency from "./EditGroceryFrequency"; import EditGroceryItem from "./EditGroceryItem"; -import {ImageBackground, StyleSheet} from "react-native"; -import {IGrocery} from "@/hooks/firebase/types/groceryData"; +import { ImageBackground, StyleSheet } from "react-native"; +import { IGrocery } from "@/hooks/firebase/types/groceryData"; import firestore from "@react-native-firebase/firestore"; -import {UserProfile} from "@/hooks/firebase/types/profileTypes"; -import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; +import { UserProfile } from "@/hooks/firebase/types/profileTypes"; +import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; const GroceryItem = ({ item, handleItemApproved, + onInputFocus, }: { item: IGrocery; handleItemApproved: (id: string, changes: Partial) => void; + onInputFocus: (y: number) => void; }) => { const { updateGroceryItem } = useGroceryContext(); const { profileData } = useAuthContext(); @@ -24,12 +26,14 @@ const GroceryItem = ({ const [openFreqEdit, setOpenFreqEdit] = useState(false); const [isEditingTitle, setIsEditingTitle] = useState(false); const [newTitle, setNewTitle] = useState(item.title ?? ""); - const [category, setCategory] = useState(item.category ?? GroceryCategory.None); + const [category, setCategory] = useState( + item.category ?? GroceryCategory.None + ); const [itemCreator, setItemCreator] = useState(null); const closeEdit = () => { setIsEditingTitle(false); - } + }; const handleTitleChange = (newTitle: string) => { updateGroceryItem({ id: item?.id, title: newTitle }); @@ -82,57 +86,70 @@ const GroceryItem = ({ setOpenFreqEdit(false); }} /> - {isEditingTitle ? - : - - {isParent ? - setIsEditingTitle(true)}> - - {item.title} - - : + {isEditingTitle ? ( + + ) : ( + + {isParent ? ( + setIsEditingTitle(true)}> {item.title} - } - - } + + ) : ( + + {item.title} + + )} + + )} {!item.approved ? ( - {isParent && - <> - handleItemApproved(item.id, { approved: true }) : null} - /> - handleItemApproved(item.id, { approved: false }) : null} - /> - } + {isParent && ( + <> + handleItemApproved(item.id, { approved: true }) + : null + } + /> + handleItemApproved(item.id, { approved: false }) + : null + } + /> + + )} ) : ( - !isEditingTitle && isParent && ( + !isEditingTitle && + isParent && ( - {profileData?.pfp ? : + /> + ) : ( - - - {itemCreator ? getInitials(itemCreator.firstName, itemCreator.lastName) : ""} - + + {itemCreator + ? getInitials(itemCreator.firstName, itemCreator.lastName) + : ""} + + - } + )} Requested by {itemCreator?.firstName} diff --git a/components/pages/grocery/GroceryList.tsx b/components/pages/grocery/GroceryList.tsx index 7c495b7..b6ec6da 100644 --- a/components/pages/grocery/GroceryList.tsx +++ b/components/pages/grocery/GroceryList.tsx @@ -10,7 +10,7 @@ import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; import {IGrocery} from "@/hooks/firebase/types/groceryData"; import AddPersonIcon from "@/assets/svgs/AddPersonIcon"; -const GroceryList = () => { +const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => { const { groceries, updateGroceryItem, @@ -169,6 +169,7 @@ const GroceryList = () => { handleItemApproved={(id, changes) => updateGroceryItem({...changes, id: id}) } + onInputFocus={onInputFocus} /> )} keyExtractor={(item) => item.id.toString()} @@ -230,6 +231,7 @@ const GroceryList = () => { setSubmit: setSubmitted, closeEdit: () => setIsAddingGrocery(false) }} + onInputFocus={onInputFocus} /> )} @@ -254,6 +256,7 @@ const GroceryList = () => { handleItemApproved={(id, changes) => updateGroceryItem({...changes, id: id}) } + onInputFocus={onInputFocus} /> ) )} diff --git a/components/pages/grocery/GroceryWrapper.tsx b/components/pages/grocery/GroceryWrapper.tsx index fd91bd3..b93db37 100644 --- a/components/pages/grocery/GroceryWrapper.tsx +++ b/components/pages/grocery/GroceryWrapper.tsx @@ -1,42 +1,79 @@ -import { Dimensions, ScrollView } from "react-native"; +import { Dimensions, ScrollView, Keyboard, Platform } from "react-native"; import { View } from "react-native-ui-lib"; -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; import AddGroceryItem from "./AddGroceryItem"; import GroceryList from "./GroceryList"; import { useGroceryContext } from "@/contexts/GroceryContext"; const GroceryWrapper = () => { const { isAddingGrocery } = useGroceryContext(); - const scrollViewRef = useRef(null); // Reference to the ScrollView + const scrollViewRef = useRef(null); + const [keyboardHeight, setKeyboardHeight] = useState(0); + + useEffect(() => { + const keyboardWillShowListener = Keyboard.addListener( + Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow', + (e) => { + setKeyboardHeight(e.endCoordinates.height); + } + ); + + const keyboardWillHideListener = Keyboard.addListener( + Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide', + () => { + setKeyboardHeight(0); + } + ); + + return () => { + keyboardWillShowListener.remove(); + keyboardWillHideListener.remove(); + }; + }, []); useEffect(() => { if (isAddingGrocery && scrollViewRef.current) { scrollViewRef.current.scrollTo({ - y: 400, // Adjust this value to scroll a bit down (100 is an example) + y: 400, animated: true, }); } }, [isAddingGrocery]); + const handleInputFocus = (y: number) => { + if (scrollViewRef.current) { + // Get the window height + const windowHeight = Dimensions.get('window').height; + // Calculate the space we want to leave at the top + const topSpacing = 20; + + // Calculate the target scroll position: + // y (position of input) - topSpacing (space we want at top) + // if keyboard is shown, we need to account for its height + const scrollPosition = Math.max(0, y - topSpacing); + + scrollViewRef.current.scrollTo({ + y: scrollPosition, + animated: true, + }); + } + }; + return ( - - - - - - - - - - + <> + + + + + {!isAddingGrocery && } - + ); }; -export default GroceryWrapper; +export default GroceryWrapper; \ No newline at end of file diff --git a/components/pages/todos/AddChore.tsx b/components/pages/todos/AddChore.tsx index 4ba3fc8..0c66e8e 100644 --- a/components/pages/todos/AddChore.tsx +++ b/components/pages/todos/AddChore.tsx @@ -1,38 +1,46 @@ -import { StyleSheet } from "react-native"; +import { Dimensions, StyleSheet } from "react-native"; import React, { useState } from "react"; import { Button, ButtonSize, Text, View } from "react-native-ui-lib"; import { AntDesign } from "@expo/vector-icons"; import LinearGradient from "react-native-linear-gradient"; import AddChoreDialog from "./AddChoreDialog"; +import PlusIcon from "@/assets/svgs/PlusIcon"; const AddChore = () => { const [isVisible, setIsVisible] = useState(false); return ( - - {isVisible && } - + {isVisible && ( + + )} + ); }; @@ -53,8 +61,7 @@ const styles = StyleSheet.create({ }, button: { backgroundColor: "rgb(253, 23, 117)", - paddingVertical: 15, - paddingHorizontal: 30, + height: 53.26, borderRadius: 30, width: 335, }, diff --git a/components/pages/todos/ToDoItem.tsx b/components/pages/todos/ToDoItem.tsx index 7bc1446..55bb8c3 100644 --- a/components/pages/todos/ToDoItem.tsx +++ b/components/pages/todos/ToDoItem.tsx @@ -17,8 +17,13 @@ import AddChoreDialog from "@/components/pages/todos/AddChoreDialog"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers"; import RepeatIcon from "@/assets/svgs/RepeatIcon"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; +import { format } from "date-fns"; -const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { +const ToDoItem = (props: { + item: IToDo; + isSettings?: boolean; + is7Days?: boolean; +}) => { const { updateToDo } = useToDosContext(); const { data: members } = useGetFamilyMembers(); const { profileData } = useAuthContext(); @@ -55,6 +60,7 @@ const ToDoItem = (props: { item: IToDo; isSettings?: boolean }) => { return ( { return props.item.repeatType; } })()} + {props.item.date && + props.is7Days && + " / " + format(props.item.date, "EEEE")} )} diff --git a/components/pages/todos/ToDosList.tsx b/components/pages/todos/ToDosList.tsx index 6589427..431c293 100644 --- a/components/pages/todos/ToDosList.tsx +++ b/components/pages/todos/ToDosList.tsx @@ -14,40 +14,64 @@ import { IToDo } from "@/hooks/firebase/types/todoData"; const groupToDosByDate = (toDos: IToDo[]) => { let sortedTodos = toDos.sort((a, b) => a.date - b.date); - return sortedTodos.reduce((groups, toDo) => { - let dateKey; + return sortedTodos.reduce( + (groups, toDo) => { + let dateKey; + let subDateKey; - const isNext7Days = (date: Date) => { - const today = new Date(); - return isWithinInterval(date, { start: today, end: addDays(today, 7) }); - }; + const isNext7Days = (date: Date) => { + const today = new Date(); + return isWithinInterval(date, { start: today, end: addDays(today, 7) }); + }; - const isNext30Days = (date: Date) => { - const today = new Date(); - return isWithinInterval(date, { start: today, end: addDays(today, 30) }); - }; + const isNext30Days = (date: Date) => { + const today = new Date(); + return isWithinInterval(date, { + start: today, + end: addDays(today, 30), + }); + }; - if (toDo.date === null) { - dateKey = "No Date"; - } else if (isToday(toDo.date)) { - dateKey = "Today"; - } else if (isTomorrow(toDo.date)) { - dateKey = "Tomorrow"; - } else if (isNext7Days(toDo.date)) { - dateKey = "Next 7 Days"; - } else if (isNext30Days(toDo.date)) { - dateKey = "Next 30 Days"; - } else { - dateKey = format(toDo.date, "EEE MMM dd"); + if (toDo.date === null) { + dateKey = "No Date"; + } else if (isToday(toDo.date)) { + dateKey = "Today"; + } else if (isTomorrow(toDo.date)) { + dateKey = "Tomorrow"; + } else if (isNext7Days(toDo.date)) { + dateKey = "Next 7 Days"; + } else if (isNext30Days(toDo.date)) { + dateKey = "Next 30 Days"; + subDateKey = format(toDo.date, "MMM d"); + } else { + return groups; + } + + if (!groups[dateKey]) { + groups[dateKey] = { + items: [], + subgroups: {}, + }; + } + + if (dateKey === "Next 30 Days" && subDateKey) { + if (!groups[dateKey].subgroups[subDateKey]) { + groups[dateKey].subgroups[subDateKey] = []; + } + groups[dateKey].subgroups[subDateKey].push(toDo); + } else { + groups[dateKey].items.push(toDo); + } + + return groups; + }, + {} as { + [key: string]: { + items: IToDo[]; + subgroups: { [key: string]: IToDo[] }; + }; } - - if (!groups[dateKey]) { - groups[dateKey] = []; - } - - groups[dateKey].push(toDo); - return groups; - }, {} as { [key: string]: IToDo[] }); + ); }; const ToDosList = ({ isSettings }: { isSettings?: boolean }) => { @@ -67,11 +91,139 @@ const ToDosList = ({ isSettings }: { isSettings?: boolean }) => { })); }; - const noDateToDos = groupedToDos["No Date"] || []; + const noDateToDos = groupedToDos["No Date"]?.items || []; const datedToDos = Object.keys(groupedToDos).filter( (key) => key !== "No Date" ); + const renderTodoGroup = (dateKey: string) => { + const isExpanded = expandedGroups[dateKey] || false; + + if (dateKey === "Next 30 Days") { + const subgroups = Object.entries(groupedToDos[dateKey].subgroups).sort( + ([dateA], [dateB]) => { + const dateAObj = new Date(dateA); + const dateBObj = new Date(dateB); + return dateAObj.getTime() - dateBObj.getTime(); + } + ); + + return ( + + toggleExpand(dateKey)} + style={{ + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + paddingHorizontal: 0, + marginBottom: 4, + marginTop: 15, + }} + > + + {dateKey} + + + + + {isExpanded && + subgroups.map(([subDate, items]) => { + const sortedItems = [ + ...items.filter((toDo) => !toDo.done), + ...items.filter((toDo) => toDo.done), + ]; + + return ( + + + + {subDate} + + + + {sortedItems.map((item) => ( + + ))} + + ); + })} + + ); + } + + const sortedToDos = [ + ...groupedToDos[dateKey].items.filter((toDo) => !toDo.done), + ...groupedToDos[dateKey].items.filter((toDo) => toDo.done), + ]; + + return ( + + toggleExpand(dateKey)} + style={{ + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + paddingHorizontal: 0, + marginBottom: 4, + marginTop: 15, + }} + > + + {dateKey} + + + + + {isExpanded && + sortedToDos.map((item) => ( + + ))} + + ); + }; + return ( {noDateToDos.length > 0 && ( @@ -85,26 +237,14 @@ const ToDosList = ({ isSettings }: { isSettings?: boolean }) => { > Unscheduled - {!expandNoDate && ( - { - setExpandNoDate(!expandNoDate); - }} - /> - )} - {expandNoDate && ( - { - setExpandNoDate(!expandNoDate); - }} - /> - )} + { + setExpandNoDate(!expandNoDate); + }} + /> {expandNoDate && noDateToDos @@ -115,50 +255,7 @@ const ToDosList = ({ isSettings }: { isSettings?: boolean }) => { )} - {datedToDos.map((dateKey) => { - const isExpanded = expandedGroups[dateKey] || false; - const sortedToDos = [ - ...groupedToDos[dateKey].filter((toDo) => !toDo.done), - ...groupedToDos[dateKey].filter((toDo) => toDo.done), - ]; - - return ( - - toggleExpand(dateKey)} - style={{ - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - paddingHorizontal: 0, - marginBottom: 4, - marginTop: 15, - }} - > - - {dateKey} - - {!isExpanded && ( - - )} - {isExpanded && ( - - )} - - - {isExpanded && - sortedToDos.map((item) => ( - - ))} - - ); - })} + {datedToDos.map(renderTodoGroup)} ); }; diff --git a/components/pages/todos/ToDosPage.tsx b/components/pages/todos/ToDosPage.tsx index b8ab4c6..590a901 100644 --- a/components/pages/todos/ToDosPage.tsx +++ b/components/pages/todos/ToDosPage.tsx @@ -32,7 +32,6 @@ const ToDosPage = () => { >