Frontend: complete task list (C1–L4), security, a11y, L1 block card helper
- React: response.ok checks (address, transaction, search); block number validation; stable Table keys; API modules (addresses, transactions, blocks normalizer) - SPA: escapeHtml/safe URLs/onclick; getRpcUrl in rpcCall; cancel blocks rAF on view change; named constants; hash route decode - SPA: createBlockCardHtml + normalizeBlockDisplay (L1); DEBUG console gating; aria-live for errors; token/block/tx detail escaping - Docs: FRONTEND_REVIEW.md, FRONTEND_TASKS_AND_REVIEW.md; favicons; .gitignore *.tsbuildinfo Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
53
frontend/src/services/api/addresses.ts
Normal file
53
frontend/src/services/api/addresses.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { apiClient, ApiResponse } from './client'
|
||||
|
||||
export interface AddressInfo {
|
||||
address: string
|
||||
chain_id: number
|
||||
transaction_count: number
|
||||
token_count: number
|
||||
is_contract: boolean
|
||||
label?: string
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface AddressTransactionsParams {
|
||||
chain_id: number
|
||||
from_address: string
|
||||
page?: number
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export interface TransactionSummary {
|
||||
hash: string
|
||||
block_number: number
|
||||
from_address: string
|
||||
to_address?: string
|
||||
value: string
|
||||
status?: number
|
||||
}
|
||||
|
||||
export const addressesApi = {
|
||||
get: async (chainId: number, address: string): Promise<ApiResponse<AddressInfo>> => {
|
||||
return apiClient.get<AddressInfo>(`/api/v1/addresses/${chainId}/${address}`)
|
||||
},
|
||||
|
||||
getTransactions: async (
|
||||
chainId: number,
|
||||
address: string,
|
||||
page = 1,
|
||||
pageSize = 20
|
||||
): Promise<ApiResponse<TransactionSummary[]>> => {
|
||||
const params = new URLSearchParams({
|
||||
chain_id: chainId.toString(),
|
||||
from_address: address,
|
||||
page: page.toString(),
|
||||
page_size: pageSize.toString(),
|
||||
})
|
||||
const raw = (await apiClient.get(`/api/v1/transactions?${params.toString()}`)) as unknown as {
|
||||
data?: TransactionSummary[]
|
||||
items?: TransactionSummary[]
|
||||
}
|
||||
const data = Array.isArray(raw?.data) ? raw.data : Array.isArray(raw?.items) ? raw.items : []
|
||||
return { data }
|
||||
},
|
||||
}
|
||||
@@ -22,6 +22,12 @@ export interface BlockListParams {
|
||||
order?: 'asc' | 'desc'
|
||||
}
|
||||
|
||||
/** Normalize list response: backend may return { data: T[] } or { items: T[] }. */
|
||||
function normalizeListResponse<T>(raw: { data?: T[]; items?: T[] }): ApiResponse<T[]> {
|
||||
const data = Array.isArray(raw?.data) ? raw.data : Array.isArray(raw?.items) ? raw.items : []
|
||||
return { data }
|
||||
}
|
||||
|
||||
export const blocksApi = {
|
||||
list: async (params: BlockListParams): Promise<ApiResponse<Block[]>> => {
|
||||
const queryParams = new URLSearchParams()
|
||||
@@ -34,7 +40,8 @@ export const blocksApi = {
|
||||
if (params.sort) queryParams.append('sort', params.sort)
|
||||
if (params.order) queryParams.append('order', params.order)
|
||||
|
||||
return apiClient.get<Block[]>(`/api/v1/blocks?${queryParams.toString()}`)
|
||||
const raw = (await apiClient.get(`/api/v1/blocks?${queryParams.toString()}`)) as unknown as { data?: Block[]; items?: Block[] }
|
||||
return normalizeListResponse(raw)
|
||||
},
|
||||
|
||||
getByNumber: async (chainId: number, number: number): Promise<ApiResponse<Block>> => {
|
||||
|
||||
27
frontend/src/services/api/transactions.ts
Normal file
27
frontend/src/services/api/transactions.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { apiClient, ApiResponse } from './client'
|
||||
|
||||
export interface Transaction {
|
||||
chain_id: number
|
||||
hash: string
|
||||
block_number: number
|
||||
block_hash: string
|
||||
transaction_index: number
|
||||
from_address: string
|
||||
to_address?: string
|
||||
value: string
|
||||
gas_price?: number
|
||||
max_fee_per_gas?: number
|
||||
max_priority_fee_per_gas?: number
|
||||
gas_limit: number
|
||||
gas_used?: number
|
||||
status?: number
|
||||
input_data?: string
|
||||
contract_address?: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export const transactionsApi = {
|
||||
get: async (chainId: number, hash: string): Promise<ApiResponse<Transaction>> => {
|
||||
return apiClient.get<Transaction>(`/api/v1/transactions/${chainId}/${hash}`)
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user