diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx
index aa94045..f5ac187 100644
--- a/app/(auth)/_layout.tsx
+++ b/app/(auth)/_layout.tsx
@@ -320,20 +320,14 @@ export default function TabLayout() {
name="index"
options={{
drawerLabel: "Calendar",
- title:
- Device.deviceType === DeviceType.TABLET
- ? "Family Calendar"
- : "Calendar",
+ title: "Calendar",
}}
/>
diff --git a/app/(auth)/calendar/index.tsx b/app/(auth)/calendar/index.tsx
index b93837c..d1152d9 100644
--- a/app/(auth)/calendar/index.tsx
+++ b/app/(auth)/calendar/index.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect } from "react";
import {RefreshControl, ScrollView, View} from "react-native";
import CalendarPage from "@/components/pages/calendar/CalendarPage";
import TabletCalendarPage from "@/components/pages/(tablet_pages)/calendar/TabletCalendarPage";
@@ -6,10 +6,20 @@ import * as Device from "expo-device";
import {DeviceType} from "expo-device";
import {useCalSync} from "@/hooks/useCalSync";
import {colorMap} from "@/constants/colorMap";
+import { useSetAtom } from "jotai";
+import { selectedUserAtom } from "@/components/pages/calendar/atoms";
+import { useAuthContext } from "@/contexts/AuthContext";
export default function Screen() {
const isTablet = Device.deviceType === DeviceType.TABLET;
const {resyncAllCalendars, isSyncing} = useCalSync();
+ const setSelectedUser = useSetAtom(selectedUserAtom);
+ const {profileData} = useAuthContext()
+
+ useEffect(() => {
+ if(!isTablet && profileData) setSelectedUser({firstName: profileData.firstName, lastName: profileData.lastName, eventColor: profileData.eventColor})
+ }, [])
+
const onRefresh = React.useCallback(async () => {
try {
diff --git a/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx b/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx
index 8bdb044..38f246f 100644
--- a/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx
+++ b/components/pages/(tablet_pages)/chores/SingleUserChoreList.tsx
@@ -39,7 +39,15 @@ const groupToDosByDate = (toDos: IToDo[]) => {
});
};
- if (toDo.date === null || isToday(toDo.date)) {
+ 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";
@@ -49,7 +57,8 @@ const groupToDosByDate = (toDos: IToDo[]) => {
dateKey = "Next 30 Days";
subDateKey = format(toDo.date, "MMM d");
} else {
- return groups;
+ dateKey = "Later";
+ subDateKey = format(toDo.date, "MMM d, yyyy");
}
if (!groups[dateKey]) {
@@ -59,7 +68,7 @@ const groupToDosByDate = (toDos: IToDo[]) => {
};
}
- if (dateKey === "Next 30 Days" && subDateKey) {
+ if ((dateKey === "Next 30 Days" || dateKey === "Later") && subDateKey) {
if (!groups[dateKey].subgroups[subDateKey]) {
groups[dateKey].subgroups[subDateKey] = [];
}
@@ -103,14 +112,17 @@ const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
};
const noDateToDos = groupedToDos["No Date"]?.items || [];
- const datedToDos = Object.keys(groupedToDos).filter(
- (key) => key !== "No Date"
- );
+ 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") {
+ if (dateKey === "Next 30 Days" || dateKey === "Later") {
const subgroups = Object.entries(groupedToDos[dateKey].subgroups).sort(
([dateA], [dateB]) => {
const dateAObj = new Date(dateA);
diff --git a/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx b/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx
index 160f79a..6d99bf5 100644
--- a/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx
+++ b/components/pages/(tablet_pages)/chores/TabletChoresPage.tsx
@@ -5,14 +5,16 @@ import TabletContainer from "../tablet_components/TabletContainer";
import SingleUserChoreList from "./SingleUserChoreList";
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import { ImageBackground, StyleSheet } from "react-native";
-import { colorMap } from "@/constants/colorMap";
import { ScrollView } from "react-native-gesture-handler";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import AddChore from "../../todos/AddChore";
+import { useAtom } from "jotai";
+import { selectedUserAtom } from "@/components/pages/calendar/atoms";
const TabletChoresPage = () => {
const {data: users} = useGetFamilyMembers();
const { user: currentUser } = useAuthContext();
+ const [selectedUser] = useAtom(selectedUserAtom);
const sortedUsers = useMemo(() => {
return users
@@ -31,79 +33,77 @@ const TabletChoresPage = () => {
});
}, [users, currentUser]);
- // Function to lock the screen orientation to landscape
const lockScreenOrientation = async () => {
await ScreenOrientation.lockAsync(
ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT
);
};
- useEffect(() => {
- lockScreenOrientation(); // Lock orientation when the component mounts
+ useEffect(() => {
+ lockScreenOrientation();
- return () => {
- // Optional: Unlock to default when the component unmounts
- ScreenOrientation.unlockAsync();
+ return () => {
+ ScreenOrientation.unlockAsync();
+ };
+ }, []);
+
+ const capitalizeFirstLetter = (str: string) => {
+ if (!str) return "";
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
};
- }, []);
- const capitalizeFirstLetter = (str: string) => {
- if (!str) return "";
- return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
- };
-
- return (
-
-
-
- {sortedUsers
- ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE)
- .map((user, index) => (
-
-
- {user.pfp ? (
-
- ) : (
-
-
- {user.firstName.at(0)}
- {user.lastName.at(0)}
-
-
- )}
-
- {user.firstName}
-
-
- ({capitalizeFirstLetter(user.userType)})
-
-
-
+ return (
+
+
+
+ {sortedUsers
+ ?.filter((member) =>
+ !selectedUser ||
+ selectedUser.uid === 'family-view' ||
+ selectedUser.uid === member.uid
+ )
+ .map((user, index) => (
+
+
+ {user.pfp ? (
+
+ ) : (
+
+
+ {user.firstName.at(0)}
+ {user.lastName.at(0)}
+
+
+ )}
+
+ {user.firstName}
+
+
+ ({capitalizeFirstLetter(user.userType)})
+
+
+
+
+ ))}
+
+
+
+
- ))}
-
-
-
-
-
-
- );
+
+ );
};
const styles = StyleSheet.create({
@@ -118,10 +118,10 @@ const styles = StyleSheet.create({
color: "#2c2c2c",
},
addBtn: {
- position: 'absolute',
- bottom: 50,
- right: 220
+ position: 'absolute',
+ bottom: 50,
+ right: 220
}
});
-export default TabletChoresPage;
+export default TabletChoresPage;
\ No newline at end of file
diff --git a/components/pages/(tablet_pages)/tablet_components/UsersList.tsx b/components/pages/(tablet_pages)/tablet_components/UsersList.tsx
index bf81cb1..706e0d5 100644
--- a/components/pages/(tablet_pages)/tablet_components/UsersList.tsx
+++ b/components/pages/(tablet_pages)/tablet_components/UsersList.tsx
@@ -1,37 +1,42 @@
-import { View, Text } from "react-native-ui-lib";
+import { View, Text, TouchableOpacity } from "react-native-ui-lib";
import React, { useEffect, useMemo } from "react";
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import { ImageBackground, StyleSheet } from "react-native";
import { colorMap } from "@/constants/colorMap";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
-import { ScrollView } from "react-native-gesture-handler";
+import { useAtom } from "jotai";
+import { selectedUserAtom } from "@/components/pages/calendar/atoms";
const UsersList = () => {
const { user: currentUser } = useAuthContext();
const { data: familyMembers, refetch: refetchFamilyMembers } =
useGetFamilyMembers();
+ const [selectedUser, setSelectedUser] = useAtom(selectedUserAtom);
useEffect(() => {
refetchFamilyMembers();
}, []);
const sortedMembers = useMemo(() => {
- return familyMembers
- ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE)
- .sort((a, b) => {
- // Current user first
- if (a.uid === currentUser?.uid) return -1;
- if (b.uid === currentUser?.uid) return 1;
-
- // Then sort by user type priority
- const typePriority = {
- [ProfileType.PARENT]: 0,
- [ProfileType.CHILD]: 1,
- [ProfileType.CAREGIVER]: 2,
- };
-
- return typePriority[a.userType] - typePriority[b.userType];
- });
+ const filtered = familyMembers
+ ?.filter((member) => member.userType !== ProfileType.FAMILY_DEVICE);
+
+ const currentUserData = filtered?.find(m => m.uid === currentUser?.uid);
+ const parents = filtered?.filter(m => m.userType === ProfileType.PARENT && m.uid !== currentUser?.uid) || [];
+ const children = filtered?.filter(m => m.userType === ProfileType.CHILD && m.uid !== currentUser?.uid) || [];
+ const caregivers = filtered?.filter(m => m.userType === ProfileType.CAREGIVER && m.uid !== currentUser?.uid) || [];
+
+ const familyViewOption = {
+ uid: 'family-view',
+ firstName: 'Family',
+ lastName: 'View',
+ userType: 'Family View',
+ eventColor: colorMap.pink
+ };
+
+ return currentUserData
+ ? [currentUserData, ...parents, ...children, familyViewOption, ...caregivers]
+ : [...parents, ...children, familyViewOption, ...caregivers];
}, [familyMembers, currentUser]);
const capitalizeFirstLetter = (str: string) => {
@@ -39,37 +44,54 @@ const UsersList = () => {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
};
+ useEffect(() => {
+ console.log(selectedUser);
+ }, [selectedUser]);
+
return (
- {sortedMembers?.map((member, index) => (
-
- {member.pfp ? (
-
- ) : (
-
-
- {member.firstName.at(0)}
- {member.lastName.at(0)}
-
-
- )}
- {member.firstName}
-
- {capitalizeFirstLetter(member.userType)}
-
-
- ))}
+ {sortedMembers?.map((member, index) => (
+ {
+ if (member.uid === 'family-view') {
+ setSelectedUser(null);
+ } else {
+ setSelectedUser(selectedUser?.uid === member.uid ? null : member);
+ }
+ }}
+ style={[
+ styles.memberContainer,
+ selectedUser?.uid === member.uid && styles.selectedMember,
+ ]}
+ >
+ {member.pfp ? (
+
+ ) : (
+
+
+ {member.firstName.at(0)}
+ {member.lastName.at(0)}
+
+
+ )}
+ {member.firstName}
+
+ {capitalizeFirstLetter(member.userType)}
+
+
+ ))}
);
};
@@ -91,6 +113,13 @@ const styles = StyleSheet.create({
color: "#9b9b9b",
marginBottom: 20,
},
+ memberContainer: {
+ alignItems: "center",
+ marginBottom: 20,
+ },
+ selectedMember: {
+ opacity: 1,
+ },
});
export default UsersList;
diff --git a/components/pages/calendar/EventCalendar.tsx b/components/pages/calendar/EventCalendar.tsx
index b95f612..0fb5c90 100644
--- a/components/pages/calendar/EventCalendar.tsx
+++ b/components/pages/calendar/EventCalendar.tsx
@@ -11,6 +11,7 @@ import {
modeAtom,
selectedDateAtom,
selectedNewEventDateAtom,
+ selectedUserAtom,
} from "@/components/pages/calendar/atoms";
import {useAuthContext} from "@/contexts/AuthContext";
import {CalendarEvent} from "@/components/pages/calendar/interfaces";
@@ -21,6 +22,8 @@ import {useSyncEvents} from "@/hooks/useSyncOnScroll";
import {colorMap} from "@/constants/colorMap";
import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers";
import CachedImage from "expo-cached-image";
+import { DeviceType } from "expo-device";
+import * as Device from "expo-device"
interface EventCalendarProps {
calendarHeight: number;
@@ -377,6 +380,9 @@ export const EventCalendar: React.FC = React.memo(
const [mode, setMode] = useAtom(modeAtom);
const [isFamilyView] = useAtom(isFamilyViewAtom);
+ //tablet view filter
+ const [selectedUser] = useAtom(selectedUserAtom);
+
const setEditVisible = useSetAtom(editVisibleAtom);
const [isAllDay, setIsAllDay] = useAtom(isAllDayAtom);
const setEventForEdit = useSetAtom(eventForEditAtom);
@@ -488,8 +494,17 @@ export const EventCalendar: React.FC = React.memo(
const startOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1;
const endOffset = mode === "month" ? 40 : (mode === "week" || mode === "3days") ? 10 : 1;
+ let eventsToFilter = events;
+
+ if (selectedUser && Device.deviceType === DeviceType.TABLET) {
+ eventsToFilter = events?.filter(event =>
+ event.attendees?.includes(selectedUser.uid) ||
+ event.creatorId === selectedUser.uid
+ );
+ }
+
const filteredEvents =
- events?.filter(
+ eventsToFilter?.filter(
(event) =>
event.start &&
event.end &&
@@ -521,7 +536,7 @@ export const EventCalendar: React.FC = React.memo(
// console.log("memoizedEvents computation time:", endTime - startTime, "ms");
return {enrichedEvents, filteredEvents};
- }, [events, selectedDate, mode]);
+ }, [events, selectedDate, mode, selectedUser]);
const renderCustomDateForMonth = (date: Date) => {
const circleStyle = useMemo(
diff --git a/components/pages/calendar/atoms.ts b/components/pages/calendar/atoms.ts
index 82769ca..79e6993 100644
--- a/components/pages/calendar/atoms.ts
+++ b/components/pages/calendar/atoms.ts
@@ -18,4 +18,10 @@ export const settingsPageIndex = atom(0);
export const userSettingsView = atom(true);
export const toDosPageIndex = atom(0);
export const refreshTriggerAtom = atom(false);
-export const refreshEnabledAtom = atom(true);
\ No newline at end of file
+export const refreshEnabledAtom = atom(true);
+export const selectedUserAtom = atom<{
+ uid: string;
+ firstName: string;
+ lastName: string;
+ eventColor?: string;
+ } | null>(null);
\ No newline at end of file
diff --git a/components/pages/grocery/EditGroceryItem.tsx b/components/pages/grocery/EditGroceryItem.tsx
index 4f8e559..fd5cf83 100644
--- a/components/pages/grocery/EditGroceryItem.tsx
+++ b/components/pages/grocery/EditGroceryItem.tsx
@@ -113,24 +113,24 @@ const EditGroceryItem = ({
}}
maxLength={25}
/>
-
+ {(editGrocery.title || editGrocery.title !== "") &&
{
- if (editGrocery.closeEdit) {
- editGrocery.closeEdit();
- }
- }}
+ onPress={() => {
+ if (editGrocery.closeEdit) {
+ editGrocery.closeEdit();
+ }
+ }}
/>
-
+ }
- updateGroceryItem({ id: item.id, bought: !item.bought })
- }
- />
+
+ {item.bought &&
+ {
+ handleItemApproved(item.id, { approved: false });
+ deleteGrocery(item.id);
+ }}/>
+ }
+
+ updateGroceryItem({ id: item.id, bought: !item.bought })
+ }
+ />
+
)
)}
diff --git a/components/pages/grocery/GroceryList.tsx b/components/pages/grocery/GroceryList.tsx
index 216f71b..5fc368c 100644
--- a/components/pages/grocery/GroceryList.tsx
+++ b/components/pages/grocery/GroceryList.tsx
@@ -8,7 +8,21 @@ import {AntDesign} from "@expo/vector-icons";
import EditGroceryItem from "./EditGroceryItem";
import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
-import AddPersonIcon from "@/assets/svgs/AddPersonIcon";
+import Ionicons from '@expo/vector-icons/Ionicons';
+import AddChoreDialog from "@/components/pages/todos/AddChoreDialog";
+import {REPEAT_TYPE} from "@/hooks/firebase/types/todoData";
+import {ToDosContextProvider} from "@/contexts/ToDosContext";
+
+const shoppingTodo = {
+ id: "",
+ title: "Go shopping",
+ points: 10,
+ date: new Date(),
+ rotate: false,
+ repeatType: REPEAT_TYPE.NONE,
+ assignees: [],
+ repeatDays: []
+};
const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
const {
@@ -34,11 +48,15 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
const [pendingVisible, setPendingVisible] = useState(true);
const [approvedVisible, setApprovedVisible] = useState(true);
+ const [choreDialogVisible, setChoreDialogVisible] = useState(false);
// Group approved groceries by category
- const approvedGroceriesByCategory = approvedGroceries?.reduce(
+ let approvedGroceriesByCategory = approvedGroceries?.reduce(
(groups: any, item: IGrocery) => {
- const category = item.category || "Uncategorized";
+ let category = item.category || "Uncategorized";
+ if (item.bought) {
+ category = "Done";
+ }
if (!groups[category]) {
groups[category] = [];
}
@@ -80,6 +98,12 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
setPendingGroceries(groceries?.filter((item) => !item.approved));
}, [groceries]);
+ const handleCancelAddGrocery = () => {
+ setIsAddingGrocery(false);
+ setTitle("");
+ setCategory(GroceryCategory.None)
+ }
+
return (
void}) => {
>
@@ -110,7 +132,6 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
void}) => {
{pendingGroceries?.length} pending
-
-
+ setChoreDialogVisible(true)}>
+
+
+ {choreDialogVisible && }
+
@@ -220,27 +247,29 @@ const GroceryList = ({onInputFocus}: {onInputFocus: (y: number) => void}) => {
- {isAddingGrocery && (
-
- setIsAddingGrocery(false)
- }}
- onInputFocus={onInputFocus}
- />
-
- )}
+
+
+
{/* Render Approved Groceries Grouped by Category */}
{approvedGroceries?.length > 0
? approvedVisible && (
{
+ if (a !== "Done") return -1;
+ if (b === "Done") return 1;
+ return 0;
+ })}
renderItem={({item: category}) => (
{/* Render Category Header */}
diff --git a/components/pages/grocery/GroceryWrapper.tsx b/components/pages/grocery/GroceryWrapper.tsx
index b93db37..e62e667 100644
--- a/components/pages/grocery/GroceryWrapper.tsx
+++ b/components/pages/grocery/GroceryWrapper.tsx
@@ -71,7 +71,6 @@ const GroceryWrapper = () => {
- {!isAddingGrocery && }
>
);
};
diff --git a/components/pages/todos/AddChoreDialog.tsx b/components/pages/todos/AddChoreDialog.tsx
index 582b721..777c30b 100644
--- a/components/pages/todos/AddChoreDialog.tsx
+++ b/components/pages/todos/AddChoreDialog.tsx
@@ -240,8 +240,8 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
if (value) {
setTodo((oldValue) => ({
...oldValue,
- date: new Date(),
repeatType: value.toString(),
+ repeatDays: []
}));
}
}}
@@ -315,20 +315,22 @@ const AddChoreDialog = (addChoreDialogProps: IAddChoreDialog) => {
setSelectedAttendees={setSelectedAssignees}
/>
-
-
- Take Turns
-
-
- setTodo((oldValue) => ({...oldValue, rotate: value}))
- }
- />
-
+ {todo.repeatType !== REPEAT_TYPE.NONE && selectedAssignees?.length > 1 &&
+
+
+ Take Turns
+
+
+ setTodo((oldValue) => ({...oldValue, rotate: value}))
+ }
+ />
+
+ }
diff --git a/hooks/firebase/useUpdateSubUser.ts b/hooks/firebase/useUpdateSubUser.ts
index 8a86aa9..6581237 100644
--- a/hooks/firebase/useUpdateSubUser.ts
+++ b/hooks/firebase/useUpdateSubUser.ts
@@ -42,8 +42,8 @@ export const useUpdateSubUser = () => {
}
},
onSuccess: () => {
- queryClient.invalidateQueries({queryKey: ["getChildrenByParentId"]})
queryClient.invalidateQueries({queryKey: ["familyMembers"]})
+ queryClient.invalidateQueries({queryKey: ["profiles"]})
}
});
}
\ No newline at end of file