From 497c556a8741896167c5a0480f3e9963ede5d0f3 Mon Sep 17 00:00:00 2001 From: thschleicher Date: Fri, 5 Jan 2024 20:53:47 +0100 Subject: [PATCH 01/24] removed "entered" log, fixed loading symbol, implemented category update with editing --- app/(tabs)/budget/category.tsx | 2 +- app/(tabs)/budget/editCategory.tsx | 3 ++- app/(tabs)/budget/index.tsx | 2 +- app/(tabs)/home/index.tsx | 6 ++---- components/common/loadingSymbol.tsx | 14 +++++++------- services/database.ts | 12 ++++++++++++ 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/app/(tabs)/budget/category.tsx b/app/(tabs)/budget/category.tsx index a58d02b..ca96d05 100644 --- a/app/(tabs)/budget/category.tsx +++ b/app/(tabs)/budget/category.tsx @@ -14,7 +14,7 @@ export default function Page() { const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.guid = ? ORDER BY expense_datetime desc;", args: [category_guid]}); const handleEditCategory = () => { - router.push({pathname: "./(tabs)/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); + router.push({pathname: "/(tabs)/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); } const handleBackButton = () => { diff --git a/app/(tabs)/budget/editCategory.tsx b/app/(tabs)/budget/editCategory.tsx index b3b8ec7..d45958d 100644 --- a/app/(tabs)/budget/editCategory.tsx +++ b/app/(tabs)/budget/editCategory.tsx @@ -2,6 +2,7 @@ import { router, useLocalSearchParams } from "expo-router"; import { useState } from "react"; import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; +import { updateCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; const addCategory = () => { @@ -48,7 +49,7 @@ const addCategory = () => { router.back(); }}/> { - console.log("Implement Saving here!"); + updateCategory(category_guid.toString(), categoryName, categoryColor, selectedType, amount); router.back(); }}/> diff --git a/app/(tabs)/budget/index.tsx b/app/(tabs)/budget/index.tsx index 3f2aa97..9131dc7 100644 --- a/app/(tabs)/budget/index.tsx +++ b/app/(tabs)/budget/index.tsx @@ -38,7 +38,7 @@ export default function Page() { }; const handleCategoryPress = (item: {[column: string]: any;}) => { - router.push({pathname: "./(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) + router.push({pathname: "/(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) } return ( diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index d57614f..8f78e74 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useMemo } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'; import { Calendar } from 'react-native-calendars'; import { FlatList, RefreshControl } from 'react-native-gesture-handler'; @@ -6,9 +6,8 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { ExpenseItem, LoadingSymbol, Plus, TextInputBar, Welcome } from '../../../components'; import useFetch from '../../../hooks/useFetch'; -import { addExpense, executeQuery } from "../../../services/database"; -import { useAuth } from '../../contexts/AuthContext'; import { useRouter } from "expo-router"; +import { addExpense } from "../../../services/database"; import { SimpleDate } from '../../../util/SimpleDate'; import { useTheme } from '../../contexts/ThemeContext'; @@ -22,7 +21,6 @@ type MarkedDates = { } const constructMarkedDates = (data : {[column: string]: any}) => { - console.log("entered") let markedDates: MarkedDates = {}; data.forEach((value: any) => { const dateKey: string = String(value["expense_datetime"]).split(" ")[0] diff --git a/components/common/loadingSymbol.tsx b/components/common/loadingSymbol.tsx index b7e1a03..2769dcb 100644 --- a/components/common/loadingSymbol.tsx +++ b/components/common/loadingSymbol.tsx @@ -1,15 +1,15 @@ import React, { useEffect, useRef } from "react"; -import { StyleSheet, View, Animated, Easing, ActivityIndicator } from "react-native"; +import { ActivityIndicator, Animated, Easing, StyleSheet, View } from "react-native"; +import { useTheme } from "../../app/contexts/ThemeContext"; const LoadingSymbol = () => { - const color = ["blue", "red", "purple", "green", "yellow", "orange"]; - const random = Math.floor(Math.random() * color.length); + const {colors} = useTheme(); const spinValue = useRef(new Animated.Value(0)).current; const spin = spinValue.interpolate({ inputRange: [0, 1], - outputRange: ["0deg", "360deg"], + outputRange: [0, 360], }); useEffect(() => { @@ -25,7 +25,6 @@ const LoadingSymbol = () => { const styles = StyleSheet.create({ container: { - backgroundColor: color[random], width: "100%", height: "100%", position: "absolute", @@ -35,13 +34,14 @@ const LoadingSymbol = () => { loader: { width: 100, height: 100, - transform: [{ rotate: spin }], + transform: [{ rotate: spin + "deg"}], + zIndex: 999, }, }); return ( - + ); }; diff --git a/services/database.ts b/services/database.ts index cbee2b1..7fe3921 100644 --- a/services/database.ts +++ b/services/database.ts @@ -24,6 +24,18 @@ export const initDatabase = async () => { } }; +export const updateCategory = async (guid: string, name: string, color: string, type: string, allocated_amount: number) => { + + try { + await db.transactionAsync(async (tx) => { + await tx.executeSqlAsync("UPDATE category SET name = ?, color = ?, type = ?, allocated_amount = ? WHERE guid = ?", [name, color, type, allocated_amount, guid]); + }); + } catch (error) { + console.log("Error updating category: ", error); + throw error; + } +}; + export const addCategory = async (name: string, color: string, type: string, allocated_amount: number) => { //needs user input validation for type and color (should be validated by this function) From d7bb318c24b482d2818bab7e2963e75c1f0f2f6e Mon Sep 17 00:00:00 2001 From: Thomas Schleicher Date: Fri, 5 Jan 2024 19:56:55 +0000 Subject: [PATCH 02/24] Resolve "implement updating a category" --- app/(tabs)/budget/category.tsx | 2 +- app/(tabs)/budget/editCategory.tsx | 3 ++- app/(tabs)/budget/index.tsx | 2 +- app/(tabs)/home/index.tsx | 6 ++---- components/common/loadingSymbol.tsx | 14 +++++++------- services/database.ts | 12 ++++++++++++ 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/app/(tabs)/budget/category.tsx b/app/(tabs)/budget/category.tsx index a58d02b..ca96d05 100644 --- a/app/(tabs)/budget/category.tsx +++ b/app/(tabs)/budget/category.tsx @@ -14,7 +14,7 @@ export default function Page() { const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.guid = ? ORDER BY expense_datetime desc;", args: [category_guid]}); const handleEditCategory = () => { - router.push({pathname: "./(tabs)/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); + router.push({pathname: "/(tabs)/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); } const handleBackButton = () => { diff --git a/app/(tabs)/budget/editCategory.tsx b/app/(tabs)/budget/editCategory.tsx index b3b8ec7..d45958d 100644 --- a/app/(tabs)/budget/editCategory.tsx +++ b/app/(tabs)/budget/editCategory.tsx @@ -2,6 +2,7 @@ import { router, useLocalSearchParams } from "expo-router"; import { useState } from "react"; import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; +import { updateCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; const addCategory = () => { @@ -48,7 +49,7 @@ const addCategory = () => { router.back(); }}/> { - console.log("Implement Saving here!"); + updateCategory(category_guid.toString(), categoryName, categoryColor, selectedType, amount); router.back(); }}/> diff --git a/app/(tabs)/budget/index.tsx b/app/(tabs)/budget/index.tsx index 3f2aa97..9131dc7 100644 --- a/app/(tabs)/budget/index.tsx +++ b/app/(tabs)/budget/index.tsx @@ -38,7 +38,7 @@ export default function Page() { }; const handleCategoryPress = (item: {[column: string]: any;}) => { - router.push({pathname: "./(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) + router.push({pathname: "/(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) } return ( diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index d57614f..8f78e74 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, useMemo } from 'react'; +import React, { useMemo, useRef, useState } from 'react'; import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'; import { Calendar } from 'react-native-calendars'; import { FlatList, RefreshControl } from 'react-native-gesture-handler'; @@ -6,9 +6,8 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { ExpenseItem, LoadingSymbol, Plus, TextInputBar, Welcome } from '../../../components'; import useFetch from '../../../hooks/useFetch'; -import { addExpense, executeQuery } from "../../../services/database"; -import { useAuth } from '../../contexts/AuthContext'; import { useRouter } from "expo-router"; +import { addExpense } from "../../../services/database"; import { SimpleDate } from '../../../util/SimpleDate'; import { useTheme } from '../../contexts/ThemeContext'; @@ -22,7 +21,6 @@ type MarkedDates = { } const constructMarkedDates = (data : {[column: string]: any}) => { - console.log("entered") let markedDates: MarkedDates = {}; data.forEach((value: any) => { const dateKey: string = String(value["expense_datetime"]).split(" ")[0] diff --git a/components/common/loadingSymbol.tsx b/components/common/loadingSymbol.tsx index b7e1a03..2769dcb 100644 --- a/components/common/loadingSymbol.tsx +++ b/components/common/loadingSymbol.tsx @@ -1,15 +1,15 @@ import React, { useEffect, useRef } from "react"; -import { StyleSheet, View, Animated, Easing, ActivityIndicator } from "react-native"; +import { ActivityIndicator, Animated, Easing, StyleSheet, View } from "react-native"; +import { useTheme } from "../../app/contexts/ThemeContext"; const LoadingSymbol = () => { - const color = ["blue", "red", "purple", "green", "yellow", "orange"]; - const random = Math.floor(Math.random() * color.length); + const {colors} = useTheme(); const spinValue = useRef(new Animated.Value(0)).current; const spin = spinValue.interpolate({ inputRange: [0, 1], - outputRange: ["0deg", "360deg"], + outputRange: [0, 360], }); useEffect(() => { @@ -25,7 +25,6 @@ const LoadingSymbol = () => { const styles = StyleSheet.create({ container: { - backgroundColor: color[random], width: "100%", height: "100%", position: "absolute", @@ -35,13 +34,14 @@ const LoadingSymbol = () => { loader: { width: 100, height: 100, - transform: [{ rotate: spin }], + transform: [{ rotate: spin + "deg"}], + zIndex: 999, }, }); return ( - + ); }; diff --git a/services/database.ts b/services/database.ts index cbee2b1..7fe3921 100644 --- a/services/database.ts +++ b/services/database.ts @@ -24,6 +24,18 @@ export const initDatabase = async () => { } }; +export const updateCategory = async (guid: string, name: string, color: string, type: string, allocated_amount: number) => { + + try { + await db.transactionAsync(async (tx) => { + await tx.executeSqlAsync("UPDATE category SET name = ?, color = ?, type = ?, allocated_amount = ? WHERE guid = ?", [name, color, type, allocated_amount, guid]); + }); + } catch (error) { + console.log("Error updating category: ", error); + throw error; + } +}; + export const addCategory = async (name: string, color: string, type: string, allocated_amount: number) => { //needs user input validation for type and color (should be validated by this function) From 7dd17798c44f33a53a89971589d148f1d34bab41 Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Fri, 5 Jan 2024 21:33:13 +0100 Subject: [PATCH 03/24] fix last issues --- app/(tabs)/budget/addCategory.tsx | 9 +++++---- app/(tabs)/budget/category.tsx | 10 +++++++--- app/(tabs)/budget/editCategory.tsx | 2 +- app/(tabs)/budget/index.tsx | 2 +- app/(tabs)/home/expense/[expense].tsx | 5 ++++- app/(tabs)/home/expense/_layout.tsx | 3 ++- components/common/loadingSymbol.tsx | 6 ------ 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/(tabs)/budget/addCategory.tsx b/app/(tabs)/budget/addCategory.tsx index 984d98e..bdc1a86 100644 --- a/app/(tabs)/budget/addCategory.tsx +++ b/app/(tabs)/budget/addCategory.tsx @@ -1,6 +1,7 @@ import { router, useLocalSearchParams } from "expo-router"; import { useState } from "react"; -import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; +import { StyleSheet, Text, TextInput, View } from "react-native"; +import { SafeAreaView } from 'react-native-safe-area-context'; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; import { addCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; @@ -10,18 +11,18 @@ export default function Page() { const parameters = useLocalSearchParams(); - const [categoryName, setCategoryName] = useState("Enter Category Name..."); + const [categoryName, setCategoryName] = useState(""); const [categoryColor, setCategoryColor] = useState('#' + Math.floor(Math.random()*16777215).toString(16)); const [selectedType, setSelectedType] = useState("expense"); const [amount, setAmount] = useState(0); return ( - + Add Category - { + { setCategoryName(newName); }}/> diff --git a/app/(tabs)/budget/category.tsx b/app/(tabs)/budget/category.tsx index ca96d05..3a88f32 100644 --- a/app/(tabs)/budget/category.tsx +++ b/app/(tabs)/budget/category.tsx @@ -1,11 +1,14 @@ import { FontAwesome } from "@expo/vector-icons"; -import { router, useLocalSearchParams } from "expo-router"; -import { FlatList, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { useRouter, useLocalSearchParams } from "expo-router"; +import { FlatList, StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { ExpenseItem, LoadingSymbol, TextInputBar } from "../../../components"; + import useFetch from "../../../hooks/useFetch"; import { useTheme } from "../../contexts/ThemeContext"; +import { SafeAreaView } from "react-native-safe-area-context"; export default function Page() { + const router = useRouter(); const {colors} = useTheme(); const {category_guid} = useLocalSearchParams(); @@ -14,7 +17,7 @@ export default function Page() { const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.guid = ? ORDER BY expense_datetime desc;", args: [category_guid]}); const handleEditCategory = () => { - router.push({pathname: "/(tabs)/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); + router.push({pathname: "/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); } const handleBackButton = () => { @@ -45,6 +48,7 @@ export default function Page() { title={item.expense_name} date={item.expense_datetime} value={item.expense_amount} + onPress={()=>router.push(`/home/expense/${item.guid}`)} />} keyExtractor={item => item.guid} ItemSeparatorComponent={() => { diff --git a/app/(tabs)/budget/editCategory.tsx b/app/(tabs)/budget/editCategory.tsx index d45958d..60705fa 100644 --- a/app/(tabs)/budget/editCategory.tsx +++ b/app/(tabs)/budget/editCategory.tsx @@ -20,7 +20,7 @@ const addCategory = () => { - { + { setCategoryName(newName); }}/> diff --git a/app/(tabs)/budget/index.tsx b/app/(tabs)/budget/index.tsx index 9131dc7..6615367 100644 --- a/app/(tabs)/budget/index.tsx +++ b/app/(tabs)/budget/index.tsx @@ -24,7 +24,7 @@ export default function Page() { }) }, []); - const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM expense e RIGHT JOIN category c ON e.category_guid = c.guid WHERE c.type = ? GROUP BY c.guid", args: selectedPage === "expenses" ? ["expense"] : selectedPage === "savings" ? ["saving"] : []}); + const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM category c LEFT JOIN expense e ON e.category_guid = c.guid WHERE c.type = ? GROUP BY c.guid", args: selectedPage === "expenses" ? ["expense"] : selectedPage === "savings" ? ["saving"] : []}); useEffect(() => { reFetch(); diff --git a/app/(tabs)/home/expense/[expense].tsx b/app/(tabs)/home/expense/[expense].tsx index 99a6e73..53e38ae 100644 --- a/app/(tabs)/home/expense/[expense].tsx +++ b/app/(tabs)/home/expense/[expense].tsx @@ -1,6 +1,6 @@ import { View, Text, Alert, StyleSheet } from 'react-native' import React, { useEffect, useState } from 'react' -import { router, useLocalSearchParams } from 'expo-router' +import { router, useLocalSearchParams, useRouter } from 'expo-router' import useFetch from '../../../../hooks/useFetch'; import { Category, Expense } from '../../../../types/dbItems'; import { CategorySelectorModal, AutoDecimalInput, CategorySelector, TextInputBar, DateSelectorButton, RoundedButton } from '../../../../components'; @@ -12,6 +12,7 @@ import { SIZES } from '../../../../constants/theme'; import { useTheme } from '../../../contexts/ThemeContext'; export default function Page() { + const router = useRouter(); const {colors} = useTheme(); const {expense} = useLocalSearchParams(); const {data, isEmptyResult} = useFetch({sql: "SELECT e.guid as e_guid, e.name as e_name, e.datetime as e_datetime, e.amount as e_amount, c.guid as c_guid, c.name as c_name, c.color as c_color FROM expense e INNER JOIN category c on e.category_guid = c.guid WHERE e.guid = ?", args: [expense]}); @@ -24,6 +25,8 @@ export default function Page() { const [datePickerShown, setDatePickerShown] = useState(false); const [selectedDate, setSelectedDate] = useState(new Date()); + + console.log(router.canGoBack()) useEffect(()=>{ const entry = data[0]; console.log(entry) diff --git a/app/(tabs)/home/expense/_layout.tsx b/app/(tabs)/home/expense/_layout.tsx index 211509f..ef1ee81 100644 --- a/app/(tabs)/home/expense/_layout.tsx +++ b/app/(tabs)/home/expense/_layout.tsx @@ -21,11 +21,12 @@ const _layout = () => { String(Date.now())} diff --git a/components/common/loadingSymbol.tsx b/components/common/loadingSymbol.tsx index 2769dcb..757749c 100644 --- a/components/common/loadingSymbol.tsx +++ b/components/common/loadingSymbol.tsx @@ -7,11 +7,6 @@ const LoadingSymbol = () => { const spinValue = useRef(new Animated.Value(0)).current; - const spin = spinValue.interpolate({ - inputRange: [0, 1], - outputRange: [0, 360], - }); - useEffect(() => { Animated.loop( Animated.timing(spinValue, { @@ -34,7 +29,6 @@ const LoadingSymbol = () => { loader: { width: 100, height: 100, - transform: [{ rotate: spin + "deg"}], zIndex: 999, }, }); From 457b0988834ba17820a55dcfae1fde43fbebb74b Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Fri, 5 Jan 2024 22:44:21 +0100 Subject: [PATCH 04/24] fix: category search input cannot be cleared --- components/home/addItem/CategorySelectorModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/home/addItem/CategorySelectorModal.tsx b/components/home/addItem/CategorySelectorModal.tsx index d758669..26f3a94 100644 --- a/components/home/addItem/CategorySelectorModal.tsx +++ b/components/home/addItem/CategorySelectorModal.tsx @@ -49,7 +49,7 @@ const CategorySelectorModal: React.FC = (props : Cat {selectMulitple ? "Categories" : "Category"} - + item.guid!} From 1beee68bffe6719623cfd07be14051966de4576a Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Sat, 20 Jan 2024 11:54:25 +0100 Subject: [PATCH 05/24] Major refactoring: navigation does not break anymore. the user can now navigate between the tabs without loosing context --- app/(tabs)/{budget => (budget)}/_layout.tsx | 0 .../{budget => (budget)}/addCategory.tsx | 0 app/(tabs)/{budget => (budget)}/category.tsx | 19 ++++++++--- .../{budget => (budget)}/editCategory.tsx | 0 app/(tabs)/{budget => (budget)}/index.tsx | 13 +++++--- app/(tabs)/{home => (home)}/_layout.tsx | 3 -- app/(tabs)/{home => (home)}/index.tsx | 32 ++++++++++++++----- app/(tabs)/{home => (home)}/userSettings.tsx | 0 app/(tabs)/{stats => (stats)}/index.tsx | 0 app/(tabs)/_layout.tsx | 11 ++----- app/_layout.tsx | 31 ++++++++++++++---- app/{(tabs)/home => }/expense/[expense].tsx | 21 ++++++------ app/{(tabs)/home => }/expense/_layout.tsx | 2 +- app/{(tabs)/home => }/expense/new.tsx | 17 +++++----- app/index.tsx | 2 +- components/budget/budgetHeader.tsx | 3 +- components/common/CustomCard.tsx | 1 - .../addItem => expense}/CategorySelector.tsx | 6 ++-- .../CategorySelectorModal.tsx | 12 +++---- .../DateSelectorButton.tsx | 6 ++-- components/index.tsx | 6 ++-- hooks/useFetch.ts | 29 ++++++++++++++--- services/database.ts | 3 +- 23 files changed, 137 insertions(+), 80 deletions(-) rename app/(tabs)/{budget => (budget)}/_layout.tsx (100%) rename app/(tabs)/{budget => (budget)}/addCategory.tsx (100%) rename app/(tabs)/{budget => (budget)}/category.tsx (84%) rename app/(tabs)/{budget => (budget)}/editCategory.tsx (100%) rename app/(tabs)/{budget => (budget)}/index.tsx (88%) rename app/(tabs)/{home => (home)}/_layout.tsx (90%) rename app/(tabs)/{home => (home)}/index.tsx (83%) rename app/(tabs)/{home => (home)}/userSettings.tsx (100%) rename app/(tabs)/{stats => (stats)}/index.tsx (100%) rename app/{(tabs)/home => }/expense/[expense].tsx (91%) rename app/{(tabs)/home => }/expense/_layout.tsx (93%) rename app/{(tabs)/home => }/expense/new.tsx (89%) rename components/{home/addItem => expense}/CategorySelector.tsx (89%) rename components/{home/addItem => expense}/CategorySelectorModal.tsx (89%) rename components/{home/addItem => expense}/DateSelectorButton.tsx (87%) diff --git a/app/(tabs)/budget/_layout.tsx b/app/(tabs)/(budget)/_layout.tsx similarity index 100% rename from app/(tabs)/budget/_layout.tsx rename to app/(tabs)/(budget)/_layout.tsx diff --git a/app/(tabs)/budget/addCategory.tsx b/app/(tabs)/(budget)/addCategory.tsx similarity index 100% rename from app/(tabs)/budget/addCategory.tsx rename to app/(tabs)/(budget)/addCategory.tsx diff --git a/app/(tabs)/budget/category.tsx b/app/(tabs)/(budget)/category.tsx similarity index 84% rename from app/(tabs)/budget/category.tsx rename to app/(tabs)/(budget)/category.tsx index 3a88f32..c2e3ea9 100644 --- a/app/(tabs)/budget/category.tsx +++ b/app/(tabs)/(budget)/category.tsx @@ -6,9 +6,12 @@ import { ExpenseItem, LoadingSymbol, TextInputBar } from "../../../components"; import useFetch from "../../../hooks/useFetch"; import { useTheme } from "../../contexts/ThemeContext"; import { SafeAreaView } from "react-native-safe-area-context"; +import { useNavigation } from "expo-router/src/useNavigation"; +import { useEffect } from "react"; export default function Page() { const router = useRouter(); + const navigation = useNavigation(); const {colors} = useTheme(); const {category_guid} = useLocalSearchParams(); @@ -17,13 +20,21 @@ export default function Page() { const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.guid = ? ORDER BY expense_datetime desc;", args: [category_guid]}); const handleEditCategory = () => { - router.push({pathname: "/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); + router.push({pathname: "/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}}); } const handleBackButton = () => { router.back(); } + useEffect(() => { + console.log("useEffect called") + const unsubscribe = navigation.addListener("focus", () => { + reFetch(); + }) + return unsubscribe; + }, [navigation]) + return ( @@ -40,7 +51,7 @@ export default function Page() { {isLoading ? () : ( - router.push(`/home/expense/${item.guid}`)} + onPress={()=>router.push(`/expense/${item.expense_guid}`)} />} - keyExtractor={item => item.guid} + keyExtractor={item => item.expense_guid} ItemSeparatorComponent={() => { return (); }} diff --git a/app/(tabs)/budget/editCategory.tsx b/app/(tabs)/(budget)/editCategory.tsx similarity index 100% rename from app/(tabs)/budget/editCategory.tsx rename to app/(tabs)/(budget)/editCategory.tsx diff --git a/app/(tabs)/budget/index.tsx b/app/(tabs)/(budget)/index.tsx similarity index 88% rename from app/(tabs)/budget/index.tsx rename to app/(tabs)/(budget)/index.tsx index 6615367..b03fc12 100644 --- a/app/(tabs)/budget/index.tsx +++ b/app/(tabs)/(budget)/index.tsx @@ -1,5 +1,5 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; -import { router } from 'expo-router'; +import { router, useNavigation } from 'expo-router'; import { useEffect, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; @@ -11,7 +11,7 @@ import { useTheme } from '../../contexts/ThemeContext'; export default function Page() { const {colors} = useTheme() const containerColor = colors.containerColor; - + const navigation = useNavigation(); const [selectedPage, setSelectedPage] = useState("noPageLoaded"); useEffect(() => { @@ -27,9 +27,10 @@ export default function Page() { const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM category c LEFT JOIN expense e ON e.category_guid = c.guid WHERE c.type = ? GROUP BY c.guid", args: selectedPage === "expenses" ? ["expense"] : selectedPage === "savings" ? ["saving"] : []}); useEffect(() => { - reFetch(); + reFetch() }, [selectedPage]); + const handlePageSelection = (page: string) => { if(page !== selectedPage) { setSelectedPage(page); @@ -38,15 +39,16 @@ export default function Page() { }; const handleCategoryPress = (item: {[column: string]: any;}) => { - router.push({pathname: "/(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) + router.push({pathname: "/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) } return ( + { - router.push({pathname: '/(tabs)/budget/addCategory'}); + router.push({pathname: '/addCategory'}); }}/> {isLoading ? () : ( @@ -67,6 +69,7 @@ export default function Page() { }} /> )} + ); } diff --git a/app/(tabs)/home/_layout.tsx b/app/(tabs)/(home)/_layout.tsx similarity index 90% rename from app/(tabs)/home/_layout.tsx rename to app/(tabs)/(home)/_layout.tsx index ba7964c..8e16222 100644 --- a/app/(tabs)/home/_layout.tsx +++ b/app/(tabs)/(home)/_layout.tsx @@ -24,9 +24,6 @@ export default function _Layout() { title: "test", headerShown: false, }}/> - { export default function Page() { const { colors, theme } = useTheme() - - + const navigation = useNavigation(); const router = useRouter(); const [plusShow, setPlusShow] = useState(true); const prevOffset = useRef(0); @@ -62,17 +62,24 @@ export default function Page() { } const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, c.guid AS category_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color, c.type AS category_type FROM expense e JOIN category c ON e.category_guid = c.guid ORDER BY expense_datetime desc;", args: []}); - + const expenseDates = useMemo(()=> constructMarkedDates(data) , [data]) + useEffect(() => { + const unsubscribe = navigation.addListener('focus', () => { + console.log("focus event triggered") + reFetch(); + }); + return unsubscribe; + }, [navigation]); return ( {plusShow && { - router.push("/(tabs)/home/expense/new"); + router.push("/expense/new"); // executeQuery({sql: "SELECT guid FROM category", args: []}).then((result) => { // if("rows" in result[0]) { @@ -89,7 +96,7 @@ export default function Page() { data={data} ListHeaderComponent={ <> - {router.push("/home/userSettings")}}/> + {router.push("/userSettings")}}/> } - renderItem = {({item}) => {router.push(`/(tabs)/home/expense/${guid}`)}}/>} - keyExtractor={item => item.expense_guid} + renderItem = {({item}) => + {router.push(`/expense/${guid}`)}} + />} + keyExtractor={item => item.expense_guid } ItemSeparatorComponent={() => { return (); }} diff --git a/app/(tabs)/home/userSettings.tsx b/app/(tabs)/(home)/userSettings.tsx similarity index 100% rename from app/(tabs)/home/userSettings.tsx rename to app/(tabs)/(home)/userSettings.tsx diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/(stats)/index.tsx similarity index 100% rename from app/(tabs)/stats/index.tsx rename to app/(tabs)/(stats)/index.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 79ff582..4c74a39 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -35,30 +35,25 @@ export default function Layout() { return ( - ( ), - unmountOnBlur: true, - href: "(tabs)/budget" } }/> - ( ), - unmountOnBlur: true, - href: "(tabs)/home/" } }/> - ( ), - unmountOnBlur: true, } }/> diff --git a/app/_layout.tsx b/app/_layout.tsx index f34a4ea..e99a541 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,20 +1,37 @@ -import { Slot } from 'expo-router'; +import { SplashScreen, Stack } from 'expo-router'; import React, { useEffect } from 'react'; import { addCategory, initDatabase } from '../services/database'; import { AuthProvider } from './contexts/AuthContext'; import { ThemeProvider } from './contexts/ThemeContext'; +import { useTheme } from './contexts/ThemeContext'; + export default function _layout() { - + const {colors} = useTheme(); useEffect(() => { initDatabase(); + }, []); + console.log(colors.backgroundColor) return ( - - - - - + + + + + + + + ) } \ No newline at end of file diff --git a/app/(tabs)/home/expense/[expense].tsx b/app/expense/[expense].tsx similarity index 91% rename from app/(tabs)/home/expense/[expense].tsx rename to app/expense/[expense].tsx index 53e38ae..1eb15ff 100644 --- a/app/(tabs)/home/expense/[expense].tsx +++ b/app/expense/[expense].tsx @@ -1,15 +1,15 @@ import { View, Text, Alert, StyleSheet } from 'react-native' import React, { useEffect, useState } from 'react' import { router, useLocalSearchParams, useRouter } from 'expo-router' -import useFetch from '../../../../hooks/useFetch'; -import { Category, Expense } from '../../../../types/dbItems'; -import { CategorySelectorModal, AutoDecimalInput, CategorySelector, TextInputBar, DateSelectorButton, RoundedButton } from '../../../../components'; -import colors from '../../../../constants/colors'; -import { addExpense, deleteExpense, executeQuery, updateExpense } from '../../../../services/database'; -import { SimpleDate } from '../../../../util/SimpleDate'; +import useFetch from '../../hooks/useFetch'; +import { Category, Expense } from '../../types/dbItems'; +import { CategorySelectorModal, AutoDecimalInput, CategorySelector, TextInputBar, DateSelectorButton, RoundedButton } from '../../components'; +import colors from '../../constants/colors'; +import { addExpense, deleteExpense, executeQuery, updateExpense } from '../../services/database'; +import { SimpleDate } from '../../util/SimpleDate'; import DateTimePicker from '@react-native-community/datetimepicker'; -import { SIZES } from '../../../../constants/theme'; -import { useTheme } from '../../../contexts/ThemeContext'; +import { SIZES } from '../../constants/theme'; +import { useTheme } from '../contexts/ThemeContext'; export default function Page() { const router = useRouter(); @@ -25,8 +25,6 @@ export default function Page() { const [datePickerShown, setDatePickerShown] = useState(false); const [selectedDate, setSelectedDate] = useState(new Date()); - - console.log(router.canGoBack()) useEffect(()=>{ const entry = data[0]; console.log(entry) @@ -69,8 +67,7 @@ export default function Page() { await updateExpense(selectedExpense!.guid!, expenseName, selectedCategory!.guid!, new SimpleDate(selectedDate).format("YYYY-MM-DD"), Number(formatedValue)) } if(validateInput()){ - insert(); - router.back(); + insert().then( () => router.back()) }else { Alert.alert("Invalid input", "One of the Props is not properly defined") } diff --git a/app/(tabs)/home/expense/_layout.tsx b/app/expense/_layout.tsx similarity index 93% rename from app/(tabs)/home/expense/_layout.tsx rename to app/expense/_layout.tsx index ef1ee81..f867c8a 100644 --- a/app/(tabs)/home/expense/_layout.tsx +++ b/app/expense/_layout.tsx @@ -1,7 +1,7 @@ import { StyleSheet, Text, View } from 'react-native' import { Stack } from 'expo-router' import React from 'react' -import { useTheme } from '../../../contexts/ThemeContext' +import { useTheme } from '../contexts/ThemeContext' const _layout = () => { const {colors} = useTheme(); diff --git a/app/(tabs)/home/expense/new.tsx b/app/expense/new.tsx similarity index 89% rename from app/(tabs)/home/expense/new.tsx rename to app/expense/new.tsx index f2525c2..7619489 100644 --- a/app/(tabs)/home/expense/new.tsx +++ b/app/expense/new.tsx @@ -1,12 +1,12 @@ import { View, Text, StyleSheet, Alert } from 'react-native' import React, { useRef, useState } from 'react' -import { SIZES } from '../../../../constants/theme' -import { useTheme } from '../../../contexts/ThemeContext' -import { AutoDecimalInput, CategorySelector, CategorySelectorModal, DateSelectorButton, RoundedButton, TextInputBar } from '../../../../components' -import { Category } from '../../../../types/dbItems' +import { SIZES } from '../../constants/theme' +import { useTheme } from '../contexts/ThemeContext' +import { AutoDecimalInput, CategorySelector, CategorySelectorModal, DateSelectorButton, RoundedButton, TextInputBar } from '../../components' +import { Category } from '../../types/dbItems' import DateTimePicker from '@react-native-community/datetimepicker'; -import { addExpense } from '../../../../services/database' -import { SimpleDate } from '../../../../util/SimpleDate' +import { addExpense } from '../../services/database' +import { SimpleDate } from '../../util/SimpleDate' import { useRouter } from 'expo-router' export default function AddItem() { @@ -41,8 +41,9 @@ export default function AddItem() { await addExpense(expenseName, selectedCategory?.guid!, new SimpleDate(selectedDate).format("YYYY-MM-DD"), Number(formatedValue)) } if(validateInput()){ - insert(); - router.back(); + insert().then(() => { + router.back(); + }) }else { Alert.alert("Invalid input", "One of the Props is not properly defined") } diff --git a/app/index.tsx b/app/index.tsx index 265d68b..6b489fe 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -7,7 +7,7 @@ export default function index() { const {authState} = useAuth() return ( - + ) } \ No newline at end of file diff --git a/components/budget/budgetHeader.tsx b/components/budget/budgetHeader.tsx index bca2e9b..54df035 100644 --- a/components/budget/budgetHeader.tsx +++ b/components/budget/budgetHeader.tsx @@ -77,12 +77,11 @@ const styles = StyleSheet.create({ containerStyle: { flexDirection: "row", justifyContent: "space-evenly", - marginHorizontal: 20, marginBottom: 20, + marginHorizontal: 10, marginTop: 10, }, searchBarStyle: { marginBottom: 20, - marginHorizontal: 10, } }); \ No newline at end of file diff --git a/components/common/CustomCard.tsx b/components/common/CustomCard.tsx index a866d8c..87aae81 100644 --- a/components/common/CustomCard.tsx +++ b/components/common/CustomCard.tsx @@ -40,7 +40,6 @@ export default function CustomCard(props : ViewProps) { const styles = StyleSheet.create({ container:{ borderRadius: 20, - marginHorizontal: 10, }, boxShadow: {}, }) \ No newline at end of file diff --git a/components/home/addItem/CategorySelector.tsx b/components/expense/CategorySelector.tsx similarity index 89% rename from components/home/addItem/CategorySelector.tsx rename to components/expense/CategorySelector.tsx index 27d5cfe..59e5edf 100644 --- a/components/home/addItem/CategorySelector.tsx +++ b/components/expense/CategorySelector.tsx @@ -1,8 +1,8 @@ import { StyleSheet, Text, View, TouchableOpacity } from 'react-native' import React, { useState } from 'react' -import { useTheme } from '../../../app/contexts/ThemeContext' -import { SIZES } from '../../../constants/theme'; -import { Category } from '../../../types/dbItems'; +import { useTheme } from '../../app/contexts/ThemeContext' +import { SIZES } from '../../constants/theme'; +import { Category } from '../../types/dbItems'; import CategorySelectorModal from './CategorySelectorModal'; interface CategorySelectorProps { diff --git a/components/home/addItem/CategorySelectorModal.tsx b/components/expense/CategorySelectorModal.tsx similarity index 89% rename from components/home/addItem/CategorySelectorModal.tsx rename to components/expense/CategorySelectorModal.tsx index 26f3a94..56ba20f 100644 --- a/components/home/addItem/CategorySelectorModal.tsx +++ b/components/expense/CategorySelectorModal.tsx @@ -1,11 +1,11 @@ import { Modal, NativeSyntheticEvent, StyleSheet, Text, View, FlatList } from 'react-native' import React, { useEffect, useMemo, useState } from 'react' -import { Category } from '../../../types/dbItems'; -import { useTheme } from '../../../app/contexts/ThemeContext'; -import CategoryListItem from '../../common/CategoryListItem'; -import { SIZES } from '../../../constants/theme'; -import useFetch from '../../../hooks/useFetch'; -import TextInputBar from '../../common/TextInputBar'; +import { Category } from '../../types/dbItems'; +import { useTheme } from '../../app/contexts/ThemeContext'; +import CategoryListItem from '../common/CategoryListItem'; +import { SIZES } from '../../constants/theme'; +import useFetch from '../../hooks/useFetch'; +import TextInputBar from '../common/TextInputBar'; interface CategorySelectorModalProps{ diff --git a/components/home/addItem/DateSelectorButton.tsx b/components/expense/DateSelectorButton.tsx similarity index 87% rename from components/home/addItem/DateSelectorButton.tsx rename to components/expense/DateSelectorButton.tsx index 4f07297..ec6916d 100644 --- a/components/home/addItem/DateSelectorButton.tsx +++ b/components/expense/DateSelectorButton.tsx @@ -1,8 +1,8 @@ import { View, Text, StyleSheet, TouchableOpacity, TouchableOpacityProps } from 'react-native' import React, { useState } from 'react' -import { useTheme } from '../../../app/contexts/ThemeContext'; -import { SIZES } from '../../../constants/theme'; -import { SimpleDate } from '../../../util/SimpleDate'; +import { useTheme } from '../../app/contexts/ThemeContext'; +import { SIZES } from '../../constants/theme'; +import { SimpleDate } from '../../util/SimpleDate'; import { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; interface DateSelectorProps extends ViewProps { diff --git a/components/index.tsx b/components/index.tsx index b20c5fd..29167eb 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -4,9 +4,9 @@ import Welcome from "./home/Welcome" import ExpenseItem from "./home/expenseItem" //home/addItem -import CategorySelector from "./home/addItem/CategorySelector" -import CategorySelectorModal from "./home/addItem/CategorySelectorModal" -import DateSelectorButton from "./home/addItem/DateSelectorButton" +import CategorySelector from "./expense/CategorySelector" +import CategorySelectorModal from "./expense/CategorySelectorModal" +import DateSelectorButton from "./expense/DateSelectorButton" //common import CustomCard from "./common/CustomCard" diff --git a/hooks/useFetch.ts b/hooks/useFetch.ts index cec238d..e089750 100644 --- a/hooks/useFetch.ts +++ b/hooks/useFetch.ts @@ -4,11 +4,31 @@ import { executeQuery } from "../services/database"; const useFetch = (query: Query) => { - const [isLoading, setIsLoading] = useState(false); - const [data, setData] = useState<{[column: string]: any;}[]>([]); - const [isEmptyResult, setIsEmptyResult] = useState(undefined); + const [fetchState, setFetchState] = useState<{ + data: {[column: string]: any;}[]; + isLoading: boolean; + isEmptyResult: boolean | undefined; + }>({ + data: [], + isLoading: false, + isEmptyResult: undefined + }); + + const setIsLoading = (isLoading: boolean) => { + setFetchState((prevState) => ( {...prevState, isLoading} )); + } + + const setData = (data: {[column: string]: any;}[]) => { + setFetchState((prevState) => ( {...prevState, data} )); + } + + const setIsEmptyResult = (isEmptyResult: boolean) => { + setFetchState((prevState) => ( {...prevState, isEmptyResult} )); + } + const reFetch = () => { + console.log("refetch called") setIsLoading(true); executeQuery(query).then((result) => { if("rows" in result[0]) { @@ -16,6 +36,7 @@ const useFetch = (query: Query) => { if(result[0]["rows"].length == 0){ setIsEmptyResult(true); } + console.log("len", result[0]["rows"].length) } }).catch((error: any) => { console.error("Fetching data from database has failed: ", error); @@ -28,7 +49,7 @@ const useFetch = (query: Query) => { reFetch(); }, []) - return {data, isLoading, reFetch, isEmptyResult}; + return {...fetchState, reFetch}; } export default useFetch; \ No newline at end of file diff --git a/services/database.ts b/services/database.ts index 7fe3921..ec5f656 100644 --- a/services/database.ts +++ b/services/database.ts @@ -57,7 +57,7 @@ export const addCategory = async (name: string, color: string, type: string, all export const updateExpense = async (guid: string, name: string, category_guid: string, datetime: string, amount: number) => { //needs user input validation for type and color (should be validated by this function) - + console.log("update expense called") try { await db.transactionAsync(async (tx) => { await tx.executeSqlAsync("UPDATE expense SET name = ?, category_guid = ?, datetime = ?, amount = ? WHERE guid = ?", [name, category_guid, datetime, amount, guid] @@ -67,6 +67,7 @@ export const updateExpense = async (guid: string, name: string, category_guid: s console.log("Error updating expense: ", error); throw error; } + console.log("update expense finished") }; export const addExpense = async (name: string, category_guid: string, datetime: string, amount: number) => { From bc3d243ffcfb2c4752b38aeee0719d41cecdf9df Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Sat, 20 Jan 2024 17:01:08 +0100 Subject: [PATCH 06/24] feat: filter functionality on Home screen Long press on any day to remove filter --- app/(tabs)/(home)/index.tsx | 138 +++++++++++++++++++++-------- app/expense/new.tsx | 2 +- components/common/TextInputBar.tsx | 4 +- 3 files changed, 107 insertions(+), 37 deletions(-) diff --git a/app/(tabs)/(home)/index.tsx b/app/(tabs)/(home)/index.tsx index 72bbe6c..84f0c97 100644 --- a/app/(tabs)/(home)/index.tsx +++ b/app/(tabs)/(home)/index.tsx @@ -11,16 +11,30 @@ import { addExpense } from "../../../services/database"; import { SimpleDate } from '../../../util/SimpleDate'; import { useTheme } from '../../contexts/ThemeContext'; import { useNavigation } from 'expo-router'; +import { Category, Expense } from '../../../types/dbItems'; interface MarkingProps { dots?:{color:string, selectedColor?:string, key?:string}[]; + selected?: boolean; + selectedColor?: string; } type MarkedDates = { [key: string]: MarkingProps; } +interface ExpenseEntry extends Expense { + category_name?: string; + color?: string; +} + +type Filters = { + search?: string; + month?: string; + day?: string; +} + const constructMarkedDates = (data : {[column: string]: any}) => { let markedDates: MarkedDates = {}; data.forEach((value: any) => { @@ -41,7 +55,8 @@ export default function Page() { const router = useRouter(); const [plusShow, setPlusShow] = useState(true); const prevOffset = useRef(0); - + + const [filter, setFilter] = useState({}) const profile = require("../../../assets/images/profile.jpg") @@ -53,20 +68,55 @@ export default function Page() { setPlusShow(isScrollingUp || isTop) } - const newExpense = async (title: string, category_guid: string, date: string, amount: number) => { - try { - await addExpense(title, category_guid, date, amount); - } catch (error: any) { - console.error("Adding new expense has failed: ", error); - } - } - const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, c.guid AS category_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color, c.type AS category_type FROM expense e JOIN category c ON e.category_guid = c.guid ORDER BY expense_datetime desc;", args: []}); - const expenseDates = useMemo(()=> - constructMarkedDates(data) - , [data]) - + const expenses = useMemo( + () => { + console.log("expenses updated") + return data.map((elem) => { + return { + guid: elem["expense_guid"], + name: elem["expense_name"], + amount: elem["expense_amount"], + dateTime: elem["expense_datetime"], + category_name: elem["category_name"], + color: elem["category_color"]} + }) + }, [data]) + + const filteredExpenses = useMemo( + () => { + console.log("filter called") + return expenses.filter((elem) => { + if(filter.month && filter.month.substring(5, 7) !== elem.dateTime?.substring(5, 7)){ + return false; + } + if(filter.day && filter.day.substring(8, 10) !== elem.dateTime?.substring(8, 10)){ + return false; + } + if(filter.search && !elem.name?.toLowerCase().includes(filter.search.toLowerCase())){ + return false; + } + return true; + }) + }, [expenses, filter] + ) + + const expenseDates = useMemo( + () => { + let markedDates = constructMarkedDates(data) + if(filter.day) { + const dateKey: string = String(filter.day).split(" ")[0] + if(markedDates[dateKey] === undefined){ + markedDates[dateKey] = {} + } + markedDates[dateKey].selected = true; + markedDates[dateKey].selectedColor = colors.accentColor; + } + return markedDates; + } + , [data, filter.day]) + useEffect(() => { const unsubscribe = navigation.addListener('focus', () => { console.log("focus event triggered") @@ -75,7 +125,17 @@ export default function Page() { return unsubscribe; }, [navigation]); + const hanldeDaySelect = (date: string | undefined) => { + setFilter({...filter, day: date}); + } + const handleMonthSelect = (date: string | undefined) => { + setFilter({...filter, month: date, day: undefined}); + } + const handleSearch = (text: string) => { + setFilter({...filter, search: text}); + } + console.log("rendered") return ( {plusShow && { @@ -90,41 +150,49 @@ export default function Page() { // }) }}/>} - {isLoading && } + {/* {isLoading && } */} {router.push("/userSettings")}}/> - + }} + markingType='multi-dot' + markedDates={expenseDates} + onDayPress={(date) => { + hanldeDaySelect(date.dateString) + }} + onDayLongPress={(date) => { + hanldeDaySelect(undefined) + }} + onMonthChange={(date) => { + handleMonthSelect(date.dateString) + }} + /> - - + handleSearch(text)} style={{marginBottom: 20}}> } renderItem = {({item}) => {router.push(`/expense/${guid}`)}} />} - keyExtractor={item => item.expense_guid } + keyExtractor={item => item.guid!} ItemSeparatorComponent={() => { return (); }} diff --git a/app/expense/new.tsx b/app/expense/new.tsx index 7619489..007258e 100644 --- a/app/expense/new.tsx +++ b/app/expense/new.tsx @@ -55,7 +55,7 @@ export default function AddItem() { {setSelecorModalVisible(false)}} onCategoryTap={handleCategorySelect}> {setSelecorModalVisible(true)}} selectedCategory={selectedCategory}/> - setExpenseName(text)}/> + setExpenseName(text)}/> {setDatePickerShown(true)}}/> {datePickerShown && setIsactive(false)}/> {isActive && - {handleChange("")}}> + { + console.log("cancel") + handleChange("")}}> } From 4ddc76faa4f084337719a4ab2eed57f1eb3827dd Mon Sep 17 00:00:00 2001 From: thschleicher Date: Thu, 25 Jan 2024 15:38:50 +0100 Subject: [PATCH 07/24] fixed euro sign on expense item (dont judge me I know this isnt the correct branch) and fixed the async storage so it acctually loads on the info --- app/(tabs)/(budget)/index.tsx | 7 ++++--- components/home/expenseItem.tsx | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/(tabs)/(budget)/index.tsx b/app/(tabs)/(budget)/index.tsx index b03fc12..22f61dd 100644 --- a/app/(tabs)/(budget)/index.tsx +++ b/app/(tabs)/(budget)/index.tsx @@ -1,4 +1,5 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useIsFocused } from '@react-navigation/native'; import { router, useNavigation } from 'expo-router'; import { useEffect, useState } from 'react'; import { StyleSheet, View } from 'react-native'; @@ -13,6 +14,7 @@ export default function Page() { const containerColor = colors.containerColor; const navigation = useNavigation(); const [selectedPage, setSelectedPage] = useState("noPageLoaded"); + const isFocused = useIsFocused(); useEffect(() => { AsyncStorage.getItem("currentBudgetPage").then((page) => { @@ -22,15 +24,14 @@ export default function Page() { }).catch((error) => { console.log("Error fetching previous page from Async Storage:", error); }) - }, []); + }, [isFocused]); const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM category c LEFT JOIN expense e ON e.category_guid = c.guid WHERE c.type = ? GROUP BY c.guid", args: selectedPage === "expenses" ? ["expense"] : selectedPage === "savings" ? ["saving"] : []}); useEffect(() => { - reFetch() + reFetch(); }, [selectedPage]); - const handlePageSelection = (page: string) => { if(page !== selectedPage) { setSelectedPage(page); diff --git a/components/home/expenseItem.tsx b/components/home/expenseItem.tsx index ff6eb2a..fd00c7f 100644 --- a/components/home/expenseItem.tsx +++ b/components/home/expenseItem.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { ColorValue, StyleSheet, Text, View } from 'react-native'; +import { TouchableOpacity } from 'react-native-gesture-handler'; +import { useTheme } from '../../app/contexts/ThemeContext'; import { SIZES } from '../../constants/theme'; import CustomCard from "../common/CustomCard"; -import { useTheme } from '../../app/contexts/ThemeContext'; -import { TouchableOpacity } from 'react-native-gesture-handler'; //export type ExpenseItemProps = {color: ColorValue, category: string, title: string, date: string, value : string} interface ExpenseItemProps { @@ -44,9 +44,10 @@ export default function ExpenseItem(itemProps : ExpenseItemProps) { {itemProps.value} + }} numberOfLines={1}>{itemProps.value + " €"} From 7fecf8c5d95b7fb3e4fb3289a85b5683a886ebce Mon Sep 17 00:00:00 2001 From: thschleicher Date: Thu, 25 Jan 2024 16:06:52 +0100 Subject: [PATCH 08/24] implemented delete button and fixed routing interaction with db --- app/(tabs)/(budget)/editCategory.tsx | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/(tabs)/(budget)/editCategory.tsx b/app/(tabs)/(budget)/editCategory.tsx index 60705fa..369667b 100644 --- a/app/(tabs)/(budget)/editCategory.tsx +++ b/app/(tabs)/(budget)/editCategory.tsx @@ -2,7 +2,8 @@ import { router, useLocalSearchParams } from "expo-router"; import { useState } from "react"; import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; -import { updateCategory } from "../../../services/database"; +import useFetch from "../../../hooks/useFetch"; +import { deleteCategory, deleteExpense, updateCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; const addCategory = () => { @@ -14,6 +15,8 @@ const addCategory = () => { const [selectedType, setSelectedType] = useState(category_type.toString()); const [amount, setAmount] = useState(Number.parseFloat(category_amount.toString())); + const {data} = useFetch({sql: "SELECT * FROM expense WHERE category_guid = ?", args: [category_guid.toString()]}); + return ( Edit Category @@ -44,13 +47,27 @@ const addCategory = () => { }}/> + + { + for (let i = 0; i < data.length;) { + deleteExpense(data[i].guid).then(() => { + i++ + }); + } + deleteCategory(category_guid.toString()).then(() => { + router.push("/(tabs)/(budget)"); + }); + }}/> + + { router.back(); }}/> { - updateCategory(category_guid.toString(), categoryName, categoryColor, selectedType, amount); - router.back(); + updateCategory(category_guid.toString(), categoryName, categoryColor, selectedType, amount).then(() => { + router.back(); + }); }}/> @@ -61,6 +78,9 @@ const addCategory = () => { export default addCategory; const styles = StyleSheet.create({ + deleteButtonView: { + + }, containerStyle: { flex: 1, margin: 10, From c46eec06a588700de1dde29a644a516c590f46fc Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 25 Jan 2024 16:11:04 +0100 Subject: [PATCH 09/24] Fix: refresh --- app/(tabs)/(budget)/addCategory.tsx | 3 +-- app/(tabs)/(budget)/index.tsx | 18 +++++++++++++----- app/(tabs)/_layout.tsx | 2 +- hooks/useFetch.ts | 2 -- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/(tabs)/(budget)/addCategory.tsx b/app/(tabs)/(budget)/addCategory.tsx index bdc1a86..a6c53b6 100644 --- a/app/(tabs)/(budget)/addCategory.tsx +++ b/app/(tabs)/(budget)/addCategory.tsx @@ -51,8 +51,7 @@ export default function Page() { router.back(); }}/> { - addCategory(categoryName, categoryColor, selectedType, amount); - router.back(); + addCategory(categoryName, categoryColor, selectedType, amount).then(() => router.back()); }}/> diff --git a/app/(tabs)/(budget)/index.tsx b/app/(tabs)/(budget)/index.tsx index 22f61dd..0e42958 100644 --- a/app/(tabs)/(budget)/index.tsx +++ b/app/(tabs)/(budget)/index.tsx @@ -1,20 +1,19 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; -import { useIsFocused } from '@react-navigation/native'; import { router, useNavigation } from 'expo-router'; -import { useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-native-safe-area-context'; import { BudgetHeader, CategoryItem, LoadingSymbol, Plus } from '../../../components'; import useFetch from '../../../hooks/useFetch'; import { useTheme } from '../../contexts/ThemeContext'; +import { useFocusEffect } from 'expo-router/src/useFocusEffect'; export default function Page() { const {colors} = useTheme() const containerColor = colors.containerColor; const navigation = useNavigation(); const [selectedPage, setSelectedPage] = useState("noPageLoaded"); - const isFocused = useIsFocused(); useEffect(() => { AsyncStorage.getItem("currentBudgetPage").then((page) => { @@ -24,14 +23,23 @@ export default function Page() { }).catch((error) => { console.log("Error fetching previous page from Async Storage:", error); }) - }, [isFocused]); + }, []); const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM category c LEFT JOIN expense e ON e.category_guid = c.guid WHERE c.type = ? GROUP BY c.guid", args: selectedPage === "expenses" ? ["expense"] : selectedPage === "savings" ? ["saving"] : []}); useEffect(() => { - reFetch(); + reFetch() }, [selectedPage]); + useEffect(() => { + const unsubscribe = navigation.addListener("focus", () => { + reFetch(); + }) + return unsubscribe; + + }, [navigation]) + + const handlePageSelection = (page: string) => { if(page !== selectedPage) { setSelectedPage(page); diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 4c74a39..103e5ac 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -34,7 +34,7 @@ export default function Layout() { } return ( - + { const reFetch = () => { - console.log("refetch called") setIsLoading(true); executeQuery(query).then((result) => { if("rows" in result[0]) { @@ -36,7 +35,6 @@ const useFetch = (query: Query) => { if(result[0]["rows"].length == 0){ setIsEmptyResult(true); } - console.log("len", result[0]["rows"].length) } }).catch((error: any) => { console.error("Fetching data from database has failed: ", error); From 716a41cffd22f5bf3abbe7f5f98b5916247502db Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 25 Jan 2024 16:42:43 +0100 Subject: [PATCH 10/24] feat: filter by name category --- app/(tabs)/(budget)/index.tsx | 18 ++++++++++++++---- components/budget/budgetHeader.tsx | 1 - 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/(tabs)/(budget)/index.tsx b/app/(tabs)/(budget)/index.tsx index 0e42958..e2da8ed 100644 --- a/app/(tabs)/(budget)/index.tsx +++ b/app/(tabs)/(budget)/index.tsx @@ -1,10 +1,10 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { router, useNavigation } from 'expo-router'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { BudgetHeader, CategoryItem, LoadingSymbol, Plus } from '../../../components'; +import { BudgetHeader, CategoryItem, LoadingSymbol, Plus, TextInputBar } from '../../../components'; import useFetch from '../../../hooks/useFetch'; import { useTheme } from '../../contexts/ThemeContext'; import { useFocusEffect } from 'expo-router/src/useFocusEffect'; @@ -14,8 +14,12 @@ export default function Page() { const containerColor = colors.containerColor; const navigation = useNavigation(); const [selectedPage, setSelectedPage] = useState("noPageLoaded"); + const [searchString, setSearchString] = useState(""); + + useEffect(() => { + console.log("initial effect called") AsyncStorage.getItem("currentBudgetPage").then((page) => { if(page === "expenses" || page === "savings") { setSelectedPage(page); @@ -36,7 +40,6 @@ export default function Page() { reFetch(); }) return unsubscribe; - }, [navigation]) @@ -51,10 +54,17 @@ export default function Page() { router.push({pathname: "/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) } + const filteredData = useMemo(() => { + return data.filter((item) => { + return item.category_name.toLowerCase().includes(searchString.toLowerCase()); + }) + }, [data, searchString]); + return ( + { router.push({pathname: '/addCategory'}); @@ -62,7 +72,7 @@ export default function Page() { {isLoading ? () : ( { }} /> - ); } From ab2ef25615380b85e9f2d558cdea3c24c515e3ee Mon Sep 17 00:00:00 2001 From: thschleicher Date: Thu, 25 Jan 2024 17:20:56 +0100 Subject: [PATCH 11/24] first attempt at graph --- app/(tabs)/(stats)/index.tsx | 18 ++-- components/index.tsx | 33 +++---- components/stats/Graph.tsx | 65 ++++++++++++++ components/stats/PieChart.tsx | 1 - package-lock.json | 164 +++++++++++++++++++++++++++++++++- package.json | 5 +- 6 files changed, 250 insertions(+), 36 deletions(-) create mode 100644 components/stats/Graph.tsx delete mode 100644 components/stats/PieChart.tsx diff --git a/app/(tabs)/(stats)/index.tsx b/app/(tabs)/(stats)/index.tsx index cac1e04..d92ebf4 100644 --- a/app/(tabs)/(stats)/index.tsx +++ b/app/(tabs)/(stats)/index.tsx @@ -1,13 +1,7 @@ import React from 'react'; -import { StyleSheet, View, ScrollView } from 'react-native'; -import BudgetOverview from '../../../components/stats/BudgetOverview'; +import { ScrollView, StyleSheet, View } from 'react-native'; +import { Graph } from '../../../components'; import { useTheme } from '../../contexts/ThemeContext'; -import Widget from '../../../components/stats/Widget'; -import CategoryProgressBarList from '../../../components/stats/CategoryProgressBarList'; -import BudgetRemaining from '../../../components/stats/BudgetRemaining'; -import DebugMenu from '../../../services/DebugMenu'; -import SavingsOverview from '../../../components/stats/SavingsOverview'; -import FinancialAdvice from '../../../components/stats/FinancialAdvice'; export default function Page() { const { colors } = useTheme(); @@ -32,7 +26,11 @@ export default function Page() { return ( - + + + + + {/* @@ -46,7 +44,7 @@ export default function Page() { - + */} ); diff --git a/components/index.tsx b/components/index.tsx index 29167eb..a2b1a50 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -9,13 +9,13 @@ import CategorySelectorModal from "./expense/CategorySelectorModal" import DateSelectorButton from "./expense/DateSelectorButton" //common +import AutoDecimalInput from "./common/AutoDecimalInput" import CustomCard from "./common/CustomCard" +import RoundedButton from "./common/RoundedButton" +import TextInputBar from "./common/TextInputBar" import NavigationButton from "./common/button" import LoadingSymbol from "./common/loadingSymbol" import Plus from "./common/plus" -import TextInputBar from "./common/TextInputBar" -import AutoDecimalInput from "./common/AutoDecimalInput" -import RoundedButton from "./common/RoundedButton" //login import BudgetHeader from "./budget/budgetHeader" @@ -26,25 +26,14 @@ import TypeSelectorSwitch from "./budget/typeSelectorSwitch" //login import Input from "./login/input" +//stats +import Graph from "./stats/Graph" + export { - BudgetHeader, - ButtonSetting, - CustomCard, - ExpenseItem, - Input, - LoadingSymbol, - Plus, - TextInputBar, - ToggleSetting, - Welcome, - AutoDecimalInput, - CategorySelector, - CategorySelectorModal, - DateSelectorButton, - RoundedButton, - CategoryItem, - TypeSelectorSwitch, - NavigationButton, - CustomColorPicker + AutoDecimalInput, BudgetHeader, + ButtonSetting, CategoryItem, CategorySelector, + CategorySelectorModal, CustomCard, CustomColorPicker, DateSelectorButton, ExpenseItem, Graph, Input, + LoadingSymbol, NavigationButton, Plus, RoundedButton, TextInputBar, + ToggleSetting, TypeSelectorSwitch, Welcome } diff --git a/components/stats/Graph.tsx b/components/stats/Graph.tsx new file mode 100644 index 0000000..42e083f --- /dev/null +++ b/components/stats/Graph.tsx @@ -0,0 +1,65 @@ +import { View } from "react-native"; +import { PieChart } from "react-native-chart-kit"; + +const Graph = () => { + + // const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.name AS name, c.color AS color, SUM(e.amount) as total FROM category c LEFT JOIN expense e ON e.category_guid = c.guid GROUP BY c.guid", args: []}); + + // useEffect(() => { + // console.log(data); + // }, [data]) + + const data = [ + { + name: "Seoul", + population: 21500000, + color: "rgba(131, 167, 234, 1)", + legendFontColor: "#7F7F7F", + legendFontSize: 15 + }, + { + name: "Toronto", + population: 2800000, + color: "#F00", + legendFontColor: "#7F7F7F", + legendFontSize: 15 + }, + { + name: "Beijing", + population: 527612, + color: "red", + legendFontColor: "#7F7F7F", + legendFontSize: 15 + }, + { + name: "New York", + population: 8538000, + color: "#ffffff", + legendFontColor: "#7F7F7F", + legendFontSize: 15 + }, + { + name: "Moscow", + population: 11920000, + color: "rgb(0, 0, 255)", + legendFontColor: "#7F7F7F", + legendFontSize: 15 + } + ]; + + + return ( + + + + ); +} + +export default Graph; \ No newline at end of file diff --git a/components/stats/PieChart.tsx b/components/stats/PieChart.tsx deleted file mode 100644 index f3fe67e..0000000 --- a/components/stats/PieChart.tsx +++ /dev/null @@ -1 +0,0 @@ -//honestly just fuck graphs \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index aab706f..b72a41b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "react-dom": "18.2.0", "react-native": "0.72.6", "react-native-calendars": "^1.1303.0", + "react-native-chart-kit": "^6.12.0", "react-native-gesture-handler": "~2.12.0", "react-native-reanimated": "^3.3.0", "react-native-safe-area-context": "4.6.3", @@ -7410,6 +7411,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "peer": true + }, "node_modules/bplist-creator": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", @@ -8233,6 +8240,47 @@ "hyphenate-style-name": "^1.0.3" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "peer": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "peer": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "peer": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -8522,6 +8570,32 @@ "node": ">=8" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "peer": true + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -8535,6 +8609,35 @@ "node": ">=12" } }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -8598,7 +8701,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -13484,6 +13586,12 @@ "resolved": "https://registry.npmjs.org/md5hex/-/md5hex-1.0.0.tgz", "integrity": "sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ==" }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "peer": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -14687,6 +14795,18 @@ "node": ">=4" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "peer": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", @@ -15048,6 +15168,14 @@ "node": ">=8" } }, + "node_modules/paths-js": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz", + "integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==", + "engines": { + "node": ">=0.11.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -15248,6 +15376,11 @@ "node": ">=4.0.0" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, "node_modules/postcss": { "version": "8.4.32", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", @@ -15770,6 +15903,21 @@ "moment": "^2.29.4" } }, + "node_modules/react-native-chart-kit": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-6.12.0.tgz", + "integrity": "sha512-nZLGyCFzZ7zmX0KjYeeSV1HKuPhl1wOMlTAqa0JhlyW62qV/1ZPXHgT8o9s8mkFaGxdqbspOeuaa6I9jUQDgnA==", + "dependencies": { + "lodash": "^4.17.13", + "paths-js": "^0.4.10", + "point-in-polygon": "^1.0.1" + }, + "peerDependencies": { + "react": "> 16.7.0", + "react-native": ">= 0.50.0", + "react-native-svg": "> 6.4.1" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.12.1.tgz", @@ -15829,6 +15977,20 @@ "react-native": "*" } }, + "node_modules/react-native-svg": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-14.1.0.tgz", + "integrity": "sha512-HeseElmEk+AXGwFZl3h56s0LtYD9HyGdrpg8yd9QM26X+d7kjETrRQ9vCjtxuT5dCZEIQ5uggU1dQhzasnsCWA==", + "peer": true, + "dependencies": { + "css-select": "^5.1.0", + "css-tree": "^1.1.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-swipe-gestures": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz", diff --git a/package.json b/package.json index 2e627b2..6203ad9 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "@expo/vector-icons": "^13.0.0", "@react-native-async-storage/async-storage": "1.18.2", + "@react-native-community/datetimepicker": "7.2.0", "@react-navigation/native": "^6.0.2", "expo": "~49.0.15", "expo-font": "~11.4.0", @@ -35,14 +36,14 @@ "react-dom": "18.2.0", "react-native": "0.72.6", "react-native-calendars": "^1.1303.0", + "react-native-chart-kit": "^6.12.0", "react-native-gesture-handler": "~2.12.0", "react-native-reanimated": "^3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", "react-native-uuid": "^2.0.1", "react-native-web": "~0.19.6", - "reanimated-color-picker": "^2.4.2", - "@react-native-community/datetimepicker": "7.2.0" + "reanimated-color-picker": "^2.4.2" }, "devDependencies": { "@babel/core": "^7.20.0", From 84dbae49c1a2a99d5e1280e8cdb5c71dfc7ea130 Mon Sep 17 00:00:00 2001 From: thschleicher Date: Thu, 25 Jan 2024 17:33:02 +0100 Subject: [PATCH 12/24] graph working kinda --- components/stats/Graph.tsx | 77 ++++++++++++-------------------------- 1 file changed, 24 insertions(+), 53 deletions(-) diff --git a/components/stats/Graph.tsx b/components/stats/Graph.tsx index 42e083f..33ebd2d 100644 --- a/components/stats/Graph.tsx +++ b/components/stats/Graph.tsx @@ -1,64 +1,35 @@ -import { View } from "react-native"; +import { useEffect } from "react"; import { PieChart } from "react-native-chart-kit"; +import useFetch from "../../hooks/useFetch"; const Graph = () => { - // const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.name AS name, c.color AS color, SUM(e.amount) as total FROM category c LEFT JOIN expense e ON e.category_guid = c.guid GROUP BY c.guid", args: []}); - - // useEffect(() => { - // console.log(data); - // }, [data]) - - const data = [ - { - name: "Seoul", - population: 21500000, - color: "rgba(131, 167, 234, 1)", - legendFontColor: "#7F7F7F", - legendFontSize: 15 - }, - { - name: "Toronto", - population: 2800000, - color: "#F00", - legendFontColor: "#7F7F7F", - legendFontSize: 15 - }, - { - name: "Beijing", - population: 527612, - color: "red", - legendFontColor: "#7F7F7F", - legendFontSize: 15 - }, - { - name: "New York", - population: 8538000, - color: "#ffffff", - legendFontColor: "#7F7F7F", - legendFontSize: 15 - }, - { - name: "Moscow", - population: 11920000, - color: "rgb(0, 0, 255)", - legendFontColor: "#7F7F7F", - legendFontSize: 15 - } - ]; - + const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.name AS name, c.color AS color, SUM(e.amount) as total FROM category c LEFT JOIN expense e ON e.category_guid = c.guid GROUP BY c.guid", args: []}); + useEffect(() => { + console.log(data); + }, [data]) + return ( - `rgba(255, 255, 255, ${opacity})`, + style: { + borderRadius: 16 + } + }} + accessor="total" + backgroundColor="red" + paddingLeft="15" + absolute /> - ); } From 4b06a217c52f07a02409f664b18333628517cfc6 Mon Sep 17 00:00:00 2001 From: thschleicher Date: Thu, 25 Jan 2024 18:39:16 +0100 Subject: [PATCH 13/24] graph works --- app/(tabs)/(stats)/index.tsx | 29 ++++------------------------- components/stats/Graph.tsx | 30 ++++++++++++------------------ 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/app/(tabs)/(stats)/index.tsx b/app/(tabs)/(stats)/index.tsx index d92ebf4..fc96d1a 100644 --- a/app/(tabs)/(stats)/index.tsx +++ b/app/(tabs)/(stats)/index.tsx @@ -1,33 +1,13 @@ import React from 'react'; -import { ScrollView, StyleSheet, View } from 'react-native'; +import { SafeAreaView } from 'react-native'; import { Graph } from '../../../components'; -import { useTheme } from '../../contexts/ThemeContext'; export default function Page() { - const { colors } = useTheme(); - - // Mock data #TODO Database einbinden - // what to do when amount too small? - - const spent = 120.75; - const budget = 696.96; - - - - const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.backgroundColor, - marginTop: 50, - alignItems: 'center', - }, - }); return ( - - + - + {/* @@ -45,7 +25,6 @@ export default function Page() { */} - - + ); } diff --git a/components/stats/Graph.tsx b/components/stats/Graph.tsx index 33ebd2d..6d737d8 100644 --- a/components/stats/Graph.tsx +++ b/components/stats/Graph.tsx @@ -1,35 +1,29 @@ -import { useEffect } from "react"; +import { Dimensions, View } from "react-native"; import { PieChart } from "react-native-chart-kit"; +import { useTheme } from "../../app/contexts/ThemeContext"; import useFetch from "../../hooks/useFetch"; const Graph = () => { + const {colors} = useTheme(); - const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.name AS name, c.color AS color, SUM(e.amount) as total FROM category c LEFT JOIN expense e ON e.category_guid = c.guid GROUP BY c.guid", args: []}); - - useEffect(() => { - console.log(data); - }, [data]) + const {data} = useFetch({sql: "SELECT c.name AS name, c.color AS color, SUM(e.amount) as total FROM category c LEFT JOIN expense e ON e.category_guid = c.guid GROUP BY c.guid", args: []}); + const acctual_data = data.map(item => ({...item, legendFontColor: colors.primaryText, legendFontSize: 14})); return ( + `rgba(255, 255, 255, ${opacity})`, - style: { - borderRadius: 16 - } }} + backgroundColor="transparent" accessor="total" - backgroundColor="red" paddingLeft="15" - absolute + avoidFalseZero={true} /> + ); } From 0e22e63ce945863f0f3f05adb431998d66ce9576 Mon Sep 17 00:00:00 2001 From: thschleicher Date: Thu, 25 Jan 2024 18:49:24 +0100 Subject: [PATCH 14/24] graph works + unmount on blur --- app/(tabs)/(stats)/index.tsx | 30 ++++++++++++------------------ app/(tabs)/_layout.tsx | 5 +++-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/app/(tabs)/(stats)/index.tsx b/app/(tabs)/(stats)/index.tsx index fc96d1a..6a6c0c9 100644 --- a/app/(tabs)/(stats)/index.tsx +++ b/app/(tabs)/(stats)/index.tsx @@ -1,30 +1,24 @@ import React from 'react'; import { SafeAreaView } from 'react-native'; import { Graph } from '../../../components'; +import BudgetOverview from '../../../components/stats/BudgetOverview'; +import BudgetRemaining from '../../../components/stats/BudgetRemaining'; +import SavingsOverview from '../../../components/stats/SavingsOverview'; +import Widget from '../../../components/stats/Widget'; export default function Page() { return ( - + - - - {/* - - - - - - - - - - - - - - */} + + + + + + + ); } diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 103e5ac..62d5907 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -3,9 +3,9 @@ import { StyleSheet } from "react-native"; import { FontAwesome } from "@expo/vector-icons"; import { Redirect } from "expo-router"; -import React, { useEffect } from "react"; -import { useTheme } from "../contexts/ThemeContext"; +import React from "react"; import { useAuth } from "../contexts/AuthContext"; +import { useTheme } from "../contexts/ThemeContext"; export default function Layout() { const {authState} = useAuth() @@ -52,6 +52,7 @@ export default function Layout() { ( ), } From 8149ec234f06eea369bb1586e2d74d2f02be3d08 Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 25 Jan 2024 19:18:15 +0100 Subject: [PATCH 15/24] A few changes: Auto fill amount category edit Empty list component calendar filtering in category screen, expenses can be added directly --- app/(tabs)/(budget)/category.tsx | 5 +- app/(tabs)/(budget)/editCategory.tsx | 8 ++-- app/(tabs)/(budget)/index.tsx | 49 ++++++++++++-------- app/(tabs)/(home)/index.tsx | 11 +++-- app/expense/new.tsx | 22 +++++++-- components/budget/budgetHeader.tsx | 10 ++-- components/common/EmptyListCompenent.tsx | 24 ++++++++++ components/common/TextInputBar.tsx | 2 +- components/expense/CategorySelectorModal.tsx | 4 +- components/index.tsx | 4 +- 10 files changed, 97 insertions(+), 42 deletions(-) create mode 100644 components/common/EmptyListCompenent.tsx diff --git a/app/(tabs)/(budget)/category.tsx b/app/(tabs)/(budget)/category.tsx index c2e3ea9..93d0374 100644 --- a/app/(tabs)/(budget)/category.tsx +++ b/app/(tabs)/(budget)/category.tsx @@ -1,8 +1,7 @@ import { FontAwesome } from "@expo/vector-icons"; import { useRouter, useLocalSearchParams } from "expo-router"; import { FlatList, StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import { ExpenseItem, LoadingSymbol, TextInputBar } from "../../../components"; - +import { ExpenseItem, LoadingSymbol, TextInputBar, EmptyListCompenent, Plus } from "../../../components"; import useFetch from "../../../hooks/useFetch"; import { useTheme } from "../../contexts/ThemeContext"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -37,6 +36,7 @@ export default function Page() { return ( + router.push(`/expense/new?category=${category_guid}`)}/> Back @@ -65,6 +65,7 @@ export default function Page() { ItemSeparatorComponent={() => { return (); }} + ListEmptyComponent={EmptyListCompenent} /> )} diff --git a/app/(tabs)/(budget)/editCategory.tsx b/app/(tabs)/(budget)/editCategory.tsx index 369667b..e0b2706 100644 --- a/app/(tabs)/(budget)/editCategory.tsx +++ b/app/(tabs)/(budget)/editCategory.tsx @@ -1,10 +1,11 @@ import { router, useLocalSearchParams } from "expo-router"; import { useState } from "react"; -import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; -import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; +import { StyleSheet, Text, TextInput, View } from "react-native"; +import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch, Plus } from "../../../components"; import useFetch from "../../../hooks/useFetch"; import { deleteCategory, deleteExpense, updateCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; +import { SafeAreaView } from 'react-native-safe-area-context'; const addCategory = () => { const {colors} = useTheme(); @@ -20,7 +21,6 @@ const addCategory = () => { return ( Edit Category - { @@ -29,7 +29,7 @@ const addCategory = () => { - { + { setAmount(!Number.isNaN(Number.parseFloat(value)) ? Number.parseFloat(value) : 0); }}/> diff --git a/app/(tabs)/(budget)/index.tsx b/app/(tabs)/(budget)/index.tsx index e2da8ed..9b38322 100644 --- a/app/(tabs)/(budget)/index.tsx +++ b/app/(tabs)/(budget)/index.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { BudgetHeader, CategoryItem, LoadingSymbol, Plus, TextInputBar } from '../../../components'; +import { BudgetHeader, CategoryItem, EmptyListCompenent, LoadingSymbol, Plus, TextInputBar } from '../../../components'; import useFetch from '../../../hooks/useFetch'; import { useTheme } from '../../contexts/ThemeContext'; import { useFocusEffect } from 'expo-router/src/useFocusEffect'; @@ -13,41 +13,45 @@ export default function Page() { const {colors} = useTheme() const containerColor = colors.containerColor; const navigation = useNavigation(); - const [selectedPage, setSelectedPage] = useState("noPageLoaded"); + const [selectedPage, setSelectedPage] = useState<"expense"|"saving">("expense"); const [searchString, setSearchString] = useState(""); - useEffect(() => { - console.log("initial effect called") - AsyncStorage.getItem("currentBudgetPage").then((page) => { - if(page === "expenses" || page === "savings") { - setSelectedPage(page); - } - }).catch((error) => { - console.log("Error fetching previous page from Async Storage:", error); - }) - }, []); + // useEffect(() => { + // console.log("initial effect called") + // AsyncStorage.getItem("currentBudgetPage").then((page) => { + // if(page === "expenses" || page === "savings") { + // setSelectedPage(page); + // } + // }).catch((error) => { + // console.log("Error fetching previous page from Async Storage:", error); + // }) + // }, []); - const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM category c LEFT JOIN expense e ON e.category_guid = c.guid WHERE c.type = ? GROUP BY c.guid", args: selectedPage === "expenses" ? ["expense"] : selectedPage === "savings" ? ["saving"] : []}); + const {data, isLoading, reFetch} = useFetch({sql: "SELECT c.guid AS category_guid, c.name AS category_name, c.color AS category_color, c.type AS category_type, SUM(e.amount) as total_expenses, c.allocated_amount as allocated_amount FROM category c LEFT JOIN expense e ON e.category_guid = c.guid GROUP BY c.guid", args: []}); useEffect(() => { reFetch() }, [selectedPage]); + useEffect(() => { const unsubscribe = navigation.addListener("focus", () => { + reFetch(); }) - return unsubscribe; + + const t = () => { + console.log("unsubscribed") + unsubscribe(); + } + return t; }, [navigation]) - const handlePageSelection = (page: string) => { - if(page !== selectedPage) { - setSelectedPage(page); - AsyncStorage.setItem("currentBudgetPage", page); - } + const handlePageSelection = (page: "expense" | "saving") => { + setSelectedPage(page); }; const handleCategoryPress = (item: {[column: string]: any;}) => { @@ -56,10 +60,14 @@ export default function Page() { const filteredData = useMemo(() => { return data.filter((item) => { + if(item.category_type !== selectedPage) { + return false + } return item.category_name.toLowerCase().includes(searchString.toLowerCase()); }) - }, [data, searchString]); + }, [data, searchString, selectedPage]); + console.log(selectedPage) return ( @@ -86,6 +94,7 @@ export default function Page() { ItemSeparatorComponent={() => { return (); }} + ListEmptyComponent={EmptyListCompenent} /> )} diff --git a/app/(tabs)/(home)/index.tsx b/app/(tabs)/(home)/index.tsx index 84f0c97..f76b36a 100644 --- a/app/(tabs)/(home)/index.tsx +++ b/app/(tabs)/(home)/index.tsx @@ -3,7 +3,7 @@ import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react import { Calendar } from 'react-native-calendars'; import { FlatList, RefreshControl } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { ExpenseItem, LoadingSymbol, Plus, TextInputBar, Welcome } from '../../../components'; +import { EmptyListCompenent, ExpenseItem, LoadingSymbol, Plus, TextInputBar, Welcome } from '../../../components'; import useFetch from '../../../hooks/useFetch'; import { useRouter } from "expo-router"; @@ -126,7 +126,10 @@ export default function Page() { }, [navigation]); const hanldeDaySelect = (date: string | undefined) => { - setFilter({...filter, day: date}); + if(filter.day === date) + setFilter({...filter, day: undefined}); + else + setFilter({...filter, day: date}); } const handleMonthSelect = (date: string | undefined) => { setFilter({...filter, month: date, day: undefined}); @@ -171,9 +174,6 @@ export default function Page() { onDayPress={(date) => { hanldeDaySelect(date.dateString) }} - onDayLongPress={(date) => { - hanldeDaySelect(undefined) - }} onMonthChange={(date) => { handleMonthSelect(date.dateString) }} @@ -201,6 +201,7 @@ export default function Page() { } onScroll={handleScroll} scrollEventThrottle={20} + ListEmptyComponent={EmptyListCompenent} /> ); diff --git a/app/expense/new.tsx b/app/expense/new.tsx index 007258e..d0456e7 100644 --- a/app/expense/new.tsx +++ b/app/expense/new.tsx @@ -1,18 +1,20 @@ import { View, Text, StyleSheet, Alert } from 'react-native' -import React, { useRef, useState } from 'react' +import React, { useEffect, useRef, useState } from 'react' import { SIZES } from '../../constants/theme' import { useTheme } from '../contexts/ThemeContext' import { AutoDecimalInput, CategorySelector, CategorySelectorModal, DateSelectorButton, RoundedButton, TextInputBar } from '../../components' import { Category } from '../../types/dbItems' import DateTimePicker from '@react-native-community/datetimepicker'; -import { addExpense } from '../../services/database' +import { addExpense, executeQuery } from '../../services/database' import { SimpleDate } from '../../util/SimpleDate' -import { useRouter } from 'expo-router' +import { useLocalSearchParams, useRouter } from 'expo-router' export default function AddItem() { const {colors} = useTheme(); const router = useRouter(); + const searchParams = useLocalSearchParams() + const [formatedValue, setFormatedValue] = useState(""); const [selectorModalVisible, setSelecorModalVisible] = useState(false); const [selectedCategory, setSelectedCategory] = useState() @@ -50,6 +52,20 @@ export default function AddItem() { } + useEffect(()=>{ + if(searchParams.category !== undefined){ + console.log(searchParams.category) + executeQuery({sql: "SELECT * FROM category WHERE guid = ?", args: [searchParams.category]}).then((result) =>{ + console.log("then") + if("rows" in result[0]){ + const category = result[0]["rows"][0]; + setSelectedCategory({name: category["name"], color: category["color"], guid: category["guid"]}) + } + //setSelectedCategory({name: category["name"], color: category["color"], guid: category["guid"]}) + }) + } + }, []) + return ( {setSelecorModalVisible(false)}} onCategoryTap={handleCategorySelect}> diff --git a/components/budget/budgetHeader.tsx b/components/budget/budgetHeader.tsx index 6c8bc9d..5e65614 100644 --- a/components/budget/budgetHeader.tsx +++ b/components/budget/budgetHeader.tsx @@ -4,7 +4,7 @@ import TextInputBar from "../common/TextInputBar"; type BudgetHeaderProperties = { selectedPage: string, - handlePageSelection: (page: string) => void, + handlePageSelection: (page: "expense" | "saving") => void, } type PageSelectorButtonProperties = { @@ -21,16 +21,16 @@ const BudgetHeader = (properties: BudgetHeaderProperties) => { { - properties.handlePageSelection("expenses") + properties.handlePageSelection("expense") }} /> { - properties.handlePageSelection("savings"); + properties.handlePageSelection("saving"); }} /> diff --git a/components/common/EmptyListCompenent.tsx b/components/common/EmptyListCompenent.tsx new file mode 100644 index 0000000..c0a1746 --- /dev/null +++ b/components/common/EmptyListCompenent.tsx @@ -0,0 +1,24 @@ +import { View, Text, StyleSheet } from 'react-native' +import React from 'react' +import { useTheme } from '../../app/contexts/ThemeContext' + +const EmptyListCompenent:React.FC = () => { + const {colors} = useTheme(); + return ( + + No matching Data + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + height: 70, + borderRadius: 20, + } +}) + +export default EmptyListCompenent \ No newline at end of file diff --git a/components/common/TextInputBar.tsx b/components/common/TextInputBar.tsx index 65255fe..bfeaac8 100644 --- a/components/common/TextInputBar.tsx +++ b/components/common/TextInputBar.tsx @@ -42,7 +42,7 @@ export default function TextInputBar(props: SearchBarProps) { // changed styles.container to containerStyle return ( - setIsactive(false)}/> + (setIsactive(true))} onEndEditing={()=>setIsactive(false)}/> {isActive && { diff --git a/components/expense/CategorySelectorModal.tsx b/components/expense/CategorySelectorModal.tsx index 56ba20f..ef09fd1 100644 --- a/components/expense/CategorySelectorModal.tsx +++ b/components/expense/CategorySelectorModal.tsx @@ -6,6 +6,7 @@ import CategoryListItem from '../common/CategoryListItem'; import { SIZES } from '../../constants/theme'; import useFetch from '../../hooks/useFetch'; import TextInputBar from '../common/TextInputBar'; +import EmptyListCompenent from '../common/EmptyListCompenent'; interface CategorySelectorModalProps{ @@ -43,7 +44,7 @@ const CategorySelectorModal: React.FC = (props : Cat }, [visible]) return ( - + @@ -57,6 +58,7 @@ const CategorySelectorModal: React.FC = (props : Cat ItemSeparatorComponent={() => } ListFooterComponent={() => } keyboardShouldPersistTaps="always" + ListEmptyComponent={EmptyListCompenent} > diff --git a/components/index.tsx b/components/index.tsx index 29167eb..92ba31c 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -16,6 +16,7 @@ import Plus from "./common/plus" import TextInputBar from "./common/TextInputBar" import AutoDecimalInput from "./common/AutoDecimalInput" import RoundedButton from "./common/RoundedButton" +import EmptyListCompenent from "./common/EmptyListCompenent" //login import BudgetHeader from "./budget/budgetHeader" @@ -45,6 +46,7 @@ export { CategoryItem, TypeSelectorSwitch, NavigationButton, - CustomColorPicker + CustomColorPicker, + EmptyListCompenent, } From 114ad9281894ce91540219128522c5731d9a80df Mon Sep 17 00:00:00 2001 From: Tocuro Date: Mon, 8 Jan 2024 10:39:19 +0100 Subject: [PATCH 16/24] ?? --- package-lock.json | 563 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 463 insertions(+), 100 deletions(-) diff --git a/package-lock.json b/package-lock.json index b72a41b..3c97807 100644 --- a/package-lock.json +++ b/package-lock.json @@ -78,19 +78,19 @@ } }, "node_modules/@babel/core": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", - "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", "@babel/generator": "^7.23.6", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.6", + "@babel/helpers": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", + "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -158,9 +158,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz", - "integrity": "sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz", + "integrity": "sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", @@ -403,12 +403,12 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", - "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz", + "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==", "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", + "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" }, "engines": { @@ -470,9 +470,9 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", - "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", + "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5" @@ -519,15 +519,12 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.6.tgz", - "integrity": "sha512-D7Ccq9LfkBFnow3azZGJvZYgcfeqAw3I1e5LoTpj6UKIFQilh8yqXsIGcRIqbBdsPWIz+Ze7ZZfggSj62Qp+Fg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.7.tgz", + "integrity": "sha512-b1s5JyeMvqj7d9m9KhJNHKc18gEJiSyVzVX3bwbiPalQBQpuvfPh6lA9F7Kk/dWH0TIiXRpB9yicwijY6buPng==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.23.6", + "@babel/helper-create-class-features-plugin": "^7.23.7", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", "@babel/plugin-syntax-decorators": "^7.23.3" }, "engines": { @@ -977,9 +974,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", - "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.7.tgz", + "integrity": "sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", @@ -1647,15 +1644,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.6.tgz", - "integrity": "sha512-kF1Zg62aPseQ11orDhFRw+aPG/eynNQtI+TyY+m33qJa2cJ5EEvza2P2BNTIA9E5MyqFABHEyY6CPHwgdy9aNg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz", + "integrity": "sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw==", "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", + "babel-plugin-polyfill-corejs2": "^0.4.7", + "babel-plugin-polyfill-corejs3": "^0.8.7", + "babel-plugin-polyfill-regenerator": "^0.5.4", "semver": "^6.3.1" }, "engines": { @@ -1813,9 +1810,9 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.6.tgz", - "integrity": "sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.7.tgz", + "integrity": "sha512-SY27X/GtTz/L4UryMNJ6p4fH4nsgWbz84y9FE0bQeWJP6O5BhgVCt53CotQKHCOeXJel8VyhlhujhlltKms/CA==", "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", @@ -1823,7 +1820,7 @@ "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1844,7 +1841,7 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.4", + "@babel/plugin-transform-async-generator-functions": "^7.23.7", "@babel/plugin-transform-async-to-generator": "^7.23.3", "@babel/plugin-transform-block-scoped-functions": "^7.23.3", "@babel/plugin-transform-block-scoping": "^7.23.4", @@ -1892,9 +1889,9 @@ "@babel/plugin-transform-unicode-regex": "^7.23.3", "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.6", - "babel-plugin-polyfill-corejs3": "^0.8.5", - "babel-plugin-polyfill-regenerator": "^0.5.3", + "babel-plugin-polyfill-corejs2": "^0.4.7", + "babel-plugin-polyfill-corejs3": "^0.8.7", + "babel-plugin-polyfill-regenerator": "^0.5.4", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -1953,14 +1950,14 @@ } }, "node_modules/@babel/register": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", - "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.23.7.tgz", + "integrity": "sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==", "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", "make-dir": "^2.1.0", - "pirates": "^4.0.5", + "pirates": "^4.0.6", "source-map-support": "^0.5.16" }, "engines": { @@ -2005,9 +2002,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz", - "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", + "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2029,9 +2026,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/generator": "^7.23.6", @@ -3365,6 +3362,95 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -4277,6 +4363,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", @@ -6512,9 +6607,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" @@ -6572,9 +6667,9 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/node": { - "version": "20.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", - "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", + "version": "20.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz", + "integrity": "sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==", "dependencies": { "undici-types": "~5.26.4" } @@ -6586,14 +6681,14 @@ "dev": true }, "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==" + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" }, "node_modules/@types/react": { - "version": "18.2.45", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz", - "integrity": "sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==", + "version": "18.2.47", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz", + "integrity": "sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -6700,9 +6795,9 @@ } }, "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -7685,9 +7780,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001570", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz", - "integrity": "sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==", + "version": "1.0.30001576", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", + "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", "funding": [ { "type": "opencollective", @@ -8052,9 +8147,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/core-js-compat": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.34.0.tgz", - "integrity": "sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", "dependencies": { "browserslist": "^4.22.2" }, @@ -8654,15 +8749,20 @@ "node": ">=12" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.615", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.615.tgz", - "integrity": "sha512-/bKPPcgZVUziECqDc+0HkT87+0zhaWSZHNXqF8FLd2lQcptpmUFwoCSWjCdOng9Gdq+afKArPdEg/0ZW461Eng==" + "version": "1.4.623", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz", + "integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==" }, "node_modules/emittery": { "version": "0.13.1", @@ -9552,6 +9652,86 @@ "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==" }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/foreground-child/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -10030,9 +10210,9 @@ } }, "node_modules/image-size": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", - "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", "dependencies": { "queue": "6.0.2" }, @@ -10040,7 +10220,7 @@ "image-size": "bin/image-size.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.x" } }, "node_modules/immediate": { @@ -10522,6 +10702,23 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -14506,9 +14703,9 @@ } }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "optional": true, "engines": { "node": "*" @@ -15160,6 +15357,37 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -15382,9 +15610,9 @@ "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "funding": [ { "type": "opencollective", @@ -15935,9 +16163,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.3.0.tgz", - "integrity": "sha512-LzfpPZ1qXBGy5BcUHqw3pBC0qSd22qXS3t8hWSbozXNrBkzMhhOrcILE/nEg/PHpNNp1xvGOW8NwpAMF006roQ==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz", + "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", @@ -16006,9 +16234,9 @@ } }, "node_modules/react-native-web": { - "version": "0.19.9", - "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.9.tgz", - "integrity": "sha512-m69arZbS6FV+BNSKE6R/NQwUX+CzxCkYM7AJlSLlS8dz3BDzlaxG8Bzqtzv/r3r1YFowhnZLBXVKIwovKDw49g==", + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.10.tgz", + "integrity": "sha512-IQoHiTQq8egBCVVwmTrYcFLgEFyb4LMZYEktHn4k22JMk9+QTCEz5WTfvr+jdNoeqj/7rtE81xgowKbfGO74qg==", "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-color": "^2.1.0", @@ -16887,6 +17115,28 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/string-width/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -16906,6 +17156,18 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -16959,13 +17221,13 @@ "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==" }, "node_modules/sucrase": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", - "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "7.1.6", + "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", @@ -16976,7 +17238,15 @@ "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/sucrase/node_modules/commander": { @@ -16987,6 +17257,49 @@ "node": ">= 6" } }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/sudo-prompt": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.2.5.tgz", @@ -17349,9 +17662,12 @@ } }, "node_modules/traverse": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", - "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -17829,6 +18145,53 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -17875,9 +18238,9 @@ } }, "node_modules/ws": { - "version": "8.15.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", - "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "engines": { "node": ">=10.0.0" }, From 6ca505d5050cec434e9f0022d5808bf6e2f63b5f Mon Sep 17 00:00:00 2001 From: Tocuro Date: Mon, 8 Jan 2024 10:42:31 +0100 Subject: [PATCH 17/24] updated Database.ts to use ENUM for Category Type --- services/database.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/services/database.ts b/services/database.ts index ec5f656..370108d 100644 --- a/services/database.ts +++ b/services/database.ts @@ -7,6 +7,11 @@ import { SimpleDate } from "../util/SimpleDate"; let db: SQLite.SQLiteDatabase; +export enum CategoryType { + SAVING = "saving", + EXPENSE = "expense", +} + export const initDatabase = async () => { db = SQLite.openDatabase("interactive_systeme.db"); try { @@ -24,11 +29,11 @@ export const initDatabase = async () => { } }; -export const updateCategory = async (guid: string, name: string, color: string, type: string, allocated_amount: number) => { +export const updateCategory = async (guid: string, name: string, color: string, CategoryType: CategoryType, allocated_amount: number) => { try { await db.transactionAsync(async (tx) => { - await tx.executeSqlAsync("UPDATE category SET name = ?, color = ?, type = ?, allocated_amount = ? WHERE guid = ?", [name, color, type, allocated_amount, guid]); + await tx.executeSqlAsync("UPDATE category SET name = ?, color = ?, type = ?, allocated_amount = ? WHERE guid = ?", [name, color, CategoryType, allocated_amount, guid]); }); } catch (error) { console.log("Error updating category: ", error); @@ -36,7 +41,7 @@ export const updateCategory = async (guid: string, name: string, color: string, } }; -export const addCategory = async (name: string, color: string, type: string, allocated_amount: number) => { +export const addCategory = async (name: string, color: string, CategoryType: CategoryType, allocated_amount: number) => { //needs user input validation for type and color (should be validated by this function) @@ -45,7 +50,7 @@ export const addCategory = async (name: string, color: string, type: string, all try { await db.transactionAsync(async (tx) => { await tx.executeSqlAsync("INSERT INTO category (guid, name, color, type, allocated_amount) VALUES (?, ?, ?, ?, ?);", - [UUID.toString(), name, color, type, allocated_amount] + [UUID.toString(), name, color, CategoryType, allocated_amount] ); }); } catch (error) { @@ -171,8 +176,9 @@ export const DEV_populateDatabase = async () => { for(let i=0; i < 5; i++){ let random = Math.floor(Math.random() * colors.length); - await addCategory(`Category ${i}`, colors[random], "budget", 50) - await addCategory(`Category ${i+6}`, colors[random], "budget", 50) + await addCategory(`Category ${i}`, colors[random], CategoryType.EXPENSE, 50) + random = Math.floor(Math.random() * colors.length); + await addCategory(`Category ${i+6}`, colors[random], CategoryType.SAVING, 50) } const result = await executeQuery({sql:"SELECT * from category", args:[]}) let categories: {[column: string]: any}[]; From bdc2ca22f677f1d85324c9bf19d4390705a89073 Mon Sep 17 00:00:00 2001 From: Walcher Date: Thu, 25 Jan 2024 17:23:17 +0100 Subject: [PATCH 18/24] Refactor category type to use enum --- app/(tabs)/(budget)/addCategory.tsx | 7 +++++-- components/budget/typeSelectorSwitch.tsx | 15 ++++++++------- components/stats/BudgetOverview.tsx | 5 +++-- components/stats/BudgetRemaining.tsx | 3 ++- components/stats/SavingsOverview.tsx | 5 +++-- services/database.ts | 2 +- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/app/(tabs)/(budget)/addCategory.tsx b/app/(tabs)/(budget)/addCategory.tsx index a6c53b6..eced44a 100644 --- a/app/(tabs)/(budget)/addCategory.tsx +++ b/app/(tabs)/(budget)/addCategory.tsx @@ -5,6 +5,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; import { addCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; +import { CategoryType } from "../../../services/database"; export default function Page() { const {colors} = useTheme(); @@ -13,7 +14,9 @@ export default function Page() { const [categoryName, setCategoryName] = useState(""); const [categoryColor, setCategoryColor] = useState('#' + Math.floor(Math.random()*16777215).toString(16)); - const [selectedType, setSelectedType] = useState("expense"); + + const [selectedType, setSelectedType] = useState(CategoryType.EXPENSE); + const [amount, setAmount] = useState(0); return ( @@ -35,7 +38,7 @@ export default function Page() { { + handleButtonPress={(type: CategoryType) => { setSelectedType(type); }} /> diff --git a/components/budget/typeSelectorSwitch.tsx b/components/budget/typeSelectorSwitch.tsx index dc6231b..a4edf58 100644 --- a/components/budget/typeSelectorSwitch.tsx +++ b/components/budget/typeSelectorSwitch.tsx @@ -1,10 +1,11 @@ import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { useTheme } from "../../app/contexts/ThemeContext"; +import { CategoryType } from "../../services/database"; export type TypeSelectorSwitchProperties = { - handleButtonPress: (type: string) => void, - currentSelected: string, + handleButtonPress: (type: CategoryType) => void, + currentSelected: CategoryType, } const TypeSelectorSwitch = (properties: TypeSelectorSwitchProperties) => { @@ -14,17 +15,17 @@ const TypeSelectorSwitch = (properties: TypeSelectorSwitchProperties) => { { - properties.handleButtonPress("expense"); + properties.handleButtonPress(CategoryType.EXPENSE); }} - style={[styles.touchableOpacityStyle, properties.currentSelected == "expense" ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}] + style={[styles.touchableOpacityStyle, properties.currentSelected == CategoryType.EXPENSE ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}] }> Expenses { - properties.handleButtonPress("saving"); + onPress={() => { + properties.handleButtonPress(CategoryType.SAVING); }} - style={[styles.touchableOpacityStyle, properties.currentSelected == "saving" ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}] + style={[styles.touchableOpacityStyle, properties.currentSelected == CategoryType.SAVING ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}] }> Savings diff --git a/components/stats/BudgetOverview.tsx b/components/stats/BudgetOverview.tsx index 05e89c4..8cc5c6c 100644 --- a/components/stats/BudgetOverview.tsx +++ b/components/stats/BudgetOverview.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; +import { CategoryType } from '../../services/database'; const BudgetOverview = () => { const { colors } = useTheme(); @@ -9,12 +10,12 @@ const BudgetOverview = () => { const [budget, setBudget] = useState(0); const spentQuery = { - sql: "SELECT SUM(e.amount) as total FROM expense e LEFT JOIN category c ON e.category_guid = c.guid WHERE c.type = 'budget'", + sql: `SELECT SUM(e.amount) as total FROM expense e LEFT JOIN category c ON e.category_guid = c.guid WHERE c.type = '${CategoryType.EXPENSE.toString()}'`, args: [] }; const budgetQuery = { - sql: "SELECT SUM(allocated_amount) as total FROM category WHERE type = 'budget'", + sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.EXPENSE.toString()}'`, args: [] }; diff --git a/components/stats/BudgetRemaining.tsx b/components/stats/BudgetRemaining.tsx index 37aa555..d5209fd 100644 --- a/components/stats/BudgetRemaining.tsx +++ b/components/stats/BudgetRemaining.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; +import { CategoryType } from '../../services/database'; const styles = StyleSheet.create({ text: { @@ -23,7 +24,7 @@ const BudgetRemaining = () => { }; const budgetQuery = { - sql: "SELECT SUM(allocated_amount) as total FROM category WHERE type = 'budget'", + sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.EXPENSE.toString()}'`, args: [] }; diff --git a/components/stats/SavingsOverview.tsx b/components/stats/SavingsOverview.tsx index a79234a..5162e75 100644 --- a/components/stats/SavingsOverview.tsx +++ b/components/stats/SavingsOverview.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; +import { CategoryType } from '../../services/database'; const SavingsOverview = () => { const { colors } = useTheme(); @@ -9,12 +10,12 @@ const SavingsOverview = () => { const [goal, setGoal] = useState(0); const savedQuery = { - sql: "SELECT SUM(e.amount) as total FROM expense e LEFT JOIN category c ON e.category_guid = c.guid WHERE c.type = 'savings'", + sql: `SELECT SUM(e.amount) as total FROM expense e LEFT JOIN category c ON e.category_guid = c.guid WHERE c.type = '${CategoryType.SAVING.toString()}'`, args: [] }; const goalQuery = { - sql: "SELECT SUM(allocated_amount) as total FROM category WHERE type = 'budget'", + sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.SAVING.toString()}'`, args: [] }; diff --git a/services/database.ts b/services/database.ts index 370108d..029131d 100644 --- a/services/database.ts +++ b/services/database.ts @@ -17,7 +17,7 @@ export const initDatabase = async () => { try { await db.transactionAsync(async (tx: SQLite.SQLTransactionAsync) => { await tx.executeSqlAsync( - "CREATE TABLE IF NOT EXISTS category (guid VARCHAR(36) PRIMARY KEY, name TEXT, color TEXT, type TEXT, allocated_amount DOUBLE);" + "CREATE TABLE IF NOT EXISTS category (guid VARCHAR(36) PRIMARY KEY, name TEXT, color TEXT, type string, allocated_amount DOUBLE);" ); await tx.executeSqlAsync( "CREATE TABLE IF NOT EXISTS expense (guid VARCHAR(36) PRIMARY KEY, name TEXT, category_guid VARCHAR(36), datetime DATETIME, amount DOUBLE, FOREIGN KEY (category_guid) REFERENCES category(guid));" From 85e92c85b4d1117bf31a65261b0e22568bbb9f03 Mon Sep 17 00:00:00 2001 From: Walcher Date: Thu, 25 Jan 2024 17:49:24 +0100 Subject: [PATCH 19/24] hold my beer des wird a katastrophe --- components/stats/BudgetRemaining.tsx | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/components/stats/BudgetRemaining.tsx b/components/stats/BudgetRemaining.tsx index d5209fd..98a5cf0 100644 --- a/components/stats/BudgetRemaining.tsx +++ b/components/stats/BudgetRemaining.tsx @@ -5,15 +5,33 @@ import useFetch from '../../hooks/useFetch'; import { CategoryType } from '../../services/database'; const styles = StyleSheet.create({ + container: { + margin: 10, + borderRadius: 5, + alignItems: 'center', + justifyContent: 'center' + }, text: { fontSize: 26, }, boldText: { - fontWeight: 'bold', + fontWeight: 'bold' + }, + negativeText: { + color: 'red', + }, + positiveText: { + color: 'green', }, }); -const BudgetRemaining = () => { + +interface BudgetRemainingProps { + goodColor?: string; + badColor?: string; +} + +const BudgetRemaining: React.FC = ({ goodColor = 'green', badColor = 'red' }) => { const { colors } = useTheme(); const [spent, setSpent] = useState(0); const [budget, setBudget] = useState(0); @@ -47,10 +65,18 @@ const BudgetRemaining = () => { } return ( - - You have {remaining.toFixed(2)}€ left. + + {remaining >= 0 ? ( + <> + Your remaining Budget is {remaining.toFixed(2)}€. + + ) : ( + <> + Your Budget is overspent by by -{Math.abs(remaining).toFixed(2)}€. + + )} - ); + ); }; export default BudgetRemaining; From 7a5ace403eab86d0a24871c0ec879911f7536639 Mon Sep 17 00:00:00 2001 From: Walcher Date: Thu, 25 Jan 2024 18:49:05 +0100 Subject: [PATCH 20/24] made hook to streamline getting totals and expense by type of category --- app/(tabs)/stats/index.tsx | 52 ++++++++++++++++ components/stats/BudgetOverview.tsx | 87 ++++++++++++-------------- components/stats/BudgetRemaining.tsx | 82 ------------------------ components/stats/BudgetTotal.tsx | 62 +++++++++++++++++++ components/stats/SavingsOverview.tsx | 93 ++++++++++++++-------------- hooks/useCategoryData.ts | 33 ++++++++++ 6 files changed, 233 insertions(+), 176 deletions(-) create mode 100644 app/(tabs)/stats/index.tsx delete mode 100644 components/stats/BudgetRemaining.tsx create mode 100644 components/stats/BudgetTotal.tsx create mode 100644 hooks/useCategoryData.ts diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/stats/index.tsx new file mode 100644 index 0000000..97a3b74 --- /dev/null +++ b/app/(tabs)/stats/index.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { StyleSheet, View, ScrollView } from 'react-native'; +import BudgetTotal from '../../../components/stats/BudgetTotal'; +import { useTheme } from '../../contexts/ThemeContext'; +import Widget from '../../../components/stats/Widget'; +import DebugMenu from '../../../services/DebugMenu'; +import SavingsOverview from '../../../components/stats/SavingsOverview'; +import FinancialAdvice from '../../../components/stats/FinancialAdvice'; +import BudgetOverview from '../../../components/stats/BudgetOverview'; + +export default function Page() { + const { colors } = useTheme(); + + // Mock data #TODO Database einbinden + // what to do when amount too small? + + const spent = 120.75; + const budget = 696.96; + + + + const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.backgroundColor, + marginTop: 50, + alignItems: 'center', + }, + }); + + return ( + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/components/stats/BudgetOverview.tsx b/components/stats/BudgetOverview.tsx index 8cc5c6c..de40065 100644 --- a/components/stats/BudgetOverview.tsx +++ b/components/stats/BudgetOverview.tsx @@ -1,63 +1,56 @@ import React, { useState, useEffect } from 'react'; -import { View, Text, StyleSheet } from 'react-native'; +import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; import { CategoryType } from '../../services/database'; +import {useCategoryData} from '../../hooks/useCategoryData'; -const BudgetOverview = () => { +const styles = StyleSheet.create({ + container: { + margin: 10, + borderRadius: 5, + alignItems: 'center', + justifyContent: 'center' + }, + text: { + fontSize: 26, + color: `black` + }, + boldText: { + fontWeight: 'bold' + }, + negativeText: { + color: 'red', + }, + positiveText: { + color: 'green', + }, +}); + +interface BudgetTotalProps { + goodColor?: string; + badColor?: string; +} + +const BudgetTotal: React.FC = ({ goodColor = 'green', badColor = 'red' }) => { const { colors } = useTheme(); - const [spent, setSpent] = useState(0); - const [budget, setBudget] = useState(0); + const { data, isLoading } = useCategoryData(CategoryType.EXPENSE); - const spentQuery = { - sql: `SELECT SUM(e.amount) as total FROM expense e LEFT JOIN category c ON e.category_guid = c.guid WHERE c.type = '${CategoryType.EXPENSE.toString()}'`, - args: [] - }; + const { total, expenseTotal } = data; - const budgetQuery = { - sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.EXPENSE.toString()}'`, - args: [] - }; + const remaining = total - expenseTotal; - const { data: spentData, isLoading: spentLoading } = useFetch(spentQuery); - const { data: budgetData, isLoading: budgetLoading } = useFetch(budgetQuery); - - useEffect(() => { - if (spentData) { - setSpent(spentData[0]?.total || 0); - } - if (budgetData) { - setBudget(budgetData[0]?.total || 0); - } - }, [spentData, budgetData]); - - const styles = StyleSheet.create({ - container: { - margin: 10, - borderRadius: 5, - alignItems: 'center', - justifyContent: 'center' - }, - text: { - fontSize: 26, - color: colors.primaryText - }, - boldText: { - fontWeight: 'bold' - } - }); - - if (spentLoading || budgetLoading) { + if (isLoading) { return Loading...; } return ( - - - You have spent {spent.toFixed(2)}€ out of your budget of {budget.toFixed(2)}€. - - + + <> + You have spent {expenseTotal.toFixed(2)}€ out of your Budget of {total.toFixed(2)}€ . + + ); }; -export default BudgetOverview; +export default BudgetTotal; \ No newline at end of file diff --git a/components/stats/BudgetRemaining.tsx b/components/stats/BudgetRemaining.tsx deleted file mode 100644 index 98a5cf0..0000000 --- a/components/stats/BudgetRemaining.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Text, StyleSheet } from 'react-native'; -import { useTheme } from '../../app/contexts/ThemeContext'; -import useFetch from '../../hooks/useFetch'; -import { CategoryType } from '../../services/database'; - -const styles = StyleSheet.create({ - container: { - margin: 10, - borderRadius: 5, - alignItems: 'center', - justifyContent: 'center' - }, - text: { - fontSize: 26, - }, - boldText: { - fontWeight: 'bold' - }, - negativeText: { - color: 'red', - }, - positiveText: { - color: 'green', - }, -}); - - -interface BudgetRemainingProps { - goodColor?: string; - badColor?: string; -} - -const BudgetRemaining: React.FC = ({ goodColor = 'green', badColor = 'red' }) => { - const { colors } = useTheme(); - const [spent, setSpent] = useState(0); - const [budget, setBudget] = useState(0); - - const spentQuery = { - sql: "SELECT SUM(amount) as total FROM expense", - args: [] - }; - - const budgetQuery = { - sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.EXPENSE.toString()}'`, - args: [] - }; - - const { data: spentData, isLoading: spentLoading } = useFetch(spentQuery); - const { data: budgetData, isLoading: budgetLoading } = useFetch(budgetQuery); - - useEffect(() => { - if (spentData) { - setSpent(spentData[0]?.total || 0); - } - if (budgetData) { - setBudget(budgetData[0]?.total || 0); - } - }, [spentData, budgetData]); - - const remaining = budget - spent; - - if (spentLoading || budgetLoading) { - return Loading...; - } - - return ( - - {remaining >= 0 ? ( - <> - Your remaining Budget is {remaining.toFixed(2)}€. - - ) : ( - <> - Your Budget is overspent by by -{Math.abs(remaining).toFixed(2)}€. - - )} - - ); -}; - -export default BudgetRemaining; diff --git a/components/stats/BudgetTotal.tsx b/components/stats/BudgetTotal.tsx new file mode 100644 index 0000000..6acfa6b --- /dev/null +++ b/components/stats/BudgetTotal.tsx @@ -0,0 +1,62 @@ +import React, { useState, useEffect } from 'react'; +import { Text, StyleSheet } from 'react-native'; +import { useTheme } from '../../app/contexts/ThemeContext'; +import useFetch from '../../hooks/useFetch'; +import { CategoryType } from '../../services/database'; +import {useCategoryData} from '../../hooks/useCategoryData'; + +const styles = StyleSheet.create({ + container: { + margin: 10, + borderRadius: 5, + alignItems: 'center', + justifyContent: 'center' + }, + text: { + fontSize: 26, + color: `black` + }, + boldText: { + fontWeight: 'bold' + }, + negativeText: { + color: 'red', + }, + positiveText: { + color: 'green', + }, +}); + +interface BudgetTotalProps { + goodColor?: string; + badColor?: string; +} + +const BudgetTotal: React.FC = ({ goodColor = 'green', badColor = 'red' }) => { + const { colors } = useTheme(); + const { data, isLoading } = useCategoryData(CategoryType.EXPENSE); + + const { total, expenseTotal } = data; + + const remaining = total - expenseTotal; + + if (isLoading) { + return Loading...; + } + + return ( + + {remaining >= 0 ? ( + <> + Your remaining overall Budget is {remaining.toFixed(2)}€. + + ) : ( + <> + Your Budget is overspent by -{Math.abs(remaining).toFixed(2)}€. + + )} + + ); +}; + +export default BudgetTotal; \ No newline at end of file diff --git a/components/stats/SavingsOverview.tsx b/components/stats/SavingsOverview.tsx index 5162e75..064cf5e 100644 --- a/components/stats/SavingsOverview.tsx +++ b/components/stats/SavingsOverview.tsx @@ -1,63 +1,62 @@ import React, { useState, useEffect } from 'react'; -import { View, Text, StyleSheet } from 'react-native'; +import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; import { CategoryType } from '../../services/database'; +import {useCategoryData} from '../../hooks/useCategoryData'; -const SavingsOverview = () => { +const styles = StyleSheet.create({ + container: { + margin: 10, + borderRadius: 5, + alignItems: 'center', + justifyContent: 'center' + }, + text: { + fontSize: 26, + color: `black` + }, + boldText: { + fontWeight: 'bold' + }, + negativeText: { + color: 'red', + }, + positiveText: { + color: 'green', + }, +}); + +interface BudgetTotalProps { + goodColor?: string; + badColor?: string; +} + +const BudgetTotal: React.FC = ({ goodColor = 'green', badColor = 'red' }) => { const { colors } = useTheme(); - const [saved, setSaved] = useState(0); - const [goal, setGoal] = useState(0); + const { data, isLoading } = useCategoryData(CategoryType.SAVING); - const savedQuery = { - sql: `SELECT SUM(e.amount) as total FROM expense e LEFT JOIN category c ON e.category_guid = c.guid WHERE c.type = '${CategoryType.SAVING.toString()}'`, - args: [] - }; + const { total, expenseTotal } = data; - const goalQuery = { - sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.SAVING.toString()}'`, - args: [] - }; + const remaining = total - expenseTotal; - const { data: savedData, isLoading: savedLoading } = useFetch(savedQuery); - const { data: goalData, isLoading: goalLoading } = useFetch(goalQuery); - - useEffect(() => { - if (savedData) { - setSaved(savedData[0]?.total || 0); - } - if (goalData) { - setGoal(goalData[0]?.total || 0); - } - }, [savedData, goalData]); - - const styles = StyleSheet.create({ - container: { - margin: 10, - borderRadius: 5, - alignItems: 'center', - justifyContent: 'center' - }, - text: { - fontSize: 26, - color: colors.primaryText - }, - boldText: { - fontWeight: 'bold' - } - }); - - if (savedLoading || goalLoading) { + if (isLoading) { return Loading...; } return ( - - - You have saved {saved.toFixed(2)}€ out of your goal of {goal.toFixed(2)}€. - - + + {remaining >= 0 ? ( + <> + You have saved {expenseTotal.toFixed(2)}€ out of your Goal of {total.toFixed(2)}€ . + + ) : ( + <> + You have surpassed your Savings Goal of {total.toFixed(2)}€ by {Math.abs(remaining).toFixed(2)}€. + + )} + ); }; -export default SavingsOverview; \ No newline at end of file +export default BudgetTotal; \ No newline at end of file diff --git a/hooks/useCategoryData.ts b/hooks/useCategoryData.ts new file mode 100644 index 0000000..3939a53 --- /dev/null +++ b/hooks/useCategoryData.ts @@ -0,0 +1,33 @@ +import { useState, useEffect } from 'react'; +import useFetch from './useFetch'; +import { CategoryType } from '../services/database'; + +export const useCategoryData = (CategoryType: string) => { + const [data, setData] = useState({ total: 0, expenseTotal: 0 }); + const [isLoading, setLoading] = useState(true); + + const categoryQuery = { + sql: `SELECT SUM(allocated_amount) as total FROM category WHERE type = '${CategoryType.toString()}'`, + args: [] + }; + + const expenseQuery = { + sql: `SELECT SUM(e.amount) as total FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.type = '${CategoryType.toString()}'`, + args: [] + }; + + const { data: categoryData, isLoading: categoryLoading } = useFetch(categoryQuery); + const { data: expenseData, isLoading: expenseLoading } = useFetch(expenseQuery); + + useEffect(() => { + if (categoryData && expenseData) { + setData({ + total: categoryData[0]?.total || 0, + expenseTotal: expenseData[0]?.total || 0 + }); + } + setLoading(categoryLoading || expenseLoading); + }, [categoryData, categoryLoading, expenseData, expenseLoading]); + + return { categoryData, expenseData, isLoading, data }; +}; \ No newline at end of file From 899b50ded400ad197b13a73332946650e1f1692e Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 25 Jan 2024 20:35:09 +0100 Subject: [PATCH 21/24] last changes --- app/(tabs)/(budget)/addCategory.tsx | 2 +- app/(tabs)/(budget)/editCategory.tsx | 3 +- app/(tabs)/(budget)/index.tsx | 1 - app/(tabs)/(stats)/index.tsx | 39 ++++++++++++------ app/(tabs)/stats/index.tsx | 52 ------------------------ app/expense/new.tsx | 2 - components/budget/typeSelectorSwitch.tsx | 2 +- components/stats/BudgetOverview.tsx | 4 +- components/stats/BudgetTotal.tsx | 6 +-- components/stats/SavingsOverview.tsx | 6 +-- hooks/useCategoryData.ts | 2 +- package-lock.json | 8 ++-- package.json | 2 +- services/DebugMenu.tsx | 3 +- services/database.ts | 6 +-- types/dbItems.ts | 4 ++ 16 files changed, 53 insertions(+), 89 deletions(-) delete mode 100644 app/(tabs)/stats/index.tsx diff --git a/app/(tabs)/(budget)/addCategory.tsx b/app/(tabs)/(budget)/addCategory.tsx index eced44a..695a708 100644 --- a/app/(tabs)/(budget)/addCategory.tsx +++ b/app/(tabs)/(budget)/addCategory.tsx @@ -5,7 +5,7 @@ import { SafeAreaView } from 'react-native-safe-area-context'; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; import { addCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; -import { CategoryType } from "../../../services/database"; +import { CategoryType } from "../../../types/dbItems"; export default function Page() { const {colors} = useTheme(); diff --git a/app/(tabs)/(budget)/editCategory.tsx b/app/(tabs)/(budget)/editCategory.tsx index e0b2706..19dc3e0 100644 --- a/app/(tabs)/(budget)/editCategory.tsx +++ b/app/(tabs)/(budget)/editCategory.tsx @@ -6,6 +6,7 @@ import useFetch from "../../../hooks/useFetch"; import { deleteCategory, deleteExpense, updateCategory } from "../../../services/database"; import { useTheme } from "../../contexts/ThemeContext"; import { SafeAreaView } from 'react-native-safe-area-context'; +import { CategoryType } from "../../../types/dbItems"; const addCategory = () => { const {colors} = useTheme(); @@ -13,7 +14,7 @@ const addCategory = () => { const [categoryName, setCategoryName] = useState(category_name.toString()); const [categoryColor, setCategoryColor] = useState(category_color.toString()); - const [selectedType, setSelectedType] = useState(category_type.toString()); + const [selectedType, setSelectedType] = useState(category_type === "expense" ? CategoryType.EXPENSE : CategoryType.SAVING); const [amount, setAmount] = useState(Number.parseFloat(category_amount.toString())); const {data} = useFetch({sql: "SELECT * FROM expense WHERE category_guid = ?", args: [category_guid.toString()]}); diff --git a/app/(tabs)/(budget)/index.tsx b/app/(tabs)/(budget)/index.tsx index 9b38322..fce6f05 100644 --- a/app/(tabs)/(budget)/index.tsx +++ b/app/(tabs)/(budget)/index.tsx @@ -67,7 +67,6 @@ export default function Page() { }) }, [data, searchString, selectedPage]); - console.log(selectedPage) return ( diff --git a/app/(tabs)/(stats)/index.tsx b/app/(tabs)/(stats)/index.tsx index 6a6c0c9..5113d08 100644 --- a/app/(tabs)/(stats)/index.tsx +++ b/app/(tabs)/(stats)/index.tsx @@ -1,24 +1,39 @@ import React from 'react'; -import { SafeAreaView } from 'react-native'; + import { Graph } from '../../../components'; import BudgetOverview from '../../../components/stats/BudgetOverview'; -import BudgetRemaining from '../../../components/stats/BudgetRemaining'; +import BudgetRemaining from '../../../components/stats/SavingsOverview'; import SavingsOverview from '../../../components/stats/SavingsOverview'; import Widget from '../../../components/stats/Widget'; +import FinancialAdvice from '../../../components/stats/FinancialAdvice'; +import BudgetTotal from '../../../components/stats/BudgetTotal'; +import { ScrollView } from 'react-native-gesture-handler'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import DebugMenu from '../../../services/DebugMenu'; export default function Page() { return ( - - - - - - - - - - + + + {/* */} + + + + + + + + + + + + {/* + + */} + + + ); } diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/stats/index.tsx deleted file mode 100644 index 97a3b74..0000000 --- a/app/(tabs)/stats/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { StyleSheet, View, ScrollView } from 'react-native'; -import BudgetTotal from '../../../components/stats/BudgetTotal'; -import { useTheme } from '../../contexts/ThemeContext'; -import Widget from '../../../components/stats/Widget'; -import DebugMenu from '../../../services/DebugMenu'; -import SavingsOverview from '../../../components/stats/SavingsOverview'; -import FinancialAdvice from '../../../components/stats/FinancialAdvice'; -import BudgetOverview from '../../../components/stats/BudgetOverview'; - -export default function Page() { - const { colors } = useTheme(); - - // Mock data #TODO Database einbinden - // what to do when amount too small? - - const spent = 120.75; - const budget = 696.96; - - - - const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.backgroundColor, - marginTop: 50, - alignItems: 'center', - }, - }); - - return ( - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/app/expense/new.tsx b/app/expense/new.tsx index d0456e7..bfbd7bd 100644 --- a/app/expense/new.tsx +++ b/app/expense/new.tsx @@ -54,9 +54,7 @@ export default function AddItem() { useEffect(()=>{ if(searchParams.category !== undefined){ - console.log(searchParams.category) executeQuery({sql: "SELECT * FROM category WHERE guid = ?", args: [searchParams.category]}).then((result) =>{ - console.log("then") if("rows" in result[0]){ const category = result[0]["rows"][0]; setSelectedCategory({name: category["name"], color: category["color"], guid: category["guid"]}) diff --git a/components/budget/typeSelectorSwitch.tsx b/components/budget/typeSelectorSwitch.tsx index a4edf58..f3e8f25 100644 --- a/components/budget/typeSelectorSwitch.tsx +++ b/components/budget/typeSelectorSwitch.tsx @@ -1,7 +1,7 @@ import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { useTheme } from "../../app/contexts/ThemeContext"; -import { CategoryType } from "../../services/database"; +import { CategoryType } from "../../types/dbItems"; export type TypeSelectorSwitchProperties = { handleButtonPress: (type: CategoryType) => void, diff --git a/components/stats/BudgetOverview.tsx b/components/stats/BudgetOverview.tsx index de40065..4148e6a 100644 --- a/components/stats/BudgetOverview.tsx +++ b/components/stats/BudgetOverview.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; -import { CategoryType } from '../../services/database'; +import { CategoryType } from '../../types/dbItems'; import {useCategoryData} from '../../hooks/useCategoryData'; const styles = StyleSheet.create({ @@ -47,7 +47,7 @@ const BudgetTotal: React.FC = ({ goodColor = 'green', badColor return ( <> - You have spent {expenseTotal.toFixed(2)}€ out of your Budget of {total.toFixed(2)}€ . + You have spent {expenseTotal.toFixed(2)}€ out of your Budget of {total.toFixed(2)}€ ); diff --git a/components/stats/BudgetTotal.tsx b/components/stats/BudgetTotal.tsx index 6acfa6b..f4b0aa8 100644 --- a/components/stats/BudgetTotal.tsx +++ b/components/stats/BudgetTotal.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; -import { CategoryType } from '../../services/database'; +import { CategoryType } from '../../types/dbItems'; import {useCategoryData} from '../../hooks/useCategoryData'; const styles = StyleSheet.create({ @@ -48,11 +48,11 @@ const BudgetTotal: React.FC = ({ goodColor = 'green', badColor {remaining >= 0 ? ( <> - Your remaining overall Budget is {remaining.toFixed(2)}€. + Your remaining overall Budget is {remaining.toFixed(2)}€ ) : ( <> - Your Budget is overspent by -{Math.abs(remaining).toFixed(2)}€. + Your Budget is overspent by -{Math.abs(remaining).toFixed(2)}€ )} diff --git a/components/stats/SavingsOverview.tsx b/components/stats/SavingsOverview.tsx index 064cf5e..ea906a1 100644 --- a/components/stats/SavingsOverview.tsx +++ b/components/stats/SavingsOverview.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; import useFetch from '../../hooks/useFetch'; -import { CategoryType } from '../../services/database'; +import { CategoryType } from '../../types/dbItems'; import {useCategoryData} from '../../hooks/useCategoryData'; const styles = StyleSheet.create({ @@ -48,11 +48,11 @@ const BudgetTotal: React.FC = ({ goodColor = 'green', badColor {remaining >= 0 ? ( <> - You have saved {expenseTotal.toFixed(2)}€ out of your Goal of {total.toFixed(2)}€ . + You have saved {expenseTotal.toFixed(2)}€ out of your Goal of {total.toFixed(2)}€ ) : ( <> - You have surpassed your Savings Goal of {total.toFixed(2)}€ by {Math.abs(remaining).toFixed(2)}€. + You have surpassed your Savings Goal of {total.toFixed(2)}€ by {Math.abs(remaining).toFixed(2)}€ )} diff --git a/hooks/useCategoryData.ts b/hooks/useCategoryData.ts index 3939a53..72cbe8b 100644 --- a/hooks/useCategoryData.ts +++ b/hooks/useCategoryData.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import useFetch from './useFetch'; -import { CategoryType } from '../services/database'; +import { CategoryType } from '../types/dbItems'; export const useCategoryData = (CategoryType: string) => { const [data, setData] = useState({ total: 0, expenseTotal: 0 }); diff --git a/package-lock.json b/package-lock.json index 3c97807..8eacd8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "react-native-calendars": "^1.1303.0", "react-native-chart-kit": "^6.12.0", "react-native-gesture-handler": "~2.12.0", - "react-native-reanimated": "^3.3.0", + "react-native-reanimated": "~3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", "react-native-uuid": "^2.0.1", @@ -16163,9 +16163,9 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.1.tgz", - "integrity": "sha512-F4vG9Yf9PKmE3GaWtVGUpzj3SM6YY2cx1yRHCwiMd1uY7W0gU017LfcVUorboJnj0y5QZqEriEK1Usq2Y8YZqg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.3.0.tgz", + "integrity": "sha512-LzfpPZ1qXBGy5BcUHqw3pBC0qSd22qXS3t8hWSbozXNrBkzMhhOrcILE/nEg/PHpNNp1xvGOW8NwpAMF006roQ==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", diff --git a/package.json b/package.json index 6203ad9..f8cf56a 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "react-native-calendars": "^1.1303.0", "react-native-chart-kit": "^6.12.0", "react-native-gesture-handler": "~2.12.0", - "react-native-reanimated": "^3.3.0", + "react-native-reanimated": "~3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", "react-native-uuid": "^2.0.1", diff --git a/services/DebugMenu.tsx b/services/DebugMenu.tsx index 29ce4e8..ff728ff 100644 --- a/services/DebugMenu.tsx +++ b/services/DebugMenu.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { View, Button, Alert } from 'react-native'; import { addCategory, addExpense, deleteExpenses, deleteCategories, DEV_populateDatabase, deleteDatabase } from './database'; import uuid from 'react-native-uuid'; +import { CategoryType } from '../types/dbItems'; const randomColors = ["red", "blue", "green", "purple", "yellow"]; @@ -46,7 +47,7 @@ const DebugMenu = () => { const allocated_amount = getRandomNumber(); const type = "expense"; - addCategory(name, color, type, allocated_amount) + addCategory(name, color, CategoryType.EXPENSE, allocated_amount) .then(() => Alert.alert("Category Added", `Name: ${name}, Color: ${color}`)) .catch((error: any) => console.error("Error adding category: ", error)); }; diff --git a/services/database.ts b/services/database.ts index 029131d..e4c25bf 100644 --- a/services/database.ts +++ b/services/database.ts @@ -4,13 +4,11 @@ import uuid from "react-native-uuid"; import { Query } from "expo-sqlite"; import { SimpleDate } from "../util/SimpleDate"; +import { CategoryType } from "../types/dbItems"; let db: SQLite.SQLiteDatabase; -export enum CategoryType { - SAVING = "saving", - EXPENSE = "expense", -} + export const initDatabase = async () => { db = SQLite.openDatabase("interactive_systeme.db"); diff --git a/types/dbItems.ts b/types/dbItems.ts index c2df2dc..2d9906d 100644 --- a/types/dbItems.ts +++ b/types/dbItems.ts @@ -1,3 +1,7 @@ +export enum CategoryType { + SAVING = "saving", + EXPENSE = "expense", +} export interface Category { guid? : string; name? : string; From 2e0b92d5b85392c8e70419ca0c137ef92dd0ab25 Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 25 Jan 2024 20:41:19 +0100 Subject: [PATCH 22/24] deleteEverything kept in userSettings --- app/(tabs)/(home)/userSettings.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/app/(tabs)/(home)/userSettings.tsx b/app/(tabs)/(home)/userSettings.tsx index c865110..fb163ae 100644 --- a/app/(tabs)/(home)/userSettings.tsx +++ b/app/(tabs)/(home)/userSettings.tsx @@ -4,7 +4,7 @@ import { SIZES } from '../../../constants/theme' import { SafeAreaView } from 'react-native-safe-area-context' import { ButtonSetting, ToggleSetting } from '../../../components' import { useTheme } from '../../contexts/ThemeContext' -import { deleteExpenses, DEV_populateDatabase } from '../../../services/database' +import { deleteCategories, deleteExpenses, DEV_populateDatabase } from '../../../services/database' import { useAuth } from '../../contexts/AuthContext' import { TouchableOpacity } from 'react-native-gesture-handler' @@ -50,17 +50,14 @@ export default function userSettings() { - { - deleteExpenses().then(() => { - console.log("Expenses Deleted!"); - })}} - /> - { - const del = async () => { - await DEV_populateDatabase() + { + const deleteAll = async () => { + await deleteExpenses(); + await deleteCategories(); } - del() - }}/> + deleteAll(); + }} + /> From f44a42779f87867e4a9034a3f4ea7ddde7e9952d Mon Sep 17 00:00:00 2001 From: Ghost_Element Date: Fri, 26 Jan 2024 23:53:59 +0100 Subject: [PATCH 23/24] documentation --- DOCUMENTATION.md | 4 ++-- README.md | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 971359c..e561888 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -24,12 +24,12 @@ Our design philosophy was rooted in simplicity and functionality. We aimed to cr - **Component Development**: Built reusable components for uniformity and efficiency. - **State Management and Hooks**: Implemented custom hooks for state management, ensuring smooth data flow and component reusability. -- [ ] **Stage 4: Implementation-Phase 2** +- [x] **Stage 4: Implementation-Phase 2** - **Refining Components**: Enhancing component functionality and aesthetics. - **Optimization and Debugging**: Identifying and fixing bugs, optimizing code for better performance. - **Expanding Features**: Implementing additional pages and components to enrich the application. -- [ ] **Stage 5: Final Implementation-Phase** +- [x] **Stage 5: Final Implementation-Phase** - **UI Touch-Ups**: Final tweaks to the user interface, ensuring a polished look and feel. - **Finalization**: Wrapping up the project, finalizing drafts, and completing outstanding tasks. diff --git a/README.md b/README.md index b5967dd..378d956 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,16 @@ A more convenient way to run Finanzfuchs is to download the prebuilt apk which c ## Usage -- **Adding Expenses**: Press the plus button to add expenses. -- **Removing Expenses**: Go to the budget tab and press the red button in the middle to reset expenses. -- **Login Screen**: Go to the login screen by pressing the profile picture in the top left of the home screen. +- **Adding Expenses(/Savings)**: Press the plus button to add expenses. +- **Removing Expenses(/Savings)**: Go to the certain expense and press it. You will then be directed to the edit expense tab, where you can change the expense as you wish. +- **Adding Categories**: Go to the budget tab and press the plus button to add a new category with custom color. +- **Removing Categories**: Go to a certain category by pressing on the budget tab and choosing one. At the top is a big button with a pencil on it, which when you press it takes you to the edit category tab, where you can customize the category or remove it completely with all expenses it contains. +- **Searching for Expenses, Savings and Categories**: At the Home Screen, the Budget tab or in a certain category you can search for certain expenses, categories or savings by writing its name in the Search bar. +- **Home Screen**: Go to the login screen by pressing the profile picture in the top left of the home screen. Furthermore, at the bottom there are the last expenses, which were added. + - **Calendar**: The calendar has little colored dots under certain days indicating, that at that day an expense has been added. By pressing at a day the list of expenses below it will be sorted out and only showing the expenses which were added at that day. + - **List of Expenses**: The list shows the most recently added expenses which can be searched through and filtered by using the calendar. + - **Profile**: By pressing at the profile picture on the home screen you get to the My Profile tab where you can activate the dark mode, reset the database and sign out of the app. +- **Stats**: Here is a Graph showing an Overview of your Budget and the sums of all expenses and savings. ## Team From 675ffe97bca8cae606e098d2face53a3d0f48673 Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Sat, 27 Jan 2024 00:06:35 +0100 Subject: [PATCH 24/24] missing deps --- package-lock.json | 24 +++++++----------------- package.json | 1 + 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8eacd8d..20b4a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "react-native-reanimated": "~3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", + "react-native-svg": "13.9.0", "react-native-uuid": "^2.0.1", "react-native-web": "~0.19.6", "reanimated-color-picker": "^2.4.2" @@ -7509,8 +7510,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "peer": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/bplist-creator": { "version": "0.1.0", @@ -8339,7 +8339,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -8355,7 +8354,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "peer": true, "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -8368,7 +8366,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "peer": true, "engines": { "node": ">= 6" }, @@ -8669,7 +8666,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -8688,8 +8684,7 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ], - "peer": true + ] }, "node_modules/domexception": { "version": "4.0.0", @@ -8708,7 +8703,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "peer": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -8723,7 +8717,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "peer": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -13786,8 +13779,7 @@ "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "peer": true + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, "node_modules/media-typer": { "version": "0.3.0", @@ -14996,7 +14988,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "peer": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -16206,10 +16197,9 @@ } }, "node_modules/react-native-svg": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-14.1.0.tgz", - "integrity": "sha512-HeseElmEk+AXGwFZl3h56s0LtYD9HyGdrpg8yd9QM26X+d7kjETrRQ9vCjtxuT5dCZEIQ5uggU1dQhzasnsCWA==", - "peer": true, + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.9.0.tgz", + "integrity": "sha512-Ey18POH0dA0ob/QiwCBVrxIiwflhYuw0P0hBlOHeY4J5cdbs8ngdKHeWC/Kt9+ryP6fNoEQ1PUgPYw2Bs/rp5Q==", "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3" diff --git a/package.json b/package.json index f8cf56a..b86787c 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-native-reanimated": "~3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", + "react-native-svg": "13.9.0", "react-native-uuid": "^2.0.1", "react-native-web": "~0.19.6", "reanimated-color-picker": "^2.4.2"