first commit
Some checks failed
Deploy / lint (push) Failing after 7s
Deploy / test (push) Has been skipped
Deploy / deploy (push) Has been skipped

This commit is contained in:
Dennis Thiessen
2026-02-20 17:31:01 +01:00
commit 61ab24490d
160 changed files with 17034 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
import { useState } from 'react';
import { useTickers, useAddTicker, useDeleteTicker } from '../../hooks/useTickers';
import { ConfirmDialog } from '../ui/ConfirmDialog';
import { SkeletonTable } from '../ui/Skeleton';
import { formatDateTime } from '../../lib/format';
export function TickerManagement() {
const { data: tickers, isLoading, isError, error } = useTickers();
const addTicker = useAddTicker();
const deleteTicker = useDeleteTicker();
const [newSymbol, setNewSymbol] = useState('');
const [deleteTarget, setDeleteTarget] = useState<string | null>(null);
function handleAdd(e: React.FormEvent) {
e.preventDefault();
const symbol = newSymbol.trim().toUpperCase();
if (!symbol) return;
addTicker.mutate(symbol, { onSuccess: () => setNewSymbol('') });
}
function handleConfirmDelete() {
if (!deleteTarget) return;
deleteTicker.mutate(deleteTarget, { onSuccess: () => setDeleteTarget(null) });
}
return (
<div className="space-y-6">
<form onSubmit={handleAdd} className="flex gap-3">
<input
type="text"
value={newSymbol}
onChange={(e) => setNewSymbol(e.target.value)}
placeholder="Enter ticker symbol (e.g. AAPL)"
className="flex-1 input-glass px-3 py-2.5 text-sm"
/>
<button
type="submit"
disabled={addTicker.isPending || !newSymbol.trim()}
className="btn-gradient px-4 py-2.5 text-sm disabled:opacity-50 disabled:cursor-not-allowed"
>
<span>{addTicker.isPending ? 'Adding…' : 'Add Ticker'}</span>
</button>
</form>
{isLoading && <SkeletonTable rows={5} cols={3} />}
{isError && <p className="text-sm text-red-400">{(error as Error)?.message || 'Failed to load tickers'}</p>}
{tickers && tickers.length > 0 && (
<div className="glass overflow-x-auto">
<table className="w-full text-sm text-left">
<thead className="border-b border-white/[0.06] text-gray-500">
<tr>
<th className="px-4 py-3 font-medium text-xs uppercase tracking-wider">Symbol</th>
<th className="px-4 py-3 font-medium text-xs uppercase tracking-wider">Added</th>
<th className="px-4 py-3 font-medium text-xs uppercase tracking-wider text-right">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-white/[0.04]">
{tickers.map((ticker) => (
<tr key={ticker.id} className="hover:bg-white/[0.03] transition-all duration-150">
<td className="px-4 py-3 font-medium text-gray-100">{ticker.symbol}</td>
<td className="px-4 py-3 text-gray-400">{formatDateTime(ticker.created_at)}</td>
<td className="px-4 py-3 text-right">
<button
onClick={() => setDeleteTarget(ticker.symbol)}
disabled={deleteTicker.isPending}
className="rounded-lg border border-red-500/20 bg-red-500/10 px-3 py-1 text-xs text-red-400 hover:bg-red-500/20 disabled:opacity-50 transition-all duration-200"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{tickers && tickers.length === 0 && (
<p className="text-sm text-gray-500">No tickers registered yet. Add one above.</p>
)}
<ConfirmDialog
open={deleteTarget !== null}
title="Delete Ticker"
message={`Are you sure you want to delete ${deleteTarget}? This action cannot be undone.`}
onConfirm={handleConfirmDelete}
onCancel={() => setDeleteTarget(null)}
/>
</div>
);
}