Shopping List backend implementation

- Implemented fetching, create and update of groceries in db
This commit is contained in:
Dejan
2024-10-11 16:11:46 +02:00
parent a05de1b333
commit 9b94aa8e70
8 changed files with 272 additions and 208 deletions

View File

@ -1,5 +1,5 @@
import { StyleSheet } from "react-native"; import { StyleSheet } from "react-native";
import React, { useState } from "react"; import React from "react";
import { import {
Dialog, Dialog,
Text, Text,
@ -11,9 +11,9 @@ import {
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import { import {
GroceryFrequency, GroceryFrequency,
IGrocery,
useGroceryContext, useGroceryContext,
} from "@/contexts/GroceryContext"; } from "@/contexts/GroceryContext";
import { IGrocery } from "@/hooks/firebase/types/groceryData";
interface EditGroceryFrequencyProps { interface EditGroceryFrequencyProps {
visible: boolean; visible: boolean;
onClose: () => void; onClose: () => void;
@ -42,7 +42,7 @@ const EditGroceryFrequency = (props: EditGroceryFrequencyProps) => {
<Switch <Switch
value={props.item.recurring} value={props.item.recurring}
onValueChange={(value) => onValueChange={(value) =>
updateGroceryItem(props.item.id, { recurring: value }) updateGroceryItem({id: props.item.id, recurring: value})
} }
onColor={"lime"} onColor={"lime"}
/> />
@ -56,8 +56,9 @@ const EditGroceryFrequency = (props: EditGroceryFrequencyProps) => {
const selectedFrequency = const selectedFrequency =
GroceryFrequency[item as keyof typeof GroceryFrequency]; GroceryFrequency[item as keyof typeof GroceryFrequency];
if (selectedFrequency) { if (selectedFrequency) {
updateGroceryItem(props.item.id, { updateGroceryItem({
frequency: selectedFrequency, id: props.item.id,
frequency: selectedFrequency,
}); });
} else { } else {
console.error("Invalid frequency selected"); console.error("Invalid frequency selected");

View File

@ -1,34 +1,21 @@
import { import {Checkbox, Text, TouchableOpacity, View,} from "react-native-ui-lib";
View, import React, {useEffect, useState} from "react";
Text, import {AntDesign} from "@expo/vector-icons";
Button, import {GroceryCategory, useGroceryContext,} from "@/contexts/GroceryContext";
TouchableOpacity,
Checkbox,
ButtonSize,
} from "react-native-ui-lib";
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 {
GroceryCategory,
IGrocery,
useGroceryContext,
} from "@/contexts/GroceryContext";
import EditGroceryFrequency from "./EditGroceryFrequency"; import EditGroceryFrequency from "./EditGroceryFrequency";
import EditGroceryItem from "./EditGroceryItem"; import EditGroceryItem from "./EditGroceryItem";
import { StyleSheet } from "react-native"; import {StyleSheet} from "react-native";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
const GroceryItem = ({ const GroceryItem = ({
item, item,
handleItemApproved, handleItemApproved,
}: { }: {
item: IGrocery; item: IGrocery;
handleItemApproved: (id: number, changes: Partial<IGrocery>) => void; handleItemApproved: (id: string, changes: Partial<IGrocery>) => void;
}) => { }) => {
const { updateGroceryItem, groceries } = useGroceryContext(); const { updateGroceryItem } = useGroceryContext();
const { profileType } = useAuthContext();
const [openFreqEdit, setOpenFreqEdit] = useState<boolean>(false); const [openFreqEdit, setOpenFreqEdit] = useState<boolean>(false);
const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false); const [isEditingTitle, setIsEditingTitle] = useState<boolean>(false);
const [newTitle, setNewTitle] = useState<string>(""); const [newTitle, setNewTitle] = useState<string>("");
@ -37,11 +24,11 @@ const GroceryItem = ({
); );
const handleTitleChange = (newTitle: string) => { const handleTitleChange = (newTitle: string) => {
updateGroceryItem(item.id, { title: newTitle }); updateGroceryItem({id: item?.id, title: newTitle});
}; };
const handleCategoryChange = (newCategory: GroceryCategory) => { const handleCategoryChange = (newCategory: GroceryCategory) => {
updateGroceryItem(item.id, { category: newCategory }); updateGroceryItem({id: item?.id, category: newCategory});
}; };
useEffect(() => { useEffect(() => {
@ -114,7 +101,7 @@ const GroceryItem = ({
containerStyle={styles.checkbox} containerStyle={styles.checkbox}
hitSlop={20} hitSlop={20}
onValueChange={() => onValueChange={() =>
updateGroceryItem(item.id, { bought: !item.bought }) updateGroceryItem({ id: item.id, bought: !item.bought })
} }
/> />
)} )}

View File

@ -1,18 +1,13 @@
import { FlatList, StyleSheet } from "react-native"; import {FlatList, StyleSheet} from "react-native";
import React, { RefObject, useEffect, useState } from "react"; import React, {useEffect, useState} from "react";
import { View, Text, ListItem, Button, TextField, TextFieldRef } from "react-native-ui-lib"; import {Button, Text, View} from "react-native-ui-lib";
import GroceryItem from "./GroceryItem"; import GroceryItem from "./GroceryItem";
import { import {GroceryCategory, GroceryFrequency, useGroceryContext,} from "@/contexts/GroceryContext";
IGrocery,
GroceryCategory,
GroceryFrequency,
useGroceryContext,
} from "@/contexts/GroceryContext";
import HeaderTemplate from "@/components/shared/HeaderTemplate"; import HeaderTemplate from "@/components/shared/HeaderTemplate";
import CategoryDropdown from "./CategoryDropdown"; import {AntDesign, MaterialIcons} from "@expo/vector-icons";
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
import EditGroceryItem from "./EditGroceryItem"; import EditGroceryItem from "./EditGroceryItem";
import { ProfileType, useAuthContext } from "@/contexts/AuthContext"; import {ProfileType, useAuthContext} from "@/contexts/AuthContext";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
const GroceryList = () => { const GroceryList = () => {
const { const {
@ -24,10 +19,10 @@ const GroceryList = () => {
} = useGroceryContext(); } = useGroceryContext();
const { profileData } = useAuthContext(); const { profileData } = useAuthContext();
const [approvedGroceries, setapprovedGroceries] = useState<IGrocery[]>( const [approvedGroceries, setapprovedGroceries] = useState<IGrocery[]>(
groceries.filter((item) => item.approved === true) groceries?.filter((item) => item.approved === true)
); );
const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>( const [pendingGroceries, setPendingGroceries] = useState<IGrocery[]>(
groceries.filter((item) => item.approved !== true) groceries?.filter((item) => item.approved !== true)
); );
const [category, setCategory] = useState<GroceryCategory>( const [category, setCategory] = useState<GroceryCategory>(
GroceryCategory.Bakery GroceryCategory.Bakery
@ -39,7 +34,7 @@ const GroceryList = () => {
const [approvedVisible, setApprovedVisible] = useState<boolean>(true); const [approvedVisible, setApprovedVisible] = useState<boolean>(true);
// Group approved groceries by category // Group approved groceries by category
const approvedGroceriesByCategory = approvedGroceries.reduce( const approvedGroceriesByCategory = approvedGroceries?.reduce(
(groups: any, item: IGrocery) => { (groups: any, item: IGrocery) => {
const category = item.category || "Uncategorized"; const category = item.category || "Uncategorized";
if (!groups[category]) { if (!groups[category]) {
@ -76,8 +71,8 @@ const GroceryList = () => {
}, [category]); }, [category]);
useEffect(() => { useEffect(() => {
setapprovedGroceries(groceries.filter((item) => item.approved === true)); setapprovedGroceries(groceries?.filter((item) => item.approved === true));
setPendingGroceries(groceries.filter((item) => item.approved !== true)); setPendingGroceries(groceries?.filter((item) => item.approved !== true));
}, [groceries]); }, [groceries]);
return ( return (
@ -93,8 +88,8 @@ const GroceryList = () => {
style={{ borderRadius: 50 }} style={{ borderRadius: 50 }}
> >
<Text text70BL color="#46a80a"> <Text text70BL color="#46a80a">
{approvedGroceries.length} list{" "} {approvedGroceries?.length} list{" "}
{approvedGroceries.length === 1 ? ( {approvedGroceries?.length === 1 ? (
<Text text70BL color="#46a80a"> <Text text70BL color="#46a80a">
item item
</Text> </Text>
@ -111,7 +106,7 @@ const GroceryList = () => {
style={{ borderRadius: 50 }} style={{ borderRadius: 50 }}
> >
<Text text70BL color="#e28800"> <Text text70BL color="#e28800">
{pendingGroceries.length} pending {pendingGroceries?.length} pending
</Text> </Text>
</View> </View>
<Button <Button
@ -161,18 +156,18 @@ const GroceryList = () => {
}} }}
> >
<Text text70 center color="#e28800"> <Text text70 center color="#e28800">
{pendingGroceries.length.toString()} {pendingGroceries?.length.toString()}
</Text> </Text>
</View> </View>
</View> </View>
{pendingGroceries.length > 0 {pendingGroceries?.length > 0
? pendingVisible && ( ? pendingVisible && (
<FlatList <FlatList
data={pendingGroceries} data={pendingGroceries}
renderItem={({ item }) => ( renderItem={({ item }) => (
<GroceryItem <GroceryItem
item={item} item={item}
handleItemApproved={updateGroceryItem} handleItemApproved={(id, changes) => updateGroceryItem({...changes, id: id})}
/> />
)} )}
keyExtractor={(item) => item.id.toString()} keyExtractor={(item) => item.id.toString()}
@ -217,7 +212,7 @@ const GroceryList = () => {
}} }}
> >
<Text text70 center color="#46a80a"> <Text text70 center color="#46a80a">
{approvedGroceries.length.toString()} {approvedGroceries?.length.toString()}
</Text> </Text>
</View> </View>
</View> </View>
@ -234,7 +229,7 @@ const GroceryList = () => {
)} )}
{/* Render Approved Groceries Grouped by Category */} {/* Render Approved Groceries Grouped by Category */}
{approvedGroceries.length > 0 {approvedGroceries?.length > 0
? approvedVisible && ( ? approvedVisible && (
<FlatList <FlatList
data={Object.keys(approvedGroceriesByCategory)} data={Object.keys(approvedGroceriesByCategory)}
@ -250,7 +245,7 @@ const GroceryList = () => {
<GroceryItem <GroceryItem
key={grocery.id} key={grocery.id}
item={grocery} item={grocery}
handleItemApproved={updateGroceryItem} handleItemApproved={(id, changes) => updateGroceryItem({...changes, id: id})}
/> />
) )
)} )}

View File

@ -1,6 +1,9 @@
import { MaterialCommunityIcons } from "@expo/vector-icons"; import {createContext, useContext, useState} from "react";
import { createContext, useContext, useState } from "react";
import fuzzysort from "fuzzysort"; import fuzzysort from "fuzzysort";
import {useCreateGrocery} from "@/hooks/firebase/useCreateGrocery";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
import {useUpdateGrocery} from "@/hooks/firebase/useUpdateGrocery";
import {useGetGroceries} from "@/hooks/firebase/useGetGroceries";
export enum GroceryFrequency { export enum GroceryFrequency {
Never = "Never", Never = "Never",
@ -11,16 +14,6 @@ export enum GroceryFrequency {
Quarterly = "Quarterly", Quarterly = "Quarterly",
} }
export interface IGrocery {
id: number;
title: string;
category: GroceryCategory;
approved: boolean;
recurring: boolean;
frequency: GroceryFrequency;
bought: boolean;
}
export enum GroceryCategory { export enum GroceryCategory {
Fruit = "Fruit", Fruit = "Fruit",
Dairy = "Dairy", Dairy = "Dairy",
@ -52,10 +45,136 @@ const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
[GroceryCategory.Frozen]: "snowflake", [GroceryCategory.Frozen]: "snowflake",
};*/ };*/
const initialGroceryList = [
{
id: 0,
title: "Carrots",
category: GroceryCategory.Vegetables,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 1,
title: "Steak",
category: GroceryCategory.Meat,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 2,
title: "Chicken Breast",
category: GroceryCategory.Poultry,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 3,
title: "Greek Yoghurt",
category: GroceryCategory.Dairy,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
];
const groceryExamples = {
[GroceryCategory.Fruit]: [
'apple', 'apples', 'banana', 'bananas', 'orange', 'oranges', 'grape', 'grapes',
'pear', 'pears', 'pineapple', 'pineapples', 'kiwi', 'kiwis', 'strawberry',
'strawberries', 'blueberry', 'blueberries', 'mango', 'mangoes', 'watermelon',
'watermelons', 'peach', 'peaches', 'plum', 'plums', 'cherry', 'cherries',
'raspberry', 'raspberries', 'blackberry', 'blackberries', 'pomegranate',
'pomegranates', 'lemon', 'lemons', 'lime', 'limes', 'coconut', 'coconuts'
],
[GroceryCategory.Dairy]: [
'milk', 'whole milk', 'skim milk', 'almond milk', 'soy milk', 'cheese',
'cheeses', 'yoghurt', 'yogurt', 'greek yoghurt', 'greek yogurt', 'butter',
'margarine', 'cream', 'whipping cream', 'heavy cream', 'ice cream', 'ice creams',
'sour cream', 'whipped cream', 'cream cheese', 'cream cheeses', 'buttermilk',
'cottage cheese', 'ghee', 'kefir'
],
[GroceryCategory.Vegetables]: [
'carrot', 'carrots', 'broccoli', 'lettuce', 'lettuces', 'spinach', 'kale',
'cabbage', 'cabbages', 'cauliflower', 'zucchini', 'zucchinis', 'onion', 'onions',
'garlic', 'pepper', 'peppers', 'bell pepper', 'bell peppers', 'tomato', 'tomatoes',
'cucumber', 'cucumbers', 'potato', 'potatoes', 'sweet potato', 'sweet potatoes',
'beet', 'beets', 'eggplant', 'eggplants', 'celery', 'radish', 'radishes',
'asparagus', 'mushroom', 'mushrooms'
],
[GroceryCategory.Meat]: [
'steak', 'steaks', 'beef', 'pork', 'lamb', 'veal', 'ribeye', 'tenderloin',
'filet mignon', 'bacon', 'ham', 'sausage', 'sausages', 'salami', 'ground beef',
'beef mince', 'hamburger meat', 'prosciutto', 'brisket', 'pork chop', 'pork chops'
],
[GroceryCategory.Poultry]: [
'chicken', 'whole chicken', 'chickens', 'chicken breast', 'chicken breasts',
'breast of chicken', 'chicken thigh', 'chicken thighs', 'chicken leg', 'chicken legs',
'chicken wing', 'chicken wings', 'wing', 'wings', 'drumsticks', 'chicken drumstick',
'turkey', 'whole turkey', 'ground turkey', 'turkey breast', 'turkey breasts',
'turkey leg', 'duck', 'ducks', 'goose', 'quail', 'pheasant'
],
[GroceryCategory.Bakery]: [
'bread', 'breads', 'whole wheat bread', 'sourdough bread', 'bagel', 'bagels',
'croissant', 'croissants', 'muffin', 'muffins', 'buns', 'hamburger bun',
'hamburger buns', 'hotdog bun', 'hotdog buns', 'donut', 'donuts', 'roll', 'rolls',
'dinner roll', 'dinner rolls', 'scone', 'scones', 'toast', 'ciabatta',
'focaccia', 'brioche', 'pita', 'pitas', 'naan', 'baguette', 'baguettes',
'pastry', 'pastries', 'pretzel', 'pretzels'
],
[GroceryCategory.Beverages]: [
'coffee', 'decaf coffee', 'iced coffee', 'cold brew', 'tea', 'iced tea',
'black tea', 'green tea', 'herbal tea', 'juice', 'apple juice', 'orange juice',
'grape juice', 'water', 'sparkling water', 'flavored water', 'soda', 'cola',
'diet soda', 'beer', 'lager', 'ale', 'wine', 'red wine', 'white wine',
'whiskey', 'whisky', 'vodka', 'rum', 'gin', 'smoothie', 'smoothies', 'milkshake',
'milkshakes', 'energy drink', 'energy drinks', 'sports drink', 'sports drinks',
'lemonade', 'sparkling lemonade', 'iced lemonade', 'sparkling water', 'cider',
'hard cider', 'kombucha'
],
[GroceryCategory.Snacks]: [
'chips', 'potato chips', 'tortilla chips', 'corn chips', 'candy', 'candies',
'chocolate', 'chocolates', 'dark chocolate', 'milk chocolate', 'white chocolate',
'cookies', 'popcorn', 'pretzel', 'pretzels', 'nuts', 'mixed nuts', 'almonds',
'cashews', 'trail mix', 'granola bar', 'granola bars', 'protein bar', 'protein bars',
'crackers', 'gummies', 'gummy bears', 'fruit snacks', 'dried fruit',
'peanut butter', 'rice cakes', 'snack cakes'
],
[GroceryCategory.Household]: [
'detergent', 'laundry detergent', 'dish soap', 'dishwasher detergent',
'toilet paper', 'paper towels', 'trash bags', 'bin liners', 'broom', 'mop',
'cleaner', 'surface cleaner', 'multi-purpose cleaner', 'disinfectant', 'bleach',
'fabric softener', 'vacuum bags', 'aluminum foil', 'plastic wrap', 'cling wrap',
'light bulbs', 'batteries', 'laundry soap', 'sponges', 'scrubbers',
'dishwashing liquid'
],
[GroceryCategory.PersonalCare]: [
'shampoo', 'conditioner', 'hair shampoo', 'hair conditioner', 'soap', 'bar soap',
'liquid soap', 'toothpaste', 'toothbrush', 'toothbrushes', 'electric toothbrush',
'deodorant', 'antiperspirant', 'lotion', 'moisturizer', 'razor', 'shaving razor',
'shaving cream', 'body wash', 'face wash', 'sunscreen', 'hair gel', 'hair spray',
'nail polish', 'cotton swabs', 'lip balm', 'chapstick', 'hand cream', 'sanitizer'
],
[GroceryCategory.Frozen]: [
'ice cream', 'ice creams', 'frozen pizza', 'frozen pizzas', 'frozen vegetables',
'frozen veg', 'frozen peas', 'frozen fruit', 'frozen fish', 'frozen chicken',
'frozen wings', 'frozen fries', 'frozen french fries', 'frozen shrimp',
'frozen prawns', 'frozen dumplings', 'frozen waffles', 'frozen pancakes',
'frozen pie', 'frozen pies', 'frozen lasagna', 'frozen burrito', 'frozen burritos',
'frozen nuggets', 'frozen pastry', 'frozen pastries', 'frozen meals'
],
};
interface IGroceryContext { interface IGroceryContext {
groceries: IGrocery[]; groceries: IGrocery[];
//iconMapping: { [key in GroceryCategory]: MaterialIconNames }; //iconMapping: { [key in GroceryCategory]: MaterialIconNames };
updateGroceryItem: (id: number, changes: Partial<IGrocery>) => void; updateGroceryItem: (changes: Partial<IGrocery>) => void;
isAddingGrocery: boolean; isAddingGrocery: boolean;
setIsAddingGrocery: (value: boolean) => void; setIsAddingGrocery: (value: boolean) => void;
addGrocery: (grocery: IGrocery) => void; addGrocery: (grocery: IGrocery) => void;
@ -68,142 +187,14 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
children, children,
}) => { }) => {
const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false); const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false);
const [groceries, setGroceries] = useState<IGrocery[]>([
{ const { mutateAsync: createGrocery } = useCreateGrocery();
id: 0, const { mutateAsync: updateGrocery } = useUpdateGrocery();
title: "Carrots", const { data: groceries } = useGetGroceries();
category: GroceryCategory.Vegetables,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 1,
title: "Steak",
category: GroceryCategory.Meat,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 2,
title: "Chicken Breast",
category: GroceryCategory.Poultry,
approved: true,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
{
id: 3,
title: "Greek Yoghurt",
category: GroceryCategory.Dairy,
approved: false,
bought: false,
recurring: false,
frequency: GroceryFrequency.Never,
},
]);
const addGrocery = (grocery: IGrocery) => { const addGrocery = (grocery: IGrocery) => {
setGroceries((prevGroceries) => [ createGrocery(grocery);
...prevGroceries,
{
...grocery,
id: prevGroceries.length
? prevGroceries[prevGroceries.length - 1].id + 1
: 0,
},
]);
};
const groceryExamples = {
[GroceryCategory.Fruit]: [
'apple', 'apples', 'banana', 'bananas', 'orange', 'oranges', 'grape', 'grapes',
'pear', 'pears', 'pineapple', 'pineapples', 'kiwi', 'kiwis', 'strawberry',
'strawberries', 'blueberry', 'blueberries', 'mango', 'mangoes', 'watermelon',
'watermelons', 'peach', 'peaches', 'plum', 'plums', 'cherry', 'cherries',
'raspberry', 'raspberries', 'blackberry', 'blackberries', 'pomegranate',
'pomegranates', 'lemon', 'lemons', 'lime', 'limes', 'coconut', 'coconuts'
],
[GroceryCategory.Dairy]: [
'milk', 'whole milk', 'skim milk', 'almond milk', 'soy milk', 'cheese',
'cheeses', 'yoghurt', 'yogurt', 'greek yoghurt', 'greek yogurt', 'butter',
'margarine', 'cream', 'whipping cream', 'heavy cream', 'ice cream', 'ice creams',
'sour cream', 'whipped cream', 'cream cheese', 'cream cheeses', 'buttermilk',
'cottage cheese', 'ghee', 'kefir'
],
[GroceryCategory.Vegetables]: [
'carrot', 'carrots', 'broccoli', 'lettuce', 'lettuces', 'spinach', 'kale',
'cabbage', 'cabbages', 'cauliflower', 'zucchini', 'zucchinis', 'onion', 'onions',
'garlic', 'pepper', 'peppers', 'bell pepper', 'bell peppers', 'tomato', 'tomatoes',
'cucumber', 'cucumbers', 'potato', 'potatoes', 'sweet potato', 'sweet potatoes',
'beet', 'beets', 'eggplant', 'eggplants', 'celery', 'radish', 'radishes',
'asparagus', 'mushroom', 'mushrooms'
],
[GroceryCategory.Meat]: [
'steak', 'steaks', 'beef', 'pork', 'lamb', 'veal', 'ribeye', 'tenderloin',
'filet mignon', 'bacon', 'ham', 'sausage', 'sausages', 'salami', 'ground beef',
'beef mince', 'hamburger meat', 'prosciutto', 'brisket', 'pork chop', 'pork chops'
],
[GroceryCategory.Poultry]: [
'chicken', 'whole chicken', 'chickens', 'chicken breast', 'chicken breasts',
'breast of chicken', 'chicken thigh', 'chicken thighs', 'chicken leg', 'chicken legs',
'chicken wing', 'chicken wings', 'wing', 'wings', 'drumsticks', 'chicken drumstick',
'turkey', 'whole turkey', 'ground turkey', 'turkey breast', 'turkey breasts',
'turkey leg', 'duck', 'ducks', 'goose', 'quail', 'pheasant'
],
[GroceryCategory.Bakery]: [
'bread', 'breads', 'whole wheat bread', 'sourdough bread', 'bagel', 'bagels',
'croissant', 'croissants', 'muffin', 'muffins', 'buns', 'hamburger bun',
'hamburger buns', 'hotdog bun', 'hotdog buns', 'donut', 'donuts', 'roll', 'rolls',
'dinner roll', 'dinner rolls', 'scone', 'scones', 'toast', 'ciabatta',
'focaccia', 'brioche', 'pita', 'pitas', 'naan', 'baguette', 'baguettes',
'pastry', 'pastries', 'pretzel', 'pretzels'
],
[GroceryCategory.Beverages]: [
'coffee', 'decaf coffee', 'iced coffee', 'cold brew', 'tea', 'iced tea',
'black tea', 'green tea', 'herbal tea', 'juice', 'apple juice', 'orange juice',
'grape juice', 'water', 'sparkling water', 'flavored water', 'soda', 'cola',
'diet soda', 'beer', 'lager', 'ale', 'wine', 'red wine', 'white wine',
'whiskey', 'whisky', 'vodka', 'rum', 'gin', 'smoothie', 'smoothies', 'milkshake',
'milkshakes', 'energy drink', 'energy drinks', 'sports drink', 'sports drinks',
'lemonade', 'sparkling lemonade', 'iced lemonade', 'sparkling water', 'cider',
'hard cider', 'kombucha'
],
[GroceryCategory.Snacks]: [
'chips', 'potato chips', 'tortilla chips', 'corn chips', 'candy', 'candies',
'chocolate', 'chocolates', 'dark chocolate', 'milk chocolate', 'white chocolate',
'cookies', 'popcorn', 'pretzel', 'pretzels', 'nuts', 'mixed nuts', 'almonds',
'cashews', 'trail mix', 'granola bar', 'granola bars', 'protein bar', 'protein bars',
'crackers', 'gummies', 'gummy bears', 'fruit snacks', 'dried fruit',
'peanut butter', 'rice cakes', 'snack cakes'
],
[GroceryCategory.Household]: [
'detergent', 'laundry detergent', 'dish soap', 'dishwasher detergent',
'toilet paper', 'paper towels', 'trash bags', 'bin liners', 'broom', 'mop',
'cleaner', 'surface cleaner', 'multi-purpose cleaner', 'disinfectant', 'bleach',
'fabric softener', 'vacuum bags', 'aluminum foil', 'plastic wrap', 'cling wrap',
'light bulbs', 'batteries', 'laundry soap', 'sponges', 'scrubbers',
'dishwashing liquid'
],
[GroceryCategory.PersonalCare]: [
'shampoo', 'conditioner', 'hair shampoo', 'hair conditioner', 'soap', 'bar soap',
'liquid soap', 'toothpaste', 'toothbrush', 'toothbrushes', 'electric toothbrush',
'deodorant', 'antiperspirant', 'lotion', 'moisturizer', 'razor', 'shaving razor',
'shaving cream', 'body wash', 'face wash', 'sunscreen', 'hair gel', 'hair spray',
'nail polish', 'cotton swabs', 'lip balm', 'chapstick', 'hand cream', 'sanitizer'
],
[GroceryCategory.Frozen]: [
'ice cream', 'ice creams', 'frozen pizza', 'frozen pizzas', 'frozen vegetables',
'frozen veg', 'frozen peas', 'frozen fruit', 'frozen fish', 'frozen chicken',
'frozen wings', 'frozen fries', 'frozen french fries', 'frozen shrimp',
'frozen prawns', 'frozen dumplings', 'frozen waffles', 'frozen pancakes',
'frozen pie', 'frozen pies', 'frozen lasagna', 'frozen burrito', 'frozen burritos',
'frozen nuggets', 'frozen pastry', 'frozen pastries', 'frozen meals'
],
}; };
const fuzzyMatchGroceryCategory = (groceryTitle: string): GroceryCategory => { const fuzzyMatchGroceryCategory = (groceryTitle: string): GroceryCategory => {
@ -221,12 +212,8 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
return bestMatchCategory; return bestMatchCategory;
}; };
const updateGroceryItem = (id: number, changes: Partial<IGrocery>) => { const updateGroceryItem = (changes: Partial<IGrocery>) => {
setGroceries((prevGroceries) => updateGrocery(changes);
prevGroceries.map((grocery) =>
grocery.id === id ? { ...grocery, ...changes } : grocery
)
);
}; };
return ( return (

View File

@ -0,0 +1,12 @@
import {GroceryCategory, GroceryFrequency} from "@/contexts/GroceryContext";
export interface IGrocery {
id: string;
title: string;
category: GroceryCategory;
approved: boolean;
recurring: boolean;
frequency: GroceryFrequency;
bought: boolean;
familyId?: string,
}

View File

@ -0,0 +1,26 @@
import { useMutation, useQueryClient } from "react-query";
import firestore from "@react-native-firebase/firestore";
import { useAuthContext } from "@/contexts/AuthContext";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
export const useCreateGrocery = () => {
const { profileData } = useAuthContext();
const queryClients = useQueryClient();
return useMutation({
mutationKey: ["createGrocery"],
mutationFn: async (groceryData: Partial<IGrocery>) => {
try {
const newDoc = firestore().collection('Groceries').doc();
await firestore()
.collection("Groceries")
.add({...groceryData, id: newDoc.id, familyId: profileData?.familyId})
} catch (e) {
console.error(e)
}
},
onSuccess: () => {
queryClients.invalidateQueries("groceries")
}
})
}

View File

@ -0,0 +1,31 @@
import {useQuery} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {useAuthContext} from "@/contexts/AuthContext";
export const useGetGroceries = () => {
const { user, profileData } = useAuthContext();
return useQuery({
queryKey: ["groceries", user?.uid],
queryFn: async () => {
const snapshot = await firestore()
.collection("Groceries")
.where("familyId", "==", profileData?.familyId)
.get();
return snapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
title: data.title,
category: data.category,
approved: data.approved,
bought: data.bought,
recurring: data.recurring,
frequency: data.frequency
};
});
}
})
};

View File

@ -0,0 +1,25 @@
import {useMutation, useQueryClient} from "react-query";
import firestore from "@react-native-firebase/firestore";
import {IGrocery} from "@/hooks/firebase/types/groceryData";
export const useUpdateGrocery = () => {
const queryClients = useQueryClient()
return useMutation({
mutationKey: ["updateGrocery"],
mutationFn: async (groceryData: Partial<IGrocery>) => {
console.log(groceryData);
try {
await firestore()
.collection("Groceries")
.doc(groceryData.id)
.update(groceryData);
} catch (e) {
console.error(e)
}
},
onSuccess: () => {
queryClients.invalidateQueries("events")
}
})
}