diff --git a/client/components/tracker/MobileTrackerRow.tsx b/client/components/tracker/MobileTrackerRow.tsx index 028b659..f8d136d 100644 --- a/client/components/tracker/MobileTrackerRow.tsx +++ b/client/components/tracker/MobileTrackerRow.tsx @@ -273,6 +273,7 @@ export function MobileTrackerRow({ row, year, month, refresh, index, onEditBill, size="icon" variant="ghost" className="h-7 w-7 opacity-100 transition-opacity text-muted-foreground hover:text-foreground hover:bg-accent lg:opacity-0 lg:group-hover:opacity-100" title="Edit bill" + aria-label={`Edit ${row.name}`} onClick={() => onEditBill?.(row)} > diff --git a/client/components/tracker/TrackerRow.tsx b/client/components/tracker/TrackerRow.tsx index 2b43fcf..5824b30 100644 --- a/client/components/tracker/TrackerRow.tsx +++ b/client/components/tracker/TrackerRow.tsx @@ -438,6 +438,7 @@ export function TrackerRow({ row, year, month, refresh, index, onEditBill, moveC size="icon" variant="ghost" className="h-6 w-6 shrink-0 opacity-100 transition-opacity text-muted-foreground hover:text-foreground hover:bg-accent lg:opacity-0 lg:group-hover:opacity-100" title="Edit bill" + aria-label={`Edit ${row.name}`} onClick={() => onEditBill?.(row)} > diff --git a/client/pages/TrackerPage.tsx b/client/pages/TrackerPage.tsx index 5845d97..244f263 100644 --- a/client/pages/TrackerPage.tsx +++ b/client/pages/TrackerPage.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useMemo, useCallback, useDeferredValue, type ReactNode } from 'react'; import { useSearchParams, useNavigate } from 'react-router-dom'; -import { ChevronLeft, ChevronRight, AlertCircle, CheckCircle2, Plus, Search, RefreshCw, Landmark, ArrowUpToLine, ArrowUp, ArrowDown, BellOff, EyeOff, Settings2, Download, Printer, FileSpreadsheet, CalendarDays } from 'lucide-react'; +import { ChevronLeft, ChevronRight, AlertCircle, CheckCircle2, Plus, Search, RefreshCw, Landmark, ArrowUpToLine, ArrowUp, ArrowDown, BellOff, EyeOff, Settings2, Download, Printer, FileSpreadsheet, CalendarDays, Keyboard } from 'lucide-react'; import { toast } from 'sonner'; import { api } from '@/api'; import { useTracker, useDriftReport, useInvalidateTrackerData, usePrefetchTracker, useSettings, useSimplefinStatus, useSaveSettings } from '@/hooks/useQueries'; @@ -319,6 +319,8 @@ export default function TrackerPage() { // Multi-select: bill ids checked for a bulk pay/skip action. const [selectedIds, setSelectedIds] = useState>(new Set()); const [bulkBusy, setBulkBusy] = useState(false); + // Screen-reader announcement for drag/keyboard reorder (visually hidden). + const [reorderAnnouncement, setReorderAnnouncement] = useState(''); // Use React Query for data fetching const { data, isLoading: loading, isError, error, refetch, dataUpdatedAt } = useTracker(year, month); @@ -775,12 +777,19 @@ export default function TrackerPage() { if (cur && cur.bucket === bucket) nextRows[i] = replacement.shift()!; } const moved = orderedBucketRows.find((row, index) => row.id !== (sourceRows.filter(item => item.bucket === bucket)[index]?.id)); + if (moved) { + const pos = orderedBucketRows.findIndex(r => r.id === moved.id) + 1; + setReorderAnnouncement(`Moved ${moved.name} to position ${pos} of ${orderedBucketRows.length}.`); + } persistTrackerOrder(nextRows, moved?.id || orderedBucketRows[0]?.id || null); } return (
+ {/* Visually-hidden live region: announces drag/keyboard reorder to screen readers */} +
{reorderAnnouncement}
+ {/* ── Header ── */}
@@ -873,6 +882,40 @@ export default function TrackerPage() { + + + + + + Keyboard shortcuts + +
+ {[ + ['Move between bills', 'j / k · ↑ / ↓'], + ['Edit focused bill', 'Enter'], + ['Toggle paid', 'p'], + ['Blur / deselect row', 'Esc'], + ['Command palette', '⌘K / Ctrl+K'], + ].map(([label, keys]) => ( +
+ {label} + {keys} +
+ ))} +
+ +

Focus any bill row (Tab or click), then use the keys above.

+
+
+