everything refactored

This commit is contained in:
Walcher 2024-01-02 14:38:47 +01:00
parent 0240eb2562
commit 62e71d1b49
10 changed files with 407 additions and 50 deletions

View file

@ -1,61 +1,69 @@
import { Query } from 'expo-sqlite';
import { StyleSheet, Text, View } from 'react-native';
import { addCategory, deleteDatabase, deleteExpenses, executeQuery, initDatabase } from '../../../services/database';
import React from 'react';
import { StyleSheet, View, ScrollView } from 'react-native';
import BudgetOverview from '../../../components/stats/BudgetOverview';
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';
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 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: {
flex: 1,
justifyContent: 'space-evenly',
backgroundColor: colors.backgroundColor,
marginTop: 50,
alignItems: 'center',
},
text: {
fontSize: 40,
color: "yellow",
}
});
return (
<View style={styles.container}>
<Text style={styles.text} onPress={() => {
deleteExpenses().then(() => {
console.log("Expenses Deleted!");
})
}}>Reset Expenses</Text>
<Text style={styles.text} onPress={() => {
deleteDatabase();
console.log("Database Deleted!");
}}>Reset Database</Text>
<Text style={styles.text} onPress={() => {
initDatabase().then(() => {
console.log("Database Initialized!");
});
}}>Init Database</Text>
<Text style={styles.text} onPress={() => {
addCategory("Category", "green", "expense", 500).then(() => {
const getCategoryQuery: Query = {sql: "SELECT guid FROM category", args: []};
executeQuery(getCategoryQuery).then((result) => {
if("rows" in result[0]) {
console.log(result[0]["rows"]);
}
})
console.log("Category added with success!");
})
}}>Add new Category Expense</Text>
<Text style={styles.text} onPress={() => {
addCategory("Category", "yellow", "saving", 420).then(() => {
const getCategoryQuery: Query = {sql: "SELECT guid FROM category", args: []};
executeQuery(getCategoryQuery).then((result) => {
if("rows" in result[0]) {
console.log(result[0]["rows"]);
}
})
console.log("Category added with success!");
})
}}>Add new Category Savings</Text>
</View>);
<View style={styles.container}>
<ScrollView>
<DebugMenu />
<Widget title="Budget Overview" />
<Widget>
<BudgetRemaining budget={budget} spent={spent} />
</Widget>
<Widget text='#TODO Insert Pie-Chart' image='../../../assets/images/8b14el.jpg'/>
<Widget>
<BudgetOverview budget={budget} spent={spent} />
</Widget>
<Widget title="Budget Progress">
<CategoryProgressBarList categories={BudgetData} />
</Widget>
<Widget title="Savings Progress">
<CategoryProgressBarList categories={SavingsData} />
</Widget>
</ScrollView>
</View>
);
}

BIN
assets/images/8b14el.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View file

@ -0,0 +1,38 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useTheme } from '../../app/contexts/ThemeContext';
interface StatsBudgetProps {
spent: number;
budget: number;
}
const BudgetOverview: React.FC<StatsBudgetProps> = ({ spent, budget }) => {
const { colors } = useTheme();
const styles = StyleSheet.create({
container: {
margin: 10,
borderRadius: 5,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 26,
color: colors.primaryText,
},
boldText: {
fontWeight: 'bold',
},
});
return (
<View style={styles.container}>
<Text style={styles.text}>
You have spent <Text style={styles.boldText}>{spent.toFixed(2)}</Text> out of your budget of <Text style={styles.boldText}>{budget.toFixed(2)}</Text>.
</Text>
</View>
);
};
export default BudgetOverview

View file

@ -0,0 +1,38 @@
import React from 'react';
import { Text, StyleSheet } from 'react-native';
import { useTheme } from '../../app/contexts/ThemeContext';
interface BudgetRemainingProps {
budget: number;
spent: number;
}
const BudgetRemaining: React.FC<BudgetRemainingProps> = ({ budget, spent }) => {
const { colors, theme } = useTheme();
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',
},
});
return (
<Text style={styles.text}>
You have <Text style={styles.boldText}>{remaining.toFixed(2)}</Text> left.
</Text>
);
};
export default BudgetRemaining;

View file

@ -0,0 +1,69 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useTheme } from '../../app/contexts/ThemeContext';
interface CategoryProgressBarProps {
categoryName: string;
color?: string;
maxValue: number;
currentValue: number;
}
const CategoryProgressBar: React.FC<CategoryProgressBarProps> = ({
categoryName,
color,
maxValue,
currentValue,
}) => {
const { colors } = useTheme();
const progress = (currentValue / maxValue) * 100;
const progressText = `${currentValue}€ / ${maxValue}`;
const dynamicStyles = StyleSheet.create({
progressBarFill: {
height: '100%',
width: `${progress}%`,
backgroundColor: color || colors.accentColor,
alignItems: 'center',
justifyContent: 'center',
},
progressText: {
color: colors.primaryText,
fontSize: 20,
fontWeight: 'bold',
},
});
const styles = StyleSheet.create({
container: {
padding: 10,
},
progressBarContainer: {
flexDirection: 'row',
height: 50,
backgroundColor: colors.elementSelectedColor,
borderRadius: 15,
overflow: 'hidden',
marginTop: 4,
},
categoryName: {
color: colors.primaryText,
fontSize: 20,
fontWeight: 'bold',
},
});
return (
<View style={styles.container}>
<Text style={styles.categoryName}>{categoryName}</Text>
<View style={styles.progressBarContainer}>
<View style={dynamicStyles.progressBarFill}>
<Text style={dynamicStyles.progressText}>{progressText}</Text>
</View>
</View>
</View>
);
};
export default CategoryProgressBar;

View file

@ -0,0 +1,61 @@
import React, { useState } from 'react';
import { View, Text, Button, StyleSheet, TouchableOpacity } from 'react-native';
import CategoryProgressBar from './CategoryProgressBar';
import { useTheme } from '../../app/contexts/ThemeContext';
interface CategoryItem {
name: string;
color: string;
maxValue: number;
currentValue: number;
}
interface CategoryProgressBarListProps {
categories: CategoryItem[];
}
const MAX_VISIBLE_BARS = 4;
const CategoryProgressBarList: React.FC<CategoryProgressBarListProps> = ({ categories }) => {
const [visibleBars, setVisibleBars] = useState(MAX_VISIBLE_BARS);
const showMore = () => {
setVisibleBars(prevVisibleBars => prevVisibleBars + MAX_VISIBLE_BARS);
};
return (
<View>
{categories.slice(0, visibleBars).map((category, index) => (
<CategoryProgressBar
key={index}
categoryName={category.name}
color={category.color}
maxValue={category.maxValue}
currentValue={category.currentValue}
/>
))}
{visibleBars < categories.length && (
<TouchableOpacity style={styles.showMoreButton} onPress={showMore}>
<Text style={styles.buttonText}>Show More</Text>
</TouchableOpacity>
)}
</View>
);
};
const styles = StyleSheet.create({
showMoreButton: {
backgroundColor: '#EF6C00',
padding: 10,
borderRadius: 5,
margin: 10,
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
});
export default CategoryProgressBarList;

View file

@ -0,0 +1 @@
//honestly just fuck graphs

View file

@ -0,0 +1,63 @@
import React, { ReactNode } from 'react';
import { View, StyleSheet, Text, Image } from 'react-native'; // Add the missing import statement for Image
import { useTheme } from '../../app/contexts/ThemeContext';
interface WidgetProps {
title?: string;
text?: string;
children?: ReactNode;
image?: string;
}
const Widget: React.FC<WidgetProps> = ({ title, text, children, image }) => { // Add the 'image' prop to the destructuring
const { colors } = useTheme();
const styles = StyleSheet.create({
widgetContainer: {
backgroundColor: colors.widgetBackgroundColor,
borderColor: colors.widgetBorderColor,
borderRadius: 5,
padding: 16,
marginVertical: 8,
marginHorizontal: 10,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,
},
widgetTitle: {
color: colors.primaryText,
fontSize: 26,
fontWeight: 'bold',
marginBottom: 8,
textAlign: 'center',
},
widgetText: {
color: colors.primaryText,
fontSize: 16,
marginBottom: 8,
textAlign: 'center',
},
imageStyle: {
width: '100%',
height: 200,
borderRadius: 5,
marginBottom: 8,
},
});
return (
<View style={styles.widgetContainer}>
{!!title && <Text style={styles.widgetTitle}>{title}</Text>}
{!!text && <Text style={styles.widgetText}>{text}</Text>}
{!!image && <Image source={{ uri: image }} style={styles.imageStyle} />}
{children}
</View>
);
};
export default Widget;

View file

@ -12,6 +12,9 @@ export default {
elementDefaultColor: "#E0E0E0",
elementSelectedColor: "#9E9E9E",
accentColor: "#EF6C00",
widgetBackgroundColor: "#F7F7F7",
widgetBorderColor: "#E0E0E0",
},
dark: {
primaryText: "#FFFFFF",
@ -26,5 +29,8 @@ export default {
elementDefaultColor: "#535353",
elementSelectedColor: "#B3B3B3",
accentColor: "#EF6C00",
widgetBackgroundColor: "#252525",
widgetBorderColor: "#535353",
}
}

73
services/DebugMenu.tsx Normal file
View file

@ -0,0 +1,73 @@
import React from 'react';
import { View, Button, Alert } from 'react-native';
import { addCategory, addExpense, deleteExpenses, deleteCategories, DEV_populateDatabase } from './database';
import uuid from 'react-native-uuid';
const randomColors = ["red", "blue", "green", "purple", "yellow"];
const getRandomColor = () => {
return randomColors[Math.floor(Math.random() * randomColors.length)];
};
const getRandomName = () => {
return `RandomName-${Math.floor(Math.random() * 1000)}`;
};
const getRandomNumber = () => {
return Math.floor(Math.random() * 1000);
};
const DebugMenu = () => {
const handleNukeDatabase = () => {
return deleteExpenses(), deleteCategories()
};
const handlePopulateDatabase = () => {
return DEV_populateDatabase()
};
const handleDeleteExpenses = () => {
return deleteExpenses();
}
const handleDeleteCategories = () => {
return deleteCategories();
}
//for some reason this function does not work
const handleAddCategory = () => {
const name = getRandomName();
const color = getRandomColor();
const allocated_amount = getRandomNumber();
const type = "expense";
addCategory(name, color, type, allocated_amount)
.then(() => Alert.alert("Category Added", `Name: ${name}, Color: ${color}`))
.catch((error: any) => console.error("Error adding category: ", error));
};
//for some reason this function does not work
const handleAddExpense = () => {
const name = getRandomName();
const categoryGuid = uuid.v4().toString();
const datetime = new Date().toISOString();
const amount = Math.floor(Math.random() * 1000);
addExpense(name, categoryGuid, datetime, amount)
.then(() => Alert.alert("Expense Added", `Name: ${name}, Amount: ${amount}`))
.catch((error: any) => console.error("Error adding expense: ", error));
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Nuke Database" onPress={handleNukeDatabase} />
<Button title="DEV_populateDatabase" onPress={handlePopulateDatabase} />
<Button title="Delete All Expenses" onPress={handleDeleteExpenses} />
<Button title="Delete All Categories" onPress={handleDeleteCategories} />
<Button title="Add Random Category" onPress={handleAddCategory} />
<Button title="Add Random Expense" onPress={handleAddExpense} />
</View>
);
};
export default DebugMenu;