Dashboard: accounts list in name order, counting the numbers in names
The accounts table kept the broker's order until the Account header was clicked, and that sort compared names as plain text, so XXX10 came before XXX2. It now opens sorted by name, comparing numbers as numbers (XXX1, XXX2 … XXX10; Vest's padded -02 between -1 and -10), and the other columns break ties by name so equal rows hold still between syncs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8c863bdc1d
commit
618976bf5f
+19
-11
@@ -21,6 +21,11 @@ function statusRank(account: AccountState): number {
|
|||||||
return 3; // In Trade
|
return 3; // In Trade
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Account names in counting order: XXX2 before XXX10, not after it. */
|
||||||
|
function byName(a: AccountState, b: AccountState): number {
|
||||||
|
return a.name.localeCompare(b.name, undefined, { numeric: true });
|
||||||
|
}
|
||||||
|
|
||||||
function fmt(value: number) {
|
function fmt(value: number) {
|
||||||
return value.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
return value.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
}
|
}
|
||||||
@@ -98,7 +103,7 @@ function maskName(name: string, privacy: boolean): string {
|
|||||||
function SortHeader({ label, col, sortKey, sortDir, onSort }: {
|
function SortHeader({ label, col, sortKey, sortDir, onSort }: {
|
||||||
label: string;
|
label: string;
|
||||||
col: SortKey;
|
col: SortKey;
|
||||||
sortKey: SortKey | null;
|
sortKey: SortKey;
|
||||||
sortDir: SortDir;
|
sortDir: SortDir;
|
||||||
onSort: (col: SortKey) => void;
|
onSort: (col: SortKey) => void;
|
||||||
}) {
|
}) {
|
||||||
@@ -527,7 +532,7 @@ function FirmRows({ state, firm, hideDead, privacy, sortKey, sortDir, tradeSymbo
|
|||||||
firm: FirmConfig;
|
firm: FirmConfig;
|
||||||
hideDead: boolean;
|
hideDead: boolean;
|
||||||
privacy: boolean;
|
privacy: boolean;
|
||||||
sortKey: SortKey | null;
|
sortKey: SortKey;
|
||||||
sortDir: SortDir;
|
sortDir: SortDir;
|
||||||
tradeSymbol: string;
|
tradeSymbol: string;
|
||||||
onChanged: () => void;
|
onChanged: () => void;
|
||||||
@@ -554,20 +559,22 @@ function FirmRows({ state, firm, hideDead, privacy, sortKey, sortDir, tradeSymbo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedAccounts = sortKey === null ? state.accounts : [...state.accounts].sort((a, b) => {
|
// Ties on any other column fall back to name order, so equal rows don't
|
||||||
|
// shuffle with the broker's list between syncs.
|
||||||
|
const sortedAccounts = [...state.accounts].sort((a, b) => {
|
||||||
const dir = sortDir === 'asc' ? 1 : -1;
|
const dir = sortDir === 'asc' ? 1 : -1;
|
||||||
switch (sortKey) {
|
switch (sortKey) {
|
||||||
case 'name': return dir * a.name.localeCompare(b.name);
|
case 'name': return dir * byName(a, b);
|
||||||
case 'balance': return dir * (a.amount - b.amount);
|
case 'balance': return dir * (a.amount - b.amount) || byName(a, b);
|
||||||
case 'dayPnL': return dir * (a.realizedPnL - b.realizedPnL);
|
case 'dayPnL': return dir * (a.realizedPnL - b.realizedPnL) || byName(a, b);
|
||||||
case 'daysTraded': return dir * (a.daysTraded - b.daysTraded);
|
case 'daysTraded': return dir * (a.daysTraded - b.daysTraded) || byName(a, b);
|
||||||
case 'target': {
|
case 'target': {
|
||||||
const at = a.effectiveProfitTarget ?? a.accountType?.profitTarget ?? 0;
|
const at = a.effectiveProfitTarget ?? a.accountType?.profitTarget ?? 0;
|
||||||
const bt = b.effectiveProfitTarget ?? b.accountType?.profitTarget ?? 0;
|
const bt = b.effectiveProfitTarget ?? b.accountType?.profitTarget ?? 0;
|
||||||
return dir * (at - bt);
|
return dir * (at - bt) || byName(a, b);
|
||||||
}
|
}
|
||||||
case 'status': return dir * (statusRank(a) - statusRank(b));
|
case 'status': return dir * (statusRank(a) - statusRank(b)) || byName(a, b);
|
||||||
default: return 0;
|
default: return byName(a, b);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -732,7 +739,8 @@ export default function Home() {
|
|||||||
const [catalog, setCatalog] = useState<{ lastSyncAt: string | null; lastError: string | null; masterUrl: string | null } | null>(null);
|
const [catalog, setCatalog] = useState<{ lastSyncAt: string | null; lastError: string | null; masterUrl: string | null } | null>(null);
|
||||||
const [hideDead, setHideDead] = useState(false);
|
const [hideDead, setHideDead] = useState(false);
|
||||||
const [privacy, setPrivacy] = useState(false);
|
const [privacy, setPrivacy] = useState(false);
|
||||||
const [sortKey, setSortKey] = useState<SortKey | null>(null);
|
// Name order until a header is clicked, so XXX1 always sits above XXX2.
|
||||||
|
const [sortKey, setSortKey] = useState<SortKey>('name');
|
||||||
const [sortDir, setSortDir] = useState<SortDir>('asc');
|
const [sortDir, setSortDir] = useState<SortDir>('asc');
|
||||||
|
|
||||||
// Trade controls
|
// Trade controls
|
||||||
|
|||||||
Reference in New Issue
Block a user