fix: improve StockTable save behavior, accessibility, and structure
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useMemo } from "react";
|
import { useState, useMemo, type ReactNode } from "react";
|
||||||
|
|
||||||
interface Stock {
|
interface Stock {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -45,6 +45,7 @@ export default function StockTable({ stocks, onNotesSave, saveError }: StockTabl
|
|||||||
const paged = filtered.slice(page * pageSize, (page + 1) * pageSize);
|
const paged = filtered.slice(page * pageSize, (page + 1) * pageSize);
|
||||||
|
|
||||||
const handleSort = (field: SortField) => {
|
const handleSort = (field: SortField) => {
|
||||||
|
setPage(0);
|
||||||
if (sortField === field) {
|
if (sortField === field) {
|
||||||
setSortDirection((d) => (d === "asc" ? "desc" : "asc"));
|
setSortDirection((d) => (d === "asc" ? "desc" : "asc"));
|
||||||
} else {
|
} else {
|
||||||
@@ -63,15 +64,15 @@ export default function StockTable({ stocks, onNotesSave, saveError }: StockTabl
|
|||||||
setSavingNotes(editingTicker);
|
setSavingNotes(editingTicker);
|
||||||
try {
|
try {
|
||||||
await onNotesSave(editingTicker, editingNotes);
|
await onNotesSave(editingTicker, editingNotes);
|
||||||
|
setEditingTicker(null);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to save notes:", e);
|
console.error("Failed to save notes:", e);
|
||||||
} finally {
|
} finally {
|
||||||
setSavingNotes(null);
|
setSavingNotes(null);
|
||||||
setEditingTicker(null);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const SortHeader = ({ field, children }: { field: SortField; children: React.ReactNode }) => (
|
const SortHeader = ({ field, children }: { field: SortField; children: ReactNode }) => (
|
||||||
<th
|
<th
|
||||||
className="text-left py-2 px-3 font-medium text-gray-700 cursor-pointer hover:text-gray-900 select-none"
|
className="text-left py-2 px-3 font-medium text-gray-700 cursor-pointer hover:text-gray-900 select-none"
|
||||||
onClick={() => handleSort(field)}
|
onClick={() => handleSort(field)}
|
||||||
@@ -83,23 +84,12 @@ export default function StockTable({ stocks, onNotesSave, saveError }: StockTabl
|
|||||||
|
|
||||||
if (stocks.length === 0) {
|
if (stocks.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<p className="text-gray-500 py-8">No stocks tracked yet. Visit the stocks page to add some.</p>
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">Stock Database</h2>
|
|
||||||
<p className="text-sm text-gray-600 mt-1">Manage tracked stocks and their analysis notes.</p>
|
|
||||||
</div>
|
|
||||||
<p className="text-gray-500 py-8">No stocks tracked yet. Visit the stocks page to add some.</p>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-bold text-gray-900">Stock Database</h2>
|
|
||||||
<p className="text-sm text-gray-600 mt-1">Manage tracked stocks and their analysis notes.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{saveError && (
|
{saveError && (
|
||||||
<div className="bg-red-50 text-red-700 px-4 py-2 rounded-lg text-sm">{saveError}</div>
|
<div className="bg-red-50 text-red-700 px-4 py-2 rounded-lg text-sm">{saveError}</div>
|
||||||
)}
|
)}
|
||||||
@@ -129,20 +119,28 @@ export default function StockTable({ stocks, onNotesSave, saveError }: StockTabl
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{paged.map((stock) => (
|
{paged.map((stock) => (
|
||||||
<tr key={stock.ticker} className="border-b border-gray-100 hover:bg-gray-50">
|
<tr key={stock.id} className="border-b border-gray-100 hover:bg-gray-50">
|
||||||
<td className="py-2 px-3 font-medium text-gray-900">{stock.ticker}</td>
|
<td className="py-2 px-3 font-medium text-gray-900">{stock.ticker}</td>
|
||||||
<td className="py-2 px-3">
|
<td className="py-2 px-3">
|
||||||
{editingTicker === stock.ticker ? (
|
{editingTicker === stock.ticker ? (
|
||||||
<input
|
<div className="flex items-center gap-2">
|
||||||
type="text"
|
<input
|
||||||
value={editingNotes}
|
type="text"
|
||||||
onChange={(e) => setEditingNotes(e.target.value)}
|
value={editingNotes}
|
||||||
onBlur={saveNotes}
|
onChange={(e) => setEditingNotes(e.target.value)}
|
||||||
onKeyDown={(e) => { if (e.key === "Enter") saveNotes(); if (e.key === "Escape") setEditingTicker(null); }}
|
onKeyDown={(e) => { if (e.key === "Enter") saveNotes(); if (e.key === "Escape") setEditingTicker(null); }}
|
||||||
className="w-full border border-blue-300 rounded px-2 py-1 text-sm focus:ring-2 focus:ring-blue-500"
|
className="flex-1 border border-blue-300 rounded px-2 py-1 text-sm focus:ring-2 focus:ring-blue-500"
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
) : (
|
<button
|
||||||
|
onClick={saveNotes}
|
||||||
|
disabled={savingNotes === stock.ticker}
|
||||||
|
className="text-xs bg-blue-600 text-white px-2 py-1 rounded hover:bg-blue-700 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{savingNotes === stock.ticker ? "Saving..." : "Save"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<span
|
<span
|
||||||
className="text-gray-600 cursor-pointer hover:text-gray-900 block py-1"
|
className="text-gray-600 cursor-pointer hover:text-gray-900 block py-1"
|
||||||
onClick={() => startEditing(stock)}
|
onClick={() => startEditing(stock)}
|
||||||
|
|||||||
Reference in New Issue
Block a user