130 lines
4.8 KiB
TypeScript
130 lines
4.8 KiB
TypeScript
import { FontAwesome } from '@expo/vector-icons';
|
|
import { Link, useFocusEffect } from 'expo-router';
|
|
import { useCallback, useState } from 'react';
|
|
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
import { BarChart } from 'react-native-gifted-charts';
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
import TransactionList from '../../components/TransactionList';
|
|
import { getDB } from '../../db';
|
|
import { Transaction } from '../../types';
|
|
|
|
export default function DashboardScreen() {
|
|
const insets = useSafeAreaInsets();
|
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
|
const [balance, setBalance] = useState(0);
|
|
const [income, setIncome] = useState(0);
|
|
const [expense, setExpense] = useState(0);
|
|
const [chartData, setChartData] = useState<any[]>([]);
|
|
|
|
const fetchData = useCallback(async () => {
|
|
try {
|
|
const db = getDB();
|
|
const result = await db.getAllAsync<Transaction>('SELECT * FROM transactions ORDER BY date DESC');
|
|
setTransactions(result);
|
|
|
|
let totalIncome = 0;
|
|
let totalExpense = 0;
|
|
const expensesByCategory: Record<string, number> = {};
|
|
|
|
result.forEach(t => {
|
|
if (t.type === 'income') {
|
|
totalIncome += t.amount;
|
|
} else {
|
|
totalExpense += t.amount;
|
|
expensesByCategory[t.category] = (expensesByCategory[t.category] || 0) + t.amount;
|
|
}
|
|
});
|
|
|
|
setIncome(totalIncome);
|
|
setExpense(totalExpense);
|
|
setBalance(totalIncome - totalExpense);
|
|
|
|
// Prepare Chart Data
|
|
const data = Object.keys(expensesByCategory).map(cat => ({
|
|
value: expensesByCategory[cat],
|
|
label: cat.substring(0, 3), // Short label
|
|
frontColor: '#EF4444',
|
|
}));
|
|
setChartData(data);
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching data', error);
|
|
}
|
|
}, []);
|
|
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
fetchData();
|
|
}, [fetchData])
|
|
);
|
|
|
|
return (
|
|
<View className="flex-1 bg-gray-100 dark:bg-gray-900" style={{ paddingTop: insets.top }}>
|
|
<ScrollView className="flex-1 px-6">
|
|
{/* Header */}
|
|
<View className="flex-row justify-between items-center py-6">
|
|
<View>
|
|
<Text className="text-gray-500 text-sm">Total Balance</Text>
|
|
<Text className="text-4xl font-bold text-gray-900 dark:text-white">€{balance.toFixed(2)}</Text>
|
|
</View>
|
|
<Link href="/modal" asChild>
|
|
<TouchableOpacity className="bg-blue-600 w-12 h-12 rounded-full items-center justify-center shadow-lg">
|
|
<FontAwesome name="plus" size={20} color="white" />
|
|
</TouchableOpacity>
|
|
</Link>
|
|
</View>
|
|
|
|
{/* Summary Cards */}
|
|
<View className="flex-row space-x-4 mb-8">
|
|
<View className="flex-1 bg-green-500 p-4 rounded-2xl shadow-sm">
|
|
<View className="flex-row items-center mb-2">
|
|
<View className="bg-white/20 w-8 h-8 rounded-full items-center justify-center mr-2">
|
|
<FontAwesome name="arrow-up" size={12} color="white" />
|
|
</View>
|
|
<Text className="text-white/80 text-sm font-medium">Income</Text>
|
|
</View>
|
|
<Text className="text-white text-xl font-bold">€{income.toFixed(2)}</Text>
|
|
</View>
|
|
<View className="flex-1 bg-red-500 p-4 rounded-2xl shadow-sm">
|
|
<View className="flex-row items-center mb-2">
|
|
<View className="bg-white/20 w-8 h-8 rounded-full items-center justify-center mr-2">
|
|
<FontAwesome name="arrow-down" size={12} color="white" />
|
|
</View>
|
|
<Text className="text-white/80 text-sm font-medium">Expenses</Text>
|
|
</View>
|
|
<Text className="text-white text-xl font-bold">€{expense.toFixed(2)}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Spending Chart */}
|
|
{chartData.length > 0 && (
|
|
<View className="bg-white dark:bg-gray-800 p-4 rounded-2xl shadow-sm mb-6">
|
|
<Text className="text-lg font-bold text-gray-900 dark:text-white mb-4">Expenses by Category</Text>
|
|
<BarChart
|
|
data={chartData}
|
|
barWidth={30}
|
|
noOfSections={3}
|
|
barBorderRadius={4}
|
|
frontColor="#EF4444"
|
|
yAxisThickness={0}
|
|
xAxisThickness={0}
|
|
hideRules
|
|
isAnimated
|
|
/>
|
|
</View>
|
|
)}
|
|
|
|
{/* Recent Transactions */}
|
|
<View className="mb-4 flex-row justify-between items-end">
|
|
<Text className="text-xl font-bold text-gray-900 dark:text-white">Recent Transactions</Text>
|
|
<TouchableOpacity>
|
|
<Text className="text-blue-600 font-medium">See All</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<TransactionList transactions={transactions} />
|
|
</ScrollView>
|
|
</View>
|
|
);
|
|
}
|