mirror of
https://github.com/urosran/cally.git
synced 2025-07-10 15:17:17 +00:00
Merge branch 'dev'
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
import { View, Text, TouchableOpacity, Icon } from "react-native-ui-lib";
|
import { View, Text, TouchableOpacity, Icon } from "react-native-ui-lib";
|
||||||
import React, { useState } from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { useToDosContext } from "@/contexts/ToDosContext";
|
import { useToDosContext } from "@/contexts/ToDosContext";
|
||||||
import {
|
import {
|
||||||
addDays,
|
addDays,
|
||||||
@ -20,7 +20,7 @@ const groupToDosByDate = (toDos: IToDo[]) => {
|
|||||||
const dateB = b.date === null ? new Date() : b.date;
|
const dateB = b.date === null ? new Date() : b.date;
|
||||||
return dateA - dateB;
|
return dateA - dateB;
|
||||||
});
|
});
|
||||||
|
|
||||||
return sortedTodos.reduce(
|
return sortedTodos.reduce(
|
||||||
(groups, toDo) => {
|
(groups, toDo) => {
|
||||||
let dateKey;
|
let dateKey;
|
||||||
@ -44,7 +44,7 @@ const groupToDosByDate = (toDos: IToDo[]) => {
|
|||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
return date < today;
|
return date < today;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isOverdue(toDo.date) && !toDo.done) {
|
if (isOverdue(toDo.date) && !toDo.done) {
|
||||||
dateKey = "Overdue";
|
dateKey = "Overdue";
|
||||||
} else if (toDo.date === null || isToday(toDo.date)) {
|
} else if (toDo.date === null || isToday(toDo.date)) {
|
||||||
@ -95,8 +95,18 @@ const filterToDosByUser = (toDos: IToDo[], uid: string | undefined) => {
|
|||||||
|
|
||||||
const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
|
const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
|
||||||
const { toDos } = useToDosContext();
|
const { toDos } = useToDosContext();
|
||||||
const userTodos = filterToDosByUser(toDos, user.uid);
|
const [localTodos, setLocalTodos] = useState([]);
|
||||||
const groupedToDos = groupToDosByDate(userTodos);
|
const [groupedToDos, setGroupedToDos] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const userTodos = filterToDosByUser(toDos, user.uid);
|
||||||
|
setLocalTodos(userTodos);
|
||||||
|
}, [toDos, user]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const grouped = groupToDosByDate(localTodos);
|
||||||
|
setGroupedToDos(grouped);
|
||||||
|
}, [localTodos]);
|
||||||
|
|
||||||
const [expandedGroups, setExpandedGroups] = useState<{
|
const [expandedGroups, setExpandedGroups] = useState<{
|
||||||
[key: string]: boolean;
|
[key: string]: boolean;
|
||||||
@ -186,7 +196,12 @@ const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{sortedItems.map((item) => (
|
{sortedItems.map((item) => (
|
||||||
<ToDoItem key={item.id} item={item} is7Days={false} />
|
<ToDoItem
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
is7Days={false}
|
||||||
|
localTodos={localTodos}
|
||||||
|
setLocalTodos={setLocalTodos}/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -235,6 +250,8 @@ const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
is7Days={dateKey === "Next 7 Days"}
|
is7Days={dateKey === "Next 7 Days"}
|
||||||
|
localTodos={localTodos}
|
||||||
|
setLocalTodos={setLocalTodos}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@ -278,7 +295,7 @@ const SingleUserChoreList = ({ user }: { user: UserProfile }) => {
|
|||||||
{expandNoDate &&
|
{expandNoDate &&
|
||||||
noDateToDos
|
noDateToDos
|
||||||
.sort((a, b) => Number(a.done) - Number(b.done))
|
.sort((a, b) => Number(a.done) - Number(b.done))
|
||||||
.map((item) => <ToDoItem key={item.id} item={item} />)}
|
.map((item) => <ToDoItem key={item.id} item={item} localTodos={localTodos} setLocalTodos={setLocalTodos} />)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ const EditGroceryItem = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setGrocery(defaultGroceryItem);
|
closeEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
|
@ -39,6 +39,7 @@ const GroceryItem = ({
|
|||||||
return `${firstName.charAt(0).toUpperCase()}${lastName.charAt(0).toUpperCase()}`;
|
return `${firstName.charAt(0).toUpperCase()}${lastName.charAt(0).toUpperCase()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isEditable = (isParent || isCaregiver) && item.approved && !item.bought;
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
key={item.id}
|
key={item.id}
|
||||||
@ -49,109 +50,108 @@ const GroceryItem = ({
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<TouchableOpacity onPress={isEditable ? () => setIsEditingTitle(true) : null}>
|
||||||
row
|
<View
|
||||||
spread
|
row
|
||||||
centerV
|
spread
|
||||||
style={{
|
centerV
|
||||||
paddingHorizontal: isEditingTitle ? 0 : 13,
|
style={{
|
||||||
paddingVertical: isEditingTitle ? 0 : 10,
|
paddingHorizontal: isEditingTitle ? 0 : 13,
|
||||||
minHeight: 44.64,
|
paddingVertical: isEditingTitle ? 0 : 10,
|
||||||
}}
|
minHeight: 44.64,
|
||||||
>
|
}}
|
||||||
<EditGroceryFrequency
|
>
|
||||||
visible={openFreqEdit}
|
<EditGroceryFrequency
|
||||||
key={item.id}
|
visible={openFreqEdit}
|
||||||
item={item}
|
key={item.id}
|
||||||
onClose={() => setOpenFreqEdit(false)}
|
item={item}
|
||||||
/>
|
onClose={() => setOpenFreqEdit(false)}
|
||||||
|
|
||||||
{isEditingTitle ? (
|
|
||||||
<EditGroceryItem
|
|
||||||
editGrocery={item}
|
|
||||||
onInputFocus={onInputFocus}
|
|
||||||
closeEdit={closeEdit}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
{isEditingTitle ? (
|
||||||
<View flex>
|
<EditGroceryItem
|
||||||
{(isParent || isCaregiver) && !item.bought ? (
|
editGrocery={item}
|
||||||
<TouchableOpacity onPress={() => setIsEditingTitle(true)}>
|
onInputFocus={onInputFocus}
|
||||||
|
closeEdit={closeEdit}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View flex>
|
||||||
|
{(isParent || isCaregiver) && !item.bought ? (
|
||||||
|
<Text
|
||||||
|
text70T
|
||||||
|
style={[
|
||||||
|
styles.title,
|
||||||
|
{
|
||||||
|
textDecorationLine: item.bought ? "line-through" : "none",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
<Text
|
<Text
|
||||||
text70T
|
text70T
|
||||||
style={[
|
style={[styles.title, { textDecorationLine: item.bought ? "line-through" : "none", }]}
|
||||||
styles.title,
|
|
||||||
{
|
|
||||||
textDecorationLine: item.bought ? "line-through" : "none",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
)}
|
||||||
) : (
|
</View>
|
||||||
<Text
|
)}
|
||||||
text70T
|
|
||||||
style={[styles.title, { textDecorationLine: item.bought ? "line-through" : "none", }]}
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!item.approved ? (
|
{!item.approved ? (
|
||||||
<View row centerV>
|
<View row centerV>
|
||||||
{(isParent || isCaregiver) && (
|
{(isParent || isCaregiver) && (
|
||||||
<>
|
<>
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name="check"
|
name="check"
|
||||||
size={24}
|
size={24}
|
||||||
style={{ color: "blue", marginRight: 15 }}
|
style={{ color: "blue", marginRight: 15 }}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
handleItemApproved(item.id, { approved: true })
|
handleItemApproved(item.id, { approved: true })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AntDesign
|
||||||
|
name="close"
|
||||||
|
size={24}
|
||||||
|
style={{ color: "red" }}
|
||||||
|
onPress={() => {
|
||||||
|
handleItemApproved(item.id, { approved: false });
|
||||||
|
deleteGrocery(item.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
!isEditingTitle &&
|
||||||
|
(isParent || isCaregiver) && (
|
||||||
|
<View row>
|
||||||
|
{item.bought &&
|
||||||
|
<AntDesign
|
||||||
|
name="close"
|
||||||
|
size={24}
|
||||||
|
style={{ color: "grey", marginRight: 10 }}
|
||||||
|
onPress={() => deleteGrocery(item.id)}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
<Checkbox
|
||||||
<AntDesign
|
value={item.bought}
|
||||||
name="close"
|
containerStyle={[styles.checkbox, { borderRadius: 50 }]}
|
||||||
size={24}
|
style={styles.checked}
|
||||||
style={{ color: "red" }}
|
borderRadius={50}
|
||||||
onPress={() => {
|
color="#fd1575"
|
||||||
handleItemApproved(item.id, { approved: false });
|
hitSlop={20}
|
||||||
deleteGrocery(item.id);
|
onValueChange={() => {
|
||||||
}}
|
const updatedApprovedGroceries = approvedGroceries.map((grocery) => grocery.id === item.id ? {...grocery, bought: !item.bought} : grocery);
|
||||||
/>
|
setApprovedGroceries(updatedApprovedGroceries);
|
||||||
</>
|
updateGroceryItem({ id: item.id, bought: !item.bought })
|
||||||
)}
|
}}
|
||||||
</View>
|
/>
|
||||||
) : (
|
</View>
|
||||||
!isEditingTitle &&
|
)
|
||||||
(isParent || isCaregiver) && (
|
)}
|
||||||
<View row>
|
</View>
|
||||||
{item.bought &&
|
</TouchableOpacity>
|
||||||
<AntDesign
|
|
||||||
name="close"
|
|
||||||
size={24}
|
|
||||||
style={{ color: "grey", marginRight: 10 }}
|
|
||||||
onPress={() => deleteGrocery(item.id)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
<Checkbox
|
|
||||||
value={item.bought}
|
|
||||||
containerStyle={[styles.checkbox, { borderRadius: 50 }]}
|
|
||||||
style={styles.checked}
|
|
||||||
borderRadius={50}
|
|
||||||
color="#fd1575"
|
|
||||||
hitSlop={20}
|
|
||||||
onValueChange={() => {
|
|
||||||
const updatedApprovedGroceries = approvedGroceries.map((grocery) => grocery.id === item.id ? {...grocery, bought: !item.bought} : grocery);
|
|
||||||
setApprovedGroceries(updatedApprovedGroceries);
|
|
||||||
updateGroceryItem({ id: item.id, bought: !item.bought })
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{!item.approved && (
|
{!item.approved && (
|
||||||
<>
|
<>
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import {Text, View} from "react-native-ui-lib";
|
import { Text, View } from "react-native-ui-lib";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
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";
|
|
||||||
import FireworksOrangeIcon from "@/assets/svgs/FireworksOrangeIcon";
|
import FireworksOrangeIcon from "@/assets/svgs/FireworksOrangeIcon";
|
||||||
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
|
||||||
|
export const transformNumber = (value: number, progressLimit: number) => {
|
||||||
|
return Math.floor((value / progressLimit) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROGRESS_LIMIT = 1000;
|
||||||
|
|
||||||
const ProgressCard = ({children}: { children?: React.ReactNode }) => {
|
const ProgressCard = ({children}: { children?: React.ReactNode }) => {
|
||||||
const {maxPoints} = useToDosContext();
|
const { profileData } = useAuthContext();
|
||||||
|
|
||||||
|
const weeklyPoints = profileData?.weeklyPoints ?? 0;
|
||||||
|
const transformedWeeklyPoints = transformNumber(weeklyPoints, PROGRESS_LIMIT);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
backgroundColor="white"
|
backgroundColor="white"
|
||||||
@ -16,11 +26,11 @@ const ProgressCard = ({children}: { children?: React.ReactNode }) => {
|
|||||||
<View row centerV>
|
<View row centerV>
|
||||||
<FireworksOrangeIcon/>
|
<FireworksOrangeIcon/>
|
||||||
<Text marginL-15 text70 style={{fontFamily: 'Manrope_600SemiBold', fontSize: 14}}>
|
<Text marginL-15 text70 style={{fontFamily: 'Manrope_600SemiBold', fontSize: 14}}>
|
||||||
You have earned XX points this week!{" "}
|
You have earned {weeklyPoints} points this week!{" "}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
progress={50}
|
progress={transformedWeeklyPoints}
|
||||||
progressColor="#ea156c"
|
progressColor="#ea156c"
|
||||||
style={{
|
style={{
|
||||||
height: 21,
|
height: 21,
|
||||||
@ -31,7 +41,7 @@ const ProgressCard = ({children}: { children?: React.ReactNode }) => {
|
|||||||
/>
|
/>
|
||||||
<View row spread>
|
<View row spread>
|
||||||
<Text style={{fontSize: 13, color: '#858585'}}>0</Text>
|
<Text style={{fontSize: 13, color: '#858585'}}>0</Text>
|
||||||
<Text style={{fontSize: 13, color: '#858585'}}>{maxPoints}</Text>
|
<Text style={{fontSize: 13, color: '#858585'}}>{PROGRESS_LIMIT}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View centerV centerH>
|
<View centerV centerH>
|
||||||
{children}
|
{children}
|
||||||
|
@ -27,6 +27,7 @@ const ToDosPage = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { BarChart } from "react-native-gifted-charts";
|
import { BarChart } from "react-native-gifted-charts";
|
||||||
import { UserProfile } from "@/hooks/firebase/types/profileTypes";
|
import { UserProfile } from "@/hooks/firebase/types/profileTypes";
|
||||||
|
import {chartDayMap} from "@/constants/common";
|
||||||
|
|
||||||
const FamilyChart = ({ children }: {
|
const FamilyChart = ({ children }: {
|
||||||
children: Array<UserProfile>;
|
children: Array<UserProfile>;
|
||||||
@ -52,7 +53,7 @@ const FamilyChart = ({ children }: {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
stacks: stacks,
|
stacks: stacks,
|
||||||
label: day,
|
label: chartDayMap[day],
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setDataList(data);
|
setDataList(data);
|
||||||
|
@ -7,8 +7,8 @@ import { Ionicons } from "@expo/vector-icons";
|
|||||||
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
|
import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
|
||||||
import { ProfileType } from "@/contexts/AuthContext";
|
import { ProfileType } from "@/contexts/AuthContext";
|
||||||
import { ScrollView } from "react-native-gesture-handler";
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
import {useFocusEffect} from "@react-navigation/core";
|
import { useFocusEffect } from "@react-navigation/core";
|
||||||
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
import { UserProfile } from "@/hooks/firebase/types/profileTypes";
|
||||||
|
|
||||||
const FamilyChoresProgress = ({
|
const FamilyChoresProgress = ({
|
||||||
setPageIndex,
|
setPageIndex,
|
||||||
@ -47,7 +47,7 @@ const FamilyChoresProgress = ({
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View centerH>
|
<View centerH>
|
||||||
<Text style={{ fontFamily: "Manrope_700Bold", fontSize: 23 }}>
|
<Text style={{ fontFamily: "Manrope_700Bold", fontSize: 23 }}>
|
||||||
Family Chores Progress Report
|
Family Todos Progress Report
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View row spread marginT-35 marginB-20>
|
<View row spread marginT-35 marginB-20>
|
||||||
@ -92,7 +92,7 @@ const FamilyChoresProgress = ({
|
|||||||
style={{ fontFamily: "Manrope_700Bold", fontSize: 15 }}
|
style={{ fontFamily: "Manrope_700Bold", fontSize: 15 }}
|
||||||
marginV-20
|
marginV-20
|
||||||
>
|
>
|
||||||
Chore Tracker
|
Todos Tracker
|
||||||
</Text>
|
</Text>
|
||||||
{children?.map((child) => (
|
{children?.map((child) => (
|
||||||
<View
|
<View
|
||||||
@ -133,7 +133,7 @@ const FamilyChoresProgress = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<View centerV>
|
<View centerV>
|
||||||
<Text style={{ fontSize: 15, fontFamily: "Manrope_700Bold" }}>
|
<Text style={{ fontSize: 15, fontFamily: "Manrope_700Bold" }}>
|
||||||
{`${child?.weeklyCompletedTodos ?? 0}/y chores completed`}
|
{`${child?.weeklyCompletedTodos ?? 0} todos completed`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,57 +1,53 @@
|
|||||||
import React from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { BarChart } from "react-native-gifted-charts";
|
import { BarChart } from "react-native-gifted-charts";
|
||||||
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
|
import {chartDayMap, weekOrder} from "@/constants/common";
|
||||||
|
|
||||||
const UserChart = () => {
|
const UserChart = ({ profileData }: {
|
||||||
const barColor = "#05a8b6"
|
profileData: UserProfile | undefined;
|
||||||
const data = [
|
}) => {
|
||||||
{
|
const [dataList, setDataList] = useState([]);
|
||||||
value: 290, // Direct value of the bar
|
|
||||||
frontColor: barColor, // Color of the bar
|
const barColor = "#05a8b6";
|
||||||
label: "M",
|
useEffect(() => {
|
||||||
},
|
let weeklyDayPoints = profileData?.weeklyDayPoints || {
|
||||||
{
|
Monday: 0,
|
||||||
value: 190,
|
Tuesday: 0,
|
||||||
frontColor: barColor,
|
Wednesday: 0,
|
||||||
label: "Tu",
|
Thursday: 0,
|
||||||
},
|
Friday: 0,
|
||||||
{
|
Saturday: 0,
|
||||||
value: 210,
|
Sunday: 0,
|
||||||
frontColor: barColor,
|
};
|
||||||
label: "W",
|
|
||||||
},
|
// Sort by day
|
||||||
{
|
const sortedWeeklyPoints = Object.fromEntries(
|
||||||
value: 410,
|
weekOrder.map(day => [day, weeklyDayPoints[day]])
|
||||||
frontColor: barColor,
|
);
|
||||||
label: "Th",
|
|
||||||
},
|
const data = Object.keys(sortedWeeklyPoints).map((day) => {
|
||||||
{
|
const value = weeklyDayPoints[day];
|
||||||
value: 220,
|
|
||||||
frontColor: barColor,
|
return {
|
||||||
label: "F",
|
value: value,
|
||||||
},
|
frontColor: barColor,
|
||||||
{
|
label: chartDayMap[day],
|
||||||
value: 160,
|
}
|
||||||
frontColor: barColor,
|
});
|
||||||
label: "Sa",
|
setDataList(data);
|
||||||
},
|
}, [])
|
||||||
{
|
|
||||||
value: 160,
|
|
||||||
frontColor: barColor,
|
|
||||||
label: "Su",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BarChart
|
<BarChart
|
||||||
data={data}
|
data={dataList}
|
||||||
width={255}
|
width={255}
|
||||||
height={150} // Height of the chart
|
height={150} // Height of the chart
|
||||||
barWidth={20} // Width of each bar
|
barWidth={20} // Width of each bar
|
||||||
noOfSections={5} // Number of horizontal sections (for 0 to 1000 in steps of 200)
|
noOfSections={5} // Number of horizontal sections (for 0 to 1000 in steps of 200)
|
||||||
maxValue={1000} // Max value on the chart
|
maxValue={500} // Max value on the chart
|
||||||
stepValue={200} // Step size for horizontal lines
|
stepValue={100} // Step size for horizontal lines
|
||||||
yAxisThickness={0} // Hide the Y-axis line
|
yAxisThickness={0} // Hide the Y-axis line
|
||||||
yAxisLabelTexts={["0", "200", "400", "600", "800", "1000"]} // Custom Y-axis labels
|
// yAxisLabelTexts={["0", "200", "400", "600", "800", "1000"]} // Custom Y-axis labels
|
||||||
hideRules={false} // Show the horizontal lines
|
hideRules={false} // Show the horizontal lines
|
||||||
rulesColor="#dadada" // Color for the horizontal lines
|
rulesColor="#dadada" // Color for the horizontal lines
|
||||||
barBorderTopLeftRadius={5} // Round the bars
|
barBorderTopLeftRadius={5} // Round the bars
|
||||||
|
@ -1,18 +1,37 @@
|
|||||||
import {Button, ButtonSize, Dialog, ProgressBar, Text, TouchableOpacity, View,} from "react-native-ui-lib";
|
import { Button, ButtonSize, Dialog, ProgressBar, Text, TouchableOpacity, View, } from "react-native-ui-lib";
|
||||||
import React, {useState} from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {StyleSheet} from "react-native";
|
import {Alert, StyleSheet } from "react-native";
|
||||||
import UserChart from "./UserChart";
|
import UserChart from "./UserChart";
|
||||||
import ProgressCard from "../ProgressCard";
|
import ProgressCard, {transformNumber} from "../ProgressCard";
|
||||||
import {AntDesign, Ionicons} from "@expo/vector-icons";
|
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
||||||
import {ScrollView} from "react-native-gesture-handler";
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
import FireworksOrangeIcon from "@/assets/svgs/FireworksOrangeIcon";
|
import FireworksOrangeIcon from "@/assets/svgs/FireworksOrangeIcon";
|
||||||
|
import { useAuthContext } from "@/contexts/AuthContext";
|
||||||
|
import { useSpendPoints } from "@/hooks/firebase/useUpdateUserData";
|
||||||
|
|
||||||
const UserChoresProgress = ({
|
const PROGRESS_LIMIT = 5000;
|
||||||
setPageIndex,
|
|
||||||
}: {
|
const UserChoresProgress = ({ setPageIndex }: { setPageIndex: (value: number) => void; }) => {
|
||||||
setPageIndex: (value: number) => void;
|
|
||||||
}) => {
|
|
||||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||||
|
const { profileData, refreshProfileData } = useAuthContext();
|
||||||
|
const { mutateAsync: spendPoints } = useSpendPoints();
|
||||||
|
|
||||||
|
const allTimePoints = profileData?.allTimePoints ?? 0;
|
||||||
|
let transformedAllTimePoints = transformNumber(allTimePoints, PROGRESS_LIMIT);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refreshProfileData();
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onClickSpendPoints = (pointsToSpend: number) => {
|
||||||
|
if (allTimePoints < pointsToSpend) {
|
||||||
|
Alert.alert("Not Enough Points", "You do not have enough points to spend.");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
spendPoints(pointsToSpend).then(() => setModalVisible(false)).then(refreshProfileData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View marginT-20 paddingB-20>
|
<View marginT-20 paddingB-20>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
@ -52,7 +71,7 @@ const UserChoresProgress = ({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.card} paddingL-10>
|
<View style={styles.card} paddingL-10>
|
||||||
<UserChart/>
|
<UserChart profileData={profileData}/>
|
||||||
</View>
|
</View>
|
||||||
<View row spread marginT-20 marginB-8 centerV>
|
<View row spread marginT-20 marginB-8 centerV>
|
||||||
<Text text70 style={{fontFamily: "Manrope_600SemiBold"}}>
|
<Text text70 style={{fontFamily: "Manrope_600SemiBold"}}>
|
||||||
@ -86,11 +105,11 @@ const UserChoresProgress = ({
|
|||||||
text70
|
text70
|
||||||
style={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
|
style={{fontFamily: "Manrope_600SemiBold", fontSize: 14}}
|
||||||
>
|
>
|
||||||
You have 1200 points saved!
|
You have {allTimePoints} points saved!
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
progress={80}
|
progress={transformedAllTimePoints}
|
||||||
progressColor="#ff9900"
|
progressColor="#ff9900"
|
||||||
style={{
|
style={{
|
||||||
height: 21,
|
height: 21,
|
||||||
@ -101,7 +120,7 @@ const UserChoresProgress = ({
|
|||||||
/>
|
/>
|
||||||
<View row spread>
|
<View row spread>
|
||||||
<Text style={{fontSize: 13, color: "#858585"}}>0</Text>
|
<Text style={{fontSize: 13, color: "#858585"}}>0</Text>
|
||||||
<Text style={{fontSize: 13, color: "#858585"}}>5000</Text>
|
<Text style={{fontSize: 13, color: "#858585"}}>{PROGRESS_LIMIT}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@ -114,12 +133,13 @@ const UserChoresProgress = ({
|
|||||||
How would you like to spend your points?
|
How would you like to spend your points?
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
label="Skip a Chore Cor a Day - 150 pts"
|
label="Skip a Todo For a Day - 150 pts"
|
||||||
text70
|
text70
|
||||||
marginB-15
|
marginB-15
|
||||||
backgroundColor="#05a8b6"
|
backgroundColor="#05a8b6"
|
||||||
size={ButtonSize.large}
|
size={ButtonSize.large}
|
||||||
labelStyle={styles.bigButtonText}
|
labelStyle={styles.bigButtonText}
|
||||||
|
onPress={() => onClickSpendPoints(150)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label="Extra Screen Time - 100 pts"
|
label="Extra Screen Time - 100 pts"
|
||||||
@ -128,6 +148,7 @@ const UserChoresProgress = ({
|
|||||||
backgroundColor="#ea156c"
|
backgroundColor="#ea156c"
|
||||||
size={ButtonSize.large}
|
size={ButtonSize.large}
|
||||||
labelStyle={styles.bigButtonText}
|
labelStyle={styles.bigButtonText}
|
||||||
|
onPress={() => onClickSpendPoints(100)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label="Movie Night - 50 pts"
|
label="Movie Night - 50 pts"
|
||||||
@ -136,6 +157,7 @@ const UserChoresProgress = ({
|
|||||||
backgroundColor="#7305d4"
|
backgroundColor="#7305d4"
|
||||||
size={ButtonSize.large}
|
size={ButtonSize.large}
|
||||||
labelStyle={styles.bigButtonText}
|
labelStyle={styles.bigButtonText}
|
||||||
|
onPress={() => onClickSpendPoints(50)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
label="Ice Cream Treat - 25 pts"
|
label="Ice Cream Treat - 25 pts"
|
||||||
@ -144,6 +166,7 @@ const UserChoresProgress = ({
|
|||||||
backgroundColor="#e28800"
|
backgroundColor="#e28800"
|
||||||
size={ButtonSize.large}
|
size={ButtonSize.large}
|
||||||
labelStyle={styles.bigButtonText}
|
labelStyle={styles.bigButtonText}
|
||||||
|
onPress={() => onClickSpendPoints(25)}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity onPress={() => setModalVisible(false)}>
|
<TouchableOpacity onPress={() => setModalVisible(false)}>
|
||||||
<Text text70 marginT-20 center color="#999999" style={{fontFamily: 'Manrope_500Medium'}}>
|
<Text text70 marginT-20 center color="#999999" style={{fontFamily: 'Manrope_500Medium'}}>
|
||||||
|
11
constants/common.ts
Normal file
11
constants/common.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const chartDayMap = {
|
||||||
|
Monday: "M",
|
||||||
|
Tuesday: "Tu",
|
||||||
|
Wednesday: "W",
|
||||||
|
Thursday: "Th",
|
||||||
|
Friday: "F",
|
||||||
|
Saturday: "Sa",
|
||||||
|
Sunday: "Su",
|
||||||
|
}
|
||||||
|
|
||||||
|
export const weekOrder = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
@ -11,7 +11,7 @@ export const useGetTodos = () => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
|
||||||
let snapshot;
|
let snapshot;
|
||||||
if (profileData?.userType === ProfileType.PARENT) {
|
if (profileData?.userType === ProfileType.PARENT || profileData?.userType === ProfileType.FAMILY_DEVICE) {
|
||||||
snapshot = await firestore()
|
snapshot = await firestore()
|
||||||
.collection("Todos")
|
.collection("Todos")
|
||||||
.where("familyId", "==", profileData?.familyId)
|
.where("familyId", "==", profileData?.familyId)
|
||||||
|
@ -140,7 +140,7 @@ export const useUpdateTodo = () => {
|
|||||||
Sunday: 0,
|
Sunday: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentDay = getCurrentDay(todoUpdate.date);
|
const currentDay = getCurrentDay();
|
||||||
const updatedPointsPerDay = todoData.done
|
const updatedPointsPerDay = todoData.done
|
||||||
? pointsPerDay[currentDay] + todoUpdate.points
|
? pointsPerDay[currentDay] + todoUpdate.points
|
||||||
: pointsPerDay[currentDay] - todoUpdate.points;
|
: pointsPerDay[currentDay] - todoUpdate.points;
|
||||||
@ -185,8 +185,8 @@ export const useUpdateTodo = () => {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentDay = (date) => {
|
const getCurrentDay = () => {
|
||||||
let selectedDate = new Date(date.seconds * 1000);
|
|
||||||
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||||
return days[selectedDate.getDay()];
|
const now = new Date();
|
||||||
|
return days[now.getDay()];
|
||||||
};
|
};
|
@ -3,6 +3,7 @@ import {FirebaseAuthTypes} from "@react-native-firebase/auth";
|
|||||||
import {useMutation, useQueryClient} from "@tanstack/react-query";
|
import {useMutation, useQueryClient} from "@tanstack/react-query";
|
||||||
import {useAuthContext} from "@/contexts/AuthContext";
|
import {useAuthContext} from "@/contexts/AuthContext";
|
||||||
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
import {UserProfile} from "@/hooks/firebase/types/profileTypes";
|
||||||
|
import {Alert} from "react-native";
|
||||||
|
|
||||||
export const useUpdateUserData = () => {
|
export const useUpdateUserData = () => {
|
||||||
const {user: currentUser, refreshProfileData} = useAuthContext();
|
const {user: currentUser, refreshProfileData} = useAuthContext();
|
||||||
@ -54,4 +55,28 @@ export const useUpdateUserData = () => {
|
|||||||
queryClient.invalidateQueries({queryKey: ["events"]})
|
queryClient.invalidateQueries({queryKey: ["events"]})
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSpendPoints = () => {
|
||||||
|
const { user: currentUser } = useAuthContext();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["spendPoints"],
|
||||||
|
mutationFn: async (pointsToSpend: number) => {
|
||||||
|
const userRef = firestore().collection("Profiles").doc(currentUser?.uid);
|
||||||
|
let userSnapshot = await userRef.get();
|
||||||
|
let userData = userSnapshot.data() as UserProfile;
|
||||||
|
|
||||||
|
if (!userData) return;
|
||||||
|
|
||||||
|
const allTimePoints = userData.allTimePoints || 0;
|
||||||
|
if (allTimePoints < pointsToSpend) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await userRef.update({...userData,allTimePoints: allTimePoints - pointsToSpend,})
|
||||||
|
|
||||||
|
Alert.alert("Success", `You spent ${pointsToSpend} points!`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user