-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e08b3b5
commit 07c850d
Showing
41 changed files
with
9,809 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React from 'react'; | ||
import { Wallet } from 'lucide-react'; | ||
import { TransactionForm } from './components/TransactionForm'; | ||
import { TransactionList } from './components/TransactionList'; | ||
import { Dashboard } from './components/Dashboard'; | ||
import { WalletSelector } from './components/WalletSelector'; | ||
import { ErrorBoundary } from './components/ErrorBoundary'; | ||
|
||
function App() { | ||
return ( | ||
<ErrorBoundary> | ||
<div className="min-h-screen bg-gray-100"> | ||
<nav className="bg-white shadow-sm"> | ||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | ||
<div className="flex justify-between h-16"> | ||
<div className="flex items-center"> | ||
<Wallet className="w-8 h-8 text-blue-600" /> | ||
<span className="ml-2 text-xl font-semibold text-gray-900"> | ||
Smart Finance Tracker | ||
</span> | ||
</div> | ||
<div className="flex items-center"> | ||
<WalletSelector /> | ||
</div> | ||
</div> | ||
</div> | ||
</nav> | ||
|
||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | ||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8"> | ||
<div className="lg:col-span-2"> | ||
<Dashboard /> | ||
<div className="mt-8"> | ||
<TransactionList /> | ||
</div> | ||
</div> | ||
<div> | ||
<TransactionForm /> | ||
</div> | ||
</div> | ||
</main> | ||
</div> | ||
</ErrorBoundary> | ||
); | ||
} | ||
|
||
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import { ArrowUpRight, Loader2, AlertCircle } from 'lucide-react'; | ||
import { useFinanceStore } from '../store/useFinanceStore'; | ||
import { useWalletConnection } from '../hooks/useWalletConnection'; | ||
import { executeTransfer } from '../services/transactionService'; | ||
import { formatUSD } from '../utils/currency'; | ||
import { getWalletProvider } from '../services/walletProvider'; | ||
import { validateWalletAddress } from '../utils/validation'; | ||
|
||
export function AutoSavingsTransfer() { | ||
const { stats } = useFinanceStore(); | ||
const { connected, address, type } = useWalletConnection(); | ||
const [isTransferring, setIsTransferring] = useState(false); | ||
const [error, setError] = useState<string | null>(null); | ||
const [transactionHash, setTransactionHash] = useState<string | null>(null); | ||
const [isValidAddress, setIsValidAddress] = useState(true); | ||
|
||
useEffect(() => { | ||
if (address) { | ||
setIsValidAddress(validateWalletAddress(address)); | ||
} | ||
}, [address]); | ||
|
||
const handleTransfer = async () => { | ||
if (!connected || !address || stats.savings <= 0 || !type) return; | ||
|
||
if (!isValidAddress) { | ||
setError('Invalid wallet address. Please check your connected wallet.'); | ||
return; | ||
} | ||
|
||
setIsTransferring(true); | ||
setError(null); | ||
setTransactionHash(null); | ||
|
||
try { | ||
const provider = getWalletProvider(type); | ||
console.log('Initiating transfer:', { | ||
to: address, | ||
amount: stats.savings, | ||
walletType: type | ||
}); | ||
|
||
const transaction = await executeTransfer( | ||
provider, | ||
address, | ||
stats.savings, | ||
type | ||
); | ||
|
||
setTransactionHash(transaction.hash); | ||
console.log('Transfer successful:', transaction); | ||
} catch (err: any) { | ||
console.error('Transfer failed:', err); | ||
setError(err.message || 'Failed to transfer savings. Please try again.'); | ||
} finally { | ||
setIsTransferring(false); | ||
} | ||
}; | ||
|
||
if (!connected) { | ||
return ( | ||
<div className="bg-white p-6 rounded-lg shadow-md"> | ||
<h2 className="text-xl font-semibold mb-4">Auto-Save to Wallet</h2> | ||
<p className="text-gray-600 mb-4"> | ||
Connect your {type === 'core' ? 'Core Wallet' : 'MetaMask'} to enable automatic savings transfer | ||
</p> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="bg-white p-6 rounded-lg shadow-md"> | ||
<h2 className="text-xl font-semibold mb-4">Auto-Save to Wallet</h2> | ||
<div className="space-y-4"> | ||
<div className="flex justify-between items-center"> | ||
<span className="text-gray-600">Available Savings</span> | ||
<span className="font-semibold text-green-600"> | ||
{formatUSD(stats.savings)} | ||
</span> | ||
</div> | ||
|
||
<div className="text-sm text-gray-600"> | ||
<p>Connected Wallet: {type === 'core' ? 'Core Wallet' : 'MetaMask'}</p> | ||
<div className="flex items-center gap-2"> | ||
<p>Address: {address}</p> | ||
{!isValidAddress && ( | ||
<AlertCircle className="w-4 h-4 text-red-500" /> | ||
)} | ||
</div> | ||
</div> | ||
|
||
<button | ||
onClick={handleTransfer} | ||
disabled={isTransferring || stats.savings <= 0 || !isValidAddress} | ||
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" | ||
> | ||
{isTransferring ? ( | ||
<Loader2 className="w-5 h-5 animate-spin" /> | ||
) : ( | ||
<> | ||
<span>Transfer Savings to Wallet</span> | ||
<ArrowUpRight className="w-5 h-5 ml-2" /> | ||
</> | ||
)} | ||
</button> | ||
|
||
{error && ( | ||
<div className="p-4 bg-red-50 rounded-md"> | ||
<p className="text-sm text-red-600 flex items-center gap-2"> | ||
<AlertCircle className="w-4 h-4" /> | ||
{error} | ||
</p> | ||
</div> | ||
)} | ||
|
||
{transactionHash && ( | ||
<div className="mt-4 p-4 bg-green-50 rounded-md"> | ||
<p className="text-sm text-green-600"> | ||
Transfer successful! | ||
</p> | ||
<p className="text-xs text-green-500 break-all mt-1"> | ||
Transaction hash: {transactionHash} | ||
</p> | ||
</div> | ||
)} | ||
|
||
{stats.savings <= 0 && ( | ||
<p className="text-sm text-gray-500 text-center"> | ||
No savings available to transfer | ||
</p> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React from 'react'; | ||
import { Line } from 'react-chartjs-2'; | ||
import { | ||
Chart as ChartJS, | ||
CategoryScale, | ||
LinearScale, | ||
PointElement, | ||
LineElement, | ||
Title, | ||
Tooltip, | ||
Legend, | ||
} from 'chart.js'; | ||
import { useFinanceStore } from '../store/useFinanceStore'; | ||
import { formatCurrency } from '../utils/formatters'; | ||
import { AutoSavingsTransfer } from './AutoSavingsTransfer'; | ||
|
||
ChartJS.register( | ||
CategoryScale, | ||
LinearScale, | ||
PointElement, | ||
LineElement, | ||
Title, | ||
Tooltip, | ||
Legend | ||
); | ||
|
||
export function Dashboard() { | ||
const { stats, predictions } = useFinanceStore(); | ||
|
||
const chartData = { | ||
labels: ['Current', 'Predicted'], | ||
datasets: predictions.map((pred) => ({ | ||
label: pred.category, | ||
data: [ | ||
stats.budgets.find((b) => b.category === pred.category)?.spent || 0, | ||
pred.amount, | ||
], | ||
borderColor: `hsl(${Math.random() * 360}, 70%, 50%)`, | ||
tension: 0.4, | ||
})), | ||
}; | ||
|
||
return ( | ||
<div className="space-y-6"> | ||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> | ||
<div className="bg-white p-6 rounded-lg shadow-md"> | ||
<h2 className="text-xl font-semibold mb-4">Financial Overview</h2> | ||
<div className="space-y-4"> | ||
<div className="flex justify-between items-center"> | ||
<span className="text-gray-600">Total Income</span> | ||
<span className="font-semibold text-green-600"> | ||
{formatCurrency(stats.totalIncome)} | ||
</span> | ||
</div> | ||
<div className="flex justify-between items-center"> | ||
<span className="text-gray-600">Total Expenses</span> | ||
<span className="font-semibold text-red-600"> | ||
{formatCurrency(stats.totalExpenses)} | ||
</span> | ||
</div> | ||
<div className="flex justify-between items-center"> | ||
<span className="text-gray-600">Savings</span> | ||
<span className="font-semibold text-blue-600"> | ||
{formatCurrency(stats.savings)} | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div className="bg-white p-6 rounded-lg shadow-md"> | ||
<h2 className="text-xl font-semibold mb-4">AI Predictions</h2> | ||
<Line | ||
data={chartData} | ||
options={{ | ||
responsive: true, | ||
plugins: { | ||
legend: { | ||
position: 'bottom', | ||
}, | ||
title: { | ||
display: true, | ||
text: 'Expense Predictions by Category', | ||
}, | ||
}, | ||
}} | ||
/> | ||
</div> | ||
</div> | ||
|
||
<AutoSavingsTransfer /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React, { Component, ErrorInfo, ReactNode } from 'react'; | ||
|
||
interface Props { | ||
children: ReactNode; | ||
} | ||
|
||
interface State { | ||
hasError: boolean; | ||
error: Error | null; | ||
} | ||
|
||
export class ErrorBoundary extends Component<Props, State> { | ||
public state: State = { | ||
hasError: false, | ||
error: null, | ||
}; | ||
|
||
public static getDerivedStateFromError(error: Error): State { | ||
return { hasError: true, error }; | ||
} | ||
|
||
public componentDidCatch(error: Error, errorInfo: ErrorInfo) { | ||
console.error('Uncaught error:', error, errorInfo); | ||
} | ||
|
||
public render() { | ||
if (this.state.hasError) { | ||
return ( | ||
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4"> | ||
<div className="bg-white p-8 rounded-lg shadow-md max-w-md w-full"> | ||
<h2 className="text-2xl font-bold text-red-600 mb-4">Something went wrong</h2> | ||
<p className="text-gray-600 mb-4"> | ||
{this.state.error?.message || 'An unexpected error occurred'} | ||
</p> | ||
<button | ||
onClick={() => window.location.reload()} | ||
className="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors" | ||
> | ||
Reload Page | ||
</button> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
return this.props.children; | ||
} | ||
} |
Oops, something went wrong.