3907414742
Multi-user FastAPI + React app with JWT auth, SQLite storage, and CoinGecko price integration. Dockerized with docker-compose. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
77 lines
2.5 KiB
JavaScript
77 lines
2.5 KiB
JavaScript
import React, { useState } from 'react';
|
|
|
|
const API = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
|
|
|
const styles = {
|
|
card: { background: '#1a1a1a', padding: '1.5rem', borderRadius: '12px', border: '1px solid #333', marginBottom: '1.5rem' },
|
|
title: { fontSize: '1.1rem', fontWeight: 700, marginBottom: '1rem', color: '#f7931a' },
|
|
row: { display: 'flex', gap: '0.75rem', flexWrap: 'wrap' },
|
|
input: { flex: 1, minWidth: '140px', padding: '0.65rem', background: '#2a2a2a', border: '1px solid #444', borderRadius: '8px', color: '#e0e0e0', fontSize: '1rem' },
|
|
button: { padding: '0.65rem 1.5rem', background: '#f7931a', color: '#000', border: 'none', borderRadius: '8px', fontWeight: 700, cursor: 'pointer' },
|
|
error: { color: '#ff6b6b', marginTop: '0.5rem', fontSize: '0.9rem' },
|
|
};
|
|
|
|
export default function AddPurchase({ onAdded }) {
|
|
const [amountEur, setAmountEur] = useState('');
|
|
const [priceEur, setPriceEur] = useState('');
|
|
const [error, setError] = useState('');
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
const token = localStorage.getItem('token');
|
|
try {
|
|
const res = await fetch(`${API}/purchases`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
body: JSON.stringify({
|
|
amount_eur: parseFloat(amountEur),
|
|
price_eur: parseFloat(priceEur),
|
|
}),
|
|
});
|
|
if (!res.ok) {
|
|
setError('Failed to add purchase');
|
|
return;
|
|
}
|
|
setAmountEur('');
|
|
setPriceEur('');
|
|
onAdded();
|
|
} catch {
|
|
setError('Network error');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div style={styles.card}>
|
|
<div style={styles.title}>Add Purchase</div>
|
|
<form onSubmit={handleSubmit}>
|
|
<div style={styles.row}>
|
|
<input
|
|
style={styles.input}
|
|
type="number"
|
|
step="any"
|
|
placeholder="Amount (EUR)"
|
|
value={amountEur}
|
|
onChange={e => setAmountEur(e.target.value)}
|
|
required
|
|
/>
|
|
<input
|
|
style={styles.input}
|
|
type="number"
|
|
step="any"
|
|
placeholder="BTC Price (EUR)"
|
|
value={priceEur}
|
|
onChange={e => setPriceEur(e.target.value)}
|
|
required
|
|
/>
|
|
<button style={styles.button} type="submit">Add</button>
|
|
</div>
|
|
{error && <div style={styles.error}>{error}</div>}
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|