summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/paths/instance/otp_devices
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/otp_devices')
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx43
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx26
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx18
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx15
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx33
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx84
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx50
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx117
11 files changed, 213 insertions, 179 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx
index 26f851cc8..36b31ebe8 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
index 5f1ae26a3..d5522c2d4 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,8 +19,13 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import {
+ TalerMerchantApi,
+ isRfc3548Base32Charset,
+ randomRfc3548Base32Key,
+} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import {
@@ -28,18 +33,10 @@ import {
FormProvider,
} from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputDuration } from "../../../../components/form/InputDuration.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend } from "../../../../declaration.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { isBase32RFC3548Charset, randomBase32Key } from "../../../../utils/crypto.js";
-import { QR } from "../../../../components/exception/QR.js";
-import { useInstanceContext } from "../../../../context/instance.js";
-type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+type Entity = TalerMerchantApi.OtpDeviceAddDetails;
interface Props {
onCreate: (d: Entity) => Promise<void>;
@@ -49,32 +46,32 @@ interface Props {
const algorithms = [0, 1, 2];
const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
-
export function CreatePage({ onCreate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
- const backend = useBackendContext();
const [state, setState] = useState<Partial<Entity>>({});
const [showKey, setShowKey] = useState(false);
const errors: FormErrors<Entity> = {
- otp_device_id: !state.otp_device_id ? i18n.str`required`
+ otp_device_id: !state.otp_device_id
+ ? i18n.str`required`
: !/[a-zA-Z0-9]*/.test(state.otp_device_id)
? i18n.str`no valid. only characters and numbers`
: undefined,
otp_algorithm: !state.otp_algorithm ? i18n.str`required` : undefined,
- otp_key: !state.otp_key ? i18n.str`required` :
- !isBase32RFC3548Charset(state.otp_key)
+ otp_key: !state.otp_key
+ ? i18n.str`required`
+ : !isRfc3548Base32Charset(state.otp_key)
? i18n.str`just letters and numbers from 2 to 7`
: state.otp_key.length !== 32
? i18n.str`size of the key should be 32`
: undefined,
- otp_device_description: !state.otp_device_description ? i18n.str`required`
+ otp_device_description: !state.otp_device_description
+ ? i18n.str`required`
: !/[a-zA-Z0-9]*/.test(state.otp_device_description)
? i18n.str`no valid. only characters and numbers`
: undefined,
-
};
const hasErrors = Object.keys(errors).some(
@@ -115,7 +112,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
toStr={(v) => algorithmsNames[v]}
fromStr={(v) => Number(v)}
/>
- {state.otp_algorithm && state.otp_algorithm > 0 ? (
+ {state.otp_algorithm ? (
<Fragment>
<InputWithAddon<Entity>
expand
@@ -129,7 +126,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
setShowKey(!showKey);
}}
addonAfter={
- <span class="icon" >
+ <span class="icon">
{showKey ? (
<i class="mdi mdi-eye" />
) : (
@@ -142,7 +139,11 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
data-tooltip={i18n.str`generate random secret key`}
class="button is-info mr-3"
onClick={(e) => {
- setState((s) => ({ ...s, otp_key: randomBase32Key() }));
+ setState((s) => ({
+ ...s,
+ otp_key: randomRfc3548Base32Key(),
+ }));
+ e.preventDefault();
}}
>
<i18n.Translate>random</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx
index db3842711..7723bec81 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -14,41 +14,35 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { TalerMerchantApi } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
+import { VNode, h } from "preact";
import { QR } from "../../../../components/exception/QR.js";
import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { useInstanceContext } from "../../../../context/instance.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useBackendContext } from "../../../../context/backend.js";
+import { useSessionContext } from "../../../../context/session.js";
-type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+type Entity = TalerMerchantApi.OtpDeviceAddDetails;
interface Props {
entity: Entity;
onConfirm: () => void;
}
-function isNotUndefined<X>(x: X | undefined): x is X {
- return !!x;
-}
-
export function CreatedSuccessfully({
entity,
onConfirm,
}: Props): VNode {
const { i18n } = useTranslationContext();
- const { url: backendURL } = useBackendContext()
- const { id: instanceId } = useInstanceContext();
- const issuer = new URL(backendURL).hostname;
- const qrText = `otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key}`;
- const qrTextSafe = `otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key.substring(0, 6)}...`;
+ const { state } = useSessionContext();
+ const issuer = state.backendUrl.href;
+ const qrText = `otpauth://totp/${state.instance}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key}`;
+ const qrTextSafe = `otpauth://totp/${state.instance}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key.substring(0, 6)}...`;
return (
<Template onConfirm={onConfirm} >
<p class="is-size-5">
<i18n.Translate>
- You can scan the next QR code with your device or safe the key before continue.
+ You can scan the next QR code with your device or save the key before continuing.
</i18n.Translate>
</p>
<div class="field is-horizontal">
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
index 648846793..8ab0e1f26 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,28 +19,28 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { TalerMerchantApi } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useWebhookAPI } from "../../../../hooks/webhooks.js";
+import { useSessionContext } from "../../../../context/session.js";
import { Notification } from "../../../../utils/types.js";
import { CreatePage } from "./CreatePage.js";
-import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
-export type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+export type Entity = TalerMerchantApi.OtpDeviceAddDetails;
interface Props {
onBack?: () => void;
onConfirm: () => void;
}
export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
- const { createOtpDevice } = useOtpDeviceAPI();
+ const { lib: api } = useSessionContext();
+ const { state } = useSessionContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
- const [created, setCreated] = useState<MerchantBackend.OTP.OtpDeviceAddDetails | null>(null)
+ const [created, setCreated] = useState<TalerMerchantApi.OtpDeviceAddDetails | null>(null)
if (created) {
return <CreatedSuccessfully entity={created} onConfirm={onConfirm} />
@@ -52,7 +52,7 @@ export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
<CreatePage
onBack={onBack}
onCreate={(request: Entity) => {
- return createOtpDevice(request)
+ return api.instance.addOtpDevice(state.token, request)
.then((d) => {
setCreated(request)
})
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx
index b18049674..49032c80e 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx
index 4efee9781..8ca0a9c58 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,18 +19,17 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { TalerMerchantApi } from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { CardTable } from "./Table.js";
export interface Props {
- devices: MerchantBackend.OTP.OtpDeviceEntry[];
+ devices: TalerMerchantApi.OtpDeviceEntry[];
onLoadMoreBefore?: () => void;
onLoadMoreAfter?: () => void;
onCreate: () => void;
- onDelete: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
- onSelect: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
+ onDelete: (e: TalerMerchantApi.OtpDeviceEntry) => void;
+ onSelect: (e: TalerMerchantApi.OtpDeviceEntry) => void;
}
export function ListPage({
@@ -41,9 +40,7 @@ export function ListPage({
onLoadMoreBefore,
onLoadMoreAfter,
}: Props): VNode {
- const form = { payto_uri: "" };
- const { i18n } = useTranslationContext();
return (
<section class="section is-main-section">
<CardTable
@@ -55,9 +52,7 @@ export function ListPage({
onDelete={onDelete}
onSelect={onSelect}
onLoadMoreBefore={onLoadMoreBefore}
- hasMoreBefore={!onLoadMoreBefore}
onLoadMoreAfter={onLoadMoreAfter}
- hasMoreAfter={!onLoadMoreAfter}
/>
</section>
);
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
index 0c28027fe..afe3c98e2 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,12 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { TalerMerchantApi } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { StateUpdater, useState } from "preact/hooks";
-import { MerchantBackend } from "../../../../declaration.js";
-type Entity = MerchantBackend.OTP.OtpDeviceEntry;
+type Entity = TalerMerchantApi.OtpDeviceEntry;
interface Props {
devices: Entity[];
@@ -32,8 +32,6 @@ interface Props {
onSelect: (e: Entity) => void;
onCreate: () => void;
onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
onLoadMoreAfter?: () => void;
}
@@ -44,8 +42,6 @@ export function CardTable({
onSelect,
onLoadMoreAfter,
onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
}: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
@@ -85,8 +81,6 @@ export function CardTable({
rowSelectionHandler={rowSelectionHandler}
onLoadMoreAfter={onLoadMoreAfter}
onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
/>
) : (
<EmptyTable />
@@ -104,35 +98,26 @@ interface TableProps {
onSelect: (e: Entity) => void;
rowSelectionHandler: StateUpdater<string[]>;
onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
onLoadMoreAfter?: () => void;
}
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
function Table({
instances,
onLoadMoreAfter,
onDelete,
onSelect,
onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
- {hasMoreBefore && (
+ {onLoadMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more devices before the first one`}
onClick={onLoadMoreBefore}
>
- <i18n.Translate>load newer devices</i18n.Translate>
+ <i18n.Translate>load first page</i18n.Translate>
</button>
)}
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -161,7 +146,7 @@ function Table({
onClick={(): void => onSelect(i)}
style={{ cursor: "pointer" }}
>
- {i.otp_device_id}
+ {i.device_description}
</td>
<td class="is-actions-cell right-sticky">
<div class="buttons is-right">
@@ -179,13 +164,13 @@ function Table({
})}
</tbody>
</table>
- {hasMoreAfter && (
+ {onLoadMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more devices after the last one`}
onClick={onLoadMoreAfter}
>
- <i18n.Translate>load older devices</i18n.Translate>
+ <i18n.Translate>load next page</i18n.Translate>
</button>
)}
</div>
@@ -198,7 +183,7 @@ function EmptyTable(): VNode {
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
+ <i class="mdi mdi-magnify mdi-48px" />
</span>
</p>
<p>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
index 2aae8738a..b6a077863 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,55 +19,56 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { HttpStatusCode } from "@gnu-taler/taler-util";
import {
- ErrorType,
- HttpError,
- useTranslationContext,
+ HttpStatusCode,
+ TalerError,
+ TalerMerchantApi,
+ assertUnreachable
+} from "@gnu-taler/taler-util";
+import {
+ useTranslationContext
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js";
import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceOtpDevices, useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { useSessionContext } from "../../../../context/session.js";
+import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
import { Notification } from "../../../../utils/types.js";
+import { LoginPage } from "../../../login/index.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
import { ListPage } from "./ListPage.js";
interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
onCreate: () => void;
onSelect: (id: string) => void;
}
-export default function ListOtpDevices({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const [position, setPosition] = useState<string | undefined>(undefined);
+export default function ListOtpDevices({ onCreate, onSelect }: Props): VNode {
+ // const [position, setPosition] = useState<string | undefined>(undefined);
const { i18n } = useTranslationContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { deleteOtpDevice } = useOtpDeviceAPI();
- const result = useInstanceOtpDevices({ position }, (id) => setPosition(id));
+ const { lib } = useSessionContext();
+ const { state } = useSessionContext();
+ const result = useInstanceOtpDevices();
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
+ if (!result) return <Loading />;
+ if (result instanceof TalerError) {
+ return <ErrorLoadingMerchant error={result} />;
+ }
+ if (result.type === "fail") {
+ switch (result.case) {
+ case HttpStatusCode.NotFound: {
+ return <NotFoundPageOrAdminCreate />;
+ }
+ case HttpStatusCode.Unauthorized: {
+ return <LoginPage />
+ }
+ default: {
+ assertUnreachable(result);
+ }
+ }
}
return (
@@ -75,17 +76,16 @@ export default function ListOtpDevices({
<NotificationCard notification={notif} />
<ListPage
- devices={result.data.otp_devices}
- onLoadMoreBefore={
- result.isReachingStart ? result.loadMorePrev : undefined
- }
- onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+ devices={result.body.otp_devices}
+ onLoadMoreBefore={undefined} //result.isFirstPage ? undefined : result.loadFirst}
+ onLoadMoreAfter={undefined} //result.isLastPage ? undefined : result.loadNext}
onCreate={onCreate}
onSelect={(e) => {
onSelect(e.otp_device_id);
}}
- onDelete={(e: MerchantBackend.OTP.OtpDeviceEntry) =>
- deleteOtpDevice(e.otp_device_id)
+ onDelete={(e: TalerMerchantApi.OtpDeviceEntry) => {
+ return lib.instance
+ .deleteOtpDevice(state.token, e.otp_device_id)
.then(() =>
setNotif({
message: i18n.str`validator delete successfully`,
@@ -98,8 +98,8 @@ export default function ListOtpDevices({
type: "ERROR",
description: error.message,
}),
- )
- }
+ );
+ }}
/>
</Fragment>
);
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx
index d6b1d65e0..06ea9d07a 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
index b82807cc7..35d67cbc6 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,6 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { randomRfc3548Base32Key, TalerMerchantApi } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
@@ -28,12 +29,10 @@ import {
FormProvider,
} from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { randomBase32Key } from "../../../../utils/crypto.js";
-type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
+type Entity = TalerMerchantApi.OtpDevicePatchDetails & WithId;
interface Props {
onUpdate: (d: Entity) => Promise<void>;
@@ -48,8 +47,7 @@ export function UpdatePage({ device, onUpdate, onBack }: Props): VNode {
const [state, setState] = useState<Partial<Entity>>(device);
const [showKey, setShowKey] = useState(false);
- const errors: FormErrors<Entity> = {
- };
+ const errors: FormErrors<Entity> = {};
const hasErrors = Object.keys(errors).some(
(k) => (errors as any)[k] !== undefined,
@@ -106,16 +104,23 @@ export function UpdatePage({ device, onUpdate, onBack }: Props): VNode {
label={i18n.str`Device key`}
readonly={state.otp_key === undefined}
inputType={showKey ? "text" : "password"}
- help={state.otp_key === undefined ? "Not modified" : "Be sure to be very hard to guess or use the random generator"}
+ help={
+ state.otp_key === undefined
+ ? "Not modified"
+ : "Be sure to be very hard to guess or use the random generator"
+ }
tooltip={i18n.str`Your device need to have exactly the same value`}
fromStr={(v) => v.toUpperCase()}
addonAfterAction={() => {
setShowKey(!showKey);
}}
addonAfter={
- <span class="icon" onClick={() => {
- setShowKey(!showKey);
- }}>
+ <span
+ class="icon"
+ onClick={() => {
+ setShowKey(!showKey);
+ }}
+ >
{showKey ? (
<i class="mdi mdi-eye" />
) : (
@@ -124,25 +129,34 @@ export function UpdatePage({ device, onUpdate, onBack }: Props): VNode {
</span>
}
side={
- state.otp_key === undefined ? <button
-
- onClick={(e) => {
- setState((s) => ({ ...s, otp_key: "" }));
- }}
- class="button">change key</button> :
+ state.otp_key === undefined ? (
+ <button
+ onClick={(e) => {
+ setState((s) => ({ ...s, otp_key: "" }));
+ }}
+ class="button"
+ >
+ change key
+ </button>
+ ) : (
<button
data-tooltip={i18n.str`generate random secret key`}
class="button is-info mr-3"
onClick={(e) => {
- setState((s) => ({ ...s, otp_key: randomBase32Key() }));
+ setState((s) => ({
+ ...s,
+ otp_key: randomRfc3548Base32Key(),
+ }));
}}
>
<i18n.Translate>random</i18n.Translate>
</button>
+ )
}
/>
</Fragment>
- ) : undefined} </FormProvider>
+ ) : undefined}{" "}
+ </FormProvider>
<div class="buttons is-right mt-5">
{onBack && (
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
index 52f6c6c29..99edb95c3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2023 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -20,57 +20,68 @@
*/
import {
- ErrorType,
- HttpError,
- useTranslationContext,
+ HttpStatusCode,
+ TalerError,
+ TalerMerchantApi,
+ assertUnreachable
+} from "@gnu-taler/taler-util";
+import {
+ useTranslationContext
} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js";
import { Loading } from "../../../../components/exception/loading.js";
import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { useSessionContext } from "../../../../context/session.js";
+import { useOtpDeviceDetails } from "../../../../hooks/otp.js";
import { Notification } from "../../../../utils/types.js";
+import { LoginPage } from "../../../login/index.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
+import { CreatedSuccessfully } from "../create/CreatedSuccessfully.js";
import { UpdatePage } from "./UpdatePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { useOtpDeviceAPI, useOtpDeviceDetails } from "../../../../hooks/otp.js";
-export type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
+export type Entity = TalerMerchantApi.OtpDevicePatchDetails & WithId;
interface Props {
onBack?: () => void;
onConfirm: () => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
vid: string;
}
export default function UpdateValidator({
vid,
onConfirm,
onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
}: Props): VNode {
- const { updateOtpDevice } = useOtpDeviceAPI();
const result = useOtpDeviceDetails(vid);
const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const [keyUpdated, setKeyUpdated] =
+ useState<TalerMerchantApi.OtpDeviceAddDetails | null>(null);
+ const { lib } = useSessionContext();
+ const { state } = useSessionContext();
const { i18n } = useTranslationContext();
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
+ if (!result) return <Loading />;
+ if (result instanceof TalerError) {
+ return <ErrorLoadingMerchant error={result} />;
+ }
+ if (result.type === "fail") {
+ switch (result.case) {
+ case HttpStatusCode.NotFound: {
+ return <NotFoundPageOrAdminCreate />;
+ }
+ case HttpStatusCode.Unauthorized: {
+ return <LoginPage />
+ }
+ default: {
+ assertUnreachable(result);
+ }
+ }
+ }
+
+ if (keyUpdated) {
+ return <CreatedSuccessfully entity={keyUpdated} onConfirm={onConfirm} />;
}
return (
@@ -79,15 +90,49 @@ export default function UpdateValidator({
<UpdatePage
device={{
id: vid,
- otp_algorithm: result.data.otp_algorithm,
- otp_device_description: result.data.device_description,
- otp_key: undefined,
- otp_ctr: result.data.otp_ctr
+ otp_algorithm: result.body.otp_algorithm,
+ otp_device_description: result.body.device_description,
+ otp_key: "",
+ otp_ctr: result.body.otp_ctr,
}}
onBack={onBack}
- onUpdate={(data) => {
- return updateOtpDevice(vid, data)
- .then(onConfirm)
+ onUpdate={async (newInfo) => {
+ return lib.instance
+ .updateOtpDevice(state.token, vid, newInfo)
+ .then((d) => {
+ if (d.type === "ok") {
+ if (newInfo.otp_key) {
+ setKeyUpdated({
+ otp_algorithm: newInfo.otp_algorithm,
+ otp_device_description: newInfo.otp_device_description,
+ otp_device_id: newInfo.id,
+ otp_key: newInfo.otp_key,
+ otp_ctr: newInfo.otp_ctr,
+ });
+ } else {
+ onConfirm();
+ }
+ } else {
+ switch(d.case) {
+ case HttpStatusCode.NotFound: {
+ setNotif({
+ message: i18n.str`Could not update template`,
+ type: "ERROR",
+ description: i18n.str`Template id is unknown`,
+ });
+ break;
+ }
+ case HttpStatusCode.Conflict: {
+ setNotif({
+ message: i18n.str`Could not update template`,
+ type: "ERROR",
+ description: i18n.str`The provided information is inconsistent with the current state of the template`,
+ });
+ break;
+ }
+ }
+ }
+ })
.catch((error) => {
setNotif({
message: i18n.str`could not update template`,