Skip to content

Commit 6d1dc3f

Browse files
committed
refactor: update pages to consume new data hooks
Replace inline useQuery calls and raw fetch patterns in page components with the reusable hooks created in the previous commit. This removes duplicated data-fetching logic from page files and centralizes it in the hooks layer. Pages updated: - payments: list + detail - ledger: list + booking log detail - positions: list + detail - parties: list + detail + associations tab - reconciliation: list - sagas: list + detail - market-data: list + dataset detail - internal-accounts: list + detail
1 parent 8599d78 commit 6d1dc3f

17 files changed

Lines changed: 62 additions & 531 deletions

File tree

frontend/src/features/internal-accounts/pages/[accountId].tsx

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,18 @@ import { StatusBadge } from '@/shared/status-badge'
88
import { TimeDisplay } from '@/shared/time-display'
99
import { MoneyDisplay } from '@/shared/money-display'
1010
import { AuditTrail, Breadcrumbs } from '@/shared'
11-
import { ConnectError, Code } from '@connectrpc/connect'
1211
import { useApiClients } from '@/api/context'
1312
import { useTenantContext } from '@/contexts/tenant-context'
1413
import { tenantKeys } from '@/lib/query-keys'
1514
import { ControlAction } from '@/api/gen/meridian/internal_account/v1/internal_account_pb'
15+
import { useInternalAccountDetail } from '../hooks'
1616

1717
// ---------------------------------------------------------------------------
1818
// Types
1919
// ---------------------------------------------------------------------------
2020

2121
type InternalAccountStatusLabel = 'ACTIVE' | 'SUSPENDED' | 'CLOSED' | 'UNKNOWN'
2222

23-
interface InternalAccount {
24-
accountId: string
25-
accountCode: string
26-
name: string
27-
behaviorClass: string
28-
instrumentCode: string
29-
accountStatus: number
30-
description: string
31-
createdAt?: { seconds: bigint | number; nanos?: number } | null
32-
updatedAt?: { seconds: bigint | number; nanos?: number } | null
33-
}
34-
3523
// ---------------------------------------------------------------------------
3624
// Helpers
3725
// ---------------------------------------------------------------------------
@@ -276,35 +264,10 @@ function InternalAccountTransactions({ accountId, instrumentCode }: { accountId:
276264
export function InternalAccountDetailPage() {
277265
const { accountId } = useParams<{ accountId: string }>()
278266
const { tenantSlug } = useTenantContext()
279-
const clients = useApiClients()
280267

281268
const queryKey = tenantKeys.internalAccount(tenantSlug ?? '', accountId ?? '')
282269

283-
const { data: account, isLoading, isError } = useQuery({
284-
queryKey,
285-
queryFn: async (): Promise<InternalAccount | null> => {
286-
try {
287-
const response = await clients.internalAccount.retrieveInternalAccount({ accountId: accountId ?? '' })
288-
const f = response.facility
289-
if (!f) return null
290-
return {
291-
accountId: f.accountId,
292-
accountCode: f.accountCode,
293-
name: f.name,
294-
behaviorClass: f.behaviorClass,
295-
instrumentCode: f.instrumentCode,
296-
accountStatus: f.accountStatus,
297-
description: f.description,
298-
createdAt: f.createdAt ?? null,
299-
updatedAt: f.updatedAt ?? null,
300-
}
301-
} catch (err: unknown) {
302-
if (ConnectError.from(err).code === Code.NotFound) return null
303-
throw err
304-
}
305-
},
306-
enabled: !!accountId,
307-
})
270+
const { data: account, isLoading, isError } = useInternalAccountDetail(accountId)
308271

309272
if (isLoading) {
310273
return <InternalAccountDetailSkeleton />

frontend/src/features/internal-accounts/pages/index.tsx

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import { useNavigate } from 'react-router-dom'
44
import { DataTable } from '@/shared/data-table'
55
import { StatusBadge } from '@/shared/status-badge'
66
import { TimeDisplay } from '@/shared'
7-
import { useApiClients } from '@/api/context'
8-
import { useTenantContext } from '@/contexts/tenant-context'
9-
import { tenantKeys } from '@/lib/query-keys'
107
import { Button } from '@/components/ui/button'
8+
import { useInternalAccountsTable } from '../hooks'
119
import { CreateInternalAccountDialog } from './create-internal-account-dialog'
1210

1311
interface InternalAccountRow {
@@ -90,8 +88,7 @@ const columns: ColumnDef<InternalAccountRow>[] = [
9088
]
9189

9290
export function InternalAccountsPage() {
93-
const { tenantSlug } = useTenantContext()
94-
const clients = useApiClients()
91+
const { queryKey, queryFn, tenantSlug } = useInternalAccountsTable()
9592
const navigate = useNavigate()
9693
const [createDialogOpen, setCreateDialogOpen] = React.useState(false)
9794

@@ -121,27 +118,8 @@ export function InternalAccountsPage() {
121118
/>
122119

123120
<DataTable<InternalAccountRow>
124-
queryKey={tenantKeys.internalAccounts(tenantSlug)}
125-
queryFn={async ({ pageToken, pageSize, filters }) => {
126-
const statusFilter = filters?.status ? parseInt(filters.status, 10) : 0
127-
const res = await clients.internalAccount.listInternalAccounts({
128-
behaviorClassFilter: filters?.behaviorClass ?? '',
129-
statusFilter,
130-
pagination: { pageToken: pageToken ?? '', pageSize },
131-
})
132-
return {
133-
items: res.facilities.map((f) => ({
134-
accountId: f.accountId,
135-
accountCode: f.accountCode,
136-
name: f.name,
137-
behaviorClass: f.behaviorClass,
138-
accountStatus: f.accountStatus,
139-
instrumentCode: f.instrumentCode,
140-
createdAt: f.createdAt ?? null,
141-
})),
142-
nextPageToken: res.pagination?.nextPageToken || undefined,
143-
}
144-
}}
121+
queryKey={queryKey}
122+
queryFn={queryFn}
145123
columns={columns}
146124
pageSize={25}
147125
filters={[

frontend/src/features/ledger/pages/booking-log-detail.tsx

Lines changed: 5 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { useParams } from 'react-router-dom'
2-
import { useQuery } from '@tanstack/react-query'
32
import type { ColumnDef } from '@tanstack/react-table'
43
import {
54
flexRender,
65
getCoreRowModel,
76
useReactTable,
87
} from '@tanstack/react-table'
9-
import { useApiClients } from '@/api/context'
10-
import { useTenantContext } from '@/contexts/tenant-context'
11-
import { tenantKeys } from '@/lib/query-keys'
128
import { StatusBadge } from '@/shared/status-badge'
139
import { TimeDisplay, EntityLink, Breadcrumbs } from '@/shared'
1410
import { MoneyDisplay } from '@/shared/money-display'
@@ -21,26 +17,11 @@ import {
2117
TableRow,
2218
} from '@/components/ui/table'
2319
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
20+
import { useBookingLogDetail } from '../hooks'
2421
import { BalanceIndicator } from './balance-indicator'
2522
import { DirectionBadge } from './direction-badge'
2623
import { BookingLogHeader } from './booking-log-header'
27-
import type { LedgerPosting, FinancialBookingLog } from './types'
28-
29-
function getStatusName(status: unknown): string {
30-
if (typeof status === 'string') return status
31-
if (typeof status === 'number') {
32-
const statusMap: Record<number, string> = {
33-
0: 'UNSPECIFIED',
34-
1: 'PENDING',
35-
2: 'POSTED',
36-
3: 'FAILED',
37-
4: 'CANCELLED',
38-
5: 'REVERSED',
39-
}
40-
return statusMap[status] ?? String(status)
41-
}
42-
return String(status ?? '')
43-
}
24+
import type { LedgerPosting } from './types'
4425

4526
function getDirectionName(direction: unknown): string {
4627
if (typeof direction === 'string') return direction
@@ -55,28 +36,6 @@ function getDirectionName(direction: unknown): string {
5536
return String(direction ?? '')
5637
}
5738

58-
function getCurrencyName(currency: unknown): string {
59-
if (typeof currency === 'string') return currency
60-
if (typeof currency === 'number') {
61-
const currencyMap: Record<number, string> = {
62-
0: 'UNSPECIFIED',
63-
1: 'GBP',
64-
2: 'USD',
65-
3: 'EUR',
66-
4: 'JPY',
67-
5: 'AUD',
68-
6: 'CAD',
69-
7: 'CHF',
70-
8: 'CNY',
71-
9: 'INR',
72-
10: 'SGD',
73-
11: 'HKD',
74-
}
75-
return currencyMap[currency] ?? String(currency)
76-
}
77-
return String(currency ?? '')
78-
}
79-
8039
function computeTotals(postings: LedgerPosting[], _currency: string): { debitTotal: bigint; creditTotal: bigint } {
8140
let debitTotal = 0n
8241
let creditTotal = 0n
@@ -140,7 +99,7 @@ const postingColumns: ColumnDef<LedgerPosting>[] = [
14099
accessorKey: 'status',
141100
header: 'Status',
142101
cell: ({ row }) => (
143-
<StatusBadge status={getStatusName(row.original.status)} />
102+
<StatusBadge status={row.original.status} />
144103
),
145104
},
146105
]
@@ -192,60 +151,11 @@ function PostingsTable({ postings }: { postings: LedgerPosting[] }) {
192151

193152
export function BookingLogDetailPage() {
194153
const { bookingLogId } = useParams<{ bookingLogId: string }>()
195-
const { tenantSlug } = useTenantContext()
196-
const clients = useApiClients()
197-
198-
const { data, isLoading, isError } = useQuery({
199-
queryKey: [...tenantKeys.all(tenantSlug ?? ''), 'ledger', 'bookingLog', bookingLogId],
200-
queryFn: async () => {
201-
const response = await clients.financialAccounting.retrieveFinancialBookingLog({
202-
id: bookingLogId ?? '',
203-
})
204-
205-
const log = response.financialBookingLog
206-
if (!log) return null
207154

208-
const postings: LedgerPosting[] = (log.postings ?? []).map((p) => ({
209-
id: p.id,
210-
financialBookingLogId: p.financialBookingLogId,
211-
postingDirection: getDirectionName(p.postingDirection),
212-
postingAmount: {
213-
currencyCode: typeof p.postingAmount?.currencyCode === 'string'
214-
? p.postingAmount.currencyCode
215-
: '',
216-
units: (() => {
217-
const u = p.postingAmount?.units
218-
return typeof u === 'bigint' ? u : typeof u === 'number' && Number.isSafeInteger(u) ? BigInt(u) : 0n
219-
})(),
220-
nanos: p.postingAmount?.nanos ?? 0,
221-
},
222-
accountId: p.accountId,
223-
valueDate: p.valueDate ?? null,
224-
postingResult: p.postingResult ?? '',
225-
createdAt: p.createdAt ?? null,
226-
status: getStatusName(p.status),
227-
}))
228-
229-
const bookingLog: FinancialBookingLog = {
230-
id: log.id,
231-
financialAccountType: String(log.financialAccountType ?? ''),
232-
productServiceReference: String(log.productServiceReference ?? ''),
233-
businessUnitReference: String(log.businessUnitReference ?? ''),
234-
chartOfAccountsRules: String(log.chartOfAccountsRules ?? ''),
235-
baseCurrency: getCurrencyName(log.baseCurrency),
236-
status: getStatusName(log.status),
237-
createdAt: log.createdAt ?? null,
238-
updatedAt: log.updatedAt ?? null,
239-
postings,
240-
}
241-
242-
return bookingLog
243-
},
244-
enabled: !!tenantSlug && !!bookingLogId,
245-
})
155+
const { data, isLoading, isError } = useBookingLogDetail(bookingLogId)
246156

247157
const postings = data?.postings ?? []
248-
const currency = data?.baseCurrency ?? 'GBP'
158+
const currency = data?.instrumentCode ?? 'GBP'
249159
const { debitTotal, creditTotal } = computeTotals(postings, currency)
250160

251161
if (isLoading) {

frontend/src/features/ledger/pages/booking-log-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function BookingLogHeader({ bookingLog }: BookingLogHeaderProps) {
3636
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5">
3737
<MetaField label="Account Type" value={bookingLog.financialAccountType} />
3838
<MetaField label="Business Unit" value={bookingLog.businessUnitReference} />
39-
<MetaField label="Currency" value={bookingLog.baseCurrency} />
39+
<MetaField label="Currency" value={bookingLog.instrumentCode} />
4040
<MetaField
4141
label="Postings"
4242
value={

frontend/src/features/ledger/pages/index.tsx

Lines changed: 5 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,11 @@
11
import { useNavigate } from 'react-router-dom'
22
import type { ColumnDef } from '@tanstack/react-table'
3-
import { useApiClients } from '@/api/context'
4-
import { useTenantContext } from '@/contexts/tenant-context'
5-
import { tenantKeys } from '@/lib/query-keys'
6-
import { DataTable, type DataTableQueryParams, type DataTableResult } from '@/shared/data-table'
3+
import { DataTable } from '@/shared/data-table'
74
import { StatusBadge } from '@/shared/status-badge'
85
import { TimeDisplay } from '@/shared'
6+
import { useBookingLogsTable } from '../hooks'
97
import type { FinancialBookingLog } from './types'
108

11-
function getStatusName(status: unknown): string {
12-
if (typeof status === 'string') return status
13-
if (typeof status === 'number') {
14-
const statusMap: Record<number, string> = {
15-
0: 'UNSPECIFIED',
16-
1: 'PENDING',
17-
2: 'POSTED',
18-
3: 'FAILED',
19-
4: 'CANCELLED',
20-
5: 'REVERSED',
21-
}
22-
return statusMap[status] ?? String(status)
23-
}
24-
return String(status ?? '')
25-
}
26-
27-
function getInstrumentCode(value: unknown): string {
28-
if (typeof value === 'string' && value) return value
29-
return ''
30-
}
31-
329
const columns: ColumnDef<FinancialBookingLog>[] = [
3310
{
3411
accessorKey: 'id',
@@ -64,46 +41,9 @@ const columns: ColumnDef<FinancialBookingLog>[] = [
6441
]
6542

6643
export function LedgerPage() {
67-
const { tenantSlug } = useTenantContext()
68-
const clients = useApiClients()
44+
const { queryKey, queryFn } = useBookingLogsTable()
6945
const navigate = useNavigate()
7046

71-
async function fetchBookingLogs(
72-
params: DataTableQueryParams,
73-
): Promise<DataTableResult<FinancialBookingLog>> {
74-
if (!tenantSlug) return { items: [] }
75-
76-
const statusFilter = params.filters?.status
77-
78-
const response = await clients.financialAccounting.listFinancialBookingLogs({
79-
pagination: { pageSize: params.pageSize, pageToken: params.pageToken ?? '' },
80-
...(statusFilter !== undefined && { status: statusFilter as never }),
81-
})
82-
83-
const items = (response.financialBookingLogs ?? []).map((log) => ({
84-
id: log.id,
85-
financialAccountType: String(log.financialAccountType ?? ''),
86-
productServiceReference: String(log.productServiceReference ?? ''),
87-
businessUnitReference: String(log.businessUnitReference ?? ''),
88-
chartOfAccountsRules: String(log.chartOfAccountsRules ?? ''),
89-
instrumentCode: getInstrumentCode(log.baseInstrumentCode),
90-
status: getStatusName(log.status),
91-
createdAt: log.createdAt ?? null,
92-
updatedAt: log.updatedAt ?? null,
93-
postings: (log.postings ?? []) as FinancialBookingLog['postings'],
94-
})) as FinancialBookingLog[]
95-
96-
const nextPageToken =
97-
typeof response.pagination?.nextPageToken === 'string'
98-
? response.pagination.nextPageToken
99-
: undefined
100-
101-
return {
102-
items,
103-
nextPageToken: nextPageToken || undefined,
104-
}
105-
}
106-
10747
function handleRowClick(row: FinancialBookingLog) {
10848
void navigate(`/ledger/${row.id}`)
10949
}
@@ -118,8 +58,8 @@ export function LedgerPage() {
11858
</div>
11959

12060
<DataTable<FinancialBookingLog>
121-
queryKey={[...(tenantSlug ? tenantKeys.all(tenantSlug) : ['no-tenant']), 'ledger', 'bookingLogs']}
122-
queryFn={fetchBookingLogs}
61+
queryKey={queryKey}
62+
queryFn={queryFn}
12363
columns={columns}
12464
pageSize={25}
12565
filters={[

0 commit comments

Comments
 (0)