taler-typescript-core

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

commit 7fa0b56e10d08ae12b0f41b7d70adb48d8cc700d
parent 72bc92a45a461d25be83eb78ccd9c2113e22a47e
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Thu, 28 May 2026 08:11:35 -0300

on loading error the title should be defined in the UI

also simplify error description

Diffstat:
Mpackages/web-util/src/components/ErrorLoading.tsx | 203+++++++++++++++++++++++--------------------------------------------------------
Mpackages/web-util/src/components/ErrorLoadingMerchant.tsx | 229+++++++++++--------------------------------------------------------------------
2 files changed, 91 insertions(+), 341 deletions(-)

diff --git a/packages/web-util/src/components/ErrorLoading.tsx b/packages/web-util/src/components/ErrorLoading.tsx @@ -16,163 +16,78 @@ */ import { + InternationalizationAPI, + OperationFail, TalerError, - TalerErrorCode, - assertUnreachable, + TranslatedString, } from "@gnu-taler/taler-util"; import { Fragment, VNode, h } from "preact"; import { + translateTalerError, useCommonPreferences, useTranslationContext, } from "../index.browser.js"; import { Attention } from "./Attention.js"; -export function DebugInfo({ error }: { error: any }): VNode { +export function ErrorLoading({ + title, + error, +}: { + title: TranslatedString; + error: TalerError; +}): VNode { const { i18n } = useTranslationContext(); - const [{ showDebugInfo }, update] = useCommonPreferences(); + const description = translateTalerError(error, i18n); + const [{ showDebugInfo }] = useCommonPreferences(); return ( - <div class="text-[grey]"> - <button onClick={() => update("showDebugInfo", !showDebugInfo)}> - {!showDebugInfo ? ( - <i18n.Translate>Show more information</i18n.Translate> - ) : ( - <i18n.Translate>Hide debug info</i18n.Translate> + <Attention type="danger" copy title={title}> + {(description ?? []).map((d) => ( + <p>{d}</p> + ))} + <pre + class="whitespace-break-spaces text-black" + style={{ display: showDebugInfo ? "block" : "none" }} + > + {JSON.stringify( + error.errorDetail, + function excludePrivate(key, value) { + if (key.startsWith("__")) return "..."; + return value; + }, + 2, )} - </button> - {showDebugInfo && ( - <pre class="whitespace-break-spaces text-black"> - {JSON.stringify(error, undefined, 2)} - </pre> - )} - </div> + </pre> + </Attention> ); } -export function ErrorLoading({ error }: { error: TalerError }): VNode { + +export function FailLoading<T>({ + title, + translate, + operation, +}: { + title: TranslatedString; + operation: OperationFail<T>; + translate: (f: OperationFail<T>, i18n: InternationalizationAPI) => VNode; +}): VNode { const { i18n } = useTranslationContext(); - switch (error.errorDetail.code) { - ////////////////// - // Every error that can be produce in a Http Request - ////////////////// - case TalerErrorCode.GENERIC_TIMEOUT: { - if (error.hasErrorCode(TalerErrorCode.GENERIC_TIMEOUT)) { - return ( - <Attention - type="danger" - title={i18n.str`The request reached a timeout, check your connection.`} - > - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR: { - if (error.hasErrorCode(TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR)) { - const { requestMethod, requestUrl, timeoutMs } = error.errorDetail; - return ( - <Attention type="danger" title={i18n.str`The request was cancelled.`}> - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: { - if ( - error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT) - ) { - const { requestMethod, requestUrl, timeoutMs } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`The request reached a timeout, check your connection.`} - > - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: { - if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) { - const { requestMethod, requestUrl, throttleStats } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`Too many requests were made to the server, and this action was throttled.`} - > - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: { - if ( - error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE) - ) { - const { requestMethod, requestUrl, httpStatusCode, validationError } = - error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`The server's response was malformed.`} - > - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_NETWORK_ERROR: { - if (error.hasErrorCode(TalerErrorCode.WALLET_NETWORK_ERROR)) { - const { requestMethod, requestUrl } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`Could not complete the request due to a network problem.`} - > - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { - if (error.hasErrorCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR)) { - const { requestMethod, requestUrl, httpStatusCode, errorResponse } = - error.errorDetail; - return ( - <Attention type="danger" title={i18n.str`Unexpected request error`}> - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } - assertUnreachable(1 as never); - } - ////////////////// - // Every other error - ////////////////// - // case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { - // return <Attention type="danger" title={i18n.str``}> - // </Attention> - // } - ////////////////// - // Default message for unhandled case - ////////////////// - default: - return ( - <Attention type="danger" title={i18n.str`Unexpected error`}> - {error.message} - <DebugInfo error={error.errorDetail} /> - </Attention> - ); - } + const [{ showDebugInfo }] = useCommonPreferences(); + return ( + <Attention type="danger" copy title={title}> + {translate(operation, i18n)} + <pre + class="whitespace-break-spaces text-black" + style={{ display: showDebugInfo ? "block" : "none" }} + > + {JSON.stringify( + operation, + function excludePrivate(key, value) { + if (key.startsWith("__")) return "..."; + return value; + }, + 2, + )} + </pre> + </Attention> + ); } diff --git a/packages/web-util/src/components/ErrorLoadingMerchant.tsx b/packages/web-util/src/components/ErrorLoadingMerchant.tsx @@ -1,5 +1,4 @@ /* -/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. @@ -17,208 +16,44 @@ import { TalerError, - TalerErrorCode, - assertUnreachable, + TranslatedString } from "@gnu-taler/taler-util"; import { Fragment, VNode, h } from "preact"; -import { Attention } from "./Attention.js"; import { + translateTalerError, useCommonPreferences, useTranslationContext, } from "../index.browser.js"; +import { Attention } from "./Attention.js"; + +function DebugInfo({ error }: { error: any }): VNode { + const [{ showDebugInfo }, update] = useCommonPreferences(); + if (!showDebugInfo) return <Fragment />; + return ( + <pre class="whitespace-break-spaces "> + {JSON.stringify(error, undefined, 2)} + </pre> + ); +} -export function ErrorLoading({ error }: { error: TalerError }): VNode { - const [{ showDebugInfo }] = useCommonPreferences(); +export function ErrorLoading({ + title, + error, +}: { + title: TranslatedString; + error: TalerError; +}): VNode { const { i18n } = useTranslationContext(); - switch (error.errorDetail.code) { - ////////////////// - // Every error that can be produce in a Http Request - ////////////////// - case TalerErrorCode.GENERIC_TIMEOUT: { - if (error.hasErrorCode(TalerErrorCode.GENERIC_TIMEOUT)) { - const { requestMethod, requestUrl, timeoutMs } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`The request reached a timeout, check your connection.`} - > - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify( - { requestMethod, requestUrl, timeoutMs }, - undefined, - 2, - )} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR: { - if (error.hasErrorCode(TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR)) { - const { requestMethod, requestUrl, timeoutMs } = error.errorDetail; - return ( - <Attention type="danger" title={i18n.str`The request was cancelled.`}> - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify( - { requestMethod, requestUrl, timeoutMs }, - undefined, - 2, - )} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: { - if ( - error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT) - ) { - const { requestMethod, requestUrl, timeoutMs } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`The request reached a timeout, check your connection.`} - > - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify( - { requestMethod, requestUrl, timeoutMs }, - undefined, - 2, - )} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: { - if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) { - const { requestMethod, requestUrl, throttleStats } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`Too many requests were made to the server and this action was throttled.`} - > - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify( - { requestMethod, requestUrl, throttleStats }, - undefined, - 2, - )} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: { - if ( - error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE) - ) { - const { requestMethod, requestUrl, httpStatusCode, validationError } = - error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`The server's response was malformed.`} - > - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify( - { - requestMethod, - requestUrl, - httpStatusCode, - validationError, - }, - undefined, - 2, - )} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_NETWORK_ERROR: { - if (error.hasErrorCode(TalerErrorCode.WALLET_NETWORK_ERROR)) { - const { requestMethod, requestUrl } = error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`Due to a network problem the request could not be finished.`} - > - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify({ requestMethod, requestUrl }, undefined, 2)} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { - if (error.hasErrorCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR)) { - const { requestMethod, requestUrl, httpStatusCode, errorResponse } = - error.errorDetail; - return ( - <Attention - type="danger" - title={i18n.str`The server's response was unexpected.`} - > - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify( - { requestMethod, requestUrl, httpStatusCode, errorResponse }, - undefined, - 2, - )} - </pre> - )} - </Attention> - ); - } - assertUnreachable(1 as never); - } - ////////////////// - // Every other error - ////////////////// - // case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: { - // return <Attention type="danger" title={i18n.str``}> - // </Attention> - // } - ////////////////// - // Default message for unhandled case - ////////////////// - default: - return ( - <Attention type="danger" title={i18n.str`Unexpected error`}> - {error.message} - {showDebugInfo && ( - <pre class="whitespace-break-spaces "> - {JSON.stringify(error.errorDetail, undefined, 2)} - </pre> - )} - </Attention> - ); - } + const description = translateTalerError(error, i18n); + return ( + <Attention + type="danger" + title={title} + > + {(description ?? []).map((d) => ( + <p>{d}</p> + ))} + <DebugInfo error={error.errorDetail} /> + </Attention> + ); }