From cec3a4d2382c01d308fbaf0137e8a25b573ebfc9 Mon Sep 17 00:00:00 2001 From: Walcher Date: Fri, 5 Jan 2024 14:23:47 +0100 Subject: [PATCH 1/6] add savings mock data to DEV_populateDatabase --- services/database.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/database.ts b/services/database.ts index fbb9b6c..1fe01dd 100644 --- a/services/database.ts +++ b/services/database.ts @@ -144,6 +144,7 @@ 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) } const result = await executeQuery({sql:"SELECT * from category", args:[]}) let categories: {[column: string]: any}[]; @@ -160,7 +161,7 @@ export const DEV_populateDatabase = async () => { let randomDay = Math.floor(Math.random() * 20) date.setDate(randomDay) let string = new SimpleDate(date).toISOString() - await addExpense(`Expense ${i}`, categories[random].guid, string, 15) + await addExpense(`Expense ${i}`, categories[random].guid, string, 30) } } catch(e){ console.error(e) From 9544b3eabb314aa59f5c164c3481dbcd4d0c063d Mon Sep 17 00:00:00 2001 From: Walcher Date: Fri, 5 Jan 2024 14:32:30 +0100 Subject: [PATCH 2/6] added database connection --- app/(tabs)/stats/index.tsx | 8 +++- components/stats/BudgetOverview.tsx | 48 ++++++++++++++++------ components/stats/BudgetRemaining.tsx | 61 ++++++++++++++++++---------- 3 files changed, 81 insertions(+), 36 deletions(-) diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/stats/index.tsx index b7e477d..0870688 100644 --- a/app/(tabs)/stats/index.tsx +++ b/app/(tabs)/stats/index.tsx @@ -6,6 +6,7 @@ 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'; export default function Page() { const { colors } = useTheme(); @@ -51,11 +52,14 @@ export default function Page() { - + - + + + + diff --git a/components/stats/BudgetOverview.tsx b/components/stats/BudgetOverview.tsx index 5339fb3..05e89c4 100644 --- a/components/stats/BudgetOverview.tsx +++ b/components/stats/BudgetOverview.tsx @@ -1,31 +1,55 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; +import useFetch from '../../hooks/useFetch'; -interface StatsBudgetProps { - spent: number; - budget: number; -} - -const BudgetOverview: React.FC = ({ spent, budget }) => { +const BudgetOverview = () => { const { colors } = useTheme(); + const [spent, setSpent] = useState(0); + 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'", + args: [] + }; + + const budgetQuery = { + sql: "SELECT SUM(allocated_amount) as total FROM category WHERE type = 'budget'", + 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 styles = StyleSheet.create({ container: { margin: 10, borderRadius: 5, alignItems: 'center', - justifyContent: 'center', + justifyContent: 'center' }, text: { fontSize: 26, - color: colors.primaryText, + color: colors.primaryText }, boldText: { - fontWeight: 'bold', - }, + fontWeight: 'bold' + } }); + if (spentLoading || budgetLoading) { + return Loading...; + } + return ( @@ -35,4 +59,4 @@ const BudgetOverview: React.FC = ({ spent, budget }) => { ); }; -export default BudgetOverview +export default BudgetOverview; diff --git a/components/stats/BudgetRemaining.tsx b/components/stats/BudgetRemaining.tsx index 6207ead..37aa555 100644 --- a/components/stats/BudgetRemaining.tsx +++ b/components/stats/BudgetRemaining.tsx @@ -1,35 +1,52 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { Text, StyleSheet } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; +import useFetch from '../../hooks/useFetch'; -interface BudgetRemainingProps { - budget: number; - spent: number; -} +const styles = StyleSheet.create({ + text: { + fontSize: 26, + }, + boldText: { + fontWeight: 'bold', + }, +}); -const BudgetRemaining: React.FC = ({ budget, spent }) => { +const BudgetRemaining = () => { 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 = 'budget'", + 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; - 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) { + return Loading...; + } return ( - + You have {remaining.toFixed(2)}€ left. ); From d9db03af613915eddeb2f80183904051cc80f041 Mon Sep 17 00:00:00 2001 From: Walcher Date: Fri, 5 Jan 2024 14:32:41 +0100 Subject: [PATCH 3/6] added SavingsOverview --- components/stats/SavingsOverview.tsx | 62 ++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 components/stats/SavingsOverview.tsx diff --git a/components/stats/SavingsOverview.tsx b/components/stats/SavingsOverview.tsx new file mode 100644 index 0000000..a79234a --- /dev/null +++ b/components/stats/SavingsOverview.tsx @@ -0,0 +1,62 @@ +import React, { useState, useEffect } from 'react'; +import { View, Text, StyleSheet } from 'react-native'; +import { useTheme } from '../../app/contexts/ThemeContext'; +import useFetch from '../../hooks/useFetch'; + +const SavingsOverview = () => { + const { colors } = useTheme(); + const [saved, setSaved] = useState(0); + 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'", + args: [] + }; + + const goalQuery = { + sql: "SELECT SUM(allocated_amount) as total FROM category WHERE type = 'budget'", + args: [] + }; + + 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) { + return Loading...; + } + + return ( + + + You have saved {saved.toFixed(2)}€ out of your goal of {goal.toFixed(2)}€. + + + ); +}; + +export default SavingsOverview; \ No newline at end of file From 7e29e4f0169ed241c95cfc26309ffe44ea718a2e Mon Sep 17 00:00:00 2001 From: Walcher Date: Fri, 5 Jan 2024 14:43:15 +0100 Subject: [PATCH 4/6] added "Financial" Advice --- app/(tabs)/stats/index.tsx | 4 ++ components/stats/FinancialAdvice.tsx | 59 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 components/stats/FinancialAdvice.tsx diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/stats/index.tsx index 0870688..11f17fc 100644 --- a/app/(tabs)/stats/index.tsx +++ b/app/(tabs)/stats/index.tsx @@ -7,6 +7,7 @@ import CategoryProgressBarList from '../../../components/stats/CategoryProgressB 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(); @@ -58,6 +59,9 @@ export default function Page() { + + + diff --git a/components/stats/FinancialAdvice.tsx b/components/stats/FinancialAdvice.tsx new file mode 100644 index 0000000..8f136ac --- /dev/null +++ b/components/stats/FinancialAdvice.tsx @@ -0,0 +1,59 @@ +import React, { useState, useEffect } from 'react'; +import { StyleSheet, View, Text } from 'react-native'; +import { useTheme } from '../../app/contexts/ThemeContext'; + +const FinancialAdvice = () => { + const tips = [ + "Maybe you shouldn't have bought that full-price video game. But who needs savings when you have high scores, right?", + "That daily gourmet coffee is essential, isn't it? Who needs a retirement fund when you've got caffeine!", + "Oh, another pair of designer shoes? Because the other twenty pairs just aren't enough.", + "A luxury car to drive two blocks? Obviously, walking is for peasants.", + "Sure, subscribe to all streaming services. Who needs to socialize outside when you can binge-watch shows alone forever?", + "A gym membership you never use? At least your wallet's getting a workout.", + "Booking another expensive vacation? It's not like you need to save for a rainy day or anything.", + "Another impulse purchase online? Because 'limited time offer' is definitely not a marketing tactic.", + "Eating out for every meal? Clearly, cooking at home is way too mainstream.", + "Upgrading to the latest phone model again? It must be tough having a phone that's 6 months old." + ]; + + const { colors } = useTheme(); + + const [tip, setTip] = useState(''); + + useEffect(() => { + // Change the tip every 10 seconds + const intervalId = setInterval(() => { + const randomTip = tips[Math.floor(Math.random() * tips.length)]; + setTip(randomTip); + }, 10000); + + // Clear the interval on component unmount + return () => clearInterval(intervalId); + }, [tips]); + + const styles = StyleSheet.create({ + container: { + margin: 10, + borderRadius: 5, + alignItems: 'center', + justifyContent: 'center' + }, + text: { + fontSize: 26, + color: colors.primaryText + }, + boldText: { + fontWeight: 'bold' + } + }); + + return ( + + + {tip} + + + ); +}; + +export default FinancialAdvice; From 8b68a7b7b0e5b42977ce00c03ab43c63a88964cc Mon Sep 17 00:00:00 2001 From: Walcher Date: Fri, 5 Jan 2024 14:50:33 +0100 Subject: [PATCH 5/6] made it possible to pass an override backgroundcolor to widget --- components/stats/Widget.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/stats/Widget.tsx b/components/stats/Widget.tsx index f4e3e0b..182def4 100644 --- a/components/stats/Widget.tsx +++ b/components/stats/Widget.tsx @@ -1,5 +1,5 @@ import React, { ReactNode } from 'react'; -import { View, StyleSheet, Text, Image } from 'react-native'; // Add the missing import statement for Image +import { View, StyleSheet, Text, Image } from 'react-native'; import { useTheme } from '../../app/contexts/ThemeContext'; interface WidgetProps { @@ -7,14 +7,17 @@ interface WidgetProps { text?: string; children?: ReactNode; image?: any; + backgroundColor?: string; } -const Widget: React.FC = ({ title, text, children, image }) => { // Add the 'image' prop to the destructuring +const Widget: React.FC = ({ title, text, children, image, backgroundColor }) => { const { colors } = useTheme(); + const actualBackgroundColor = backgroundColor ? backgroundColor : colors.widgetBackgroundColor; + const styles = StyleSheet.create({ widgetContainer: { - backgroundColor: colors.widgetBackgroundColor, + backgroundColor: actualBackgroundColor, borderColor: colors.widgetBorderColor, borderRadius: 5, padding: 16, From c0a70a0ed5c50d9ea7e2b0bb45bd2fec74b29a44 Mon Sep 17 00:00:00 2001 From: Walcher Date: Fri, 5 Jan 2024 14:52:20 +0100 Subject: [PATCH 6/6] finishing up --- app/(tabs)/stats/index.tsx | 26 ++++---------------------- components/stats/Widget.tsx | 1 + 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/stats/index.tsx index 11f17fc..cac1e04 100644 --- a/app/(tabs)/stats/index.tsx +++ b/app/(tabs)/stats/index.tsx @@ -18,25 +18,7 @@ export default function Page() { const spent = 120.75; const budget = 696.96; - const BudgetData = [ - { name: 'Utilities', color: '#20B2AA', maxValue: 80, currentValue: 46 }, - { name: 'Food', color: '#FF6347', maxValue: 88, currentValue: 31 }, - ]; - - const SavingsData = [ - { name: 'Education', color: '#FF6347', maxValue: 135, currentValue: 0 }, - { name: 'Rent', color: '#DA70D6', maxValue: 140, currentValue: 96 }, - { name: 'Food', color: '#F08080', maxValue: 84, currentValue: 78 }, - { name: 'Healthcare', color: '#20B2AA', maxValue: 134, currentValue: 48 }, - { name: 'Healthcare', color: '#32CD32', maxValue: 119, currentValue: 69 }, - { name: 'Clothing', color: '#32CD32', maxValue: 115, currentValue: 99 }, - ]; - - const categoryData = [ - { category: 'Food', value: 50 }, - { category: 'Rent', value: 300 }, - { category: 'Utilities', value: 100 }, - ]; + const styles = StyleSheet.create({ container: { @@ -51,6 +33,9 @@ export default function Page() { + + + @@ -59,9 +44,6 @@ export default function Page() { - - - diff --git a/components/stats/Widget.tsx b/components/stats/Widget.tsx index 182def4..3162d62 100644 --- a/components/stats/Widget.tsx +++ b/components/stats/Widget.tsx @@ -13,6 +13,7 @@ interface WidgetProps { const Widget: React.FC = ({ title, text, children, image, backgroundColor }) => { const { colors } = useTheme(); + const actualBackgroundColor = backgroundColor ? backgroundColor : colors.widgetBackgroundColor; const styles = StyleSheet.create({