68 lines
1.8 KiB
TypeScript
68 lines
1.8 KiB
TypeScript
"use client"
|
|
|
|
import { useDataClient } from "@conusai-kit/ui/data"
|
|
import { type ResourceColumn, ResourceList } from "@conusai-kit/ui/patterns"
|
|
import { Button } from "@conusai-kit/ui/primitives"
|
|
import { useState } from "react"
|
|
import type { LedgerLabels } from "./labels.js"
|
|
|
|
/** Cursor reads happen only on user action; no local financial persistence. */
|
|
export function LedgerList<T extends object>({
|
|
seed,
|
|
procedure,
|
|
input,
|
|
columns,
|
|
rowId,
|
|
href,
|
|
labels,
|
|
title,
|
|
}: {
|
|
seed: { rows: T[]; after?: string | undefined }
|
|
procedure: string
|
|
input: Record<string, unknown>
|
|
columns: ResourceColumn<T>[]
|
|
rowId: (row: T) => string
|
|
href?: ((row: T) => string) | undefined
|
|
labels: LedgerLabels
|
|
title: string
|
|
}) {
|
|
const client = useDataClient()
|
|
const [page, setPage] = useState(seed)
|
|
const [busy, setBusy] = useState(false)
|
|
const [failed, setFailed] = useState(false)
|
|
return (
|
|
<>
|
|
<ResourceList
|
|
label={title}
|
|
data={page.rows}
|
|
columns={columns}
|
|
rowId={rowId}
|
|
rowHref={href}
|
|
rowLabel={rowId}
|
|
rowLinkLabel={() => labels.open}
|
|
empty={{ title: labels.empty }}
|
|
/>
|
|
{failed ? <p role="alert">{labels.unavailable}</p> : null}
|
|
{page.after ? (
|
|
<Button
|
|
disabled={busy}
|
|
onClick={async () => {
|
|
setBusy(true)
|
|
setFailed(false)
|
|
try {
|
|
const next = (await client.call<typeof seed>(procedure, { ...input, after: page.after })).value
|
|
setPage({ ...next, rows: [...page.rows, ...next.rows] })
|
|
} catch {
|
|
setFailed(true)
|
|
} finally {
|
|
setBusy(false)
|
|
}
|
|
}}
|
|
>
|
|
{labels.more}
|
|
</Button>
|
|
) : null}
|
|
</>
|
|
)
|
|
}
|