mirror of
https://github.com/urosran/cally.git
synced 2025-07-09 22:57:16 +00:00
237 lines
9.2 KiB
TypeScript
237 lines
9.2 KiB
TypeScript
import {createContext, useContext, useState} from "react";
|
||
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 {
|
||
Never = "Never",
|
||
Daily = "Daily",
|
||
Weekly = "Weekly",
|
||
BiWeekly = "BiWeekly",
|
||
Monthly = "Monthly",
|
||
Quarterly = "Quarterly",
|
||
}
|
||
|
||
export enum GroceryCategory {
|
||
Fruit = "Fruit",
|
||
Dairy = "Dairy",
|
||
Vegetables = "Vegetables",
|
||
Meat = "Meat",
|
||
Poultry = "Poultry",
|
||
Bakery = "Bakery",
|
||
Beverages = "Beverages",
|
||
Snacks = "Snacks",
|
||
Household = "Household",
|
||
PersonalCare = "Personal Care",
|
||
Frozen = "Frozen",
|
||
None = "No Category",
|
||
}
|
||
|
||
/*type MaterialIconNames = keyof typeof MaterialCommunityIcons.glyphMap;
|
||
const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
|
||
//за сад се иконице за категорију бирају одавде
|
||
[GroceryCategory.Fruit]: "food-apple",
|
||
[GroceryCategory.Dairy]: "cheese",
|
||
[GroceryCategory.Vegetables]: "carrot",
|
||
[GroceryCategory.Meat]: "food-steak",
|
||
[GroceryCategory.Poultry]: "food-drumstick",
|
||
[GroceryCategory.Bakery]: "bread-slice",
|
||
[GroceryCategory.Beverages]: "cup-water",
|
||
[GroceryCategory.Snacks]: "candy",
|
||
[GroceryCategory.Household]: "home",
|
||
[GroceryCategory.PersonalCare]: "face-man-profile",
|
||
[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 {
|
||
groceries: IGrocery[];
|
||
//iconMapping: { [key in GroceryCategory]: MaterialIconNames };
|
||
updateGroceryItem: (changes: Partial<IGrocery>) => void;
|
||
isAddingGrocery: boolean;
|
||
setIsAddingGrocery: (value: boolean) => void;
|
||
addGrocery: (grocery: IGrocery) => void;
|
||
fuzzyMatchGroceryCategory: (groceryTitle: string) => GroceryCategory;
|
||
}
|
||
|
||
const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
|
||
|
||
export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
|
||
children,
|
||
}) => {
|
||
const [isAddingGrocery, setIsAddingGrocery] = useState<boolean>(false);
|
||
|
||
const { mutateAsync: createGrocery } = useCreateGrocery();
|
||
const { mutateAsync: updateGrocery } = useUpdateGrocery();
|
||
const { data: groceries } = useGetGroceries();
|
||
|
||
|
||
const addGrocery = (grocery: IGrocery) => {
|
||
createGrocery(grocery);
|
||
};
|
||
|
||
const fuzzyMatchGroceryCategory = (groceryTitle: string): GroceryCategory => {
|
||
let bestMatchCategory = GroceryCategory.None;
|
||
let bestMatchScore = -1000;
|
||
|
||
Object.entries(groceryExamples).forEach(([category, examples]) => {
|
||
const matches = fuzzysort.go(groceryTitle, examples);
|
||
if (matches.length > 0 && matches[0].score > bestMatchScore) {
|
||
bestMatchScore = matches[0].score;
|
||
bestMatchCategory = category as GroceryCategory;
|
||
}
|
||
});
|
||
|
||
return bestMatchCategory;
|
||
};
|
||
|
||
const updateGroceryItem = (changes: Partial<IGrocery>) => {
|
||
updateGrocery(changes);
|
||
};
|
||
|
||
return (
|
||
<GroceryContext.Provider
|
||
value={{
|
||
groceries: groceries ?? [],
|
||
fuzzyMatchGroceryCategory,
|
||
//iconMapping,
|
||
updateGroceryItem,
|
||
isAddingGrocery,
|
||
setIsAddingGrocery,
|
||
addGrocery,
|
||
}}
|
||
>
|
||
{children}
|
||
</GroceryContext.Provider>
|
||
);
|
||
};
|
||
|
||
export const useGroceryContext = () => useContext(GroceryContext)!;
|