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:
defiQUG
2026-02-10 18:43:37 -08:00
parent 9970cf547e
commit f774f8f644
16 changed files with 847 additions and 315 deletions

View File

@@ -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>> => {