taler-typescript-core

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

commit 47a2966562d691cbeffeccf0f915dfcd89a5f8fa
parent ea8356b5f1f315f09245221a143451f24e6c7ea1
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Fri,  9 Jan 2026 10:49:22 -0300

fix #10839

Diffstat:
Mpackages/merchant-backoffice-ui/src/Routing.tsx | 3---
Mpackages/merchant-backoffice-ui/src/components/SolveMFA.tsx | 4++++
Mpackages/merchant-backoffice-ui/src/components/form/InputDuration.tsx | 67+++++++++++++++++++++++++++++++++----------------------------------
Mpackages/merchant-backoffice-ui/src/hooks/reports.ts | 2+-
Mpackages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/accessTokens/create/CreatePage.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/accessTokens/list/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx | 5+++++
Mpackages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx | 5+++++
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/groups/create/CreatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/groups/list/Table.tsx | 22++++++++++++----------
Mpackages/merchant-backoffice-ui/src/paths/instance/groups/list/index.tsx | 6+-----
Mpackages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx | 5+++++
Mpackages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/password/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/pots/create/CreatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/pots/list/Table.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/reports/create/CreatePage.tsx | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mpackages/merchant-backoffice-ui/src/paths/instance/reports/list/Table.tsx | 35+++++++++++++++++++++++++----------
Mpackages/merchant-backoffice-ui/src/paths/instance/reports/update/UpdatePage.tsx | 110++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/CreatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx | 24++++++++++++++++--------
Mpackages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx | 2++
Mpackages/merchant-backoffice-ui/src/paths/login/index.tsx | 3+++
Mpackages/merchant-backoffice-ui/src/paths/newAccount/index.tsx | 3+++
Mpackages/taler-util/src/types-taler-merchant.ts | 4++--
45 files changed, 363 insertions(+), 110 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx b/packages/merchant-backoffice-ui/src/Routing.tsx @@ -449,9 +449,6 @@ export function Routing(_p: Props): VNode { onCreate={() => { route(InstancePaths.groups_new); }} - onSelect={(id: string) => { - route(InstancePaths.groups_update.replace(":cid", id)); - }} /> <Route path={InstancePaths.groups_new} diff --git a/packages/merchant-backoffice-ui/src/components/SolveMFA.tsx b/packages/merchant-backoffice-ui/src/components/SolveMFA.tsx @@ -104,6 +104,8 @@ function SolveChallenge({ return i18n.str`Challenge expired`; case TalerErrorCode.MERCHANT_TAN_TOO_MANY_ATTEMPTS: return i18n.str`Too many attempts`; + default: + assertUnreachable(fail); } }; @@ -297,6 +299,8 @@ export function SolveMFAChallenges({ return i18n.str`Already solved.`; case TalerErrorCode.MERCHANT_TAN_TOO_EARLY: return i18n.str`It is too early to request another transmission of the challenge.`; + default: + assertUnreachable(fail); } }; const doComplete = onCompleted.withArgs(solved); diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx @@ -18,7 +18,7 @@ * * @author Sebastian Javier Marchano (sebasjm) */ -import { Duration } from "@gnu-taler/taler-util"; +import { Duration, InternationalizationAPI, TalerProtocolDuration, TranslatedString } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { formatDuration, intervalToDuration } from "date-fns"; import { ComponentChildren, h, VNode } from "preact"; @@ -38,42 +38,18 @@ export interface Props<T> extends InputProps<T> { useProtocolDuration?: boolean; } -export function InputDuration<T>({ - name, - expand, - placeholder, - tooltip, - label, - help, - readonly, - withForever, - withoutClear, - side, - useProtocolDuration, -}: Props<keyof T>): VNode { - const [opened, setOpened] = useState(false); - const { i18n } = useTranslationContext(); - - const { error, required, value: anyValue, onChange } = useField<T>(name); - let strValue = ""; - const value: Duration = - anyValue && anyValue.d_us !== undefined - ? Duration.fromTalerProtocolDuration(anyValue) - : anyValue; - if (!value) { - strValue = ""; - } else if (value.d_ms === "forever") { - strValue = i18n.str`Forever`; - } else { - if (value.d_ms === undefined) { +export function durationToString(i18n:InternationalizationAPI, d: Duration | undefined): TranslatedString { + if (!d) return "" as TranslatedString + if (d.d_ms === "forever") return i18n.str`Forever` + if (d.d_ms === undefined) { throw Error( `assertion error: duration should have a d_ms but got '${JSON.stringify( - value, + d, )}'`, ); } - strValue = formatDuration( - intervalToDuration({ start: 0, end: value.d_ms }), + return formatDuration( + intervalToDuration({ start: 0, end: d.d_ms }), { locale: { formatDistance: (name, value) => { @@ -104,8 +80,31 @@ export function InputDuration<T>({ }, }, }, - ); - } + ) as TranslatedString +} + +export function InputDuration<T>({ + name, + expand, + placeholder, + tooltip, + label, + help, + readonly, + withForever, + withoutClear, + side, + useProtocolDuration, +}: Props<keyof T>): VNode { + const [opened, setOpened] = useState(false); + const { i18n } = useTranslationContext(); + + const { error, required, value: anyValue, onChange } = useField<T>(name); + const value: Duration | undefined = + anyValue && anyValue.d_us !== undefined + ? Duration.fromTalerProtocolDuration(anyValue) + : anyValue; + const strValue = durationToString(i18n, value) return ( <div class="field is-horizontal"> diff --git a/packages/merchant-backoffice-ui/src/hooks/reports.ts b/packages/merchant-backoffice-ui/src/hooks/reports.ts @@ -15,7 +15,7 @@ */ // FIX default import https://github.com/microsoft/TypeScript/issues/49189 -import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; +import { AccessToken, opFixedSuccess, ReportsSummaryResponse, TalerHttpError, TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util"; import _useSWR, { SWRHook, mutate } from "swr"; import { useSessionContext } from "../context/session.js"; const useSWR = _useSWR as unknown as SWRHook; diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx @@ -21,6 +21,7 @@ import { AccessToken, + assertUnreachable, Duration, HttpStatusCode, MerchantAuthMethod, @@ -199,6 +200,8 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode { return i18n.str`Conflict.`; case HttpStatusCode.NotFound: return i18n.str`Not found.`; + default: + assertUnreachable(fail); } }; const retry = create.lambda((ids: string[]) => [ diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accessTokens/create/CreatePage.tsx @@ -140,6 +140,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Check the password.`; case HttpStatusCode.NotFound: return i18n.str`Instance not found.`; + default: + assertUnreachable(fail); } }; create.onSuccess = onCreated; 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 @@ -92,6 +92,8 @@ export default function AccessTokenListPage({ onCreate }: Props): VNode { return i18n.str`Forbidden.`; case HttpStatusCode.NotFound: return i18n.str`Not found.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx @@ -25,6 +25,7 @@ import { PaytoParseError, Paytos, TalerMerchantApi, + assertUnreachable, opEmptySuccess, } from "@gnu-taler/taler-util"; import { @@ -169,6 +170,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Not found.`; case HttpStatusCode.Conflict: return i18n.str`Conflict.`; + default: + assertUnreachable(fail); } }; @@ -216,6 +219,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { case PaytoParseError.WRONG_PREFIX: case PaytoParseError.INCOMPLETE: return i18n.str`Unsupported type of account`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx @@ -26,6 +26,7 @@ import { PaytoString, Paytos, TalerMerchantApi, + assertUnreachable, opEmptySuccess, succeedOrThrow, } from "@gnu-taler/taler-util"; @@ -232,6 +233,8 @@ export function UpdatePage({ account, onUpdated, onBack }: Props): VNode { return i18n.str`Not found`; case HttpStatusCode.Conflict: return i18n.str`Conflict`; + default: + assertUnreachable(fail); } }; const repeat = update.lambda((ids: string[]) => [ @@ -278,6 +281,8 @@ export function UpdatePage({ account, onUpdated, onBack }: Props): VNode { case PaytoParseError.WRONG_PREFIX: case PaytoParseError.INCOMPLETE: return i18n.str`Unsupported type of account`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -76,6 +76,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -66,6 +66,8 @@ export function CardTable({ return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; return ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx @@ -109,6 +109,8 @@ export function UpdatePage({ category, onUpdated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/groups/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/groups/create/CreatePage.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -78,6 +78,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/groups/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/groups/list/Table.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -37,7 +37,7 @@ type Entity = TalerMerchantApi.GroupEntry; interface Props { devices: Entity[]; - onSelect: (e: Entity) => void; + // onSelect: (e: Entity) => void; onCreate: () => void; onLoadMoreBefore?: () => void; onLoadMoreAfter?: () => void; @@ -46,7 +46,7 @@ interface Props { export function CardTable({ devices, onCreate, - onSelect, + // onSelect, onLoadMoreAfter, onLoadMoreBefore, }: Props): VNode { @@ -66,6 +66,8 @@ export function CardTable({ return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; return ( @@ -100,7 +102,7 @@ export function CardTable({ <Table instances={devices} onDelete={remove} - onSelect={onSelect} + // onSelect={onSelect} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} onLoadMoreAfter={onLoadMoreAfter} @@ -120,7 +122,7 @@ interface TableProps { rowSelection: string[]; instances: Entity[]; onDelete: SafeHandlerTemplate<[id: string], unknown>; - onSelect: (e: Entity) => void; + // onSelect: (e: Entity) => void; rowSelectionHandler: StateUpdater<string[]>; onLoadMoreBefore?: () => void; onLoadMoreAfter?: () => void; @@ -130,7 +132,7 @@ function Table({ instances, onLoadMoreAfter, onDelete, - onSelect, + // onSelect, onLoadMoreBefore, }: TableProps): VNode { const { i18n } = useTranslationContext(); @@ -162,14 +164,14 @@ function Table({ return ( <tr key={i.group_serial}> <td - onClick={(): void => onSelect(i)} - style={{ cursor: "pointer" }} + // onClick={(): void => onSelect(i)} + // style={{ cursor: "pointer" }} > {i.group_name} </td> <td - onClick={(): void => onSelect(i)} - style={{ cursor: "pointer" }} + // onClick={(): void => onSelect(i)} + // style={{ cursor: "pointer" }} > {i.description} </td> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/groups/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/groups/list/index.tsx @@ -34,10 +34,9 @@ import { CardTable } from "./Table.js"; interface Props { onCreate: () => void; - onSelect: (id: string) => void; } -export default function ListProductGroups({ onCreate, onSelect }: Props): VNode { +export default function ListProductGroups({ onCreate }: Props): VNode { const result = useInstanceProductGroups(); if (!result) return <Loading />; @@ -67,9 +66,6 @@ export default function ListProductGroups({ onCreate, onSelect }: Props): VNode onLoadMoreBefore={undefined} //result.isFirstPage ? undefined : result.loadFirst} onLoadMoreAfter={undefined} //result.isLastPage ? undefined : result.loadNext} onCreate={onCreate} - onSelect={(e) => { - onSelect(String(e.group_serial)); - }} /> </section> </Fragment> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx @@ -28,6 +28,7 @@ import { OrderVersion, TalerMerchantApi, TalerProtocolDuration, + assertUnreachable, durationAdd, } from "@gnu-taler/taler-util"; import { @@ -334,6 +335,8 @@ export function CreatePage({ return i18n.str`Product with ID "${fail.body.product_id}" is out of stock.`; case HttpStatusCode.UnavailableForLegalReasons: return i18n.str`No exchange would accept a payment because of KYC requirements.`; + default: + assertUnreachable(fail); } }; const addProductToTheInventoryList = ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx @@ -23,6 +23,7 @@ import { AccessToken, Amounts, AmountString, + assertUnreachable, HttpStatusCode, MerchantContractVersion, TalerErrorCode, @@ -176,6 +177,8 @@ function Table({ return i18n.str`The order doesn't exist`; case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized.`; + default: + assertUnreachable(fail); } }; return ( @@ -410,6 +413,8 @@ export function RefundModal({ return i18n.str`Gone`; case HttpStatusCode.UnavailableForLegalReasons: return i18n.str`There are pending KYC requirements.`; + default: + assertUnreachable(fail); } }; //FIXME: parameters in the translation diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx @@ -161,6 +161,8 @@ export default function OrderList({ return i18n.str`Gone.`; case HttpStatusCode.UnavailableForLegalReasons: return i18n.str`UnavailableForLegalReasons.`; + default: + assertUnreachable(fail); } }; return ( 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 @@ -22,6 +22,7 @@ import { HttpStatusCode, TalerMerchantApi, + assertUnreachable, isRfc3548Base32Charset, randomRfc3548Base32Key, } from "@gnu-taler/taler-util"; @@ -109,6 +110,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; 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 @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -66,6 +66,8 @@ export function CardTable({ return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; return ( 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 @@ -20,6 +20,7 @@ */ import { + assertUnreachable, HttpStatusCode, randomRfc3548Base32Key, TalerMerchantApi, @@ -73,6 +74,8 @@ export function UpdatePage({ device, onUpdated, onBack }: Props): VNode { return i18n.str`Template id is unknown`; case HttpStatusCode.Conflict: return i18n.str`The provided information is inconsistent with the current state of the template`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/password/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/password/index.tsx @@ -121,6 +121,8 @@ export default function PasswordPage({ onCancel, onChange }: Props): VNode { return i18n.str`Not found.`; case "bad-current-pwd": return i18n.str`The current password is wrong.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/pots/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/pots/create/CreatePage.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -77,6 +77,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/pots/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/pots/list/Table.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -66,6 +66,8 @@ export function CardTable({ return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; return ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerError, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerError, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -57,6 +57,8 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { return i18n.str`Not found.`; case HttpStatusCode.Conflict: return i18n.str`Conflict.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx @@ -24,6 +24,7 @@ import { Amounts, HttpStatusCode, TalerMerchantApi, + assertUnreachable, } from "@gnu-taler/taler-util"; import { useLocalNotificationBetter, @@ -161,6 +162,8 @@ function Table({ return i18n.str`Product not found.`; case HttpStatusCode.Conflict: return i18n.str`This change was made without outdated info.`; + default: + assertUnreachable(fail); } }; return ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx @@ -75,6 +75,8 @@ export default function ProductList({ onCreate, onSelect }: Props): VNode { return i18n.str`Not found.`; case HttpStatusCode.Conflict: return i18n.str`Conflict.`; + default: + assertUnreachable(fail); } }; if (!result) return <Loading />; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerError, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerError, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -64,6 +64,8 @@ export function UpdatePage({ product, onBack, onConfirm }: Props): VNode { return i18n.str`Not found.`; case HttpStatusCode.Conflict: return i18n.str`Conflict.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reports/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reports/create/CreatePage.tsx @@ -19,7 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { + assertUnreachable, + HttpStatusCode, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -33,8 +37,12 @@ import { FormProvider, } from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; +import { InputDuration } from "../../../../components/form/InputDuration.js"; +import { InputSelector } from "../../../../components/form/InputSelector.js"; import { useSessionContext } from "../../../../context/session.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { assert } from "console"; +import { NotificationCard } from "../../../../components/menu/index.js"; const TALER_SCREEN_ID = 37; @@ -45,23 +53,41 @@ interface Props { onBack?: () => void; } +const mimeTypes = [ + undefined, + "application/json", + "application/pdf", + "text/html", + "text/csv", + "application/xml", + "application/zip", + "image/png", + "image/jpeg", +] as const; + +type MimeTypes = (typeof mimeTypes)[number]; + export function CreatePage({ onCreated, onBack }: Props): VNode { const { i18n } = useTranslationContext(); - const [state, setState] = useState<Partial<Entity>>({}); + const { state: session, lib, config } = useSessionContext(); + const noGenerators = !config.report_generators.length; + const forcedGenerator = config.report_generators.length === 1; + const [state, setState] = useState<Partial<Entity>>( + forcedGenerator ? { program_section: config.report_generators[0] } : {}, + ); const errors = undefinedIfEmpty<FormErrors<Entity>>({ description: !state.description ? i18n.str`Required` : undefined, data_source: !state.data_source ? i18n.str`Required` : undefined, mime_type: !state.mime_type ? i18n.str`Required` : undefined, - program_section: !state.mime_type ? i18n.str`Required` : undefined, - report_frequency: !state.mime_type ? i18n.str`Required` : undefined, - // report_frequency_shift: !state.mime_type ? i18n.str`Required` : undefined, - target_address: !state.mime_type ? i18n.str`Required` : undefined, + program_section: !state.program_section ? i18n.str`Required` : undefined, + report_frequency: !state.report_frequency ? i18n.str`Required` : undefined, + report_frequency_shift: !state.mime_type ? i18n.str`Required` : undefined, + target_address: !state.target_address ? i18n.str`Required` : undefined, }); const hasErrors = errors !== undefined; - const { state: session, lib } = useSessionContext(); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); const data = !!errors @@ -78,9 +104,21 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; - + if (noGenerators) { + return ( + <NotificationCard + notification={{ + message: i18n.str`No report generator configured in the server`, + description: i18n.str`Contant the system administrator to create a report generator before scheduling one.`, + type: "WARN", + }} + /> + ); + } return ( <div> <LocalNotificationBannerBulma notification={notification} /> @@ -100,38 +138,51 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { /> <Input<Entity> + name="target_address" + label={i18n.str`Address`} + help={i18n.str`Where the report program should send the report`} + /> + + <Input<Entity> name="data_source" label={i18n.str`Source`} help={i18n.str`Base URL to request the data from.`} /> - <Input<Entity> + <InputSelector<Entity> name="mime_type" label={i18n.str`Type`} help={i18n.str`Type of the data source`} + values={mimeTypes as any} + toStr={(v: MimeTypes) => { + switch (v) { + case undefined: + return i18n.str`Choose one`; + default: { + return v; + } + } + }} /> - <Input<Entity> + <InputSelector<Entity> name="program_section" label={i18n.str`Program`} + readonly={forcedGenerator} help={i18n.str`Merchant backend configuration section specifying the program to use to transmit the report`} + values={config.report_generators} /> - <Input<Entity> - name="target_address" - label={i18n.str`Address`} - help={i18n.str`Where the report program should send the report`} - /> - - <Input<Entity> + <InputDuration<Entity> name="report_frequency" label={i18n.str`Report frequency`} + useProtocolDuration /> - <Input<Entity> + {/* <Input<Entity> name="report_frequency_shift" label={i18n.str`Description`} - /> + /> */} <div class="buttons is-right mt-5"> {onBack && ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reports/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reports/list/Table.tsx @@ -19,7 +19,12 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { + assertUnreachable, + Duration, + HttpStatusCode, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -30,6 +35,7 @@ import { import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; import { useSessionContext } from "../../../../context/session.js"; +import { durationToString } from "../../../../components/form/InputDuration.js"; const TALER_SCREEN_ID = 38; @@ -66,6 +72,8 @@ export function CardTable({ return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; return ( @@ -85,7 +93,12 @@ export function CardTable({ class="has-tooltip-left" data-tooltip={i18n.str`Add new reports`} > - <button class="button is-info" accessKey="+" type="button" onClick={onCreate}> + <button + class="button is-info" + accessKey="+" + type="button" + onClick={onCreate} + > <span class="icon is-small"> <i class="mdi mdi-plus mdi-36px" /> </span> @@ -137,7 +150,8 @@ function Table({ return ( <div class="table-container"> {onLoadMoreBefore && ( - <button type="button" + <button + type="button" class="button is-fullwidth" data-tooltip={i18n.str`Load more devices before the first one`} onClick={onLoadMoreBefore} @@ -149,13 +163,10 @@ function Table({ <thead> <tr> <th> - <i18n.Translate>ID</i18n.Translate> - </th> - <th> - <i18n.Translate>Name</i18n.Translate> + <i18n.Translate>Description</i18n.Translate> </th> <th> - <i18n.Translate>Total products</i18n.Translate> + <i18n.Translate>Frequency</i18n.Translate> </th> <th /> </tr> @@ -174,7 +185,10 @@ function Table({ onClick={(): void => onSelect(i)} style={{ cursor: "pointer" }} > - {i.report_frequency} + {durationToString( + i18n, + Duration.fromTalerProtocolDuration(i.report_frequency), + )} </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> @@ -193,7 +207,8 @@ function Table({ </tbody> </table> {onLoadMoreAfter && ( - <button type="button" + <button + type="button" class="button is-fullwidth" data-tooltip={i18n.str`Load more devices after the last one`} onClick={onLoadMoreAfter} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reports/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reports/update/UpdatePage.tsx @@ -20,26 +20,48 @@ */ import { + assertUnreachable, HttpStatusCode, - TalerMerchantApi + TalerMerchantApi, } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, useLocalNotificationBetter, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { FormProvider } from "../../../../components/form/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; import { useSessionContext } from "../../../../context/session.js"; import { WithId } from "../../../../declaration.js"; +import { NotificationCard } from "../../../../components/menu/index.js"; +import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { InputSelector } from "../../../../components/form/InputSelector.js"; +import { InputDuration } from "../../../../components/form/InputDuration.js"; const TALER_SCREEN_ID = 39; type Entity = TalerMerchantApi.ReportDetailResponse & WithId; +const mimeTypes = [ + undefined, + "application/json", + "application/pdf", + "text/html", + "text/csv", + "application/xml", + "application/zip", + "image/png", + "image/jpeg", +] as const; + +type MimeTypes = (typeof mimeTypes)[number]; + interface Props { onUpdated: () => void; onBack?: () => void; @@ -50,7 +72,10 @@ export function UpdatePage({ report, onUpdated, onBack }: Props): VNode { const { state: { token }, lib, + config, } = useSessionContext(); + const noGenerators = !config.report_generators.length; + const forcedGenerator = config.report_generators.length === 1; // FIXME: if the product list is big the will bring a lot of info // const inventoryResult = useInstanceProducts(); @@ -61,9 +86,7 @@ export function UpdatePage({ report, onUpdated, onBack }: Props): VNode { // ? [] // : inventoryResult.body; - const [state, setState] = useState< - Partial<Entity & { product_map: { id: string; description: string }[] }> - >({ + const [state, setState] = useState<Partial<Entity>>({ ...report, }); @@ -86,11 +109,25 @@ export function UpdatePage({ report, onUpdated, onBack }: Props): VNode { // setState({ ...state, product_map }); // }); // }, []); + const errors = undefinedIfEmpty<FormErrors<Entity>>({ + description: !state.description ? i18n.str`Required` : undefined, + data_source: !state.data_source ? i18n.str`Required` : undefined, + mime_type: !state.mime_type ? i18n.str`Required` : undefined, + program_section: !state.program_section ? i18n.str`Required` : undefined, + report_frequency: !state.report_frequency ? i18n.str`Required` : undefined, + report_frequency_shift: !state.report_frequency_shift + ? i18n.str`Required` + : undefined, + target_address: !state.target_address ? i18n.str`Required` : undefined, + }); + + const hasErrors = errors !== undefined; + const [notification, safeFunctionHandler] = useLocalNotificationBetter(); const data = state as TalerMerchantApi.ReportAddRequest; const update = safeFunctionHandler( lib.instance.updateScheduledReport.bind(lib.instance), - !token ? undefined : [token, report.id, data], + !token || hasErrors ? undefined : [token, report.id, data], ); update.onSuccess = onUpdated; update.onFail = (fail) => { @@ -99,13 +136,25 @@ export function UpdatePage({ report, onUpdated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; + if (noGenerators) { + return ( + <NotificationCard + notification={{ + message: i18n.str`No report generator configured in the server`, + description: i18n.str`Contant the system administrator to create a report generator before scheduling one.`, + type: "WARN", + }} + /> + ); + } return ( <div> <LocalNotificationBannerBulma notification={notification} /> - <section class="section"> <section class="hero is-hero-bar"> <div class="hero-body"> @@ -127,10 +176,53 @@ export function UpdatePage({ report, onUpdated, onBack }: Props): VNode { <section class="section is-main-section"> <div class="columns"> <div class="column is-four-fifths"> - <FormProvider object={state} valueHandler={setState}> + <FormProvider object={state} valueHandler={setState} errors={errors}> <Input<Entity> name="description" label={i18n.str`Description`} + help={i18n.str`Description of the report. Possibly included in the report message`} + /> + + <Input<Entity> + name="target_address" + label={i18n.str`Address`} + help={i18n.str`Where the report program should send the report`} + /> + + <Input<Entity> + name="data_source" + label={i18n.str`Source`} + help={i18n.str`Base URL to request the data from.`} + /> + + <InputSelector<Entity> + name="mime_type" + label={i18n.str`Type`} + help={i18n.str`Type of the data source`} + values={mimeTypes as any} + toStr={(v: MimeTypes) => { + switch (v) { + case undefined: + return i18n.str`Choose one`; + default: { + return v; + } + } + }} + /> + + <InputSelector<Entity> + name="program_section" + label={i18n.str`Program`} + readonly={forcedGenerator} + help={i18n.str`Merchant backend configuration section specifying the program to use to transmit the report`} + values={config.report_generators} + /> + + <InputDuration<Entity> + name="report_frequency" + label={i18n.str`Report frequency`} + useProtocolDuration /> </FormProvider> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx @@ -98,6 +98,8 @@ export default function ListTemplates({ return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -28,6 +28,7 @@ import { TalerMerchantApi, TalerProtocolDuration, TranslatedString, + assertUnreachable, } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, @@ -236,6 +237,8 @@ export function UpdatePage({ template, onUpdated, onBack }: Props): VNode { return i18n.str`Not found`; case HttpStatusCode.Conflict: return i18n.str`Conflict`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx @@ -21,6 +21,7 @@ import { AmountString, + assertUnreachable, HttpStatusCode, TalerMerchantApi, UsingTemplateDetails, @@ -107,6 +108,8 @@ export function UsePage({ return i18n.str`No more stock for product with ID "${fail.body.product_id}".`; case HttpStatusCode.UnavailableForLegalReasons: return i18n.str`No exchange would accept a payment because of KYC requirements.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/create/CreatePage.tsx @@ -21,6 +21,7 @@ import { AbsoluteTime, + assertUnreachable, Duration, HttpStatusCode, TalerMerchantApi, @@ -96,6 +97,8 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/list/index.tsx @@ -91,6 +91,8 @@ export default function TokenFamilyList({ onCreate, onSelect }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx @@ -19,7 +19,7 @@ * @author Christian Blättler */ -import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -76,6 +76,8 @@ export function UpdatePage({ onUpdated, onBack, tokenFamily }: Props) { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx @@ -112,6 +112,8 @@ export default function ListTransfer({}: Props): VNode { return i18n.str`Not found.`; case HttpStatusCode.Conflict: return i18n.str`Wire transfer already confirmed.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx @@ -21,6 +21,7 @@ import { AccessToken, + assertUnreachable, Duration, HttpStatusCode, TalerMerchantApi, @@ -163,6 +164,8 @@ export function UpdatePage({ return i18n.str`Unauthorized.`; case HttpStatusCode.NotFound: return i18n.str`Instance not found.`; + default: + assertUnreachable(fail); } }; const retry = update.lambda((challengesIds: string[]) => [ diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx @@ -19,7 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { assertUnreachable, HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { + assertUnreachable, + HttpStatusCode, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -94,13 +98,17 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { lib.instance.addWebhook.bind(lib.instance), !session.token || hasErrors ? undefined : [session.token, data], ); - create.onSuccess = onCreate - create.onFail = (fail) =>{ - switch(fail.case) { - case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized.` - case HttpStatusCode.NotFound: return i18n.str`Not found.` + create.onSuccess = onCreate; + create.onFail = (fail) => { + switch (fail.case) { + case HttpStatusCode.Unauthorized: + return i18n.str`Unauthorized.`; + case HttpStatusCode.NotFound: + return i18n.str`Not found.`; + default: + assertUnreachable(fail); } - } + }; return ( <div> @@ -268,7 +276,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <div class="buttons is-right mt-5"> {onBack && ( - <button type="button"class="button" onClick={onBack}> + <button type="button" class="button" onClick={onBack}> <i18n.Translate>Cancel</i18n.Translate> </button> )} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx @@ -21,6 +21,7 @@ import { AccessToken, + assertUnreachable, HttpStatusCode, TalerMerchantApi, } from "@gnu-taler/taler-util"; @@ -72,6 +73,8 @@ export function CardTable({ return i18n.str`Unauthorized.`; case HttpStatusCode.NotFound: return i18n.str`Not found.`; + default: + assertUnreachable(fail); } }; const deleteById = deleteWebhook.lambda((id: string) => diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx @@ -110,6 +110,8 @@ export function UpdatePage({ webhook, onConfirm, onBack }: Props): VNode { return i18n.str`Not found.`; case HttpStatusCode.Conflict: return i18n.str`Conflict.`; + default: + assertUnreachable(fail); } }; diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx @@ -20,6 +20,7 @@ */ import { + assertUnreachable, Duration, HttpStatusCode, LoginTokenRequest, @@ -96,6 +97,8 @@ export function LoginPage({ showCreateAccount }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.NotFound: return i18n.str`Not found`; + default: + assertUnreachable(fail); } }; const retry = login.lambda((ids: string[]) => [ diff --git a/packages/merchant-backoffice-ui/src/paths/newAccount/index.tsx b/packages/merchant-backoffice-ui/src/paths/newAccount/index.tsx @@ -15,6 +15,7 @@ */ import { + assertUnreachable, Duration, HttpStatusCode, InstanceConfigurationMessage, @@ -170,6 +171,8 @@ export function NewAccount({ onCancel, onCreated }: Props): VNode { return i18n.str`Unauthorized`; case HttpStatusCode.Conflict: return i18n.str`There is another instance with this username.`; + default: + assertUnreachable(fail); } }; const retry = create.lambda((ids: string[]) => [create.args![0], ids]); diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts @@ -1241,7 +1241,7 @@ export interface MerchantVersionResponse { // Maps available report generator configuration section names // to descriptions of the respective report generator. // Since **v25**. - report_generators?: { [section_name: string]: string }; + report_generators: string[]; // Array of exchanges trusted by the merchant. // Since protocol **v6**. @@ -4054,7 +4054,7 @@ export const codecForTalerMerchantConfigResponse = .property("currencies", codecForMap(codecForCurrencySpecificiation())) .property( "report_generators", - codecOptionalDefault(codecForMap(codecForString()), {}), + codecOptionalDefault(codecForList(codecForString()), []), ) .property("exchanges", codecForList(codecForExchangeConfigInfo())) .property("implementation", codecOptional(codecForString()))