Add full-stack BTC portfolio web app
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>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import React 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' },
|
||||
table: { width: '100%', borderCollapse: 'collapse' },
|
||||
th: { textAlign: 'left', padding: '0.5rem 0.75rem', borderBottom: '1px solid #333', color: '#888', fontSize: '0.85rem' },
|
||||
td: { padding: '0.6rem 0.75rem', borderBottom: '1px solid #222', fontSize: '0.95rem' },
|
||||
deleteBtn: { background: 'none', border: '1px solid #555', color: '#ff6b6b', borderRadius: '6px', padding: '0.25rem 0.6rem', cursor: 'pointer', fontSize: '0.85rem' },
|
||||
empty: { color: '#555', textAlign: 'center', padding: '1rem' },
|
||||
};
|
||||
|
||||
export default function PurchaseList({ purchases, onDeleted }) {
|
||||
const handleDelete = async (id) => {
|
||||
const token = localStorage.getItem('token');
|
||||
await fetch(`${API}/purchases/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
onDeleted();
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.card}>
|
||||
<div style={styles.title}>Purchases</div>
|
||||
{purchases.length === 0 ? (
|
||||
<div style={styles.empty}>No purchases yet.</div>
|
||||
) : (
|
||||
<table style={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={styles.th}>Date</th>
|
||||
<th style={styles.th}>Amount (€)</th>
|
||||
<th style={styles.th}>Price (€/BTC)</th>
|
||||
<th style={styles.th}>BTC</th>
|
||||
<th style={styles.th}></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{purchases.map(p => (
|
||||
<tr key={p.id}>
|
||||
<td style={styles.td}>{new Date(p.created_at).toLocaleDateString()}</td>
|
||||
<td style={styles.td}>€{p.amount_eur.toLocaleString()}</td>
|
||||
<td style={styles.td}>€{p.price_eur.toLocaleString()}</td>
|
||||
<td style={styles.td}>{(p.amount_eur / p.price_eur).toFixed(6)}</td>
|
||||
<td style={styles.td}>
|
||||
<button style={styles.deleteBtn} onClick={() => handleDelete(p.id)}>Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user