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:
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>
+ );
}