changes to grocery, todos

This commit is contained in:
ivic00
2024-09-12 15:39:20 +02:00
parent 8d85cbdaad
commit 53f7118656
24 changed files with 643 additions and 208 deletions

View File

@ -1,23 +1,18 @@
import { ScrollView } from "react-native"; import { Text, View } from "react-native-ui-lib";
import { Button, FloatingButton, Text, View } from "react-native-ui-lib";
import Octicons from "@expo/vector-icons/Octicons";
import GroceryList from "@/components/pages/grocery/GroceryList"; import GroceryList from "@/components/pages/grocery/GroceryList";
import AddGroceryItem from "@/components/pages/grocery/AddGroceryItem"; import AddGroceryItem from "@/components/pages/grocery/AddGroceryItem";
import { useAuthContext } from "@/contexts/AuthContext"; import { GroceryProvider } from "@/contexts/GroceryContext";
import { GroceryProvider, useGroceryContext } from "@/contexts/GroceryContext";
import TopDisplay from "@/components/pages/grocery/TopDisplay";
import React from "react"; import React from "react";
import HeaderTemplate from "@/components/shared/HeaderTemplate";
import { ScrollView } from "react-native-gesture-handler";
export default function Screen() { export default function Screen() {
return ( return (
<GroceryProvider> <GroceryProvider>
<View> <ScrollView>
<TopDisplay />
<View>
<GroceryList /> <GroceryList />
</View>
</View>
<AddGroceryItem /> <AddGroceryItem />
</ScrollView>
</GroceryProvider> </GroceryProvider>
); );
} }

View File

@ -1,5 +1,5 @@
import {Stack} from "expo-router"; import {Stack} from "expo-router";
export default function Layout() { export default function Layout() {
return <Stack screenOptions={{title: "Login"}}/> return <Stack screenOptions={{title: ""}}/>
} }

View File

View File

@ -0,0 +1,5 @@
import Entry from "@/components/pages/main/Entry";
export default function Screen() {
return <Entry />;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -10,12 +10,13 @@ import {
PanningProvider, PanningProvider,
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import { useGroceryContext } from "@/contexts/GroceryContext"; import { useGroceryContext } from "@/contexts/GroceryContext";
import { FontAwesome6 } from "@expo/vector-icons";
interface AddGroceryItemProps { interface AddGroceryItemProps {
visible: boolean; visible: boolean;
onClose: () => void; onClose: () => void;
} }
const AddGroceryItem = () => { const AddGroceryItem = () => {
const { setIsShopping, isShopping } = useGroceryContext(); const { isAddingGrocery, setIsAddingGrocery } = useGroceryContext();
const [visible, setVisible] = useState<boolean>(false); const [visible, setVisible] = useState<boolean>(false);
const handleShowDialog = () => { const handleShowDialog = () => {
@ -24,23 +25,6 @@ const AddGroceryItem = () => {
const handleHideDialog = () => { const handleHideDialog = () => {
setVisible(false); setVisible(false);
}; };
const addGroceryDialog = (
<Dialog
visible={visible}
onDismiss={handleHideDialog}
panDirection={PanningProvider.Directions.DOWN}
containerStyle={{ borderRadius: 12, backgroundColor: "white" }}
>
<View style={styles.container}>
<Text style={styles.title}>New Grocery</Text>
<View style={styles.divider} />
<View style={styles.inner}>
<Text>Category</Text>
</View>
</View>
</Dialog>
);
return ( return (
<View <View
row row
@ -53,39 +37,18 @@ const AddGroceryItem = () => {
height: 60, height: 60,
}} }}
> >
{!isShopping ? (
<View style={styles.btnContainer} row>
<Button
label="View shopping list"
color="#337a11"
flex-2
marginR-5
backgroundColor="#c6e0b3"
onPress={() => setIsShopping(true)}
/>
<Button
label="Create new"
color="white"
flex-1
backgroundColor="#19ad61"
enableShadow
onPress={() => {}}
/>
</View>
) : (
<View style={styles.btnContainer} row> <View style={styles.btnContainer} row>
<Button <Button
color="white" color="white"
backgroundColor="#81a861" backgroundColor="#fd1775"
label="finish shopping" label="Add item"
text60L text70L
iconSource={() => <FontAwesome6 name="add" size={18} color="white" />}
style={styles.finishShopBtn} style={styles.finishShopBtn}
enableShadow enableShadow
onPress={() => setIsShopping(false)} onPress={() => {setIsAddingGrocery(true)}}
/> />
</View> </View>
)}
{addGroceryDialog}
</View> </View>
); );
}; };

View File

@ -0,0 +1,27 @@
import React from "react";
import { View, Text, TouchableOpacity } from "react-native-ui-lib";
import { GroceryCategory } from "@/contexts/GroceryContext";
import { ScrollView } from "react-native-gesture-handler";
const CategoryDropdown = () => {
const groceryCategories = Object.values(GroceryCategory);
return (
<ScrollView height={100}>
{groceryCategories.map((category) => (
<TouchableOpacity onPress={() => {}}>
<View
key={category}
style={{
padding: 10,
}}
>
<Text>{category}</Text>
</View>
</TouchableOpacity>
))}
</ScrollView>
);
};
export default CategoryDropdown;

View File

@ -20,7 +20,7 @@ const GroceryItem = ({
item: IGrocery; item: IGrocery;
handleItemApproved: (id: number, changes: Partial<IGrocery>) => void; handleItemApproved: (id: number, changes: Partial<IGrocery>) => void;
}) => { }) => {
const { iconMapping, updateGroceryItem, groceries, isShopping } = const { updateGroceryItem, groceries } =
useGroceryContext(); useGroceryContext();
const { profileType } = useAuthContext(); const { profileType } = useAuthContext();
@ -38,8 +38,10 @@ const GroceryItem = ({
return ( return (
<ListItem <ListItem
style={{borderRadius: 50, marginVertical: 5, height: 55}}
backgroundColor="white" backgroundColor="white"
padding-3 centerV
padding-0
onPress={() => { onPress={() => {
setOpenFreqEdit(true); setOpenFreqEdit(true);
}} }}
@ -52,8 +54,8 @@ const GroceryItem = ({
setOpenFreqEdit(false); setOpenFreqEdit(false);
}} }}
/> />
<ListItem.Part left containerStyle={{ flex: 1, paddingStart: 15 }}> <ListItem.Part left containerStyle={{ flex: 1, paddingStart: 20 }}>
<View {/* <View
height={50} height={50}
width={50} width={50}
style={{ borderRadius: 15 }} style={{ borderRadius: 15 }}
@ -66,7 +68,7 @@ const GroceryItem = ({
color="orange" color="orange"
/> />
} }
/> />*/}
<View> <View>
{!isEditingTitle ? ( {!isEditingTitle ? (
<TouchableOpacity onPress={() => setIsEditingTitle(true)}> <TouchableOpacity onPress={() => setIsEditingTitle(true)}>
@ -89,11 +91,10 @@ const GroceryItem = ({
}} }}
/> />
)} )}
<Text>{iconMapping[item.category]}</Text>
</View> </View>
</ListItem.Part> </ListItem.Part>
<ListItem.Part right containerStyle={{ paddingEnd: 15 }}> <ListItem.Part right containerStyle={{ paddingEnd: 20 }}>
{profileType == ProfileType.PARENT && !isShopping ? ( {!item.approved ? (
<View row> <View row>
<Button <Button
padding-0 padding-0

View File

@ -1,33 +1,147 @@
import { FlatList } from "react-native"; import { FlatList } from "react-native";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { View, Text, ListItem, Button } from "react-native-ui-lib"; import {
View,
Text,
ListItem,
Button,
TextField,
Picker,
} from "react-native-ui-lib";
import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
import { useAuthContext } from "@/contexts/AuthContext"; import { useAuthContext } from "@/contexts/AuthContext";
import AntDesign from "@expo/vector-icons/AntDesign"; import AntDesign from "@expo/vector-icons/AntDesign";
import AddGroceryItem from "./AddGroceryItem"; import AddGroceryItem from "./AddGroceryItem";
import GroceryItem from "./GroceryItem"; import GroceryItem from "./GroceryItem";
import { useGroceryContext } from "@/contexts/GroceryContext"; import {
GroceryCategory,
IGrocery,
useGroceryContext,
} from "@/contexts/GroceryContext";
import HeaderTemplate from "@/components/shared/HeaderTemplate";
import CategoryDropdown from "./CategoryDropdown";
const GroceryList = () => { const GroceryList = () => {
const { groceries, updateGroceryItem } = useGroceryContext(); const { groceries, updateGroceryItem, isAddingGrocery } = useGroceryContext();
const [approvedGroceries, setapprovedGroceries] = useState<IGrocery[]>(
groceries.filter((item) => item.approved == true)
);
const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>(
groceries.filter((item) => item.approved != true)
);
useEffect(() => {
setapprovedGroceries(groceries.filter((item) => item.approved == true));
setPendingGroceries(groceries.filter((item) => item.approved != true));
}, [groceries]);
return ( return (
<View> <View marginH-20>
<FlatList <HeaderTemplate
ItemSeparatorComponent={() => ( message={"Welcome to your grocery list"}
isWelcome={false}
>
<View row spread>
<View <View
style={{ backgroundColor="#e2eed8"
height: 1.5, padding-8
backgroundColor: "#e0e0e0", style={{ borderRadius: 50 }}
marginHorizontal: 15, >
}} <Text text70BL color="#46a80a">
/> {approvedGroceries.length} list{" "}
{approvedGroceries.length == 1 ? (
<Text text70BL color="#46a80a">
item
</Text>
) : (
<Text text70BL color="#46a80a">
items
</Text>
)} )}
data={groceries} </Text>
</View>
<View
backgroundColor="#faead2"
padding-8
style={{ borderRadius: 50 }}
>
<Text text70BL color="#e28800">
{pendingGroceries.length} pending
</Text>
</View>
</View>
</HeaderTemplate>
{/* Pending Approval Section */}
<View row spread marginT-40 marginB-20 centerV>
<Text text70BL>Pending Approval</Text>
<View
centerV
style={{
aspectRatio: 1,
width: 40,
backgroundColor: "#faead2",
borderRadius: 50,
}}
>
<Text text60L center color="#e28800">
{pendingGroceries.length.toString()}
</Text>
</View>
</View>
{pendingGroceries.length > 0 ? (
<FlatList
data={pendingGroceries}
renderItem={({ item }) => ( renderItem={({ item }) => (
<GroceryItem item={item} handleItemApproved={updateGroceryItem} /> <GroceryItem item={item} handleItemApproved={updateGroceryItem} />
)} )}
keyExtractor={(item) => item.id.toString()} keyExtractor={(item) => item.id.toString()}
/> />
) : (
<Text>No items pending approval.</Text>
)}
{/* Approved Section */}
<View row spread marginT-40 marginB-20 centerV>
<Text text70BL>Shopping List</Text>
<View
centerV
style={{
aspectRatio: 1,
width: 40,
backgroundColor: "#e2eed8",
borderRadius: 50,
}}
>
<Text text60L center color="#46a80a">
{approvedGroceries.length.toString()}
</Text>
</View>
</View>
{isAddingGrocery && (
<View
style={{
backgroundColor: "white",
width: "100%",
borderRadius: 25,
padding: 15,
}}
>
<TextField placeholder="Grocery" maxLength={25} />
<CategoryDropdown />
</View>
)}
{approvedGroceries.length > 0 ? (
<FlatList
data={approvedGroceries}
renderItem={({ item }) => (
<GroceryItem item={item} handleItemApproved={updateGroceryItem} />
)}
keyExtractor={(item) => item.id.toString()}
/>
) : (
<Text>No approved items.</Text>
)}
</View> </View>
); );
}; };

View File

@ -3,10 +3,11 @@ import React, { useState } from "react";
import SignUpPage from "./SignUpPage"; import SignUpPage from "./SignUpPage";
import SignInPage from "./SignInPage"; import SignInPage from "./SignInPage";
import { useSignUp } from "@/hooks/firebase/useSignUp"; import { useSignUp } from "@/hooks/firebase/useSignUp";
import { StyleSheet } from "react-native";
const Entry = () => { const Entry = () => {
const [isRegister, setIsRegister] = useState<boolean>(false); const [isRegister, setIsRegister] = useState<boolean>(false);
const {mutateAsync: signUp} = useSignUp(); const { mutateAsync: signUp } = useSignUp();
const setRegister = () => { const setRegister = () => {
setIsRegister(true); setIsRegister(true);
@ -17,9 +18,7 @@ const Entry = () => {
return ( return (
<View> <View>
{isRegister ? ( {isRegister ? (
<SignUpPage <SignUpPage unsetRegister={unsetRegister} />
unsetRegister={unsetRegister}
/>
) : ( ) : (
<SignInPage setRegister={setRegister} /> <SignInPage setRegister={setRegister} />
)} )}

View File

@ -1,6 +1,7 @@
import { View, Text, Button, TextField } from "react-native-ui-lib"; import { View, Text, Button, TextField, ButtonSize } from "react-native-ui-lib";
import React, { useState } from "react"; import React, { useState } from "react";
import { useSignIn } from "@/hooks/firebase/useSignIn"; import { useSignIn } from "@/hooks/firebase/useSignIn";
import { StyleSheet } from "react-native";
const SignInPage = (props: { setRegister: () => any }) => { const SignInPage = (props: { setRegister: () => any }) => {
const [email, setEmail] = useState<string>(""); const [email, setEmail] = useState<string>("");
@ -13,37 +14,54 @@ const SignInPage = (props: { setRegister: () => any }) => {
}; };
return ( return (
<View padding-10> <View padding-10 centerV height={"100%"}>
<TextField <TextField
placeholder="Email" placeholder="Email"
value={email} value={email}
onChangeText={setEmail} onChangeText={setEmail}
style={{ marginBottom: 10 }} style={styles.textfield}
floatingPlaceholder
/> />
<TextField <TextField
placeholder="Password" placeholder="Password"
value={password} value={password}
onChangeText={setPassword} onChangeText={setPassword}
secureTextEntry secureTextEntry
style={{ marginBottom: 10 }} style={styles.textfield}
floatingPlaceholder
/> />
<Button label="Login" onPress={handleSignIn} style={{ marginBottom: 20 }} /> <Button
{isError && ( label="Login"
<Text center style={{ marginBottom: 20 }}>{`${error}`}</Text> onPress={handleSignIn}
)} style={{ marginBottom: 20 }}
<Text center style={{ marginBottom: 5 }}>Don't have an account?</Text> backgroundColor="#fd1775"
/>
{isError && <Text center style={{ marginBottom: 20 }}>{`${error}`}</Text>}
<View row centerH>
<Text center style={{ marginBottom: 5 }}>
Don't have an account?
</Text>
<Button <Button
onPress={props.setRegister} onPress={props.setRegister}
label="Sign Up" label="Sign Up"
link link
size={ButtonSize.xSmall}
padding-0 padding-0
margin-0 margin-0
left left
color="#fd1775"
/> />
</View> </View>
</View>
); );
}; };
const styles = StyleSheet.create({
textfield: {
backgroundColor: "white",
marginVertical: 10,
padding: 30,
height: 45,
borderRadius: 50,
},
});
export default SignInPage; export default SignInPage;

View File

@ -1,10 +1,21 @@
import React, { useState } from "react"; import React, { useState } from "react";
import {Checkbox, Button, View, Text, TextField} from "react-native-ui-lib"; import {
Checkbox,
Button,
View,
Text,
TextField,
ButtonSize,
} from "react-native-ui-lib";
import { useSignUp } from "@/hooks/firebase/useSignUp"; import { useSignUp } from "@/hooks/firebase/useSignUp";
import { ProfileType } from "@/contexts/AuthContext"; import { ProfileType } from "@/contexts/AuthContext";
import { StyleSheet } from "react-native";
import { AntDesign } from "@expo/vector-icons";
const SignUpPage = (props: { unsetRegister: () => any }) => { const SignUpPage = (props: { unsetRegister: () => any }) => {
const [email, setEmail] = useState<string>(""); const [email, setEmail] = useState<string>("");
const [firstName, setFirstName] = useState<string>("");
const [lastName, setLastName] = useState<string>("");
const [password, setPassword] = useState<string>(""); const [password, setPassword] = useState<string>("");
const [isParent, setIsParent] = useState<boolean>(true); const [isParent, setIsParent] = useState<boolean>(true);
const [isChild, setIsChild] = useState<boolean>(false); const [isChild, setIsChild] = useState<boolean>(false);
@ -20,27 +31,55 @@ const SignUpPage = (props: { unsetRegister: () => any }) => {
return ( return (
<View padding-10> <View padding-10>
<Text text30 center>
Get started with Kali
</Text>
<Text>Please enter your details.</Text>
<TextField
marginT-60
placeholder="First name"
value={firstName}
onChangeText={setFirstName}
style={styles.textfield}
/>
<TextField
placeholder="Last name"
value={lastName}
onChangeText={setLastName}
style={styles.textfield}
/>
<TextField <TextField
placeholder="Email" placeholder="Email"
value={email} value={email}
onChangeText={setEmail} onChangeText={setEmail}
style={{ marginBottom: 10 }} style={styles.textfield}
floatingPlaceholder
/> />
<TextField <TextField
placeholder="Password" placeholder="Password"
value={password} value={password}
onChangeText={setPassword} onChangeText={setPassword}
secureTextEntry secureTextEntry
style={{ marginBottom: 10 }} style={styles.textfield}
floatingPlaceholder
/> />
<Button <Button
label="Register" label="Register"
onPress={handleSignUp} onPress={handleSignUp}
style={{ marginBottom: 10 }} style={{ marginBottom: 10, backgroundColor: "#fd1775" }}
/> />
<Text style={{ marginBottom: 10 }}>Choose Profile Type:</Text> <Button
label="Sign up with Google"
backgroundColor="white"
color="black"
iconSource={() => (
<AntDesign
name="google"
size={24}
color="black"
style={{ marginRight: 15 }}
/>
)}
/>
{/*<Text style={{ marginBottom: 10 }}>Choose Profile Type:</Text>
<Checkbox <Checkbox
label="Parent" label="Parent"
value={isParent} value={isParent}
@ -78,19 +117,33 @@ const SignUpPage = (props: { unsetRegister: () => any }) => {
setIsChild(false); setIsChild(false);
} }
}} }}
/> />*/}
<Text center style={{ marginBottom: 5, marginTop: 10 }}> <View row centerH>
<Text text70 center style={{ marginBottom: 5, marginTop: 10 }}>
Already have an account? Already have an account?
</Text> </Text>
<Button <Button
label="Sign In" label="Sign In"
margin-0 margin-0
link link
color="#fd1775"
size={ButtonSize.small}
text200 text200
onPress={props.unsetRegister} onPress={props.unsetRegister}
/> />
</View> </View>
</View>
); );
}; };
export default SignUpPage; export default SignUpPage;
const styles = StyleSheet.create({
textfield: {
backgroundColor: "white",
marginVertical: 10,
padding: 30,
height: 45,
borderRadius: 50,
},
});

View File

@ -0,0 +1,22 @@
import { Image } from "react-native";
import React from "react";
import { View, Text, Button } from "react-native-ui-lib";
const WelcomeSplash = () => {
return (
<View>
<Image
source={require("../../../assets/images/splash-clock.png")}
height={10}
width={10}
/>
<Button
label="Continue"
style={{ backgroundColor: "#fd1775" }}
onPress={() => {}}
/>
</View>
);
};
export default WelcomeSplash;

View File

@ -0,0 +1,84 @@
import { Image } from "react-native";
import React, { useRef } from "react";
import { View, Text, Button, TextField } from "react-native-ui-lib";
import Onboarding from "react-native-onboarding-swiper";
import { StyleSheet } from "react-native";
import { useAuthContext } from "@/contexts/AuthContext";
import { useSignUp } from "@/hooks/firebase/useSignUp";
const OnboardingFlow = () => {
const onboardingRef = useRef(null);
const { mutateAsync: signUp } = useSignUp();
return (
<Onboarding
showPagination={false}
ref={onboardingRef}
containerStyles={{ backgroundColor: "#f9f8f7" }}
imageContainerStyles={{
paddingBottom: 0,
paddingTop: 0,
}}
pages={[
{
backgroundColor: "#f9f8f7",
image: (
<Image
source={require("../../../assets/images/splash-clock.png")}
height={10}
width={10}
/>
),
title: <Text text30>Welcome to Kali</Text>,
subtitle: (
<View paddingB-250 marginH-20 spread>
<Text text50R>Lightening Mental Loads, One Family at a Time</Text>
<Button
label="Continue"
style={{ backgroundColor: "#fd1775" }}
onPress={() => onboardingRef.current.goToPage(1, true)}
/>
</View>
),
},
{
backgroundColor: "#f9f8f7",
title: <Text>Get started with Kali</Text>,
subtitle: (
<View
style={{
marginBottom: "auto",
width: "100%",
}}
>
<View marginH-30>
{/*<TextField style={styles.textfield} placeholder="First name" />*/}
{/*<TextField style={styles.textfield} placeholder="Last name" />*/}
<TextField style={styles.textfield} placeholder="Email" />
<TextField style={styles.textfield} placeholder="Password" />
<Button
label="Login"
backgroundColor="#ea156c"
onPress={() => {
console.log("Onboarding Done");
}}
/>
</View>
</View>
),
},
]}
/>
);
};
export default OnboardingFlow;
const styles = StyleSheet.create({
textfield: {
backgroundColor: "white",
marginVertical: 10,
padding: 30,
height: 45,
borderRadius: 50,
},
});

View File

View File

@ -12,15 +12,21 @@ import {
NumberInput, NumberInput,
NumberInputData, NumberInputData,
DateTimePicker, DateTimePicker,
Switch,
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import { AntDesign, Feather, Ionicons } from "@expo/vector-icons"; import { AntDesign, Feather, Ionicons } from "@expo/vector-icons";
import LinearGradient from "react-native-linear-gradient"; import LinearGradient from "react-native-linear-gradient";
import { PanningDirectionsEnum } from "react-native-ui-lib/src/components/panningViews/panningProvider"; import { PanningDirectionsEnum } from "react-native-ui-lib/src/components/panningViews/panningProvider";
import { useToDosContext } from "@/contexts/ToDosContext";
const AddChore = () => { const AddChore = () => {
const { addToDo, toDos } = useToDosContext();
const [newTitle, setNewTitle] = useState<string>("");
const [isVisible, setIsVisible] = useState<boolean>(false); const [isVisible, setIsVisible] = useState<boolean>(false);
const [points, setPoints] = useState<number>(10); const [points, setPoints] = useState<number>(10);
const [choreDate, setChoreDate] = useState<Date>(new Date()); const [choreDate, setChoreDate] = useState<Date>(new Date());
const [rotate, setRotate] = useState<boolean>(false);
const handleChange = (text: string) => { const handleChange = (text: string) => {
const numericValue = parseInt(text, 10); const numericValue = parseInt(text, 10);
@ -66,8 +72,38 @@ const AddChore = () => {
}} }}
visible={isVisible} visible={isVisible}
> >
<View row spread>
<Button color="#05a8b6" style={styles.topBtn} label="Cancel" onPress={() => {setIsVisible(false)}} />
<Button
style={styles.topBtn}
iconSource={() => (
<Feather name="chevron-down" size={24} color="black" />
)} onPress={() => {setIsVisible(false)}}
/>
<Button
color="#05a8b6"
style={styles.topBtn}
label="Save"
onPress={() => {
addToDo({
id: 0,
title: newTitle,
done: false,
date: choreDate,
points: points,
rotate: rotate,
});
setIsVisible(false);
console.log(toDos);
}}
/>
</View>
<TextField <TextField
placeholder="Add chore title" placeholder="Add chore title"
value={newTitle}
onChangeText={(text) => {
setNewTitle(text);
}}
placeholderTextColor="#2d2d30" placeholderTextColor="#2d2d30"
text60R text60R
marginT-15 marginT-15
@ -134,6 +170,7 @@ const AddChore = () => {
}} }}
/> />
</View> </View>
<Switch onColor={'#ea156c'} value={rotate} style={styles.rotateSwitch} onValueChange={(value) => setRotate(value)} />
<View style={styles.divider} /> <View style={styles.divider} />
<View marginH-30 marginB-10 row centerV> <View marginH-30 marginB-10 row centerV>
<Ionicons name="gift-outline" size={30} color="#919191" /> <Ionicons name="gift-outline" size={30} color="#919191" />
@ -195,4 +232,12 @@ const styles = StyleSheet.create({
backgroundColor: "rgb(253, 23, 117)", backgroundColor: "rgb(253, 23, 117)",
paddingVertical: 20, paddingVertical: 20,
}, },
topBtn: {
backgroundColor: "white",
color: "#05a8b6",
},
rotateSwitch: {
marginLeft: 'auto',
marginRight: 35
}
}); });

View File

@ -1,17 +1,37 @@
import { View, Text, Checkbox } from "react-native-ui-lib"; import { View, Text, Checkbox } from "react-native-ui-lib";
import React from "react"; import React from "react";
import { IToDo } from "@/contexts/ToDosContext"; import { IToDo, useToDosContext } from "@/contexts/ToDosContext";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
const ToDoItem = (props: { item: IToDo }) => { const ToDoItem = (props: { item: IToDo }) => {
const { updateToDo } = useToDosContext();
return ( return (
<View centerV backgroundColor="white" paddingV-10 paddingH-10 marginH-25 marginV-10 style={{borderRadius: 22}}> <View
centerV
backgroundColor="white"
paddingV-10
paddingH-10
marginH-25
marginV-10
style={{ borderRadius: 22 }}
>
<View paddingB-5 row spread> <View paddingB-5 row spread>
<Text text70R>{props.item.title}</Text> <Text text70R>{props.item.title}</Text>
<Checkbox value={props.item.done} /> <Checkbox
value={props.item.done}
onValueChange={(value) => {
updateToDo(props.item.id, { done: !props.item.done });
}}
/>
</View> </View>
<View centerH paddingV-5> <View centerH paddingV-5>
<View centerV height={2} width={"100%"} backgroundColor="#e7e7e7" centerH /> <View
centerV
height={2}
width={"100%"}
backgroundColor="#e7e7e7"
centerH
/>
</View> </View>
<View centerH row spread> <View centerH row spread>
{props.item.points && props.item.points > 0 ? ( {props.item.points && props.item.points > 0 ? (

View File

@ -27,7 +27,7 @@ const ToDosList = () => {
const groupedToDos = groupToDosByDate(toDos); const groupedToDos = groupToDosByDate(toDos);
return ( return (
<View> <View marginB-140>
{Object.keys(groupedToDos).map((dateKey) => ( {Object.keys(groupedToDos).map((dateKey) => (
<View key={dateKey}> <View key={dateKey}>
<Text text70 style={{ fontWeight: "bold", marginVertical: 8, paddingHorizontal: 20}}> <Text text70 style={{ fontWeight: "bold", marginVertical: 8, paddingHorizontal: 20}}>

View File

@ -2,14 +2,21 @@ import { View, Text } from "react-native-ui-lib";
import React from "react"; import React from "react";
import { useAuthContext } from "@/contexts/AuthContext"; import { useAuthContext } from "@/contexts/AuthContext";
const HeaderTemplate = (props: { message: string }) => { const HeaderTemplate = (props: { message: string; isWelcome: boolean; children?: React.ReactNode }) => {
const { user, profileData } = useAuthContext(); const { user, profileData } = useAuthContext();
return ( return (
<View row centerV padding-25> <View row centerV padding-25>
<View backgroundColor="pink" height={65} width={65} style={{borderRadius: 22}} marginR-20 /> <View
backgroundColor="pink"
height={65}
width={65}
style={{ borderRadius: 22 }}
marginR-20
/>
<View> <View>
<Text text70L>Welcome, {user?.email}!</Text> {props.isWelcome && <Text text70L>Welcome, {user?.email}!</Text>}
<Text text70BL>{props.message}</Text> <Text text70BL>{props.message}</Text>
{props.children && <View>{props.children}</View>}
</View> </View>
</View> </View>
); );

View File

@ -54,8 +54,8 @@ interface IGroceryContext {
groceries: IGrocery[]; groceries: IGrocery[];
iconMapping: { [key in GroceryCategory]: MaterialIconNames }; iconMapping: { [key in GroceryCategory]: MaterialIconNames };
updateGroceryItem: (id: number, changes: Partial<IGrocery>) => void; updateGroceryItem: (id: number, changes: Partial<IGrocery>) => void;
isShopping: boolean; isAddingGrocery: boolean;
setIsShopping: (value: boolean) => void; setIsAddingGrocery: (value: boolean) => void;
} }
const GroceryContext = createContext<IGroceryContext | undefined>(undefined); const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
@ -63,7 +63,7 @@ const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
children, children,
}) => { }) => {
const [isShopping, setIsShopping] = useState<boolean>(false); const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false);
const [groceries, setGroceries] = useState<IGrocery[]>([ const [groceries, setGroceries] = useState<IGrocery[]>([
{ {
id: 0, id: 0,
@ -113,7 +113,7 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
return ( return (
<GroceryContext.Provider <GroceryContext.Provider
value={{ groceries, iconMapping, updateGroceryItem, isShopping, setIsShopping }} value={{ groceries, iconMapping, updateGroceryItem, isAddingGrocery, setIsAddingGrocery }}
> >
{children} {children}
</GroceryContext.Provider> </GroceryContext.Provider>

View File

@ -5,10 +5,12 @@ export interface IToDo {
done: boolean; done: boolean;
date: Date; date: Date;
points?: number; points?: number;
rotate: boolean;
} }
interface IToDosContext { interface IToDosContext {
toDos: IToDo[]; toDos: IToDo[];
updateToDo: (id: number, changes: Partial<IToDo>) => void; updateToDo: (id: number, changes: Partial<IToDo>) => void;
addToDo: (newToDo: IToDo) => void;
} }
const ToDosContext = createContext<IToDosContext>(undefined!); const ToDosContext = createContext<IToDosContext>(undefined!);
@ -17,40 +19,76 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
children, children,
}) => { }) => {
const [toDos, setToDos] = useState<IToDo[]>([ const [toDos, setToDos] = useState<IToDo[]>([
{ id: 0, title: "Pay: Credit card", done: false, date: new Date() }, {
{ id: 1, title: "Monthly Log story", done: false, date: new Date() }, id: 0,
{ id: 2, title: "Write: Arcade Highlights", done: false, date: new Date() }, title: "Pay: Credit card",
done: false,
date: new Date(),
rotate: true,
},
{
id: 1,
title: "Monthly Log story",
done: false,
date: new Date(),
rotate: false,
},
{
id: 2,
title: "Write: Arcade Highlights",
done: false,
date: new Date(),
rotate: true,
},
{ {
id: 3, id: 3,
title: "Dressup: Cat", title: "Dressup: Cat",
done: false, done: false,
date: new Date(Date.now() + 86400000), date: new Date(Date.now() + 86400000),
points: 40, points: 40,
rotate: false,
}, },
{ {
id: 4, id: 4,
title: "Trim: Nails", title: "Trim: Nails",
done: false, done: false,
date: new Date(Date.now() + 86400000), date: new Date(Date.now() + 86400000),
rotate: false,
}, },
{ {
id: 5, id: 5,
title: "Monthly Log", title: "Monthly Log",
done: false, done: false,
date: new Date(Date.now() + 2 * 86400000), date: new Date(Date.now() + 2 * 86400000),
rotate: true,
}, },
{ {
id: 6, id: 6,
title: "Do it", title: "Do it",
done: false, done: false,
date: new Date(Date.now() + 3 * 86400000), date: new Date(Date.now() + 3 * 86400000),
rotate: false,
}, },
]); ]);
const updateToDo = (id: number, changes: Partial<IToDo>) => {}; const updateToDo = (id: number, changes: Partial<IToDo>) => {
setToDos((prevToDos) =>
prevToDos.map((toDo) => (toDo.id === id ? { ...toDo, ...changes } : toDo))
);
};
const addToDo = (newToDo: IToDo) => {
setToDos((prevToDos) => [
...prevToDos,
{
...newToDo,
id: prevToDos.length ? prevToDos[prevToDos.length - 1].id + 1 : 0,
},
]);
};
return ( return (
<ToDosContext.Provider value={{ toDos, updateToDo }}> <ToDosContext.Provider value={{ toDos, updateToDo, addToDo }}>
{children} {children}
</ToDosContext.Provider> </ToDosContext.Provider>
); );

33
package-lock.json generated
View File

@ -40,6 +40,7 @@
"react-native-calendars": "^1.1306.0", "react-native-calendars": "^1.1306.0",
"react-native-gesture-handler": "~2.16.1", "react-native-gesture-handler": "~2.16.1",
"react-native-linear-gradient": "^2.8.3", "react-native-linear-gradient": "^2.8.3",
"react-native-onboarding-swiper": "^1.3.0",
"react-native-reanimated": "~3.10.1", "react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1", "react-native-screens": "3.31.1",
@ -52,6 +53,7 @@
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/react": "~18.2.45", "@types/react": "~18.2.45",
"@types/react-native-onboarding-swiper": "^1.1.9",
"@types/react-test-renderer": "^18.0.7", "@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1", "jest": "^29.2.1",
"jest-expo": "~51.0.3", "jest-expo": "~51.0.3",
@ -8294,6 +8296,17 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@types/react-native-onboarding-swiper": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@types/react-native-onboarding-swiper/-/react-native-onboarding-swiper-1.1.9.tgz",
"integrity": "sha512-xCvsk7e6gQ9tEsK/xUPGA88mjF4sz8xFWAt+8de5xyjvrp3/nY0RJKPUd8ZtZzhGqcDm2tdsfI6KdNS5XuDxuA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*",
"react-native": "*"
}
},
"node_modules/@types/react-test-renderer": { "node_modules/@types/react-test-renderer": {
"version": "18.3.0", "version": "18.3.0",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz",
@ -12589,9 +12602,9 @@
} }
}, },
"node_modules/firebase-functions": { "node_modules/firebase-functions": {
"version": "5.1.0", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-5.1.0.tgz", "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-5.1.1.tgz",
"integrity": "sha512-VO46n9lqljrNiqOv4BbnFHYxY+yYCdZcOeUIF1t9DbFxBbVPztHdMM9MvpfCDp0nzXP2PugdmghSgM0hORrNvw==", "integrity": "sha512-KkyKZE98Leg/C73oRyuUYox04PQeeBThdygMfeX+7t1cmKWYKa/ZieYa89U8GHgED+0mF7m7wfNZOfbURYxIKg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/cors": "^2.8.5", "@types/cors": "^2.8.5",
@ -19625,6 +19638,20 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/react-native-onboarding-swiper": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/react-native-onboarding-swiper/-/react-native-onboarding-swiper-1.3.0.tgz",
"integrity": "sha512-2ZPMrZrJFgR5dmVWIj60x/vTBWrm0BZPuc2w7Cz2Sq/8ChypCi3oL8F7GYMrzky1fmknCS6Z0WPphfZVpnLUnQ==",
"license": "MIT",
"dependencies": {
"tinycolor2": "^1.4.1"
},
"peerDependencies": {
"prop-types": "*",
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-reanimated": { "node_modules/react-native-reanimated": {
"version": "3.10.1", "version": "3.10.1",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz",

View File

@ -47,6 +47,7 @@
"react-native-calendars": "^1.1306.0", "react-native-calendars": "^1.1306.0",
"react-native-gesture-handler": "~2.16.1", "react-native-gesture-handler": "~2.16.1",
"react-native-linear-gradient": "^2.8.3", "react-native-linear-gradient": "^2.8.3",
"react-native-onboarding-swiper": "^1.3.0",
"react-native-reanimated": "~3.10.1", "react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1", "react-native-screens": "3.31.1",
@ -59,6 +60,7 @@
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/react": "~18.2.45", "@types/react": "~18.2.45",
"@types/react-native-onboarding-swiper": "^1.1.9",
"@types/react-test-renderer": "^18.0.7", "@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1", "jest": "^29.2.1",
"jest-expo": "~51.0.3", "jest-expo": "~51.0.3",

View File

@ -3297,6 +3297,14 @@
resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz"
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
"@types/react-native-onboarding-swiper@^1.1.9":
version "1.1.9"
resolved "https://registry.npmjs.org/@types/react-native-onboarding-swiper/-/react-native-onboarding-swiper-1.1.9.tgz"
integrity sha512-xCvsk7e6gQ9tEsK/xUPGA88mjF4sz8xFWAt+8de5xyjvrp3/nY0RJKPUd8ZtZzhGqcDm2tdsfI6KdNS5XuDxuA==
dependencies:
"@types/react" "*"
react-native "*"
"@types/react-test-renderer@^18.0.7": "@types/react-test-renderer@^18.0.7":
version "18.3.0" version "18.3.0"
resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz" resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz"
@ -5675,9 +5683,9 @@ find-yarn-workspace-root@~2.0.0:
"@google-cloud/storage" "^7.7.0" "@google-cloud/storage" "^7.7.0"
firebase-functions@^5.1.0: firebase-functions@^5.1.0:
version "5.1.0" version "5.1.1"
resolved "https://registry.npmjs.org/firebase-functions/-/firebase-functions-5.1.0.tgz" resolved "https://registry.npmjs.org/firebase-functions/-/firebase-functions-5.1.1.tgz"
integrity sha512-VO46n9lqljrNiqOv4BbnFHYxY+yYCdZcOeUIF1t9DbFxBbVPztHdMM9MvpfCDp0nzXP2PugdmghSgM0hORrNvw== integrity sha512-KkyKZE98Leg/C73oRyuUYox04PQeeBThdygMfeX+7t1cmKWYKa/ZieYa89U8GHgED+0mF7m7wfNZOfbURYxIKg==
dependencies: dependencies:
"@types/cors" "^2.8.5" "@types/cors" "^2.8.5"
"@types/express" "4.17.3" "@types/express" "4.17.3"
@ -8970,6 +8978,13 @@ react-native-linear-gradient@^2.8.3:
resolved "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz" resolved "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz"
integrity sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA== integrity sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA==
react-native-onboarding-swiper@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/react-native-onboarding-swiper/-/react-native-onboarding-swiper-1.3.0.tgz"
integrity sha512-2ZPMrZrJFgR5dmVWIj60x/vTBWrm0BZPuc2w7Cz2Sq/8ChypCi3oL8F7GYMrzky1fmknCS6Z0WPphfZVpnLUnQ==
dependencies:
tinycolor2 "^1.4.1"
react-native-reanimated@*, "react-native-reanimated@>= 1.0.0", react-native-reanimated@>=2.0.0, react-native-reanimated@~3.10.1: react-native-reanimated@*, "react-native-reanimated@>= 1.0.0", react-native-reanimated@>=2.0.0, react-native-reanimated@~3.10.1:
version "3.10.1" version "3.10.1"
resolved "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz" resolved "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.10.1.tgz"
@ -10237,7 +10252,7 @@ through2@^2.0.1:
readable-stream "~2.3.6" readable-stream "~2.3.6"
xtend "~4.0.1" xtend "~4.0.1"
tinycolor2@^1.4.2: tinycolor2@^1.4.1, tinycolor2@^1.4.2:
version "1.6.0" version "1.6.0"
resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz" resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz"
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==