diff --git a/app/(auth)/grocery/index.tsx b/app/(auth)/grocery/index.tsx index eff5da5..5c9d1e4 100644 --- a/app/(auth)/grocery/index.tsx +++ b/app/(auth)/grocery/index.tsx @@ -3,16 +3,12 @@ import GroceryList from "@/components/pages/grocery/GroceryList"; import AddGroceryItem from "@/components/pages/grocery/AddGroceryItem"; import { GroceryProvider } from "@/contexts/GroceryContext"; import React from "react"; -import HeaderTemplate from "@/components/shared/HeaderTemplate"; -import { ScrollView } from "react-native-gesture-handler"; +import GroceryWrapper from "@/components/pages/grocery/GroceryWrapper"; export default function Screen() { return ( - - - - + ); } diff --git a/app/(auth)/todos/index.tsx b/app/(auth)/todos/index.tsx index b158e9e..af1215d 100644 --- a/app/(auth)/todos/index.tsx +++ b/app/(auth)/todos/index.tsx @@ -1,4 +1,5 @@ import AddChore from "@/components/pages/todos/AddChore"; +import ProgressCard from "@/components/pages/todos/ProgressCard"; import ToDoItem from "@/components/pages/todos/ToDoItem"; import ToDosList from "@/components/pages/todos/ToDosList"; import HeaderTemplate from "@/components/shared/HeaderTemplate"; @@ -13,7 +14,8 @@ export default function Screen() { - + + diff --git a/components/pages/grocery/CategoryDropdown.tsx b/components/pages/grocery/CategoryDropdown.tsx index bd1c6c0..7cb787a 100644 --- a/components/pages/grocery/CategoryDropdown.tsx +++ b/components/pages/grocery/CategoryDropdown.tsx @@ -1,26 +1,34 @@ import React from "react"; import { View, Text, TouchableOpacity } from "react-native-ui-lib"; -import { GroceryCategory } from "@/contexts/GroceryContext"; +import { GroceryCategory, useGroceryContext } from "@/contexts/GroceryContext"; import { ScrollView } from "react-native-gesture-handler"; -const CategoryDropdown = () => { +const CategoryDropdown = (props: { + setSelectedCategory: (category: GroceryCategory) => void; +}) => { const groceryCategories = Object.values(GroceryCategory); return ( - - {groceryCategories.map((category) => ( - {}}> - + + {groceryCategories.map((category) => ( + { + props.setSelectedCategory(category); }} > - {category} - - - ))} - + + {category} + + + ))} + + ); }; diff --git a/components/pages/grocery/EditGroceryItem.tsx b/components/pages/grocery/EditGroceryItem.tsx new file mode 100644 index 0000000..6e4bf68 --- /dev/null +++ b/components/pages/grocery/EditGroceryItem.tsx @@ -0,0 +1,28 @@ +import { View, Text } from 'react-native' +import React from 'react' +import { TextField } from 'react-native-ui-lib' +import CategoryDropdown from './CategoryDropdown' +import { GroceryCategory } from '@/contexts/GroceryContext' + +const EditGroceryItem = (props: {title: string, setTitle: (value: string) => void, setCategory: (category: GroceryCategory) => void}) => { + return ( + + props.setTitle(value)} + maxLength={25} + /> + + + ) +} + +export default EditGroceryItem \ No newline at end of file diff --git a/components/pages/grocery/GroceryItem.tsx b/components/pages/grocery/GroceryItem.tsx index 5395947..ef12135 100644 --- a/components/pages/grocery/GroceryItem.tsx +++ b/components/pages/grocery/GroceryItem.tsx @@ -9,9 +9,14 @@ import React, { useEffect, useState } from "react"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import { MaterialCommunityIcons, AntDesign } from "@expo/vector-icons"; import { ListItem } from "react-native-ui-lib"; -import { IGrocery, useGroceryContext } from "@/contexts/GroceryContext"; +import { + GroceryCategory, + IGrocery, + useGroceryContext, +} from "@/contexts/GroceryContext"; import EditGroceryFrequency from "./EditGroceryFrequency"; import { TextInput } from "react-native"; +import EditGroceryItem from "./EditGroceryItem"; const GroceryItem = ({ item, @@ -20,8 +25,7 @@ const GroceryItem = ({ item: IGrocery; handleItemApproved: (id: number, changes: Partial) => void; }) => { - const { updateGroceryItem, groceries } = - useGroceryContext(); + const { updateGroceryItem, groceries } = useGroceryContext(); const { profileType } = useAuthContext(); const [openFreqEdit, setOpenFreqEdit] = useState(false); @@ -32,16 +36,20 @@ const GroceryItem = ({ updateGroceryItem(item.id, { title: newTitle }); }; + const handleCategoryChange = (newCategory: GroceryCategory) => { + updateGroceryItem(item.id, { category: newCategory }); + }; + useEffect(() => { setNewTitle(item.title); }, []); return ( { setOpenFreqEdit(true); }} @@ -55,7 +63,7 @@ const GroceryItem = ({ }} /> - {/* } />*/} - - {!isEditingTitle ? ( + {!isEditingTitle ? ( + setIsEditingTitle(true)}> {item.title} - ) : ( + { /* - )} - + */} + + ) : ( + + )} {!item.approved ? ( diff --git a/components/pages/grocery/GroceryList.tsx b/components/pages/grocery/GroceryList.tsx index e17d5df..e8ba6c9 100644 --- a/components/pages/grocery/GroceryList.tsx +++ b/components/pages/grocery/GroceryList.tsx @@ -1,38 +1,69 @@ import { FlatList } from "react-native"; import React, { useEffect, useState } from "react"; -import { - View, - Text, - ListItem, - Button, - TextField, - Picker, -} from "react-native-ui-lib"; -import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; -import { useAuthContext } from "@/contexts/AuthContext"; -import AntDesign from "@expo/vector-icons/AntDesign"; -import AddGroceryItem from "./AddGroceryItem"; +import { View, Text, ListItem, Button, TextField } from "react-native-ui-lib"; import GroceryItem from "./GroceryItem"; import { - GroceryCategory, IGrocery, + GroceryCategory, + GroceryFrequency, useGroceryContext, } from "@/contexts/GroceryContext"; import HeaderTemplate from "@/components/shared/HeaderTemplate"; import CategoryDropdown from "./CategoryDropdown"; +import { MaterialIcons } from "@expo/vector-icons"; +import EditGroceryItem from "./EditGroceryItem"; const GroceryList = () => { - const { groceries, updateGroceryItem, isAddingGrocery } = useGroceryContext(); + const { + groceries, + updateGroceryItem, + isAddingGrocery, + setIsAddingGrocery, + addGrocery, + } = useGroceryContext(); const [approvedGroceries, setapprovedGroceries] = useState( - groceries.filter((item) => item.approved == true) + groceries.filter((item) => item.approved === true) ); const [pendingGroceries, setPendingGroceries] = useState( - groceries.filter((item) => item.approved != true) + groceries.filter((item) => item.approved !== true) + ); + const [category, setCategory] = useState( + GroceryCategory.Bakery + ); + const [title, setTitle] = useState(""); + + // Group approved groceries by category + const approvedGroceriesByCategory = approvedGroceries.reduce( + (groups: any, item: IGrocery) => { + const category = item.category || "Uncategorized"; + if (!groups[category]) { + groups[category] = []; + } + groups[category].push(item); + return groups; + }, + {} ); useEffect(() => { - setapprovedGroceries(groceries.filter((item) => item.approved == true)); - setPendingGroceries(groceries.filter((item) => item.approved != true)); + if (title?.length > 2 && title?.length <= 25) { + addGrocery({ + id: 0, + title: title, + category: category, + approved: false, + recurring: false, + frequency: GroceryFrequency.Never, + bought: false, + }); + + setIsAddingGrocery(false); + } + }, [category]); + + useEffect(() => { + setapprovedGroceries(groceries.filter((item) => item.approved === true)); + setPendingGroceries(groceries.filter((item) => item.approved !== true)); }, [groceries]); return ( @@ -49,7 +80,7 @@ const GroceryList = () => { > {approvedGroceries.length} list{" "} - {approvedGroceries.length == 1 ? ( + {approvedGroceries.length === 1 ? ( item @@ -69,6 +100,13 @@ const GroceryList = () => { {pendingGroceries.length} pending + + + + ); +}; + +export default ProgressCard; diff --git a/components/pages/todos/ToDoItem.tsx b/components/pages/todos/ToDoItem.tsx index f6d37c9..9207cc0 100644 --- a/components/pages/todos/ToDoItem.tsx +++ b/components/pages/todos/ToDoItem.tsx @@ -8,15 +8,25 @@ const ToDoItem = (props: { item: IToDo }) => { return ( - {props.item.title} + + {props.item.title} + { @@ -29,7 +39,9 @@ const ToDoItem = (props: { item: IToDo }) => { centerV height={2} width={"100%"} - backgroundColor="#e7e7e7" + style={{ + backgroundColor: props.item.done ? '#b8b8b8' : "#e7e7e7", + }} centerH /> diff --git a/components/pages/todos/ToDosList.tsx b/components/pages/todos/ToDosList.tsx index 3f5e5f0..1571c3f 100644 --- a/components/pages/todos/ToDosList.tsx +++ b/components/pages/todos/ToDosList.tsx @@ -2,50 +2,77 @@ import { View, Text } from "react-native-ui-lib"; import React from "react"; import { IToDo, useToDosContext } from "@/contexts/ToDosContext"; import ToDoItem from "./ToDoItem"; -import { format, isToday, isTomorrow } from "date-fns"; -import { ScrollView } from "react-native-gesture-handler"; +import { format, isToday, isTomorrow } from "date-fns"; const groupToDosByDate = (toDos: IToDo[]) => { - return toDos.reduce((groups, toDo) => { - const dateKey = isToday(toDo.date) - ? "Today • " + format(toDo.date, "EEE MMM dd") - : isTomorrow(toDo.date) - ? "Tomorrow • " + format(toDo.date, "EEE MMM dd") - : format(toDo.date, "EEE MMM dd"); - - if (!groups[dateKey]) { - groups[dateKey] = []; - } - - groups[dateKey].push(toDo); - return groups; - }, {} as { [key: string]: IToDo[] }); - }; + return toDos.reduce((groups, toDo) => { + let dateKey; + + if (toDo.date === null) { + dateKey = "No Date"; + } else if (isToday(toDo.date)) { + dateKey = "Today • " + format(toDo.date, "EEE MMM dd"); + } else if (isTomorrow(toDo.date)) { + dateKey = "Tomorrow • " + format(toDo.date, "EEE MMM dd"); + } else { + dateKey = format(toDo.date, "EEE MMM dd"); + } + + if (!groups[dateKey]) { + groups[dateKey] = []; + } + + groups[dateKey].push(toDo); + return groups; + }, {} as { [key: string]: IToDo[] }); +}; const ToDosList = () => { const { toDos } = useToDosContext(); const groupedToDos = groupToDosByDate(toDos); + const noDateToDos = groupedToDos["No Date"] || []; + const datedToDos = Object.keys(groupedToDos).filter((key) => key !== "No Date"); + return ( - {Object.keys(groupedToDos).map((dateKey) => ( - - - {dateKey} - - {groupedToDos[dateKey].map((item) => ( - - ))} + + {noDateToDos.length > 0 && ( + + {noDateToDos + .sort((a, b) => Number(a.done) - Number(b.done)) + .map((item) => ( + + ))} - ))} + )} + + {datedToDos.map((dateKey) => { + const sortedToDos = [ + ...groupedToDos[dateKey].filter((toDo) => !toDo.done), + ...groupedToDos[dateKey].filter((toDo) => toDo.done), + ]; + + return ( + + + {dateKey} + + {sortedToDos.map((item) => ( + + ))} + + ); + })} ); }; export default ToDosList; - -/*const groupToDosByDate = (toDos: IToDo[]) => { - return toDos.reduce((groups, toDo) => { - const dateKey - }) -}*/ diff --git a/contexts/GroceryContext.tsx b/contexts/GroceryContext.tsx index d658cba..b68340c 100644 --- a/contexts/GroceryContext.tsx +++ b/contexts/GroceryContext.tsx @@ -56,6 +56,7 @@ interface IGroceryContext { updateGroceryItem: (id: number, changes: Partial) => void; isAddingGrocery: boolean; setIsAddingGrocery: (value: boolean) => void; + addGrocery: (grocery: IGrocery) => void; } const GroceryContext = createContext(undefined); @@ -103,6 +104,17 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({ }, ]); + const addGrocery = (grocery: IGrocery) => { + setGroceries((prevGroceries) => [ + ...prevGroceries, + { + ...grocery, + id: prevGroceries.length ? prevGroceries[prevGroceries.length - 1].id + 1 : 0, + }, + ]); + }; + + const updateGroceryItem = (id: number, changes: Partial) => { setGroceries((prevGroceries) => prevGroceries.map((grocery) => @@ -113,7 +125,7 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({ return ( {children} diff --git a/contexts/ToDosContext.tsx b/contexts/ToDosContext.tsx index 0da2ac5..aeee412 100644 --- a/contexts/ToDosContext.tsx +++ b/contexts/ToDosContext.tsx @@ -1,12 +1,22 @@ import { createContext, FC, ReactNode, useContext, useState } from "react"; + export interface IToDo { id: number; title: string; done: boolean; - date: Date; + date: Date | null; points?: number; rotate: boolean; + repeatType: string; } + +export const repeatOptions = [ + { label: "None", value: "None" }, + { label: "Every week", value: "Every week" }, + { label: "Once a month", value: "Once a month" }, + { label: "Once a year", value: "Once a year" }, +]; + interface IToDosContext { toDos: IToDo[]; updateToDo: (id: number, changes: Partial) => void; @@ -25,6 +35,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ done: false, date: new Date(), rotate: true, + repeatType: "Every week" }, { id: 1, @@ -32,6 +43,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ done: false, date: new Date(), rotate: false, + repeatType: "Every week" }, { id: 2, @@ -39,6 +51,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ done: false, date: new Date(), rotate: true, + repeatType: "Every week" }, { id: 3, @@ -47,6 +60,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ date: new Date(Date.now() + 86400000), points: 40, rotate: false, + repeatType: "Every week" }, { id: 4, @@ -54,6 +68,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ done: false, date: new Date(Date.now() + 86400000), rotate: false, + repeatType: "Once a Month" }, { id: 5, @@ -61,6 +76,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ done: false, date: new Date(Date.now() + 2 * 86400000), rotate: true, + repeatType: "Once a Month" }, { id: 6, @@ -68,6 +84,23 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({ done: false, date: new Date(Date.now() + 3 * 86400000), rotate: false, + repeatType: "Once a year" + }, + { + id: 7, + title: "Buy Nautica Voyage", + done: false, + date: null, + rotate: false, + repeatType: "None" + }, + { + id: 8, + title: "Sell Dan's Xbox", + done: false, + date: null, + rotate: false, + repeatType: "None" }, ]);