Skip to content
Merged
5 changes: 4 additions & 1 deletion frontend/src/features/accounts/hooks/use-accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ export function useAccountDetail(accountId: string | undefined) {
partyId: f.orgPartyId || undefined,
}
} catch (err: unknown) {
if (ConnectError.from(err).code === Code.NotFound) return null
const code = ConnectError.from(err).code
if (code === Code.NotFound) return null
// Log but don't crash for server errors (e.g. balance hydration failures)
console.error('Failed to retrieve account:', err)
throw err
}
},
Expand Down
37 changes: 33 additions & 4 deletions frontend/src/features/accounts/pages/[accountId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function AccountDetailSkeleton() {
// Not found
// ---------------------------------------------------------------------------

function AccountNotFound() {
function AccountNotFound({ accountId }: { accountId?: string }) {
return (
<div data-testid="account-not-found" className="p-6">
<Breadcrumbs items={[{ label: 'Accounts', href: '/accounts' }, { label: 'Not found' }]} />
Expand All @@ -50,6 +50,12 @@ function AccountNotFound() {
<p className="mt-2 text-sm text-muted-foreground">
The account you are looking for does not exist or has been removed.
</p>
{accountId && (
<p className="mt-3 text-sm">
Looking for an internal account?{' '}
<EntityLink type="internal-account" id={accountId} label="View internal account" />
</p>
)}
</div>
</div>
)
Expand Down Expand Up @@ -346,15 +352,38 @@ function AccountLiens({ accountId, instrumentCode }: { accountId: string; instru
export function AccountDetailPage() {
const { accountId } = useParams<{ accountId: string }>()

const { data: account, isLoading, isError } = useAccountDetail(accountId)
const { data: account, isLoading, isError, refetch, isFetching } = useAccountDetail(accountId)

if (isLoading) {
return <AccountDetailSkeleton />
}

// null = 404 from server; isError = network/server failure; undefined = query not yet resolved
if (isError || account === null || account === undefined) {
return <AccountNotFound />
if (account === null) {
return <AccountNotFound accountId={accountId} />
}

if (isError || account === undefined) {
return (
<div data-testid="account-error" className="p-6">
<Breadcrumbs items={[{ label: 'Accounts', href: '/accounts' }, { label: accountId ?? 'Error' }]} />
<div className="mt-8 text-center">
<h2 className="text-xl font-semibold">Failed to load account</h2>
<p className="mt-2 text-sm text-muted-foreground">
There was a problem loading this account. Please try again.
</p>
<Button
variant="outline"
size="sm"
className="mt-4"
disabled={isFetching}
onClick={() => void refetch()}
>
{isFetching ? 'Retrying…' : 'Retry'}
</Button>
</div>
</div>
)
}

return (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/accounts/pages/account-detail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('AccountDetailPage - loading and error states', () => {
expect(screen.getByTestId('account-detail-skeleton')).toBeInTheDocument()
})

it('shows 404 state for non-existent account', async () => {
it('shows error state for failed account fetch', async () => {
server.use(
http.post('*/meridian.current_account.v1.CurrentAccountService/RetrieveCurrentAccount', () =>
HttpResponse.json({ message: 'not found' }, { status: 404 }),
Expand All @@ -68,7 +68,7 @@ describe('AccountDetailPage - loading and error states', () => {
renderDetailPage('nonexistent-id')

await waitFor(() => {
expect(screen.getByTestId('account-not-found')).toBeInTheDocument()
expect(screen.getByTestId('account-error')).toBeInTheDocument()
})
})
})
Expand Down
Loading
Loading