MostActiveStocks: add Save button to upsert ticker and trigger background trading graph; show saving/saved state
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -21,6 +21,8 @@ export default function MostActiveStocks() {
|
|||||||
const [stocks, setStocks] = useState<MostActiveStock[]>([]);
|
const [stocks, setStocks] = useState<MostActiveStock[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [saving, setSaving] = useState<Record<string, boolean>>({});
|
||||||
|
const [saved, setSaved] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -46,6 +48,29 @@ export default function MostActiveStocks() {
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [fetchData]);
|
}, [fetchData]);
|
||||||
|
|
||||||
|
const handleSave = async (symbol: string) => {
|
||||||
|
setSaving((p) => ({ ...p, [symbol]: true }));
|
||||||
|
setSaved((p) => ({ ...p, [symbol]: false }));
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/stocks", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ symbol }),
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
const data = await res.json().catch(() => null);
|
||||||
|
throw new Error(data?.error || "Failed to save stock");
|
||||||
|
}
|
||||||
|
// trigger analysis in background (non-blocking)
|
||||||
|
fetch(`/api/analyze`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ticker: symbol }) }).catch(() => {});
|
||||||
|
setSaved((p) => ({ ...p, [symbol]: true }));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
setSaving((p) => ({ ...p, [symbol]: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-xl shadow-lg p-6 border border-gray-200">
|
<div className="bg-white rounded-xl shadow-lg p-6 border border-gray-200">
|
||||||
@@ -104,6 +129,7 @@ export default function MostActiveStocks() {
|
|||||||
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Price</th>
|
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Price</th>
|
||||||
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Change %</th>
|
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Change %</th>
|
||||||
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Volume</th>
|
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Volume</th>
|
||||||
|
<th className="text-right px-6 py-4 text-sm font-semibold text-gray-600">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -123,6 +149,21 @@ export default function MostActiveStocks() {
|
|||||||
{formatChangePercent(stock.changePercent)}
|
{formatChangePercent(stock.changePercent)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-right text-gray-600">{formatVolume(stock.volume)}</td>
|
<td className="px-6 py-4 text-right text-gray-600">{formatVolume(stock.volume)}</td>
|
||||||
|
<td className="px-6 py-4 text-right">
|
||||||
|
<button
|
||||||
|
onClick={() => handleSave(stock.symbol)}
|
||||||
|
disabled={!!saving[stock.symbol]}
|
||||||
|
className={`px-3 py-1 rounded-md text-sm font-medium transition-colors ${
|
||||||
|
saving[stock.symbol]
|
||||||
|
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||||
|
: saved[stock.symbol]
|
||||||
|
? "bg-green-600 text-white"
|
||||||
|
: "bg-blue-600 text-white hover:bg-blue-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{saving[stock.symbol] ? "Saving..." : saved[stock.symbol] ? "Saved" : "Save"}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user