commit 621aa0cd8116328bd0e5243e25d8d3b383638a19
parent 9b802d38e51fc7b3bae5ee019283dfd552bdaff4
Author: Sebastian <sebasjm@gmail.com>
Date: Tue, 26 Aug 2025 12:44:27 -0300
fix #10292
Diffstat:
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>
);