taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 621aa0cd8116328bd0e5243e25d8d3b383638a19
parent 9b802d38e51fc7b3bae5ee019283dfd552bdaff4
Author: Sebastian <sebasjm@gmail.com>
Date:   Tue, 26 Aug 2025 12:44:27 -0300

fix #10292

Diffstat:
Mpackages/merchant-backoffice-ui/src/hooks/access-tokens.ts | 4++--
Mpackages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/Table.tsx | 76++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/index.tsx | 13++++++-------
3 files changed, 60 insertions(+), 33 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/hooks/access-tokens.ts b/packages/merchant-backoffice-ui/src/hooks/access-tokens.ts @@ -42,10 +42,10 @@ export function useInstanceAccessTokens() { const [offset, setOffset] = useState<number | undefined>(); - async function fetcher([token, tid]: [AccessToken, string]) { + async function fetcher([token, tid]: [AccessToken, number]) { return await lib.instance.listAccessTokens(token, { limit: PAGINATED_LIST_REQUEST, - offset: tid, + offset: !tid? undefined :String(tid), order: "dec", }); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/Table.tsx @@ -19,14 +19,15 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { - TokenInfo -} from "@gnu-taler/taler-util"; +import { TokenInfo } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; -import { datetimeFormatForSettings, usePreference } from "../../../../hooks/preference.js"; +import { + datetimeFormatForSettings, + usePreference, +} from "../../../../hooks/preference.js"; type Entity = TokenInfo; @@ -34,12 +35,16 @@ interface Props { tokens: Entity[]; onDelete: (e: Entity) => void; onCreate: () => void; + onLoadMoreBefore?: () => void; + onLoadMoreAfter?: () => void; } export function CardTable({ tokens, onCreate, onDelete, + onLoadMoreAfter, + onLoadMoreBefore, }: Props): VNode { const [rowSelection, rowSelectionHandler] = useState<string[]>([]); @@ -76,6 +81,8 @@ export function CardTable({ onDelete={onDelete} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} + onLoadMoreAfter={onLoadMoreAfter} + onLoadMoreBefore={onLoadMoreBefore} /> ) : ( <EmptyTable /> @@ -91,14 +98,31 @@ interface TableProps { tokens: Entity[]; onDelete: (e: Entity) => void; rowSelectionHandler: StateUpdater<string[]>; + onLoadMoreBefore?: () => void; + onLoadMoreAfter?: () => void; } -function Table({ tokens, onDelete }: TableProps): VNode { +function Table({ + tokens, + onDelete, + onLoadMoreAfter, + onLoadMoreBefore, +}: TableProps): VNode { const { i18n } = useTranslationContext(); const [settings] = usePreference(); return ( <Fragment> <div class="table-container"> + {onLoadMoreBefore && ( + <button + class="button is-fullwidth" + data-tooltip={i18n.str`Load more devices before the first one`} + onClick={onLoadMoreBefore} + > + <i18n.Translate>Load first page</i18n.Translate> + </button> + )} + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> <thead> <tr> @@ -122,40 +146,36 @@ function Table({ tokens, onDelete }: TableProps): VNode { return ( <tr key={idx}> <td - // onClick={(): void => onSelect(t)} - // style={{ cursor: "pointer" }} + // onClick={(): void => onSelect(t)} + // style={{ cursor: "pointer" }} > {t.scope} </td> <td - // onClick={(): void => onSelect(t)} - // style={{ cursor: "pointer" }} + // onClick={(): void => onSelect(t)} + // style={{ cursor: "pointer" }} > {t.creation_time.t_s === "never" ? i18n.str`Never` : format( - new Date( - t.creation_time.t_s * 1000, - ), - datetimeFormatForSettings(settings), - )} + new Date(t.creation_time.t_s * 1000), + datetimeFormatForSettings(settings), + )} </td> <td - // onClick={(): void => onSelect(t)} - // style={{ cursor: "pointer" }} + // onClick={(): void => onSelect(t)} + // style={{ cursor: "pointer" }} > {t.expiration.t_s === "never" ? i18n.str`Never` : format( - new Date( - t.expiration.t_s * 1000, - ), - datetimeFormatForSettings(settings), - )} + new Date(t.expiration.t_s * 1000), + datetimeFormatForSettings(settings), + )} </td> <td - // onClick={(): void => onSelect(t)} - // style={{ cursor: "pointer" }} + // onClick={(): void => onSelect(t)} + // style={{ cursor: "pointer" }} > {t.description} </td> @@ -175,12 +195,20 @@ function Table({ tokens, onDelete }: TableProps): VNode { </span> </div> </td> - </tr> ); })} </tbody> </table> + {onLoadMoreAfter && ( + <button + class="button is-fullwidth" + data-tooltip={i18n.str`Load more devices after the last one`} + onClick={onLoadMoreAfter} + > + <i18n.Translate>Load next page</i18n.Translate> + </button> + )} </div> </Fragment> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/index.tsx @@ -47,9 +47,7 @@ interface Props { export default function AccessTokenListPage({ onCreate }: Props): VNode { const result = useInstanceAccessTokens(); const { state, lib } = useSessionContext(); - const [deleting, setDeleting] = useState< - (TokenInfo) | null - >(null); + const [deleting, setDeleting] = useState<TokenInfo | null>(null); const { i18n } = useTranslationContext(); const [notif, setNotif] = useState<Notification | undefined>(undefined); @@ -84,11 +82,12 @@ export default function AccessTokenListPage({ onCreate }: Props): VNode { ...o, id: String(o.serial), }))} + onLoadMoreBefore={result.loadFirst} + onLoadMoreAfter={result.loadNext} onCreate={onCreate} onDelete={(d) => { - setDeleting(d) + setDeleting(d); }} - /> {deleting && ( <ConfirmModal @@ -119,7 +118,8 @@ export default function AccessTokenListPage({ onCreate }: Props): VNode { setNotif({ message: i18n.str`Could not delete the access token`, type: "ERROR", - description: error instanceof Error ? error.message : undefined, + description: + error instanceof Error ? error.message : undefined, }); } setDeleting(null); @@ -132,7 +132,6 @@ export default function AccessTokenListPage({ onCreate }: Props): VNode { </p> </ConfirmModal> )} - </section> </Fragment> );