Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
coder-apr-5 authored Dec 1, 2024
1 parent e08b3b5 commit 07c850d
Show file tree
Hide file tree
Showing 41 changed files with 9,809 additions and 0 deletions.
47 changes: 47 additions & 0 deletions App.tsx
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;
136 changes: 136 additions & 0 deletions components/AutoSavingsTransfer.tsx
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>
);
}
93 changes: 93 additions & 0 deletions components/Dashboard.tsx
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>
);
}
48 changes: 48 additions & 0 deletions components/ErrorBoundary.tsx
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;
}
}
Loading

0 comments on commit 07c850d

Please sign in to comment.