import {Text, TouchableOpacity, View} from "react-native-ui-lib"; import React, {useEffect, useState} from "react"; import {useToDosContext} from "@/contexts/ToDosContext"; import ToDoItem from "./ToDoItem"; import {addDays, format, isToday, isTomorrow, isWithinInterval,} from "date-fns"; import {AntDesign} from "@expo/vector-icons"; import {IToDo} from "@/hooks/firebase/types/todoData"; import DropdownIcon from "@/assets/svgs/DropdownIcon"; import {Dropdown} from "react-native-element-dropdown"; import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; import {ActivityIndicator, Dimensions, StyleSheet} from "react-native"; import {UserProfile} from "@/hooks/firebase/types/profileTypes"; const FILTER_OPTIONS = { ME: "Me", EVERYONE: "Everyone" }; const groupToDosByDate = (toDos: IToDo[]) => { let sortedTodos = toDos.sort((a, b) => { const dateA = a.date === null ? new Date() : a.date; const dateB = b.date === null ? new Date() : b.date; return dateA - dateB; }); 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 isNext30Days = (date: Date) => { const today = new Date(); return isWithinInterval(date, { start: today, end: addDays(today, 30), }); }; const isOverdue = (date: Date) => { const today = new Date(); today.setHours(0, 0, 0, 0); return date < today; }; if (isOverdue(toDo.date) && !toDo.done) { dateKey = "Overdue"; } else if (toDo.date === null || 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 { dateKey = "Later"; subDateKey = format(toDo.date, "MMM d, yyyy"); } if (!groups[dateKey]) { groups[dateKey] = { items: [], subgroups: {}, }; } if ((dateKey === "Next 30 Days" || dateKey === "Later") && 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[] }; }; } ); }; const resolveFilterOptions = (members, user) => { let options = []; members?.forEach((member) => { let label = member?.firstName; if (member.uid === user?.uid) { label = FILTER_OPTIONS.ME; } if (member.userType !== ProfileType.FAMILY_DEVICE) { options.push({value: member?.uid, label: label}); } }); options.push({value: FILTER_OPTIONS.EVERYONE, label: FILTER_OPTIONS.EVERYONE}) return options; } const ToDosList = ({ isSettings, members }: { isSettings?: boolean, members?: Array }) => { const { toDos } = useToDosContext(); const [localTodos, setLocalTodos] = useState([]); useEffect(() => { setLocalTodos(toDos); }, [toDos]); const [groupedToDos, setGroupedToDos] = useState([]); const { user } = useAuthContext() const [expandedGroups, setExpandedGroups] = useState<{ [key: string]: boolean; }>({}); const [expandNoDate, setExpandNoDate] = useState(true); const [selectedFilter, setSelectedFilter] = useState(null); const [filterOptions, setFilterOptions] = useState([]); useEffect(() => { let options = resolveFilterOptions(members, user); setFilterOptions(options); let selectedOption = options?.find((option) => option.value === user?.uid); setSelectedFilter(selectedOption); }, [JSON.stringify(members)]); useEffect(() => { if (localTodos && selectedFilter) { let resolvedGroupedTodos; if (selectedFilter?.value === FILTER_OPTIONS.EVERYONE) { let filtered = localTodos?.filter((todo) => todo.assignees?.some(assigneeId => members.some(member => member.uid === assigneeId) ) ); resolvedGroupedTodos = groupToDosByDate(filtered ?? []); } else { let filtered = localTodos?.filter((todo) => todo.assignees?.includes(selectedFilter.value)) || []; resolvedGroupedTodos = groupToDosByDate(filtered || []); } setGroupedToDos(resolvedGroupedTodos || []); } }, [selectedFilter, JSON.stringify(localTodos)]); const toggleExpand = (dateKey: string) => { setExpandedGroups((prev) => ({ ...prev, [dateKey]: !prev[dateKey], })); }; const noDateToDos = groupedToDos["No Date"]?.items || []; const datedToDos = Object.keys(groupedToDos) .filter((key) => key !== "No Date") .sort((a, b) => { const order = ["Overdue", "Today", "Tomorrow", "Next 7 Days", "Next 30 Days", "Later"]; return order.indexOf(a) - order.indexOf(b); }); const renderTodoGroup = (dateKey: string) => { const isExpanded = expandedGroups[dateKey] || false; if (dateKey === "Next 30 Days" || dateKey === "Later") { 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) => ( ))} ); }; const containerHeight = Dimensions.get('screen').height; return ( {!toDos && } ( )} renderItem={(item, selected) => { return ( {item.label} ); }} onChange={(item) => { setSelectedFilter(item); }} /> {noDateToDos.length > 0 && ( Unscheduled { setExpandNoDate(!expandNoDate); }} /> {expandNoDate && noDateToDos .sort((a, b) => Number(a.done) - Number(b.done)) .map((item) => ( ))} )} {datedToDos.map(renderTodoGroup)} ); }; 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, minHeight: 10, maxHeight: "100%", width: 187 }, itemStyle: { padding: 0, margin: 0 }, loaderContainer: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', alignItems: 'center', zIndex: 999, }, }); export default ToDosList;