Skip to content

Commit 07c850d

Browse files
authored
Add files via upload
1 parent e08b3b5 commit 07c850d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+9809
-0
lines changed

App.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import { Wallet } from 'lucide-react';
3+
import { TransactionForm } from './components/TransactionForm';
4+
import { TransactionList } from './components/TransactionList';
5+
import { Dashboard } from './components/Dashboard';
6+
import { WalletSelector } from './components/WalletSelector';
7+
import { ErrorBoundary } from './components/ErrorBoundary';
8+
9+
function App() {
10+
return (
11+
<ErrorBoundary>
12+
<div className="min-h-screen bg-gray-100">
13+
<nav className="bg-white shadow-sm">
14+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
15+
<div className="flex justify-between h-16">
16+
<div className="flex items-center">
17+
<Wallet className="w-8 h-8 text-blue-600" />
18+
<span className="ml-2 text-xl font-semibold text-gray-900">
19+
Smart Finance Tracker
20+
</span>
21+
</div>
22+
<div className="flex items-center">
23+
<WalletSelector />
24+
</div>
25+
</div>
26+
</div>
27+
</nav>
28+
29+
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
30+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
31+
<div className="lg:col-span-2">
32+
<Dashboard />
33+
<div className="mt-8">
34+
<TransactionList />
35+
</div>
36+
</div>
37+
<div>
38+
<TransactionForm />
39+
</div>
40+
</div>
41+
</main>
42+
</div>
43+
</ErrorBoundary>
44+
);
45+
}
46+
47+
export default App;

components/AutoSavingsTransfer.tsx

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { ArrowUpRight, Loader2, AlertCircle } from 'lucide-react';
3+
import { useFinanceStore } from '../store/useFinanceStore';
4+
import { useWalletConnection } from '../hooks/useWalletConnection';
5+
import { executeTransfer } from '../services/transactionService';
6+
import { formatUSD } from '../utils/currency';
7+
import { getWalletProvider } from '../services/walletProvider';
8+
import { validateWalletAddress } from '../utils/validation';
9+
10+
export function AutoSavingsTransfer() {
11+
const { stats } = useFinanceStore();
12+
const { connected, address, type } = useWalletConnection();
13+
const [isTransferring, setIsTransferring] = useState(false);
14+
const [error, setError] = useState<string | null>(null);
15+
const [transactionHash, setTransactionHash] = useState<string | null>(null);
16+
const [isValidAddress, setIsValidAddress] = useState(true);
17+
18+
useEffect(() => {
19+
if (address) {
20+
setIsValidAddress(validateWalletAddress(address));
21+
}
22+
}, [address]);
23+
24+
const handleTransfer = async () => {
25+
if (!connected || !address || stats.savings <= 0 || !type) return;
26+
27+
if (!isValidAddress) {
28+
setError('Invalid wallet address. Please check your connected wallet.');
29+
return;
30+
}
31+
32+
setIsTransferring(true);
33+
setError(null);
34+
setTransactionHash(null);
35+
36+
try {
37+
const provider = getWalletProvider(type);
38+
console.log('Initiating transfer:', {
39+
to: address,
40+
amount: stats.savings,
41+
walletType: type
42+
});
43+
44+
const transaction = await executeTransfer(
45+
provider,
46+
address,
47+
stats.savings,
48+
type
49+
);
50+
51+
setTransactionHash(transaction.hash);
52+
console.log('Transfer successful:', transaction);
53+
} catch (err: any) {
54+
console.error('Transfer failed:', err);
55+
setError(err.message || 'Failed to transfer savings. Please try again.');
56+
} finally {
57+
setIsTransferring(false);
58+
}
59+
};
60+
61+
if (!connected) {
62+
return (
63+
<div className="bg-white p-6 rounded-lg shadow-md">
64+
<h2 className="text-xl font-semibold mb-4">Auto-Save to Wallet</h2>
65+
<p className="text-gray-600 mb-4">
66+
Connect your {type === 'core' ? 'Core Wallet' : 'MetaMask'} to enable automatic savings transfer
67+
</p>
68+
</div>
69+
);
70+
}
71+
72+
return (
73+
<div className="bg-white p-6 rounded-lg shadow-md">
74+
<h2 className="text-xl font-semibold mb-4">Auto-Save to Wallet</h2>
75+
<div className="space-y-4">
76+
<div className="flex justify-between items-center">
77+
<span className="text-gray-600">Available Savings</span>
78+
<span className="font-semibold text-green-600">
79+
{formatUSD(stats.savings)}
80+
</span>
81+
</div>
82+
83+
<div className="text-sm text-gray-600">
84+
<p>Connected Wallet: {type === 'core' ? 'Core Wallet' : 'MetaMask'}</p>
85+
<div className="flex items-center gap-2">
86+
<p>Address: {address}</p>
87+
{!isValidAddress && (
88+
<AlertCircle className="w-4 h-4 text-red-500" />
89+
)}
90+
</div>
91+
</div>
92+
93+
<button
94+
onClick={handleTransfer}
95+
disabled={isTransferring || stats.savings <= 0 || !isValidAddress}
96+
className="w-full flex items-center justify-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
97+
>
98+
{isTransferring ? (
99+
<Loader2 className="w-5 h-5 animate-spin" />
100+
) : (
101+
<>
102+
<span>Transfer Savings to Wallet</span>
103+
<ArrowUpRight className="w-5 h-5 ml-2" />
104+
</>
105+
)}
106+
</button>
107+
108+
{error && (
109+
<div className="p-4 bg-red-50 rounded-md">
110+
<p className="text-sm text-red-600 flex items-center gap-2">
111+
<AlertCircle className="w-4 h-4" />
112+
{error}
113+
</p>
114+
</div>
115+
)}
116+
117+
{transactionHash && (
118+
<div className="mt-4 p-4 bg-green-50 rounded-md">
119+
<p className="text-sm text-green-600">
120+
Transfer successful!
121+
</p>
122+
<p className="text-xs text-green-500 break-all mt-1">
123+
Transaction hash: {transactionHash}
124+
</p>
125+
</div>
126+
)}
127+
128+
{stats.savings <= 0 && (
129+
<p className="text-sm text-gray-500 text-center">
130+
No savings available to transfer
131+
</p>
132+
)}
133+
</div>
134+
</div>
135+
);
136+
}

components/Dashboard.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import React from 'react';
2+
import { Line } from 'react-chartjs-2';
3+
import {
4+
Chart as ChartJS,
5+
CategoryScale,
6+
LinearScale,
7+
PointElement,
8+
LineElement,
9+
Title,
10+
Tooltip,
11+
Legend,
12+
} from 'chart.js';
13+
import { useFinanceStore } from '../store/useFinanceStore';
14+
import { formatCurrency } from '../utils/formatters';
15+
import { AutoSavingsTransfer } from './AutoSavingsTransfer';
16+
17+
ChartJS.register(
18+
CategoryScale,
19+
LinearScale,
20+
PointElement,
21+
LineElement,
22+
Title,
23+
Tooltip,
24+
Legend
25+
);
26+
27+
export function Dashboard() {
28+
const { stats, predictions } = useFinanceStore();
29+
30+
const chartData = {
31+
labels: ['Current', 'Predicted'],
32+
datasets: predictions.map((pred) => ({
33+
label: pred.category,
34+
data: [
35+
stats.budgets.find((b) => b.category === pred.category)?.spent || 0,
36+
pred.amount,
37+
],
38+
borderColor: `hsl(${Math.random() * 360}, 70%, 50%)`,
39+
tension: 0.4,
40+
})),
41+
};
42+
43+
return (
44+
<div className="space-y-6">
45+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
46+
<div className="bg-white p-6 rounded-lg shadow-md">
47+
<h2 className="text-xl font-semibold mb-4">Financial Overview</h2>
48+
<div className="space-y-4">
49+
<div className="flex justify-between items-center">
50+
<span className="text-gray-600">Total Income</span>
51+
<span className="font-semibold text-green-600">
52+
{formatCurrency(stats.totalIncome)}
53+
</span>
54+
</div>
55+
<div className="flex justify-between items-center">
56+
<span className="text-gray-600">Total Expenses</span>
57+
<span className="font-semibold text-red-600">
58+
{formatCurrency(stats.totalExpenses)}
59+
</span>
60+
</div>
61+
<div className="flex justify-between items-center">
62+
<span className="text-gray-600">Savings</span>
63+
<span className="font-semibold text-blue-600">
64+
{formatCurrency(stats.savings)}
65+
</span>
66+
</div>
67+
</div>
68+
</div>
69+
70+
<div className="bg-white p-6 rounded-lg shadow-md">
71+
<h2 className="text-xl font-semibold mb-4">AI Predictions</h2>
72+
<Line
73+
data={chartData}
74+
options={{
75+
responsive: true,
76+
plugins: {
77+
legend: {
78+
position: 'bottom',
79+
},
80+
title: {
81+
display: true,
82+
text: 'Expense Predictions by Category',
83+
},
84+
},
85+
}}
86+
/>
87+
</div>
88+
</div>
89+
90+
<AutoSavingsTransfer />
91+
</div>
92+
);
93+
}

components/ErrorBoundary.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React, { Component, ErrorInfo, ReactNode } from 'react';
2+
3+
interface Props {
4+
children: ReactNode;
5+
}
6+
7+
interface State {
8+
hasError: boolean;
9+
error: Error | null;
10+
}
11+
12+
export class ErrorBoundary extends Component<Props, State> {
13+
public state: State = {
14+
hasError: false,
15+
error: null,
16+
};
17+
18+
public static getDerivedStateFromError(error: Error): State {
19+
return { hasError: true, error };
20+
}
21+
22+
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
23+
console.error('Uncaught error:', error, errorInfo);
24+
}
25+
26+
public render() {
27+
if (this.state.hasError) {
28+
return (
29+
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
30+
<div className="bg-white p-8 rounded-lg shadow-md max-w-md w-full">
31+
<h2 className="text-2xl font-bold text-red-600 mb-4">Something went wrong</h2>
32+
<p className="text-gray-600 mb-4">
33+
{this.state.error?.message || 'An unexpected error occurred'}
34+
</p>
35+
<button
36+
onClick={() => window.location.reload()}
37+
className="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
38+
>
39+
Reload Page
40+
</button>
41+
</div>
42+
</div>
43+
);
44+
}
45+
46+
return this.props.children;
47+
}
48+
}

0 commit comments

Comments
 (0)