mirror of
https://github.com/urosran/cally.git
synced 2025-07-16 18:16:17 +00:00
Merge branch 'dev'
# Conflicts: # app/(auth)/calendar/index.tsx # components/pages/calendar/ManuallyAddEventModal.tsx # components/pages/main/SignUpPage.tsx # package.json
This commit is contained in:
@ -5,6 +5,7 @@ import {Button, Picker, PickerModes, SegmentedControl, Text, View} from "react-n
|
|||||||
import {MaterialIcons} from "@expo/vector-icons";
|
import {MaterialIcons} from "@expo/vector-icons";
|
||||||
import {AddEventDialog} from "@/components/pages/calendar/AddEventDialog";
|
import {AddEventDialog} from "@/components/pages/calendar/AddEventDialog";
|
||||||
import {useGetEvents} from "@/hooks/firebase/useGetEvents";
|
import {useGetEvents} from "@/hooks/firebase/useGetEvents";
|
||||||
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
import {ManuallyAddEventModal} from "@/components/pages/calendar/ManuallyAddEventModal";
|
import {ManuallyAddEventModal} from "@/components/pages/calendar/ManuallyAddEventModal";
|
||||||
|
|
||||||
const modeMap = new Map([
|
const modeMap = new Map([
|
||||||
@ -19,6 +20,7 @@ const months = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function Screen() {
|
export default function Screen() {
|
||||||
|
const { profileData } = useAuthContext();
|
||||||
const [calendarHeight, setCalendarHeight] = useState(0);
|
const [calendarHeight, setCalendarHeight] = useState(0);
|
||||||
const [mode, setMode] = useState<"week" | "month" | "day">("week");
|
const [mode, setMode] = useState<"week" | "month" | "day">("week");
|
||||||
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
||||||
@ -55,7 +57,7 @@ export default function Screen() {
|
|||||||
return (
|
return (
|
||||||
<View style={{flex: 1, height: "100%", padding: 10}}>
|
<View style={{flex: 1, height: "100%", padding: 10}}>
|
||||||
<View style={{height: 60, justifyContent: "space-evenly", alignItems: "flex-start"}}>
|
<View style={{height: 60, justifyContent: "space-evenly", alignItems: "flex-start"}}>
|
||||||
<Text>Welcome Dalia</Text>
|
<Text>Welcome {profileData?.firstName}</Text>
|
||||||
<Text>Let's get your week started!</Text>
|
<Text>Let's get your week started!</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import { Text, View } from "react-native-ui-lib";
|
|
||||||
import GroceryList from "@/components/pages/grocery/GroceryList";
|
|
||||||
import AddGroceryItem from "@/components/pages/grocery/AddGroceryItem";
|
|
||||||
import { GroceryProvider } from "@/contexts/GroceryContext";
|
import { GroceryProvider } from "@/contexts/GroceryContext";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import GroceryWrapper from "@/components/pages/grocery/GroceryWrapper";
|
import GroceryWrapper from "@/components/pages/grocery/GroceryWrapper";
|
||||||
|
@ -2,24 +2,18 @@ import AddChore from "@/components/pages/todos/AddChore";
|
|||||||
import ProgressCard from "@/components/pages/todos/ProgressCard";
|
import ProgressCard from "@/components/pages/todos/ProgressCard";
|
||||||
import ToDoItem from "@/components/pages/todos/ToDoItem";
|
import ToDoItem from "@/components/pages/todos/ToDoItem";
|
||||||
import ToDosList from "@/components/pages/todos/ToDosList";
|
import ToDosList from "@/components/pages/todos/ToDosList";
|
||||||
|
import ToDosPage from "@/components/pages/todos/ToDosPage";
|
||||||
import HeaderTemplate from "@/components/shared/HeaderTemplate";
|
import HeaderTemplate from "@/components/shared/HeaderTemplate";
|
||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
import { ToDosContextProvider, useToDosContext } from "@/contexts/ToDosContext";
|
import { ToDosContextProvider, useToDosContext } from "@/contexts/ToDosContext";
|
||||||
import { AntDesign } from "@expo/vector-icons";
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
import { Button, ButtonSize, View } from "react-native-ui-lib";
|
import { Button, ButtonSize, View, Text } from "react-native-ui-lib";
|
||||||
|
|
||||||
export default function Screen() {
|
export default function Screen() {
|
||||||
return (
|
return (
|
||||||
<ToDosContextProvider>
|
<ToDosContextProvider>
|
||||||
<ScrollView>
|
<ToDosPage />
|
||||||
<View backgroundColor="#f9f8f7">
|
|
||||||
<HeaderTemplate message="Here are your To Do's" isWelcome={true} />
|
|
||||||
<ProgressCard />
|
|
||||||
<ToDosList />
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
<AddChore />
|
|
||||||
</ToDosContextProvider>
|
</ToDosContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ export const AddEventDialog = () => {
|
|||||||
}}
|
}}
|
||||||
onPress={handleOpenManualInputModal}
|
onPress={handleOpenManualInputModal}
|
||||||
>
|
>
|
||||||
<Text style={{color: "white"}}>Manually Input</Text>
|
<Text style={{color: "white"}}>Create New</Text>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -62,7 +62,7 @@ export const AddEventDialog = () => {
|
|||||||
}}
|
}}
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
<Text style={{color: "white"}}>Scan an Image</Text>
|
<Text style={{color: "white"}}>Event</Text>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -73,7 +73,18 @@ export const AddEventDialog = () => {
|
|||||||
}}
|
}}
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
<Text style={{color: "white"}}>Paste in text</Text>
|
<Text style={{color: "white"}}>To Do</Text>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
marginBottom: 10,
|
||||||
|
backgroundColor: "#007bff",
|
||||||
|
opacity: 0.5
|
||||||
|
}}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<Text style={{color: "white"}}>Upload Image</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -1,260 +1,320 @@
|
|||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Colors,
|
Colors,
|
||||||
DateTimePicker,
|
DateTimePicker,
|
||||||
LoaderScreen,
|
LoaderScreen,
|
||||||
Modal,
|
Modal,
|
||||||
Picker,
|
Picker,
|
||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextField,
|
TextField,
|
||||||
View
|
View,
|
||||||
} from "react-native-ui-lib";
|
} from "react-native-ui-lib";
|
||||||
import {ScrollView, TouchableOpacity} from "react-native-gesture-handler";
|
import { ScrollView, TouchableOpacity } from "react-native-gesture-handler";
|
||||||
import {useSafeAreaInsets} from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import {useState} from "react";
|
import { useState } from "react";
|
||||||
import {MaterialIcons} from "@expo/vector-icons";
|
import { MaterialIcons } from "@expo/vector-icons";
|
||||||
import {PickerMultiValue} from "react-native-ui-lib/src/components/picker/types";
|
import { PickerMultiValue } from "react-native-ui-lib/src/components/picker/types";
|
||||||
import {useAuthContext} from "@/contexts/AuthContext";
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
import {useCreateEvent} from "@/hooks/firebase/useCreateEvent";
|
import { useCreateEvent } from "@/hooks/firebase/useCreateEvent";
|
||||||
import {EventData} from "@/hooks/firebase/types/eventData";
|
import { EventData } from "@/hooks/firebase/types/eventData";
|
||||||
|
|
||||||
const daysOfWeek = [
|
const daysOfWeek = [
|
||||||
{label: "Monday", value: "monday"},
|
{ label: "Monday", value: "monday" },
|
||||||
{label: "Tuesday", value: "tuesday"},
|
{ label: "Tuesday", value: "tuesday" },
|
||||||
{label: "Wednesday", value: "wednesday"},
|
{ label: "Wednesday", value: "wednesday" },
|
||||||
{label: "Thursday", value: "thursday"},
|
{ label: "Thursday", value: "thursday" },
|
||||||
{label: "Friday", value: "friday"},
|
{ label: "Friday", value: "friday" },
|
||||||
{label: "Saturday", value: "saturday"},
|
{ label: "Saturday", value: "saturday" },
|
||||||
{label: "Sunday", value: "sunday"},
|
{ label: "Sunday", value: "sunday" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ManuallyAddEventModal = ({show, close, initialDate}: { show: boolean, close: () => void, initialDate?: Date }) => {
|
export const ManuallyAddEventModal = ({
|
||||||
const {user} = useAuthContext()
|
show,
|
||||||
const insets = useSafeAreaInsets();
|
close,
|
||||||
|
}: {
|
||||||
|
show: boolean;
|
||||||
|
close: () => void;
|
||||||
|
}) => {
|
||||||
|
const { user } = useAuthContext();
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
|
|
||||||
const [isAllDay, setIsAllDay] = useState(false);
|
const [isAllDay, setIsAllDay] = useState(false);
|
||||||
const [startTime, setStartTime] = useState(initialDate ?? new Date());
|
const [startTime, setStartTime] = useState(initialDate ?? new Date());
|
||||||
const [endTime, setEndTime] = useState(new Date())
|
const [endTime, setEndTime] = useState(new Date());
|
||||||
|
|
||||||
const [startDate, setStartDate] = useState(new Date());
|
const [startDate, setStartDate] = useState(new Date());
|
||||||
const [endDate, setEndDate] = useState(new Date())
|
const [endDate, setEndDate] = useState(new Date());
|
||||||
|
|
||||||
const [repeatInterval, setRepeatInterval] = useState<PickerMultiValue>([]);
|
const [repeatInterval, setRepeatInterval] = useState<PickerMultiValue>([]);
|
||||||
|
|
||||||
const {mutateAsync: createEvent, isLoading, isError} = useCreateEvent()
|
const { mutateAsync: createEvent, isLoading, isError } = useCreateEvent();
|
||||||
|
|
||||||
const formatDateTime = (date: Date) => {
|
const formatDateTime = (date: Date) => {
|
||||||
return date.toLocaleDateString('en-US', {
|
return date.toLocaleDateString("en-US", {
|
||||||
weekday: 'long',
|
weekday: "long",
|
||||||
month: 'short',
|
month: "short",
|
||||||
day: 'numeric'
|
day: "numeric",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const combineDateAndTime = (date: Date, time: Date): Date => {
|
const combineDateAndTime = (date: Date, time: Date): Date => {
|
||||||
const combined = new Date(date);
|
const combined = new Date(date);
|
||||||
combined.setHours(time.getHours());
|
combined.setHours(time.getHours());
|
||||||
combined.setMinutes(time.getMinutes());
|
combined.setMinutes(time.getMinutes());
|
||||||
combined.setSeconds(0);
|
combined.setSeconds(0);
|
||||||
combined.setMilliseconds(0);
|
combined.setMilliseconds(0);
|
||||||
return combined;
|
return combined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
let finalStartDate: Date;
|
let finalStartDate: Date;
|
||||||
let finalEndDate: Date;
|
let finalEndDate: Date;
|
||||||
|
|
||||||
if (isAllDay) {
|
if (isAllDay) {
|
||||||
finalStartDate = new Date(startDate);
|
finalStartDate = new Date(startDate);
|
||||||
finalStartDate.setHours(0, 0, 0, 0);
|
finalStartDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
finalEndDate = new Date(startDate);
|
finalEndDate = new Date(startDate);
|
||||||
finalEndDate.setHours(23, 59, 59, 999);
|
finalEndDate.setHours(23, 59, 59, 999);
|
||||||
} else {
|
} else {
|
||||||
finalStartDate = combineDateAndTime(startDate, startTime);
|
finalStartDate = combineDateAndTime(startDate, startTime);
|
||||||
finalEndDate = combineDateAndTime(endDate, endTime);
|
finalEndDate = combineDateAndTime(endDate, endTime);
|
||||||
}
|
|
||||||
|
|
||||||
const eventData: Partial<EventData> = {
|
|
||||||
title,
|
|
||||||
startDate: finalStartDate,
|
|
||||||
endDate: finalEndDate,
|
|
||||||
repeatDays: repeatInterval.map(x => x.toString()),
|
|
||||||
allDay: isAllDay
|
|
||||||
};
|
|
||||||
|
|
||||||
await createEvent(eventData)
|
|
||||||
|
|
||||||
close();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRepeatLabel = () => {
|
|
||||||
const selectedDays = repeatInterval
|
|
||||||
const allDays = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
|
|
||||||
const workDays = ["monday", "tuesday", "wednesday", "thursday", "friday"];
|
|
||||||
|
|
||||||
const isEveryWorkDay = workDays.every(day => selectedDays.includes(day));
|
|
||||||
|
|
||||||
const isEveryDay = allDays.every(day => selectedDays.includes(day));
|
|
||||||
|
|
||||||
if (isEveryDay) {
|
|
||||||
return "Every day";
|
|
||||||
} else if (isEveryWorkDay && !selectedDays.includes("saturday") && !selectedDays.includes("sunday")) {
|
|
||||||
return "Every work day";
|
|
||||||
} else {
|
|
||||||
return selectedDays
|
|
||||||
.map(item => daysOfWeek.find(day => day.value === item)?.label)
|
|
||||||
.join(", ");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading && !isError) {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
visible={show}
|
|
||||||
animationType="slide"
|
|
||||||
onRequestClose={close}
|
|
||||||
transparent={false}
|
|
||||||
>
|
|
||||||
<LoaderScreen message={'Saving event...'} color={Colors.grey40}/>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventData: Partial<EventData> = {
|
||||||
|
title,
|
||||||
|
startDate: finalStartDate,
|
||||||
|
endDate: finalEndDate,
|
||||||
|
repeatDays: repeatInterval.map((x) => x.toString()),
|
||||||
|
allDay: isAllDay,
|
||||||
|
};
|
||||||
|
|
||||||
|
await createEvent(eventData);
|
||||||
|
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRepeatLabel = () => {
|
||||||
|
const selectedDays = repeatInterval;
|
||||||
|
const allDays = [
|
||||||
|
"sunday",
|
||||||
|
"monday",
|
||||||
|
"tuesday",
|
||||||
|
"wednesday",
|
||||||
|
"thursday",
|
||||||
|
"friday",
|
||||||
|
"saturday",
|
||||||
|
];
|
||||||
|
const workDays = ["monday", "tuesday", "wednesday", "thursday", "friday"];
|
||||||
|
|
||||||
|
const isEveryWorkDay = workDays.every((day) => selectedDays.includes(day));
|
||||||
|
|
||||||
|
const isEveryDay = allDays.every((day) => selectedDays.includes(day));
|
||||||
|
|
||||||
|
if (isEveryDay) {
|
||||||
|
return "Every day";
|
||||||
|
} else if (
|
||||||
|
isEveryWorkDay &&
|
||||||
|
!selectedDays.includes("saturday") &&
|
||||||
|
!selectedDays.includes("sunday")
|
||||||
|
) {
|
||||||
|
return "Every work day";
|
||||||
|
} else {
|
||||||
|
return selectedDays
|
||||||
|
.map((item) => daysOfWeek.find((day) => day.value === item)?.label)
|
||||||
|
.join(", ");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading && !isError) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
visible={show}
|
visible={show}
|
||||||
animationType="slide"
|
animationType="slide"
|
||||||
onRequestClose={close}
|
onRequestClose={close}
|
||||||
transparent={false}
|
transparent={false}
|
||||||
>
|
>
|
||||||
<View style={{
|
<LoaderScreen message={"Saving event..."} color={Colors.grey40} />
|
||||||
flex: 1,
|
</Modal>
|
||||||
backgroundColor: "#fff",
|
|
||||||
paddingTop: insets.top, // Safe area inset for top
|
|
||||||
paddingBottom: insets.bottom, // Safe area inset for bottom
|
|
||||||
paddingLeft: insets.left, // Safe area inset for left
|
|
||||||
paddingRight: insets.right, // Safe area inset for right
|
|
||||||
}}>
|
|
||||||
<View style={{flexDirection: "row", justifyContent: "space-between", padding: 16}}>
|
|
||||||
<TouchableOpacity onPress={close}>
|
|
||||||
<Text style={{color: "#007bff"}}>Cancel</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<Text style={{fontWeight: "bold", fontSize: 16}}>Add event</Text>
|
|
||||||
<TouchableOpacity onPress={handleSave}>
|
|
||||||
<Text style={{color: "#007bff"}}>Save</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<ScrollView contentContainerStyle={{paddingHorizontal: 16, paddingTop: 10}}>
|
|
||||||
<View style={{marginVertical: 10}}>
|
|
||||||
<TextField
|
|
||||||
placeholder={'Title'}
|
|
||||||
floatingPlaceholder
|
|
||||||
value={title}
|
|
||||||
onChangeText={setTitle}
|
|
||||||
showCharCounter
|
|
||||||
maxLength={200}
|
|
||||||
fieldStyle={{
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: 'black',
|
|
||||||
borderStyle: 'solid',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: 20
|
|
||||||
}}>
|
|
||||||
<View style={{flexDirection: "row", alignItems: "center"}}>
|
|
||||||
<MaterialIcons name="schedule" size={24} color="gray"/>
|
|
||||||
<Text style={{marginLeft: 10}}>All-day</Text>
|
|
||||||
</View>
|
|
||||||
<Switch
|
|
||||||
value={isAllDay}
|
|
||||||
onValueChange={(value) => setIsAllDay(value)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: 20
|
|
||||||
}}>
|
|
||||||
<DateTimePicker
|
|
||||||
mode="date"
|
|
||||||
dateFormatter={formatDateTime}
|
|
||||||
value={startDate}
|
|
||||||
onChange={setStartDate}
|
|
||||||
/>
|
|
||||||
{!isAllDay && (
|
|
||||||
<DateTimePicker
|
|
||||||
mode="time"
|
|
||||||
value={startTime}
|
|
||||||
onChange={setStartTime}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{!isAllDay && (
|
|
||||||
<View style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: 20
|
|
||||||
}}>
|
|
||||||
<DateTimePicker
|
|
||||||
mode="date"
|
|
||||||
dateFormatter={formatDateTime}
|
|
||||||
value={endDate}
|
|
||||||
onChange={setEndDate}
|
|
||||||
/>
|
|
||||||
<DateTimePicker
|
|
||||||
mode="time"
|
|
||||||
value={endTime}
|
|
||||||
onChange={setEndTime}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<View style={{flexDirection: "row", alignItems: "center", marginBottom: 20}}>
|
|
||||||
<MaterialIcons name="repeat" size={24} color="gray"/>
|
|
||||||
<Picker
|
|
||||||
value={repeatInterval}
|
|
||||||
//@ts-ignore
|
|
||||||
onChange={(items: PickerMultiValue) => setRepeatInterval(items)}
|
|
||||||
placeholder="Doest not repeat"
|
|
||||||
style={{marginLeft: 10, flex: 1}}
|
|
||||||
mode={Picker.modes.MULTI}
|
|
||||||
getLabel={getRepeatLabel}
|
|
||||||
|
|
||||||
>
|
|
||||||
{daysOfWeek.map((option) => (
|
|
||||||
<Picker.Item key={option.value} label={option.label} value={option.value}/>
|
|
||||||
))}
|
|
||||||
</Picker>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={{flexDirection: "row", alignItems: "center", marginBottom: 20}}>
|
|
||||||
<MaterialIcons name="person-add" size={24} color="gray"/>
|
|
||||||
<TouchableOpacity style={{marginLeft: 10, flexDirection: "row", alignItems: "center", flex: 1}}>
|
|
||||||
<Avatar size={40} backgroundColor={Colors.yellow10}/>
|
|
||||||
<View style={{marginLeft: 10}}>
|
|
||||||
<Text>Other</Text>
|
|
||||||
<Text style={{color: "gray"}}>{user?.email}</Text>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
</Modal>
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={show}
|
||||||
|
animationType="slide"
|
||||||
|
onRequestClose={close}
|
||||||
|
transparent={false}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
paddingTop: insets.top, // Safe area inset for top
|
||||||
|
paddingBottom: insets.bottom, // Safe area inset for bottom
|
||||||
|
paddingLeft: insets.left, // Safe area inset for left
|
||||||
|
paddingRight: insets.right, // Safe area inset for right
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
padding: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TouchableOpacity onPress={close}>
|
||||||
|
<Text style={{ color: "#007bff" }}>Cancel</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={{ fontWeight: "bold", fontSize: 16 }}>Add event</Text>
|
||||||
|
<TouchableOpacity onPress={handleSave}>
|
||||||
|
<Text style={{ color: "#007bff" }}>Save</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
contentContainerStyle={{ paddingHorizontal: 16, paddingTop: 10 }}
|
||||||
|
>
|
||||||
|
<View style={{ marginVertical: 10 }}>
|
||||||
|
<TextField
|
||||||
|
placeholder={"Title"}
|
||||||
|
floatingPlaceholder
|
||||||
|
value={title}
|
||||||
|
onChangeText={setTitle}
|
||||||
|
showCharCounter
|
||||||
|
maxLength={200}
|
||||||
|
fieldStyle={{
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "black",
|
||||||
|
borderStyle: "solid",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||||
|
<MaterialIcons name="schedule" size={24} color="gray" />
|
||||||
|
<Text style={{ marginLeft: 10 }}>All-day</Text>
|
||||||
|
</View>
|
||||||
|
<Switch
|
||||||
|
value={isAllDay}
|
||||||
|
onValueChange={(value) => setIsAllDay(value)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DateTimePicker
|
||||||
|
mode="date"
|
||||||
|
dateFormatter={formatDateTime}
|
||||||
|
value={startDate}
|
||||||
|
onChange={setStartDate}
|
||||||
|
display="default"
|
||||||
|
/>
|
||||||
|
{!isAllDay && (
|
||||||
|
<DateTimePicker
|
||||||
|
mode="time"
|
||||||
|
value={startTime}
|
||||||
|
onChange={setStartTime}
|
||||||
|
display="spinner"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{!isAllDay && (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DateTimePicker
|
||||||
|
mode="date"
|
||||||
|
dateFormatter={formatDateTime}
|
||||||
|
value={endDate}
|
||||||
|
onChange={setEndDate}
|
||||||
|
display="default"
|
||||||
|
/>
|
||||||
|
<DateTimePicker
|
||||||
|
mode="time"
|
||||||
|
value={endTime}
|
||||||
|
onChange={setEndTime}
|
||||||
|
minuteInterval={1}
|
||||||
|
display="spinner"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="repeat" size={24} color="gray" />
|
||||||
|
<Picker
|
||||||
|
value={repeatInterval}
|
||||||
|
//@ts-ignore
|
||||||
|
onChange={(items: PickerMultiValue) => setRepeatInterval(items)}
|
||||||
|
placeholder="Doest not repeat"
|
||||||
|
style={{ marginLeft: 10, flex: 1 }}
|
||||||
|
mode={Picker.modes.MULTI}
|
||||||
|
getLabel={getRepeatLabel}
|
||||||
|
>
|
||||||
|
{daysOfWeek.map((option) => (
|
||||||
|
<Picker.Item
|
||||||
|
key={option.value}
|
||||||
|
label={option.label}
|
||||||
|
value={option.value}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="person-add" size={24} color="gray" />
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{
|
||||||
|
marginLeft: 10,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
flex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar size={40} backgroundColor={Colors.yellow10} />
|
||||||
|
<View style={{ marginLeft: 10 }}>
|
||||||
|
<Text>Other</Text>
|
||||||
|
<Text style={{ color: "gray" }}>{user?.email}</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,28 +1,60 @@
|
|||||||
import { View, Text } from 'react-native'
|
import { View, Text } from "react-native";
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from "react";
|
||||||
import { TextField } from 'react-native-ui-lib'
|
import { TextField } from "react-native-ui-lib";
|
||||||
import CategoryDropdown from './CategoryDropdown'
|
import {
|
||||||
import { GroceryCategory } from '@/contexts/GroceryContext'
|
GroceryCategory,
|
||||||
|
IGrocery,
|
||||||
|
useGroceryContext,
|
||||||
|
} from "@/contexts/GroceryContext";
|
||||||
|
import { TouchableWithoutFeedback } from "react-native-gesture-handler";
|
||||||
|
|
||||||
const EditGroceryItem = (props: {title: string, setTitle: (value: string) => void, setCategory: (category: GroceryCategory) => void}) => {
|
interface IEditGrocery {
|
||||||
return (
|
id?: number;
|
||||||
<View
|
title: string;
|
||||||
style={{
|
setTitle: (value: string) => void;
|
||||||
backgroundColor: "white",
|
setCategory?: (category: GroceryCategory) => void;
|
||||||
width: "100%",
|
category: GroceryCategory;
|
||||||
borderRadius: 25,
|
setSubmit?: (value: boolean) => void;
|
||||||
padding: 15,
|
updateCategory?: (id: number, changes: Partial<IGrocery>) => void;
|
||||||
}}
|
closeEdit?: (value: boolean) => void;
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
placeholder="Grocery"
|
|
||||||
value={props.title}
|
|
||||||
onChangeText={(value) => props.setTitle(value)}
|
|
||||||
maxLength={25}
|
|
||||||
/>
|
|
||||||
<CategoryDropdown setSelectedCategory={props.setCategory} />
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditGroceryItem
|
const EditGroceryItem = ({ editGrocery }: { editGrocery: IEditGrocery }) => {
|
||||||
|
const { fuzzyMatchGroceryCategory } = useGroceryContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editGrocery.setCategory)
|
||||||
|
editGrocery.setCategory(fuzzyMatchGroceryCategory(editGrocery.title));
|
||||||
|
}, [editGrocery.title]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
backgroundColor: "white",
|
||||||
|
width: "100%",
|
||||||
|
borderRadius: 25,
|
||||||
|
padding: 15,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
placeholder="Grocery"
|
||||||
|
value={editGrocery.title}
|
||||||
|
onChangeText={(value) => {
|
||||||
|
editGrocery.setTitle(value);
|
||||||
|
}}
|
||||||
|
onSubmitEditing={() => {
|
||||||
|
if (editGrocery.setSubmit) editGrocery.setSubmit(true);
|
||||||
|
if (editGrocery.closeEdit) editGrocery.closeEdit(false);
|
||||||
|
if (editGrocery.updateCategory && editGrocery.id)
|
||||||
|
editGrocery.updateCategory(editGrocery.id, {
|
||||||
|
category: fuzzyMatchGroceryCategory(editGrocery.title), title: editGrocery.title
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
maxLength={25}
|
||||||
|
/>
|
||||||
|
<Text>{editGrocery.category}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditGroceryItem;
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
import EditGroceryFrequency from "./EditGroceryFrequency";
|
import EditGroceryFrequency from "./EditGroceryFrequency";
|
||||||
import { TextInput } from "react-native";
|
import { TextInput } from "react-native";
|
||||||
import EditGroceryItem from "./EditGroceryItem";
|
import EditGroceryItem from "./EditGroceryItem";
|
||||||
|
import { TouchableWithoutFeedback } from "react-native-gesture-handler";
|
||||||
|
|
||||||
const GroceryItem = ({
|
const GroceryItem = ({
|
||||||
item,
|
item,
|
||||||
@ -31,6 +32,7 @@ const GroceryItem = ({
|
|||||||
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>("");
|
||||||
|
const [category, setCategory] = useState<GroceryCategory>(GroceryCategory.None);
|
||||||
|
|
||||||
const handleTitleChange = (newTitle: string) => {
|
const handleTitleChange = (newTitle: string) => {
|
||||||
updateGroceryItem(item.id, { title: newTitle });
|
updateGroceryItem(item.id, { title: newTitle });
|
||||||
@ -46,6 +48,7 @@ const GroceryItem = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
|
key={item.id}
|
||||||
style={{ borderRadius: 50, marginVertical: 5, height: 55 }}
|
style={{ borderRadius: 50, marginVertical: 5, height: 55 }}
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
centerV
|
centerV
|
||||||
@ -63,49 +66,24 @@ const GroceryItem = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ListItem.Part left containerStyle={{ flex: 1, paddingStart: 20 }}>
|
<ListItem.Part left containerStyle={{ flex: 1, paddingStart: 20 }}>
|
||||||
{/* <View
|
|
||||||
height={50}
|
|
||||||
width={50}
|
|
||||||
style={{ borderRadius: 15 }}
|
|
||||||
backgroundColor="#e6f1ed"
|
|
||||||
marginR-10
|
|
||||||
children={
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={iconMapping[item.category]}
|
|
||||||
size={50}
|
|
||||||
color="orange"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>*/}
|
|
||||||
{!isEditingTitle ? (
|
{!isEditingTitle ? (
|
||||||
<View>
|
<View>
|
||||||
<TouchableOpacity onPress={() => setIsEditingTitle(true)}>
|
<TouchableOpacity onPress={() => setIsEditingTitle(true)}>
|
||||||
<Text text70BL>{item.title}</Text>
|
<Text text70BL>{item.title}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{ /*
|
|
||||||
<TextInput
|
|
||||||
value={item.title}
|
|
||||||
onChangeText={handleTitleChange}
|
|
||||||
onBlur={() => {
|
|
||||||
setIsEditingTitle(false);
|
|
||||||
console.log(groceries);
|
|
||||||
}}
|
|
||||||
autoFocus
|
|
||||||
style={{
|
|
||||||
fontSize: 16,
|
|
||||||
padding: 0,
|
|
||||||
margin: 0,
|
|
||||||
fontWeight: "bold",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
*/}
|
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<EditGroceryItem
|
<EditGroceryItem
|
||||||
title={item.title}
|
editGrocery={{
|
||||||
setTitle={handleTitleChange}
|
id: item.id,
|
||||||
setCategory={handleCategoryChange}
|
title: newTitle,
|
||||||
/>
|
setTitle: setNewTitle,
|
||||||
|
category:category,
|
||||||
|
updateCategory: updateGroceryItem,
|
||||||
|
closeEdit: setIsEditingTitle,
|
||||||
|
setCategory: setCategory,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</ListItem.Part>
|
</ListItem.Part>
|
||||||
<ListItem.Part right containerStyle={{ paddingEnd: 20 }}>
|
<ListItem.Part right containerStyle={{ paddingEnd: 20 }}>
|
||||||
|
@ -12,6 +12,7 @@ import HeaderTemplate from "@/components/shared/HeaderTemplate";
|
|||||||
import CategoryDropdown from "./CategoryDropdown";
|
import CategoryDropdown from "./CategoryDropdown";
|
||||||
import { MaterialIcons } from "@expo/vector-icons";
|
import { MaterialIcons } from "@expo/vector-icons";
|
||||||
import EditGroceryItem from "./EditGroceryItem";
|
import EditGroceryItem from "./EditGroceryItem";
|
||||||
|
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
|
||||||
const GroceryList = () => {
|
const GroceryList = () => {
|
||||||
const {
|
const {
|
||||||
@ -21,6 +22,7 @@ const GroceryList = () => {
|
|||||||
setIsAddingGrocery,
|
setIsAddingGrocery,
|
||||||
addGrocery,
|
addGrocery,
|
||||||
} = useGroceryContext();
|
} = useGroceryContext();
|
||||||
|
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)
|
||||||
);
|
);
|
||||||
@ -31,6 +33,7 @@ const GroceryList = () => {
|
|||||||
GroceryCategory.Bakery
|
GroceryCategory.Bakery
|
||||||
);
|
);
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
|
const [submit, setSubmitted] = useState<boolean>(false);
|
||||||
|
|
||||||
// Group approved groceries by category
|
// Group approved groceries by category
|
||||||
const approvedGroceriesByCategory = approvedGroceries.reduce(
|
const approvedGroceriesByCategory = approvedGroceries.reduce(
|
||||||
@ -46,19 +49,27 @@ const GroceryList = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (title?.length > 2 && title?.length <= 25) {
|
if (submit) {
|
||||||
addGrocery({
|
if (title?.length > 2 && title?.length <= 25) {
|
||||||
id: 0,
|
addGrocery({
|
||||||
title: title,
|
id: 0,
|
||||||
category: category,
|
title: title,
|
||||||
approved: false,
|
category: category,
|
||||||
recurring: false,
|
approved: profileData?.userType === ProfileType.PARENT ? true : false,
|
||||||
frequency: GroceryFrequency.Never,
|
recurring: false,
|
||||||
bought: false,
|
frequency: GroceryFrequency.Never,
|
||||||
});
|
bought: false,
|
||||||
|
});
|
||||||
|
|
||||||
setIsAddingGrocery(false);
|
setIsAddingGrocery(false);
|
||||||
|
setSubmitted(false);
|
||||||
|
setTitle("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}, [submit]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
/**/
|
||||||
}, [category]);
|
}, [category]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -67,7 +78,7 @@ const GroceryList = () => {
|
|||||||
}, [groceries]);
|
}, [groceries]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View marginH-20>
|
<View marginH-20 marginB-20>
|
||||||
<HeaderTemplate
|
<HeaderTemplate
|
||||||
message={"Welcome to your grocery list"}
|
message={"Welcome to your grocery list"}
|
||||||
isWelcome={false}
|
isWelcome={false}
|
||||||
@ -101,8 +112,8 @@ const GroceryList = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
backgroundColor='transparent'
|
backgroundColor="transparent"
|
||||||
paddingH-10
|
paddingH-10
|
||||||
iconSource={() => (
|
iconSource={() => (
|
||||||
<MaterialIcons name="person-add-alt" size={24} color="gray" />
|
<MaterialIcons name="person-add-alt" size={24} color="gray" />
|
||||||
)}
|
)}
|
||||||
@ -157,7 +168,15 @@ const GroceryList = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{isAddingGrocery && (
|
{isAddingGrocery && (
|
||||||
<EditGroceryItem title={title} setTitle={setTitle} setCategory={setCategory} />
|
<EditGroceryItem
|
||||||
|
editGrocery={{
|
||||||
|
title: title,
|
||||||
|
setCategory: setCategory,
|
||||||
|
category: category,
|
||||||
|
setTitle: setTitle,
|
||||||
|
setSubmit: setSubmitted,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Render Approved Groceries Grouped by Category */}
|
{/* Render Approved Groceries Grouped by Category */}
|
||||||
|
@ -1,145 +1,150 @@
|
|||||||
import React, {useState} from "react";
|
import React, { useState } from "react";
|
||||||
import {Button, ButtonSize, Text, TextField, View,} from "react-native-ui-lib";
|
import {
|
||||||
import {useSignUp} from "@/hooks/firebase/useSignUp";
|
Button,
|
||||||
import {ProfileType} from "@/contexts/AuthContext";
|
ButtonSize,
|
||||||
import {StyleSheet} from "react-native";
|
Checkbox,
|
||||||
import {AntDesign} from "@expo/vector-icons";
|
Text,
|
||||||
|
TextField,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from "react-native-ui-lib";
|
||||||
|
import { useSignUp } from "@/hooks/firebase/useSignUp";
|
||||||
|
import { ProfileType } from "@/contexts/AuthContext";
|
||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
|
|
||||||
const SignUpPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"register" | "login" | "reset-password">> }) => {
|
const SignUpPage = ({setTab}: { setTab: React.Dispatch<React.SetStateAction<"register" | "login" | "reset-password">> }) => {
|
||||||
const [email, setEmail] = useState<string>("");
|
const [email, setEmail] = useState<string>("");
|
||||||
const [firstName, setFirstName] = useState<string>("");
|
const [firstName, setFirstName] = useState<string>("");
|
||||||
const [lastName, setLastName] = useState<string>("");
|
const [lastName, setLastName] = useState<string>("");
|
||||||
const [password, setPassword] = useState<string>("");
|
const [password, setPassword] = useState<string>("");
|
||||||
const [isParent, setIsParent] = useState<boolean>(true);
|
|
||||||
const [isChild, setIsChild] = useState<boolean>(false);
|
|
||||||
const [isCaregiver, setIsCaregiver] = useState<boolean>(false);
|
|
||||||
const [profileType, setProfileType] = useState<ProfileType>(
|
|
||||||
ProfileType.PARENT
|
|
||||||
);
|
|
||||||
const {mutateAsync: signUp} = useSignUp();
|
|
||||||
|
|
||||||
const handleSignUp = async () => {
|
const [isPasswordVisible, setIsPasswordVisible] = useState<boolean>(false);
|
||||||
await signUp({email, password, firstName, lastName});
|
const [allowFaceID, setAllowFaceID] = useState<boolean>(false);
|
||||||
};
|
const [acceptTerms, setAcceptTerms] = useState<boolean>(false);
|
||||||
|
const { mutateAsync: signUp } = useSignUp();
|
||||||
|
|
||||||
return (
|
const handleSignUp = async () => {
|
||||||
<View padding-10>
|
await signUp({ email, password, firstName, lastName });
|
||||||
<Text text30 center>
|
};
|
||||||
Get started with Kali
|
|
||||||
</Text>
|
|
||||||
<Text center>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
|
|
||||||
placeholder="Email"
|
|
||||||
value={email}
|
|
||||||
onChangeText={setEmail}
|
|
||||||
style={styles.textfield}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
placeholder="Password"
|
|
||||||
value={password}
|
|
||||||
onChangeText={setPassword}
|
|
||||||
secureTextEntry
|
|
||||||
style={styles.textfield}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label="Register"
|
|
||||||
onPress={handleSignUp}
|
|
||||||
style={{marginBottom: 10, backgroundColor: "#fd1775"}}
|
|
||||||
/>
|
|
||||||
<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
|
|
||||||
label="Parent"
|
|
||||||
value={isParent}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setIsParent(value);
|
|
||||||
setProfileType(ProfileType.PARENT);
|
|
||||||
if (value) {
|
|
||||||
setIsChild(false);
|
|
||||||
setIsCaregiver(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ marginBottom: 10 }}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Child"
|
|
||||||
value={isChild}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setIsChild(value);
|
|
||||||
setProfileType(ProfileType.CHILD);
|
|
||||||
if (value) {
|
|
||||||
setIsParent(false);
|
|
||||||
setIsCaregiver(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ marginBottom: 10 }}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Caregiver"
|
|
||||||
value={isCaregiver}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setIsCaregiver(value);
|
|
||||||
setProfileType(ProfileType.CAREGIVER);
|
|
||||||
if (value) {
|
|
||||||
setIsParent(false);
|
|
||||||
setIsChild(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>*/}
|
|
||||||
<View row centerH marginT-10 marginB-5 gap-5>
|
|
||||||
<Text text70 center>
|
|
||||||
Already have an account?
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
|
return (
|
||||||
<Button
|
<View padding-10 height={"100%"} flexG>
|
||||||
label="Sign In"
|
<Text text30 center>
|
||||||
flexS
|
Get started with Kali
|
||||||
margin-0
|
</Text>
|
||||||
link
|
<Text center>Please enter your details.</Text>
|
||||||
color="#fd1775"
|
<TextField
|
||||||
size={ButtonSize.small}
|
marginT-60
|
||||||
text70
|
placeholder="First name"
|
||||||
onPress={() => setTab("login")}
|
value={firstName}
|
||||||
/>
|
onChangeText={setFirstName}
|
||||||
</View>
|
style={styles.textfield}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
placeholder="Last name"
|
||||||
|
value={lastName}
|
||||||
|
onChangeText={setLastName}
|
||||||
|
style={styles.textfield}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
placeholder="Email"
|
||||||
|
value={email}
|
||||||
|
onChangeText={setEmail}
|
||||||
|
style={styles.textfield}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChangeText={setPassword}
|
||||||
|
secureTextEntry={!isPasswordVisible}
|
||||||
|
style={styles.textfield}
|
||||||
|
trailingAccessory={
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setIsPasswordVisible(!isPasswordVisible)}
|
||||||
|
>
|
||||||
|
<AntDesign
|
||||||
|
name={isPasswordVisible ? "eye" : "eyeo"}
|
||||||
|
size={24}
|
||||||
|
color="gray"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<View gap-10 marginH-10>
|
||||||
|
<View row centerV>
|
||||||
|
<Checkbox
|
||||||
|
value={allowFaceID}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
setAllowFaceID(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text text90R marginL-10>
|
||||||
|
Allow FaceID for login in future
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
<View row centerV>
|
||||||
|
<Checkbox
|
||||||
|
value={acceptTerms}
|
||||||
|
onValueChange={(value) => setAcceptTerms(value)}
|
||||||
|
/>
|
||||||
|
<View row>
|
||||||
|
<Text text90R marginL-10>
|
||||||
|
I accept the
|
||||||
|
</Text>
|
||||||
|
<TouchableOpacity>
|
||||||
|
<Text text90 style={{ textDecorationLine: "underline" }}>
|
||||||
|
{" "}
|
||||||
|
terms and conditions
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text text90R> and </Text>
|
||||||
|
<TouchableOpacity>
|
||||||
|
<Text text90 style={{ textDecorationLine: "underline" }}>
|
||||||
|
{" "}
|
||||||
|
privacy policy
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.bottomView}>
|
||||||
|
<Button
|
||||||
|
label="Register"
|
||||||
|
onPress={handleSignUp}
|
||||||
|
style={{ marginBottom: 10, backgroundColor: "#fd1775" }}
|
||||||
|
/>
|
||||||
|
<View row centerH marginT-10 marginB-5 gap-5>
|
||||||
|
<Text text70 center>
|
||||||
|
Already have an account?
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
label="Sign In"
|
||||||
|
flexS
|
||||||
|
margin-0
|
||||||
|
link
|
||||||
|
color="#fd1775"
|
||||||
|
size={ButtonSize.small}
|
||||||
|
text70
|
||||||
|
onPress={() => setTab("login")}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SignUpPage;
|
export default SignUpPage;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
textfield: {
|
textfield: {
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
marginVertical: 10,
|
marginVertical: 10,
|
||||||
padding: 30,
|
padding: 30,
|
||||||
height: 45,
|
height: 45,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
},
|
},
|
||||||
|
//mora da se izmeni kako treba
|
||||||
|
bottomView: { marginTop: 150 },
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { View, Text, TouchableOpacity } from "react-native-ui-lib";
|
import { View, Text, TouchableOpacity } from "react-native-ui-lib";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { ScrollView, StyleSheet } from "react-native";
|
import {
|
||||||
|
KeyboardAvoidingView,
|
||||||
|
Platform,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
} from "react-native";
|
||||||
import MyProfile from "./user_settings_views/MyProfile";
|
import MyProfile from "./user_settings_views/MyProfile";
|
||||||
import MyGroup from "./user_settings_views/MyGroup";
|
import MyGroup from "./user_settings_views/MyGroup";
|
||||||
import { useAuthContext } from "@/contexts/AuthContext";
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
@ -9,51 +14,57 @@ import { useAuthContext } from "@/contexts/AuthContext";
|
|||||||
const UserSettings = (props: { setSelectedPage: (page: number) => void }) => {
|
const UserSettings = (props: { setSelectedPage: (page: number) => void }) => {
|
||||||
const [selectedView, setSelectedView] = useState<boolean>(true);
|
const [selectedView, setSelectedView] = useState<boolean>(true);
|
||||||
return (
|
return (
|
||||||
<View>
|
<ScrollView
|
||||||
<ScrollView>
|
contentContainerStyle={{ flexGrow: 1 }}
|
||||||
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
|
>
|
||||||
<View row marginT-20 marginL-20 marginB-35 centerV>
|
<View style={{ flex: 1 }}>
|
||||||
<Ionicons name="chevron-back" size={22} color="#979797" />
|
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
|
||||||
<Text text70 color="#979797">
|
<View row marginT-20 marginL-20 marginB-35 centerV>
|
||||||
Return to main settings
|
<Ionicons name="chevron-back" size={22} color="#979797" />
|
||||||
|
<Text text70 color="#979797">
|
||||||
|
Return to main settings
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View marginH-20>
|
||||||
|
<Text text60R marginB-25>
|
||||||
|
User Management
|
||||||
</Text>
|
</Text>
|
||||||
|
<View style={styles.buttonSwitch} spread row>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setSelectedView(true)}
|
||||||
|
centerV
|
||||||
|
centerH
|
||||||
|
style={
|
||||||
|
selectedView == true ? styles.btnSelected : styles.btnNot
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View>
|
||||||
|
<Text text70 color={selectedView ? "white" : "black"}>
|
||||||
|
My Profile
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setSelectedView(false)}
|
||||||
|
centerV
|
||||||
|
centerH
|
||||||
|
style={
|
||||||
|
selectedView == false ? styles.btnSelected : styles.btnNot
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<View>
|
||||||
|
<Text text70 color={!selectedView ? "white" : "black"}>
|
||||||
|
My Group
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
{selectedView && <MyProfile />}
|
||||||
|
{!selectedView && <MyGroup />}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
|
||||||
<View marginH-20>
|
|
||||||
<Text text60R marginB-25>
|
|
||||||
User Management
|
|
||||||
</Text>
|
|
||||||
<View style={styles.buttonSwitch} spread row>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => setSelectedView(true)}
|
|
||||||
centerV
|
|
||||||
centerH
|
|
||||||
style={selectedView == true ? styles.btnSelected : styles.btnNot}
|
|
||||||
>
|
|
||||||
<View>
|
|
||||||
<Text text70 color={selectedView ? "white" : "black"}>
|
|
||||||
My Profile
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => setSelectedView(false)}
|
|
||||||
centerV
|
|
||||||
centerH
|
|
||||||
style={selectedView == false ? styles.btnSelected : styles.btnNot}
|
|
||||||
>
|
|
||||||
<View>
|
|
||||||
<Text text70 color={!selectedView ? "white" : "black"}>
|
|
||||||
My Group
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
{selectedView && <MyProfile />}
|
|
||||||
{!selectedView && <MyGroup />}
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,13 +2,14 @@ import { View, Text, Button } from "react-native-ui-lib";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Fontisto } from "@expo/vector-icons";
|
import { Fontisto } from "@expo/vector-icons";
|
||||||
import { ProgressBar } from "react-native-ui-lib/src/components/progressBar";
|
import { ProgressBar } from "react-native-ui-lib/src/components/progressBar";
|
||||||
|
import { useToDosContext } from "@/contexts/ToDosContext";
|
||||||
|
|
||||||
const ProgressCard = () => {
|
const ProgressCard = ({children}: {children?: React.ReactNode}) => {
|
||||||
|
const { maxPoints } = useToDosContext();
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
marginH-25
|
marginB-5
|
||||||
marginB-30
|
|
||||||
padding-15
|
padding-15
|
||||||
style={{ borderRadius: 22 }}
|
style={{ borderRadius: 22 }}
|
||||||
>
|
>
|
||||||
@ -30,16 +31,10 @@ const ProgressCard = () => {
|
|||||||
/>
|
/>
|
||||||
<View row spread>
|
<View row spread>
|
||||||
<Text color={"#868686"}>0</Text>
|
<Text color={"#868686"}>0</Text>
|
||||||
<Text color={"#868686"}>1000</Text>
|
<Text color={"#868686"}>{maxPoints}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View centerV centerH>
|
<View centerV centerH>
|
||||||
<Button
|
{children}
|
||||||
backgroundColor="transparent"
|
|
||||||
>
|
|
||||||
<Text style={{ textDecorationLine: "underline", color: "#05a8b6" }}>
|
|
||||||
View your full progress report here
|
|
||||||
</Text>
|
|
||||||
</Button>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,6 @@ const ToDoItem = (props: { item: IToDo }) => {
|
|||||||
centerV
|
centerV
|
||||||
paddingV-10
|
paddingV-10
|
||||||
paddingH-10
|
paddingH-10
|
||||||
marginH-25
|
|
||||||
marginV-10
|
marginV-10
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 22,
|
borderRadius: 22,
|
||||||
|
76
components/pages/todos/ToDosPage.tsx
Normal file
76
components/pages/todos/ToDosPage.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { View, Text, Button, ButtonSize } from "react-native-ui-lib";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import HeaderTemplate from "@/components/shared/HeaderTemplate";
|
||||||
|
import AddChore from "./AddChore";
|
||||||
|
import ProgressCard from "./ProgressCard";
|
||||||
|
import ToDosList from "./ToDosList";
|
||||||
|
import { ScrollView } from "react-native";
|
||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||||
|
import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
import FamilyChoresProgress from "./family-chores/FamilyChoresProgress";
|
||||||
|
import UserChoresProgress from "./user-chores/UserChoresProgress";
|
||||||
|
|
||||||
|
const ToDosPage = () => {
|
||||||
|
const [pageIndex, setPageIndex] = useState<number>(0);
|
||||||
|
const { profileData } = useAuthContext();
|
||||||
|
const pageLink = (
|
||||||
|
<TouchableOpacity onPress={() => setPageIndex(1)}>
|
||||||
|
<Text color="#ea156d">View family progress</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<View paddingH-25 backgroundColor="#f9f8f7" height={"100%"}>
|
||||||
|
{pageIndex == 0 && (
|
||||||
|
<View>
|
||||||
|
<ScrollView
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
>
|
||||||
|
<View>
|
||||||
|
<HeaderTemplate
|
||||||
|
message="Here are your To Do's"
|
||||||
|
isWelcome={true}
|
||||||
|
link={profileData?.userType == ProfileType.PARENT && pageLink}
|
||||||
|
/>
|
||||||
|
{profileData?.userType == ProfileType.CHILD && (
|
||||||
|
<View marginB-25>
|
||||||
|
<ProgressCard
|
||||||
|
children={
|
||||||
|
<Button
|
||||||
|
backgroundColor="transparent"
|
||||||
|
onPress={() => setPageIndex(2)}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
textDecorationLine: "underline",
|
||||||
|
color: "#05a8b6",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View your full progress report here
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<ToDosList />
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
{profileData?.userType == ProfileType.PARENT && <AddChore />}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{pageIndex == 1 && <FamilyChoresProgress setPageIndex={setPageIndex} />}
|
||||||
|
{pageIndex == 2 && <UserChoresProgress setPageIndex={setPageIndex} />}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
linkBtn: {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ToDosPage;
|
87
components/pages/todos/family-chores/FamilyChart.tsx
Normal file
87
components/pages/todos/family-chores/FamilyChart.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import { BarChart } from "react-native-gifted-charts";
|
||||||
|
|
||||||
|
const FamilyChart = () => {
|
||||||
|
// Define the data for the bars
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
value: 600, // Total value of the bar
|
||||||
|
stacks: [
|
||||||
|
{ value: 290, color: "#e7d526" }, // First part of the bar
|
||||||
|
{ value: 310, color: "#00a8b6" }, // Second part of the bar
|
||||||
|
],
|
||||||
|
label: "M",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 400,
|
||||||
|
stacks: [
|
||||||
|
{ value: 190, color: "#e7d526" },
|
||||||
|
{ value: 210, color: "#00a8b6" },
|
||||||
|
],
|
||||||
|
label: "Tu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 400,
|
||||||
|
stacks: [
|
||||||
|
{ value: 210, color: "#e7d526" },
|
||||||
|
{ value: 190, color: "#00a8b6" },
|
||||||
|
],
|
||||||
|
label: "W",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 800,
|
||||||
|
stacks: [
|
||||||
|
{ value: 410, color: "#e7d526" },
|
||||||
|
{ value: 390, color: "#00a8b6" },
|
||||||
|
],
|
||||||
|
label: "Th",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 600,
|
||||||
|
stacks: [
|
||||||
|
{ value: 220, color: "#e7d526" },
|
||||||
|
{ value: 380, color: "#00a8b6" },
|
||||||
|
],
|
||||||
|
label: "F",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 200,
|
||||||
|
stacks: [
|
||||||
|
{ value: 160, color: "#e7d526" },
|
||||||
|
{ value: 40, color: "#00a8b6" },
|
||||||
|
],
|
||||||
|
label: "Sa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 200,
|
||||||
|
stacks: [
|
||||||
|
{ value: 160, color: "#e7d526" },
|
||||||
|
{ value: 40, color: "#00a8b6" },
|
||||||
|
],
|
||||||
|
label: "Su",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BarChart
|
||||||
|
stackData={data}
|
||||||
|
width={250}
|
||||||
|
height={150} // Height of the chart
|
||||||
|
barWidth={20} // Width of each bar
|
||||||
|
noOfSections={5} // Number of horizontal sections (for 0 to 1000 in steps of 200)
|
||||||
|
maxValue={1000} // Max value on the chart
|
||||||
|
stepValue={200} // Step size for horizontal lines
|
||||||
|
yAxisThickness={0} // Hide the Y-axis line
|
||||||
|
yAxisLabelTexts={["0", "200", "400", "600", "800", "1000"]} // Custom Y-axis labels
|
||||||
|
hideRules={false} // Show the horizontal lines
|
||||||
|
rulesColor="#dadada" // Color for the horizontal lines
|
||||||
|
stackBorderTopLeftRadius={5} // Round the bars
|
||||||
|
stackBorderTopRightRadius={5} // Round the bars
|
||||||
|
spacing={16}
|
||||||
|
disableScroll
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FamilyChart;
|
@ -0,0 +1,67 @@
|
|||||||
|
import { View, Text } from "react-native-ui-lib";
|
||||||
|
import React from "react";
|
||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
import FamilyChart from "./FamilyChart";
|
||||||
|
import { TouchableOpacity } from "react-native-ui-lib/src/incubator";
|
||||||
|
|
||||||
|
const FamilyChoresProgress = ({
|
||||||
|
setPageIndex,
|
||||||
|
}: {
|
||||||
|
setPageIndex: (value: number) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<View marginT-20 marginH-5>
|
||||||
|
<TouchableOpacity onPress={() => setPageIndex(0)}>
|
||||||
|
<Text>Back to ToDos</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View centerH>
|
||||||
|
<Text text50R>Family Chores Progress Report</Text>
|
||||||
|
</View>
|
||||||
|
<View row spread marginT-25 marginB-20>
|
||||||
|
<Text text70>Points earned this week</Text>
|
||||||
|
<View row>
|
||||||
|
<View style={styles.pfpSmall} backgroundColor="#05a8b6" />
|
||||||
|
<View style={styles.pfpSmall} backgroundColor="#ebd825" />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.card} paddingL-10>
|
||||||
|
<FamilyChart />
|
||||||
|
</View>
|
||||||
|
<Text text70 marginV-20>
|
||||||
|
Chore Tracker
|
||||||
|
</Text>
|
||||||
|
<View style={styles.card} marginB-20 row spread>
|
||||||
|
<View style={styles.pfpBig} backgroundColor="#05a8b6" />
|
||||||
|
<View width={"100%"} centerV centerH>
|
||||||
|
<Text> x/y chores completed</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.card} row spread>
|
||||||
|
<View style={styles.pfpBig} backgroundColor="#ebd825" />
|
||||||
|
<View width={"100%"} centerV centerH>
|
||||||
|
<Text> x/y chores completed</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
pfpSmall: {
|
||||||
|
width: 30,
|
||||||
|
aspectRatio: 1,
|
||||||
|
borderRadius: 50,
|
||||||
|
marginHorizontal: 2,
|
||||||
|
},
|
||||||
|
pfpBig: {
|
||||||
|
width: 50,
|
||||||
|
aspectRatio: 1,
|
||||||
|
borderRadius: 50,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: 20,
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FamilyChoresProgress;
|
66
components/pages/todos/user-chores/UserChart.tsx
Normal file
66
components/pages/todos/user-chores/UserChart.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import { BarChart } from "react-native-gifted-charts";
|
||||||
|
|
||||||
|
const UserChart = () => {
|
||||||
|
const barColor = "#05a8b6"
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
value: 290, // Direct value of the bar
|
||||||
|
frontColor: barColor, // Color of the bar
|
||||||
|
label: "M",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 190,
|
||||||
|
frontColor: barColor,
|
||||||
|
label: "Tu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 210,
|
||||||
|
frontColor: barColor,
|
||||||
|
label: "W",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 410,
|
||||||
|
frontColor: barColor,
|
||||||
|
label: "Th",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 220,
|
||||||
|
frontColor: barColor,
|
||||||
|
label: "F",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 160,
|
||||||
|
frontColor: barColor,
|
||||||
|
label: "Sa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 160,
|
||||||
|
frontColor: barColor,
|
||||||
|
label: "Su",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BarChart
|
||||||
|
data={data}
|
||||||
|
width={255}
|
||||||
|
height={150} // Height of the chart
|
||||||
|
barWidth={20} // Width of each bar
|
||||||
|
noOfSections={5} // Number of horizontal sections (for 0 to 1000 in steps of 200)
|
||||||
|
maxValue={1000} // Max value on the chart
|
||||||
|
stepValue={200} // Step size for horizontal lines
|
||||||
|
yAxisThickness={0} // Hide the Y-axis line
|
||||||
|
yAxisLabelTexts={["0", "200", "400", "600", "800", "1000"]} // Custom Y-axis labels
|
||||||
|
hideRules={false} // Show the horizontal lines
|
||||||
|
rulesColor="#dadada" // Color for the horizontal lines
|
||||||
|
barBorderTopLeftRadius={5} // Round the bars
|
||||||
|
barBorderTopRightRadius={5} // Round the bars
|
||||||
|
spacing={16}
|
||||||
|
disableScroll
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserChart;
|
152
components/pages/todos/user-chores/UserChoresProgress.tsx
Normal file
152
components/pages/todos/user-chores/UserChoresProgress.tsx
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
ProgressBar,
|
||||||
|
Button,
|
||||||
|
ButtonSize,
|
||||||
|
Modal,
|
||||||
|
Dialog,
|
||||||
|
} from "react-native-ui-lib";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
import { TouchableOpacity } from "react-native-ui-lib/src/incubator";
|
||||||
|
import UserChart from "./UserChart";
|
||||||
|
import ProgressCard from "../ProgressCard";
|
||||||
|
import { AntDesign, Feather, Ionicons } from "@expo/vector-icons";
|
||||||
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
|
import { PanViewDirectionsEnum } from "react-native-ui-lib/src/incubator/panView";
|
||||||
|
|
||||||
|
const UserChoresProgress = ({
|
||||||
|
setPageIndex,
|
||||||
|
}: {
|
||||||
|
setPageIndex: (value: number) => void;
|
||||||
|
}) => {
|
||||||
|
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||||
|
return (
|
||||||
|
<View marginT-20 paddingB-20>
|
||||||
|
<ScrollView
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
>
|
||||||
|
<TouchableOpacity onPress={() => setPageIndex(0)}>
|
||||||
|
<Text>Back to ToDos</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<View centerH>
|
||||||
|
<Text text50R>Your To Dos Progress Report</Text>
|
||||||
|
</View>
|
||||||
|
<View row spread marginT-25 marginB-5>
|
||||||
|
<Text text70>Daily Goal</Text>
|
||||||
|
</View>
|
||||||
|
<ProgressCard />
|
||||||
|
<View row spread marginT-10 marginB-5>
|
||||||
|
<Text text70>Points Earned This Week</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.card} paddingL-10>
|
||||||
|
<UserChart />
|
||||||
|
</View>
|
||||||
|
<View row spread marginT-20 marginB-8 centerV>
|
||||||
|
<Text text70>Total Reward Points</Text>
|
||||||
|
<Button
|
||||||
|
size={ButtonSize.small}
|
||||||
|
label="Spend my points"
|
||||||
|
color="#50be0c"
|
||||||
|
backgroundColor="#ebf2e4"
|
||||||
|
onPress={() => setModalVisible(true)}
|
||||||
|
iconSource={() => (
|
||||||
|
<AntDesign
|
||||||
|
name="gift"
|
||||||
|
size={20}
|
||||||
|
style={{ marginRight: 5 }}
|
||||||
|
color="#50be0c"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.card}>
|
||||||
|
<View row centerV>
|
||||||
|
<Ionicons name="flower-outline" size={30} color="#8005eb" />
|
||||||
|
<Text text70 marginL-5>
|
||||||
|
You have 1200 points saved!
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<ProgressBar
|
||||||
|
progress={80}
|
||||||
|
progressColor="#ff9900"
|
||||||
|
style={{
|
||||||
|
height: 21,
|
||||||
|
backgroundColor: "#faeedb",
|
||||||
|
marginTop: 15,
|
||||||
|
marginBottom: 5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View row spread>
|
||||||
|
<Text>0</Text>
|
||||||
|
<Text>5000</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
<Dialog
|
||||||
|
visible={modalVisible}
|
||||||
|
onDismiss={() => setModalVisible(false)}
|
||||||
|
children={
|
||||||
|
<View style={styles.card} paddingH-35 paddingT-35>
|
||||||
|
<Text text60 center marginB-35>
|
||||||
|
How would you like to spend your points?
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
label="Skip a Chore Cor a Day - 150 pts"
|
||||||
|
text70
|
||||||
|
marginB-15
|
||||||
|
backgroundColor="#05a8b6"
|
||||||
|
size={ButtonSize.large}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label="Extra Screen Time - 100 pts"
|
||||||
|
text70
|
||||||
|
marginB-15
|
||||||
|
backgroundColor="#ea156c"
|
||||||
|
size={ButtonSize.large}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label="Movie Night - 50 pts"
|
||||||
|
text70
|
||||||
|
marginB-15
|
||||||
|
backgroundColor="#7305d4"
|
||||||
|
size={ButtonSize.large}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
label="Ice Cream Treat - 25 pts"
|
||||||
|
text70
|
||||||
|
marginB-15
|
||||||
|
backgroundColor="#e28800"
|
||||||
|
size={ButtonSize.large}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity onPress={() => setModalVisible(false)}>
|
||||||
|
<Text text70 center color="#999999">Go back to my to dos</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
pfpSmall: {
|
||||||
|
width: 30,
|
||||||
|
aspectRatio: 1,
|
||||||
|
borderRadius: 50,
|
||||||
|
marginHorizontal: 2,
|
||||||
|
},
|
||||||
|
pfpBig: {
|
||||||
|
width: 50,
|
||||||
|
aspectRatio: 1,
|
||||||
|
borderRadius: 50,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: 20,
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UserChoresProgress;
|
@ -2,7 +2,12 @@ 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; isWelcome: boolean; children?: React.ReactNode }) => {
|
const HeaderTemplate = (props: {
|
||||||
|
message: string;
|
||||||
|
isWelcome: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
link?: React.ReactNode;
|
||||||
|
}) => {
|
||||||
const { user, profileData } = useAuthContext();
|
const { user, profileData } = useAuthContext();
|
||||||
return (
|
return (
|
||||||
<View row centerV padding-25>
|
<View row centerV padding-25>
|
||||||
@ -14,9 +19,12 @@ const HeaderTemplate = (props: { message: string; isWelcome: boolean; children?:
|
|||||||
marginR-20
|
marginR-20
|
||||||
/>
|
/>
|
||||||
<View>
|
<View>
|
||||||
{props.isWelcome && <Text text70L>Welcome, {user?.email}!</Text>}
|
{props.isWelcome && (
|
||||||
|
<Text text70L>Welcome, {profileData?.firstName}!</Text>
|
||||||
|
)}
|
||||||
<Text text70BL>{props.message}</Text>
|
<Text text70BL>{props.message}</Text>
|
||||||
{props.children && <View>{props.children}</View>}
|
{props.children && <View>{props.children}</View>}
|
||||||
|
{props.link && <View>{props.link}</View>}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
import { createContext, useContext, useState } from "react";
|
import { createContext, useContext, useState } from "react";
|
||||||
|
import fuzzysort from "fuzzysort";
|
||||||
|
|
||||||
export enum GroceryFrequency {
|
export enum GroceryFrequency {
|
||||||
Never = "Never",
|
Never = "Never",
|
||||||
@ -32,9 +33,10 @@ export enum GroceryCategory {
|
|||||||
Household = "Household",
|
Household = "Household",
|
||||||
PersonalCare = "Personal Care",
|
PersonalCare = "Personal Care",
|
||||||
Frozen = "Frozen",
|
Frozen = "Frozen",
|
||||||
|
None = "No Category",
|
||||||
}
|
}
|
||||||
|
|
||||||
type MaterialIconNames = keyof typeof MaterialCommunityIcons.glyphMap;
|
/*type MaterialIconNames = keyof typeof MaterialCommunityIcons.glyphMap;
|
||||||
const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
|
const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
|
||||||
//за сад се иконице за категорију бирају одавде
|
//за сад се иконице за категорију бирају одавде
|
||||||
[GroceryCategory.Fruit]: "food-apple",
|
[GroceryCategory.Fruit]: "food-apple",
|
||||||
@ -48,15 +50,16 @@ const iconMapping: { [key in GroceryCategory]: MaterialIconNames } = {
|
|||||||
[GroceryCategory.Household]: "home",
|
[GroceryCategory.Household]: "home",
|
||||||
[GroceryCategory.PersonalCare]: "face-man-profile",
|
[GroceryCategory.PersonalCare]: "face-man-profile",
|
||||||
[GroceryCategory.Frozen]: "snowflake",
|
[GroceryCategory.Frozen]: "snowflake",
|
||||||
};
|
};*/
|
||||||
|
|
||||||
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: (id: number, changes: Partial<IGrocery>) => void;
|
||||||
isAddingGrocery: boolean;
|
isAddingGrocery: boolean;
|
||||||
setIsAddingGrocery: (value: boolean) => void;
|
setIsAddingGrocery: (value: boolean) => void;
|
||||||
addGrocery: (grocery: IGrocery) => void;
|
addGrocery: (grocery: IGrocery) => void;
|
||||||
|
fuzzyMatchGroceryCategory: (groceryTitle: string) => GroceryCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
|
const GroceryContext = createContext<IGroceryContext | undefined>(undefined);
|
||||||
@ -109,11 +112,114 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
...prevGroceries,
|
...prevGroceries,
|
||||||
{
|
{
|
||||||
...grocery,
|
...grocery,
|
||||||
id: prevGroceries.length ? prevGroceries[prevGroceries.length - 1].id + 1 : 0,
|
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 => {
|
||||||
|
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 = (id: number, changes: Partial<IGrocery>) => {
|
const updateGroceryItem = (id: number, changes: Partial<IGrocery>) => {
|
||||||
setGroceries((prevGroceries) =>
|
setGroceries((prevGroceries) =>
|
||||||
@ -125,7 +231,15 @@ export const GroceryProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<GroceryContext.Provider
|
<GroceryContext.Provider
|
||||||
value={{ groceries, iconMapping, updateGroceryItem, isAddingGrocery, setIsAddingGrocery, addGrocery }}
|
value={{
|
||||||
|
groceries,
|
||||||
|
fuzzyMatchGroceryCategory,
|
||||||
|
//iconMapping,
|
||||||
|
updateGroceryItem,
|
||||||
|
isAddingGrocery,
|
||||||
|
setIsAddingGrocery,
|
||||||
|
addGrocery,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</GroceryContext.Provider>
|
</GroceryContext.Provider>
|
||||||
|
@ -21,6 +21,7 @@ interface IToDosContext {
|
|||||||
toDos: IToDo[];
|
toDos: IToDo[];
|
||||||
updateToDo: (id: number, changes: Partial<IToDo>) => void;
|
updateToDo: (id: number, changes: Partial<IToDo>) => void;
|
||||||
addToDo: (newToDo: IToDo) => void;
|
addToDo: (newToDo: IToDo) => void;
|
||||||
|
maxPoints: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToDosContext = createContext<IToDosContext>(undefined!);
|
const ToDosContext = createContext<IToDosContext>(undefined!);
|
||||||
@ -35,7 +36,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
rotate: true,
|
rotate: true,
|
||||||
repeatType: "Every week"
|
repeatType: "Every week",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -43,7 +44,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
rotate: false,
|
rotate: false,
|
||||||
repeatType: "Every week"
|
repeatType: "Every week",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
@ -51,7 +52,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
rotate: true,
|
rotate: true,
|
||||||
repeatType: "Every week"
|
repeatType: "Every week",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
@ -60,7 +61,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
date: new Date(Date.now() + 86400000),
|
date: new Date(Date.now() + 86400000),
|
||||||
points: 40,
|
points: 40,
|
||||||
rotate: false,
|
rotate: false,
|
||||||
repeatType: "Every week"
|
repeatType: "Every week",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
@ -68,7 +69,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: new Date(Date.now() + 86400000),
|
date: new Date(Date.now() + 86400000),
|
||||||
rotate: false,
|
rotate: false,
|
||||||
repeatType: "Once a Month"
|
repeatType: "Once a Month",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
@ -76,7 +77,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: new Date(Date.now() + 2 * 86400000),
|
date: new Date(Date.now() + 2 * 86400000),
|
||||||
rotate: true,
|
rotate: true,
|
||||||
repeatType: "Once a Month"
|
repeatType: "Once a Month",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: 6,
|
||||||
@ -84,7 +85,8 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: new Date(Date.now() + 3 * 86400000),
|
date: new Date(Date.now() + 3 * 86400000),
|
||||||
rotate: false,
|
rotate: false,
|
||||||
repeatType: "Once a year"
|
repeatType: "Once a year",
|
||||||
|
points: 50,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: 7,
|
||||||
@ -92,7 +94,7 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: null,
|
date: null,
|
||||||
rotate: false,
|
rotate: false,
|
||||||
repeatType: "None"
|
repeatType: "None",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: 8,
|
||||||
@ -100,14 +102,34 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
done: false,
|
done: false,
|
||||||
date: null,
|
date: null,
|
||||||
rotate: false,
|
rotate: false,
|
||||||
repeatType: "None"
|
repeatType: "None",
|
||||||
|
points: 10,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const initCalc = (): number => {
|
||||||
|
return toDos.reduce(
|
||||||
|
(sum, todo) => sum + (todo.points ? todo.points : 0),
|
||||||
|
50
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateMaxPoints = () => {
|
||||||
|
const totalPoints = toDos.reduce(
|
||||||
|
(sum, todo) => sum + (todo.points ? todo.points : 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
setMaxPoints(totalPoints);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [maxPoints, setMaxPoints] = useState<number>(initCalc);
|
||||||
|
|
||||||
const updateToDo = (id: number, changes: Partial<IToDo>) => {
|
const updateToDo = (id: number, changes: Partial<IToDo>) => {
|
||||||
setToDos((prevToDos) =>
|
setToDos((prevToDos) =>
|
||||||
prevToDos.map((toDo) => (toDo.id === id ? { ...toDo, ...changes } : toDo))
|
prevToDos.map((toDo) => (toDo.id === id ? { ...toDo, ...changes } : toDo))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
calculateMaxPoints();
|
||||||
};
|
};
|
||||||
|
|
||||||
const addToDo = (newToDo: IToDo) => {
|
const addToDo = (newToDo: IToDo) => {
|
||||||
@ -118,10 +140,11 @@ export const ToDosContextProvider: FC<{ children: ReactNode }> = ({
|
|||||||
id: prevToDos.length ? prevToDos[prevToDos.length - 1].id + 1 : 0,
|
id: prevToDos.length ? prevToDos[prevToDos.length - 1].id + 1 : 0,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
calculateMaxPoints();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToDosContext.Provider value={{ toDos, updateToDo, addToDo }}>
|
<ToDosContext.Provider value={{ toDos, updateToDo, addToDo, maxPoints }}>
|
||||||
{children}
|
{children}
|
||||||
</ToDosContext.Provider>
|
</ToDosContext.Provider>
|
||||||
);
|
);
|
||||||
|
30
hooks/firebase/useSetUserData.ts
Normal file
30
hooks/firebase/useSetUserData.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
|
import {useMutation} from "react-query";
|
||||||
|
import firestore from "@react-native-firebase/firestore";
|
||||||
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
|
import {FirebaseAuthTypes} from "@react-native-firebase/auth";
|
||||||
|
|
||||||
|
export const useSetUserData = () => {
|
||||||
|
const {user: currentUser, setProfileData} = useAuthContext()
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["updateUserData"],
|
||||||
|
mutationFn: async ({newUserData, customUser}: {newUserData: Partial<UserProfile>, customUser?: FirebaseAuthTypes.User }) => {
|
||||||
|
const user = currentUser ?? customUser
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
try {
|
||||||
|
await firestore()
|
||||||
|
.collection("Profiles")
|
||||||
|
.doc(user.uid)
|
||||||
|
.set(newUserData);
|
||||||
|
|
||||||
|
const profileData = await firestore().collection("Profiles").doc(user?.uid!).get()
|
||||||
|
setProfileData(profileData.data() as UserProfile)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,16 +1,40 @@
|
|||||||
import {useMutation} from "react-query";
|
import { useMutation } from "react-query";
|
||||||
import auth from "@react-native-firebase/auth";
|
import auth from "@react-native-firebase/auth";
|
||||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
import { ProfileType } from "@/contexts/AuthContext";
|
||||||
import {ProfileType} from "@/contexts/AuthContext";
|
import { useSetUserData } from "./useSetUserData";
|
||||||
|
|
||||||
export const useSignUp = () => {
|
export const useSignUp = () => {
|
||||||
const {mutateAsync: updateUserData} = useUpdateUserData()
|
const { mutateAsync: setUserData } = useSetUserData();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["signUp"],
|
mutationKey: ["signUp"],
|
||||||
mutationFn: async ({email, password, firstName, lastName}: { email: string, password: string, firstName: string, lastName: string }) => {
|
mutationFn: async ({
|
||||||
const res = await auth().createUserWithEmailAndPassword(email, password);
|
email,
|
||||||
await updateUserData({newUserData: {userType: ProfileType.PARENT, firstName: firstName, lastName: lastName}, customUser: res.user});
|
password,
|
||||||
}
|
firstName,
|
||||||
});
|
lastName,
|
||||||
}
|
}: {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
}) => {
|
||||||
|
await auth()
|
||||||
|
.createUserWithEmailAndPassword(email, password)
|
||||||
|
.then(async (res) => {
|
||||||
|
try {
|
||||||
|
await setUserData({
|
||||||
|
newUserData: {
|
||||||
|
userType: ProfileType.PARENT,
|
||||||
|
firstName: firstName,
|
||||||
|
lastName: lastName,
|
||||||
|
},
|
||||||
|
customUser: res.user,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
317
package-lock.json
generated
317
package-lock.json
generated
@ -32,6 +32,7 @@
|
|||||||
"expo-web-browser": "~13.0.3",
|
"expo-web-browser": "~13.0.3",
|
||||||
"firebase-admin": "^12.3.1",
|
"firebase-admin": "^12.3.1",
|
||||||
"firebase-functions": "^5.1.0",
|
"firebase-functions": "^5.1.0",
|
||||||
|
"fuzzysort": "^3.0.2",
|
||||||
"jotai": "^2.9.1",
|
"jotai": "^2.9.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
@ -39,12 +40,13 @@
|
|||||||
"react-native-big-calendar": "^4.14.0",
|
"react-native-big-calendar": "^4.14.0",
|
||||||
"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-gifted-charts": "^1.4.41",
|
||||||
"react-native-linear-gradient": "^2.8.3",
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
"react-native-onboarding-swiper": "^1.3.0",
|
"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",
|
||||||
"react-native-svg": "^15.6.0",
|
"react-native-svg": "^15.7.1",
|
||||||
"react-native-ui-lib": "^7.27.0",
|
"react-native-ui-lib": "^7.27.0",
|
||||||
"react-native-web": "~0.19.10",
|
"react-native-web": "~0.19.10",
|
||||||
"react-query": "^3.39.3"
|
"react-query": "^3.39.3"
|
||||||
@ -2522,9 +2524,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/config-plugins": {
|
"node_modules/@expo/config-plugins": {
|
||||||
"version": "8.0.8",
|
"version": "8.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-8.0.9.tgz",
|
||||||
"integrity": "sha512-Fvu6IO13EUw0R9WeqxUO37FkM62YJBNcZb9DyJAOgMz7Ez/vaKQGEjKt9cwT+Q6uirtCATMgaq6VWAW7YW8xXw==",
|
"integrity": "sha512-dNCG45C7BbDPV9MdWvCbsFtJtVn4w/TJbb5b7Yr6FA8HYIlaaVM0wqUMzTPmGj54iYXw8X/Vge8uCPxg7RWgeA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/config-types": "^51.0.0-unreleased",
|
"@expo/config-types": "^51.0.0-unreleased",
|
||||||
@ -3449,9 +3451,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/vector-icons": {
|
"node_modules/@expo/vector-icons": {
|
||||||
"version": "14.0.2",
|
"version": "14.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.0.3.tgz",
|
||||||
"integrity": "sha512-70LpmXQu4xa8cMxjp1fydgRPsalefnHaXLzIwaHMEzcZhnyjw2acZz8azRrZOslPVAWlxItOa2Dd7WtD/kI+CA==",
|
"integrity": "sha512-UJAKOXPPi6ez/1QZfoFVopCH3+c12Sw+T+IIVkvONCEN7zjN1fLxxWHkZ7Spz4WO5EH2ObtaJfCe/k4rw+ftxA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
@ -9379,9 +9381,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
@ -9392,7 +9394,7 @@
|
|||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"iconv-lite": "0.4.24",
|
"iconv-lite": "0.4.24",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"qs": "6.11.0",
|
"qs": "6.13.0",
|
||||||
"raw-body": "2.5.2",
|
"raw-body": "2.5.2",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
@ -11522,24 +11524,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo": {
|
"node_modules/expo": {
|
||||||
"version": "51.0.30",
|
"version": "51.0.34",
|
||||||
"resolved": "https://registry.npmjs.org/expo/-/expo-51.0.30.tgz",
|
"resolved": "https://registry.npmjs.org/expo/-/expo-51.0.34.tgz",
|
||||||
"integrity": "sha512-eo4T2TGnKyDkLryY6lYuGVV1FZyWa1FG7Ns0aqfZ7N/PFQYDfbowVwkcEWbRppSzqSfjGXYqvVV4WZNFbpgZZw==",
|
"integrity": "sha512-l2oi+hIj/ph3qGcvM54Nyd2uF3Zq5caVmSg7AXfBUgtvcdv5Pj1EI/2xCXP9tfMNQo351CWyOwBkTGjv+GdrLg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.0",
|
"@babel/runtime": "^7.20.0",
|
||||||
"@expo/cli": "0.18.29",
|
"@expo/cli": "0.18.29",
|
||||||
"@expo/config": "9.0.3",
|
"@expo/config": "9.0.3",
|
||||||
"@expo/config-plugins": "8.0.8",
|
"@expo/config-plugins": "8.0.9",
|
||||||
"@expo/metro-config": "0.18.11",
|
"@expo/metro-config": "0.18.11",
|
||||||
"@expo/vector-icons": "^14.0.0",
|
"@expo/vector-icons": "^14.0.3",
|
||||||
"babel-preset-expo": "~11.0.14",
|
"babel-preset-expo": "~11.0.14",
|
||||||
"expo-asset": "~10.0.10",
|
"expo-asset": "~10.0.10",
|
||||||
"expo-file-system": "~17.0.1",
|
"expo-file-system": "~17.0.1",
|
||||||
"expo-font": "~12.0.9",
|
"expo-font": "~12.0.10",
|
||||||
"expo-keep-awake": "~13.0.2",
|
"expo-keep-awake": "~13.0.2",
|
||||||
"expo-modules-autolinking": "1.11.2",
|
"expo-modules-autolinking": "1.11.2",
|
||||||
"expo-modules-core": "1.12.22",
|
"expo-modules-core": "1.12.24",
|
||||||
"fbemitter": "^3.0.0",
|
"fbemitter": "^3.0.0",
|
||||||
"whatwg-url-without-unicode": "8.0.0-3"
|
"whatwg-url-without-unicode": "8.0.0-3"
|
||||||
},
|
},
|
||||||
@ -11703,9 +11705,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-font": {
|
"node_modules/expo-font": {
|
||||||
"version": "12.0.9",
|
"version": "12.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.10.tgz",
|
||||||
"integrity": "sha512-seTCyf0tbgkAnp3ZI9ZfK9QVtURQUgFnuj+GuJ5TSnN0XsOtVe1s2RxTvmMgkfuvfkzcjJ69gyRpsZS1cC8hjw==",
|
"integrity": "sha512-Q1i2NuYri3jy32zdnBaHHCya1wH1yMAsI+3CCmj9zlQzlhsS9Bdwcj2W3c5eU5FvH2hsNQy4O+O1NnM6o/pDaQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fontfaceobserver": "^2.1.0"
|
"fontfaceobserver": "^2.1.0"
|
||||||
@ -11877,9 +11879,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-modules-core": {
|
"node_modules/expo-modules-core": {
|
||||||
"version": "1.12.22",
|
"version": "1.12.24",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.22.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.24.tgz",
|
||||||
"integrity": "sha512-MX9qJRuVszyuGksOZ1QkMcUGcAZ1o2AmDigkQAl9yxqFtwEpBOxELs3rXeQol7WiEabvK+bERTF9LtSTDCVCYw==",
|
"integrity": "sha512-3geIe2ecizlp7l26iY8Nmc59z2d1RUC5nQZtI9iJoi5uHEUV/zut8e4zRLFVnZb8KOcMcEDsrvaBL5DPnqdfpg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"invariant": "^2.2.4"
|
"invariant": "^2.2.4"
|
||||||
@ -12055,37 +12057,37 @@
|
|||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.19.2",
|
"version": "4.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.3",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.6.0",
|
"cookie": "0.6.0",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"etag": "~1.8.1",
|
"etag": "~1.8.1",
|
||||||
"finalhandler": "1.2.0",
|
"finalhandler": "1.3.1",
|
||||||
"fresh": "0.5.2",
|
"fresh": "0.5.2",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
"merge-descriptors": "1.0.1",
|
"merge-descriptors": "1.0.3",
|
||||||
"methods": "~1.1.2",
|
"methods": "~1.1.2",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"path-to-regexp": "0.1.7",
|
"path-to-regexp": "0.1.10",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "~2.0.7",
|
||||||
"qs": "6.11.0",
|
"qs": "6.13.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "~1.2.1",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"send": "0.18.0",
|
"send": "0.19.0",
|
||||||
"serve-static": "1.15.0",
|
"serve-static": "1.16.2",
|
||||||
"setprototypeof": "1.2.0",
|
"setprototypeof": "1.2.0",
|
||||||
"statuses": "2.0.1",
|
"statuses": "2.0.1",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
@ -12111,14 +12113,23 @@
|
|||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/encodeurl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/finalhandler": {
|
"node_modules/express/node_modules/finalhandler": {
|
||||||
"version": "1.2.0",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
@ -12129,6 +12140,18 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/ms": {
|
"node_modules/express/node_modules/ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
@ -12147,6 +12170,45 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/send": {
|
||||||
|
"version": "0.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||||
|
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"destroy": "1.2.0",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"mime": "1.6.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"statuses": "2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/express/node_modules/send/node_modules/encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/express/node_modules/send/node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/statuses": {
|
"node_modules/express/node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
@ -12832,6 +12894,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fuzzysort": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ZyahVgxvckB1Qosn7YGWLDJJp2XlyaQ2WmZeI+d0AzW0AMqVYnz5N89G6KAKa6m/LOtv+kzJn4lhDF/yVg11Cg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/gaxios": {
|
"node_modules/gaxios": {
|
||||||
"version": "6.7.1",
|
"version": "6.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
|
||||||
@ -12998,6 +13066,16 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gifted-charts-core": {
|
||||||
|
"version": "0.1.41",
|
||||||
|
"resolved": "https://registry.npmjs.org/gifted-charts-core/-/gifted-charts-core-0.1.41.tgz",
|
||||||
|
"integrity": "sha512-17xzJ0kJw+BVbxX7J1JmDHHJt/Yp1n+h0sTrC6eK4rQg9Hzw0EC/MJcv5fLJzg6dkVo+3wQ3anWXL5itapP+nQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "7.2.3",
|
"version": "7.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
@ -17495,10 +17573,13 @@
|
|||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/merge-descriptors": {
|
"node_modules/merge-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
|
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -18906,9 +18987,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
@ -19300,12 +19381,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
@ -19614,6 +19695,30 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-gifted-charts": {
|
||||||
|
"version": "1.4.41",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-gifted-charts/-/react-native-gifted-charts-1.4.41.tgz",
|
||||||
|
"integrity": "sha512-UQ3E6ork24VFVTOhgAAbGkGplYB89mRjNEEPJKRAzwSYoJ1Gjtt1pleuY7OKpMb5TQychreFBA2oVBrwTLdeKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"gifted-charts-core": "0.1.41"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo-linear-gradient": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-linear-gradient": "*",
|
||||||
|
"react-native-svg": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"expo-linear-gradient": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native-linear-gradient": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-helmet-async": {
|
"node_modules/react-native-helmet-async": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-helmet-async/-/react-native-helmet-async-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-helmet-async/-/react-native-helmet-async-2.0.4.tgz",
|
||||||
@ -19716,9 +19821,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-native-svg": {
|
"node_modules/react-native-svg": {
|
||||||
"version": "15.6.0",
|
"version": "15.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.7.1.tgz",
|
||||||
"integrity": "sha512-TUtR+h+yi1ODsd8FHdom1TpjfWOmnaK5pri5rnSBXnMqpzq8o2zZfonHTjPX+nS3wb/Pu2XsoARgYaHNjVWXhQ==",
|
"integrity": "sha512-Xc11L4t6/DtmUwrQqHR7S45Qy3cIWpcfGlmEatVeZ9c1N8eAK79heJmGRgCOVrXESrrLEHfP/AYGf0BGyrvV6A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"css-select": "^5.1.0",
|
"css-select": "^5.1.0",
|
||||||
@ -20631,20 +20736,116 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serve-static": {
|
"node_modules/serve-static": {
|
||||||
"version": "1.15.0",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encodeurl": "~1.0.2",
|
"encodeurl": "~2.0.0",
|
||||||
"escape-html": "~1.0.3",
|
"escape-html": "~1.0.3",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "~1.3.3",
|
||||||
"send": "0.18.0"
|
"send": "0.19.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/serve-static/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/debug/node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/encodeurl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/on-finished": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/send": {
|
||||||
|
"version": "0.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||||
|
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"destroy": "1.2.0",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"mime": "1.6.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"statuses": "2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/send/node_modules/encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/serve-static/node_modules/statuses": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/set-blocking": {
|
"node_modules/set-blocking": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
"expo-web-browser": "~13.0.3",
|
"expo-web-browser": "~13.0.3",
|
||||||
"firebase-admin": "^12.3.1",
|
"firebase-admin": "^12.3.1",
|
||||||
"firebase-functions": "^5.1.0",
|
"firebase-functions": "^5.1.0",
|
||||||
|
"fuzzysort": "^3.0.2",
|
||||||
"jotai": "^2.9.1",
|
"jotai": "^2.9.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
@ -58,12 +59,13 @@
|
|||||||
"react-native-big-calendar": "^4.14.0",
|
"react-native-big-calendar": "^4.14.0",
|
||||||
"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-gifted-charts": "^1.4.41",
|
||||||
"react-native-linear-gradient": "^2.8.3",
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
"react-native-onboarding-swiper": "^1.3.0",
|
"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",
|
||||||
"react-native-svg": "^15.6.0",
|
"react-native-svg": "^15.7.1",
|
||||||
"react-native-toast-message": "^2.2.1",
|
"react-native-toast-message": "^2.2.1",
|
||||||
"react-native-ui-lib": "^7.27.0",
|
"react-native-ui-lib": "^7.27.0",
|
||||||
"react-native-web": "~0.19.10",
|
"react-native-web": "~0.19.10",
|
||||||
|
Reference in New Issue
Block a user