diff --git a/CHANGELOG.md b/CHANGELOG.md index b105395..015e982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Intermediate package/UI version bumps are grouped under the next published GitHu ## Unreleased +- Group Dashboard Needs review items under date headings, newest first, while preserving confirmation eligibility and grouped transfers/splits. + - Monochrome now uses a dedicated neutral light/dark palette for surfaces, controls and primary balance charts instead of a page-wide filter, retaining meaningful colors for income/expenses, gains/losses, statuses, forecasts and categories. Area charts follow the selected palette, with blue accents in Original and neutral accents in Monochrome. Red Filter also replaces its page filter with native red surfaces, controls and primary charts in light/dark modes, retaining the same meaningful financial/status and category colors. - Refreshed cash-balance, account and portfolio history charts with EasyUI-style cards, smooth area curves, animated reveals and range pills. Dated value readouts and keyboard/touch inspection preserve native currencies, history gaps and privacy. diff --git a/client/src/components/dashboard-module/TransactionList.test.tsx b/client/src/components/dashboard-module/TransactionList.test.tsx index 5d1257a..89b00ee 100644 --- a/client/src/components/dashboard-module/TransactionList.test.tsx +++ b/client/src/components/dashboard-module/TransactionList.test.tsx @@ -247,6 +247,27 @@ it('retains a manual conversion when a late market-rate request completes', asyn }) describe('dashboard transaction presentation', () => { + it('groups review items by descending date while keeping same-day rows together and future drafts unconfirmable', () => { + renderTransfers({ transactions: [], upcomingTransactions: [ + { id: 'old', account_id: 'source', amount: -10, date: '2020-01-01', description: 'Old bill', pending_kind: 'upcoming' }, + { id: 'new', account_id: 'source', amount: -20, date: '2020-01-03', description: 'New bill', pending_kind: 'upcoming' }, + { id: 'same', account_id: 'source', amount: -30, date: '2020-01-03', description: 'Same-day draft', pending_kind: 'mcp_review' }, + { id: 'future', account_id: 'source', amount: -40, date: '2099-01-01', description: 'Future draft', pending_kind: 'mcp_review' }, + ] }) + const queue = within(screen.getByRole('region', { name: 'Needs review' })) + expect(queue.getAllByRole('heading', { level: 4 }).map(heading => heading.textContent)).toEqual([ + 'Thu, Jan 1, 2099', 'Fri, Jan 3, 2020', 'Wed, Jan 1, 2020', + ]) + const sameDay = within(queue.getByRole('region', { name: 'Friday, Jan 3, 2020' })) + expect(sameDay.getByText('New bill')).toBeInTheDocument() + expect(sameDay.getByText('Same-day draft')).toBeInTheDocument() + expect(sameDay.getAllByRole('button', { name: /^Confirm/ })).toHaveLength(2) + const future = within(queue.getByRole('region', { name: 'Thursday, Jan 1, 2099' })) + expect(future.getByText('Future draft')).toBeInTheDocument() + expect(future.getByRole('button', { name: /^Confirm/ })).toBeDisabled() + expect(queue.queryByLabelText('Daily total')).not.toBeInTheDocument() + }) + it('combines due items and MCP drafts, keeps future drafts unconfirmable, and resolves both transfer legs once', async () => { const user = userEvent.setup() vi.mocked(apiFetch).mockReset() diff --git a/client/src/components/dashboard-module/TransactionList.tsx b/client/src/components/dashboard-module/TransactionList.tsx index b2f21bc..89b219c 100644 --- a/client/src/components/dashboard-module/TransactionList.tsx +++ b/client/src/components/dashboard-module/TransactionList.tsx @@ -1362,7 +1362,13 @@ export function TransactionList({ const values = cashFlowTransactions(items).map(convertCash) return values.some(value => value === null) ? null : values.reduce((sum, value) => sum + value!, 0) } - const reviewTransactions = [...readyTransactions, ...mcpReviewTransactions].sort((a, b) => a.date.localeCompare(b.date)) + const reviewTransactions = [...readyTransactions, ...mcpReviewTransactions].sort((a, b) => b.date.localeCompare(a.date)) + const reviewDateGroups = reviewTransactions.reduce((groups, tx) => { + const items = groups.get(tx.date) ?? [] + items.push(tx) + groups.set(tx.date, items) + return groups + }, new Map()) // Preview only the review items currently shown that can be confirmed now. // A transfer contributes both native legs, even if only one matches a filter. @@ -1735,7 +1741,12 @@ export function TransactionList({ :
{reviewTransactions.length > 0 &&

Needs review

{reviewTransactions.length}Balances change only after confirmation
- {reviewTransactions.map(tx => renderPendingTransaction(tx, tx.date <= today))} + {Array.from(reviewDateGroups, ([date, items]) =>
+
+

{format(parseISO(date), 'EEE, MMM d, yyyy')}

+
+ {items.map(tx => renderPendingTransaction(tx, tx.date <= today))} +
)} {confirmationBalancePreviews.length > 0 &&

If all ready transactions are confirmed

{confirmationBalancePreviews.map(({ account, acceptedBalance }) =>

{account.name}: {previewBalance(account.balance, account.id)} → {previewBalance(acceptedBalance, account.id)}

)}
}
} {futureTransactions.length > 0 &&