diff --git a/app/(tabs)/stats/index.tsx b/app/(tabs)/stats/index.tsx
index b7e477d..cac1e04 100644
--- a/app/(tabs)/stats/index.tsx
+++ b/app/(tabs)/stats/index.tsx
@@ -6,6 +6,8 @@ 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();
@@ -16,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: {
@@ -49,13 +33,19 @@ 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.
);
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;
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
diff --git a/components/stats/Widget.tsx b/components/stats/Widget.tsx
index f4e3e0b..3162d62 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,18 @@ 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,
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)