-
- Date
- Type
- Amount
- Units
- Memo
-
-
- {entries.map((entry, index) => (
- // No stable per-entry id; recorded_at + index as tiebreak.
-
- {formatDate(entry.effective_date)}
- {ENTRY_TYPE_LABEL[entry.entry_type] || entry.entry_type}
-
- {entry.amount_money ? formatMoney(entry.amount_money) : '—'}
-
-
- {entry.quantity_units ? : '—'}
-
- {entry.memo || '—'}
-
- ))}
-
-
- )
-}
diff --git a/frontend/src/features/valuation/entryTypeLabels.js b/frontend/src/features/valuation/entryTypeLabels.js
deleted file mode 100644
index f8cf464..0000000
--- a/frontend/src/features/valuation/entryTypeLabels.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// backend/app/models/ledger/journal_entry.py's JournalEntryType, verbatim.
-export const ENTRY_TYPE_LABEL = {
- trade_buy: 'Buy',
- trade_sell: 'Sell',
- deposit: 'Deposit',
- withdrawal: 'Withdrawal',
- dividend: 'Dividend',
- split: 'Split',
- fee_adjustment: 'Fee adjustment',
- correction: 'Correction',
- wash_sale_adjustment: 'Wash sale adjustment',
-}
diff --git a/frontend/src/features/valuation/hooks/useTransactionHistory.js b/frontend/src/features/valuation/hooks/useTransactionHistory.js
deleted file mode 100644
index 32be516..0000000
--- a/frontend/src/features/valuation/hooks/useTransactionHistory.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useCallback, useEffect, useState } from 'react'
-import { valuationApi } from '../api/valuationApi.js'
-
-const IDLE = { status: 'idle', entries: [], error: null }
-
-// ADR 6
-export function useTransactionHistory() {
- const [state, setState] = useState(IDLE)
-
- const refetch = useCallback(async () => {
- setState((prev) => ({ ...prev, status: 'loading', error: null }))
- try {
- const data = await valuationApi.getHistory()
- setState({ status: 'loaded', entries: data.entries, error: null })
- } catch (error) {
- setState({ status: 'error', entries: [], error })
- }
- }, [])
-
- useEffect(() => {
- refetch()
- }, [refetch])
-
- return { ...state, refetch }
-}
diff --git a/frontend/src/pages/AppLayout.jsx b/frontend/src/pages/AppLayout.jsx
index b33d005..546041b 100644
--- a/frontend/src/pages/AppLayout.jsx
+++ b/frontend/src/pages/AppLayout.jsx
@@ -30,7 +30,6 @@ const CUSTOMER_NAV_GROUPS = [
key: 'money',
label: 'Money',
links: [
- { to: '/transactions', label: 'Transactions' },
{ to: '/funding', label: 'Funding' },
{ to: '/lots', label: 'Tax lots' },
{ to: '/statements', label: 'Statements' },
@@ -296,7 +295,7 @@ export function AppLayout() {
{/* Group labels show inline only for the staff console -- it's what makes 2
links read as a deliberate, named cluster rather than a leftover flat row.
Customer groups lean on spacing + dividers alone; "Dashboard/Portfolio/Orders"
- vs. "Transactions/Funding/..." is self-evidently grouped without a caption. */}
+ vs. "Funding/Tax lots/..." is self-evidently grouped without a caption. */}
{group.label && variant === 'staff' && (
{group.label}
)}
diff --git a/frontend/src/pages/DashboardPage.css b/frontend/src/pages/DashboardPage.css
index 9953b57..96ded07 100644
--- a/frontend/src/pages/DashboardPage.css
+++ b/frontend/src/pages/DashboardPage.css
@@ -1,5 +1,11 @@
+/* BalanceCard/ReturnCard are content-sized Cards with no width of their own -- now that
+ PageLayout.css's .tu-page actually stretches to the full page width, a plain flex row would
+ just leave them small and left-aligned with the rest of the row empty. auto-fit lets each stat
+ grow to share the row evenly (2 columns once there's room for both at >=280px, 1 below that),
+ matching the repeat(auto-fill/auto-fit, minmax(...)) idiom already used in
+ features/portfolio/components/ModelDetail.css and AssignmentPrompt.css. */
.tu-dashboard-page__stats {
- display: flex;
- flex-wrap: wrap;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--space-4);
}
diff --git a/frontend/src/pages/FundingPage.css b/frontend/src/pages/FundingPage.css
index b68060a..7f2b992 100644
--- a/frontend/src/pages/FundingPage.css
+++ b/frontend/src/pages/FundingPage.css
@@ -19,14 +19,6 @@
}
}
-/* Matches LotsPage.css's own fix: FundingHistoryTable is wide enough to need its own horizontal
- scroll (Table.css), which only works if this page's width is pinned to the viewport rather than
- growing to the table's unwrapped content width via PageLayout.css's auto-margin centering. */
-.tu-page.tu-funding-page {
- width: 100%;
- min-width: 0;
-}
-
/* This heading is only ever reached via OutstandingBalanceBanner's onMakeDeposit ->
focusDepositForm()'s programmatic .focus() call (tabIndex=-1 takes it out of the natural tab
order) — matching FeesPage.css's focusPaymentMethod() precedent, :focus (not :focus-visible) is
diff --git a/frontend/src/pages/LotsPage.css b/frontend/src/pages/LotsPage.css
index dde3f68..6c92c0b 100644
--- a/frontend/src/pages/LotsPage.css
+++ b/frontend/src/pages/LotsPage.css
@@ -9,20 +9,3 @@
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
-
-/* `.tu-page`'s own `margin: 0 auto` (PageLayout.css, centering it under `max-width`) is a
- cross-axis auto margin on a flex item of `.tu-app-layout__main` (a column flex container) —
- per the flexbox spec, a cross-axis auto margin opts the item OUT of stretch sizing entirely, so
- `.tu-page` falls back to its own content-based width instead of the container's. With a wide
- table inside, that content-based width is the table's full unwrapped min-content size, so the
- whole page (not just the table) ends up wider than the viewport and the table's own
- `.tu-table-wrapper` (Table.css's overflow-x: auto) never gets a narrow-enough box to actually
- scroll inside. An explicit `width: 100%` gives the auto-margin centering something definite to
- center within again; `min-width: 0` then lets that 100% actually shrink to the viewport instead
- of being floored by the table's content size. Scoped to this page (the extra `tu-lots-page`
- class) rather than touched at the shared PageLayout.css/Table.css source — every other `.tu-page`
- user either has no table this wide or hasn't hit this at a narrow viewport yet. */
-.tu-page.tu-lots-page {
- width: 100%;
- min-width: 0;
-}
diff --git a/frontend/src/pages/OnboardingPage.jsx b/frontend/src/pages/OnboardingPage.jsx
index 4d467a9..e29cebd 100644
--- a/frontend/src/pages/OnboardingPage.jsx
+++ b/frontend/src/pages/OnboardingPage.jsx
@@ -46,8 +46,14 @@ export function OnboardingPage() {
Identity and funding status
{identity.status === 'loaded' && (
-
diff --git a/frontend/src/pages/PageLayout.css b/frontend/src/pages/PageLayout.css
index b46cce6..8f1a41d 100644
--- a/frontend/src/pages/PageLayout.css
+++ b/frontend/src/pages/PageLayout.css
@@ -1,5 +1,16 @@
+/* `.tu-page` is a flex item of `.tu-app-layout__main` (a column flex container). Its own
+ `margin: 0 auto` centering is a cross-axis auto margin -- per the flexbox spec, that opts the
+ item OUT of stretch sizing entirely, so without `width: 100%` it shrinks to its own content's
+ width instead of the container's. A page whose content happens to be a wide table or a filled
+ multi-column form never shows this (the content forces it back open), which is why it went
+ unnoticed until Lots/Funding/Orders/Portfolio/Dashboard were checked directly: confirmed live at
+ 1280px, Orders list rendered at 293px, Portfolio at 307px, Dashboard at 510px. `min-width: 0`
+ then lets that `width: 100%` actually shrink on a narrow viewport instead of being floored by
+ any wide-content descendant (e.g. a table) the way `min-width: auto`'s default would. */
.tu-page {
max-width: var(--content-max-width);
+ width: 100%;
+ min-width: 0;
margin: 0 auto;
padding: var(--space-8) var(--space-6);
display: flex;
diff --git a/frontend/src/pages/TransactionsPage.jsx b/frontend/src/pages/TransactionsPage.jsx
deleted file mode 100644
index 0e5c34e..0000000
--- a/frontend/src/pages/TransactionsPage.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { TransactionTable } from '../features/valuation/components/TransactionTable.jsx'
-import './PageLayout.css'
-
-export function TransactionsPage() {
- return (
-