commit 063ee8244e4304de4f14697008848dea934c98dc
parent c8f3e929d2f5551460122c9288ea77e05f42c37f
Author: Sebastian <sebasjm@gmail.com>
Date: Tue, 4 Nov 2025 14:52:55 -0300
error reporting fix in merchant backoffice
Diffstat:
7 files changed, 98 insertions(+), 34 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx b/packages/merchant-backoffice-ui/src/Routing.tsx
@@ -25,7 +25,7 @@ import {
TalerError,
TranslatedString,
} from "@gnu-taler/taler-util";
-import { LocalNotificationBannerBulma, urlPattern, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { urlPattern, useTranslationContext } from "@gnu-taler/web-util/browser";
import { createHashHistory } from "history";
import { Fragment, VNode, h } from "preact";
import { Route, Router, route } from "preact-router";
@@ -83,9 +83,9 @@ import WebhookListPage from "./paths/instance/webhooks/list/index.js";
import WebhookUpdatePage from "./paths/instance/webhooks/update/index.js";
import { LoginPage } from "./paths/login/index.js";
import { NewAccount } from "./paths/newAccount/index.js";
+import { ResetAccount } from "./paths/resetAccount/index.js";
import { Settings } from "./paths/settings/index.js";
import { Notification } from "./utils/types.js";
-import { ResetAccount } from "./paths/resetAccount/index.js";
export enum InstancePaths {
error = "/error",
diff --git a/packages/merchant-backoffice-ui/src/hooks/preference.ts b/packages/merchant-backoffice-ui/src/hooks/preference.ts
@@ -28,7 +28,6 @@ import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
export interface Preferences {
advanceOrderMode: boolean;
advanceInstanceMode: boolean;
- developerMode: boolean;
hideKycUntil: AbsoluteTime;
hideMissingAccountUntil: AbsoluteTime;
dateFormat: "ymd" | "dmy" | "mdy";
@@ -37,7 +36,6 @@ export interface Preferences {
const defaultSettings: Preferences = {
advanceOrderMode: false,
advanceInstanceMode: false,
- developerMode: false,
hideKycUntil: AbsoluteTime.never(),
hideMissingAccountUntil: AbsoluteTime.never(),
dateFormat: "ymd",
@@ -47,7 +45,6 @@ export const codecForPreferences = (): Codec<Preferences> =>
buildCodecForObject<Preferences>()
.property("advanceOrderMode", codecForBoolean())
.property("advanceInstanceMode", codecForBoolean())
- .property("developerMode", codecForBoolean())
.property("hideKycUntil", codecForAbsoluteTime)
.property("hideMissingAccountUntil", codecForAbsoluteTime)
.property(
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -141,7 +141,7 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode {
: undefined,
});
- const hasTokenErrors = tokenFormErrors === undefined;
+ const hasTokenErrors = tokenFormErrors !== undefined;
const [notification, safeFunctionHandler] = useLocalNotificationBetter();
const { state: session, lib, logIn } = useSessionContext();
const mfa = useChallengeHandler();
@@ -153,20 +153,20 @@ export function CreatePage({ onConfirm, onBack, forceId }: Props): VNode {
password: tokenForm.password!,
},
};
- // if (!newValue.address) newValue.address = {};
- // if (!newValue.jurisdiction) newValue.jurisdiction = {};
const create = safeFunctionHandler(
async (
token: AccessToken,
data: TalerMerchantApi.InstanceConfigurationMessage,
challengeIds: string[],
) => {
+ if (!data.address) data.address = {};
+ if (!data.jurisdiction) data.jurisdiction = {};
const instanceResp = await lib.instance.createInstance(token, data, {
challengeIds,
});
if (instanceResp.type === "fail") return instanceResp;
if (data.auth.password) {
- const tokenResp = await lib.instance.createAccessToken(
+ const tokenResp = await lib.subInstanceApi(data.id).instance.createAccessToken(
data.id,
data.auth.password,
FOREVER_REFRESHABLE_TOKEN(i18n.str`Instance created`),
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/index.tsx
@@ -18,22 +18,9 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
import {
- HttpStatusCode,
- InstanceConfigurationMessage,
- TalerMerchantApi,
+ TalerMerchantApi
} from "@gnu-taler/taler-util";
-import {
- LocalNotificationBannerBulma,
- useChallengeHandler,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../components/menu/index.js";
-import { SolveMFAChallenges } from "../../../components/SolveMFA.js";
-import { useSessionContext } from "../../../context/session.js";
-import { Notification } from "../../../utils/types.js";
-import { FOREVER_REFRESHABLE_TOKEN } from "../../login/index.js";
import { CreatePage } from "./CreatePage.js";
interface Props {
diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx
@@ -24,12 +24,13 @@ import {
HttpStatusCode,
LoginTokenRequest,
LoginTokenScope,
- TranslatedString
+ TranslatedString,
} from "@gnu-taler/taler-util";
import {
ButtonBetterBulma,
- LocalNotificationBannerBulma,
+ Notification,
useChallengeHandler,
+ useCommonPreference,
useLocalNotificationBetter,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
@@ -114,7 +115,31 @@ export function LoginPage({ showCreateAccount }: Props): VNode {
return (
<Fragment>
- <LocalNotificationBannerBulma notification={notification} />
+ <LocalNotificationBannerBulma
+ notification={{
+ acknowledge() {},
+ message: {
+ title: "The operation failed." as TranslatedString,
+ type: "error",
+ description: "Unauthorized" as TranslatedString,
+ debug: {
+ detail: {
+ code: 2015,
+ hint: "The merchant refused the request due to lack of authorization.",
+ detail: "Check 'Authorization' header",
+ },
+ case: 401,
+ when: {
+ t_ms: 1762199783634,
+ },
+ args: "admin, w, []",
+ },
+ when: {
+ t_ms: 1762199783634,
+ } as any,
+ },
+ }}
+ />
<div class="columns is-centered" style={{ margin: "auto" }}>
<div class="column is-two-thirds ">
<div class="modal-card" style={{ width: "100%", margin: 0 }}>
@@ -241,3 +266,51 @@ export function LoginPage({ showCreateAccount }: Props): VNode {
</Fragment>
);
}
+
+function LocalNotificationBannerBulma({
+ notification,
+}: {
+ notification?: Notification;
+}): VNode {
+ const { showDebugInfo, toggle } = useCommonPreference();
+ if (!notification) return <Fragment />;
+ const msg = notification.message;
+ switch (msg.type) {
+ case "error":
+ return (
+ <div class="notification">
+ <div class="columns is-vcentered">
+ <div class="column is-12">
+ <article class="message is-danger">
+ <div class="message-header">
+ <p>{msg.title}</p>
+ </div>
+ {msg.description && (
+ <div class="message-body">
+ <div>{msg.description}</div>
+ {!showDebugInfo ? undefined : msg.debug && (
+ <pre> {JSON.stringify(msg.debug, undefined, 2)}</pre>
+ )}
+ </div>
+ )}
+ </article>
+ </div>
+ </div>
+ </div>
+ );
+ case "info":
+ return (
+ <div class="notification">
+ <div class="columns is-vcentered">
+ <div class="column is-12">
+ <article class="message is-info">
+ <div class="message-header">
+ <p>{msg.title}</p>
+ </div>
+ </article>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
@@ -15,7 +15,7 @@
*/
import { AbsoluteTime } from "@gnu-taler/taler-util";
-import { useLang, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useCommonPreference, useLang, useTranslationContext } from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import {
FormErrors,
@@ -26,22 +26,27 @@ import { InputToggle } from "../../components/form/InputToggle.js";
import { LangSelector } from "../../components/menu/LangSelector.js";
import { Preferences, usePreference } from "../../hooks/preference.js";
+type FormType = Preferences & {developerMode: boolean}
export function Settings({ onClose }: { onClose?: () => void }): VNode {
const { i18n } = useTranslationContext();
const [value, , updateValue] = usePreference();
- const errors: FormErrors<Preferences> = {};
+ const {showDebugInfo, toggle} = useCommonPreference();
+ const errors: FormErrors<FormType> = {};
- function valueHandler(s: (d: Partial<Preferences>) => Partial<Preferences>): void {
+ function valueHandler(s: (d: Partial<FormType>) => Partial<FormType>): void {
const next = s(value);
const v: Preferences = {
advanceOrderMode: next.advanceOrderMode ?? false,
advanceInstanceMode: next.advanceInstanceMode ?? false,
- developerMode: next.developerMode ?? false,
hideMissingAccountUntil: next.hideMissingAccountUntil ?? AbsoluteTime.never(),
hideKycUntil: next.hideKycUntil ?? AbsoluteTime.never(),
dateFormat: next.dateFormat ?? "ymd",
};
+ if (next.developerMode !== undefined && next.developerMode !== showDebugInfo) {
+ toggle()
+ }
+
updateValue(v);
}
@@ -52,10 +57,13 @@ export function Settings({ onClose }: { onClose?: () => void }): VNode {
<div class="column" />
<div class="column is-four-fifths">
<div>
- <FormProvider<Preferences>
+ <FormProvider<FormType>
name="settings"
errors={errors}
- object={value}
+ object={{
+ ...value,
+ developerMode: showDebugInfo
+ }}
valueHandler={valueHandler}
>
<div class="field is-horizontal">
@@ -108,7 +116,7 @@ export function Settings({ onClose }: { onClose?: () => void }): VNode {
values={["ymd", "mdy", "dmy"]}
tooltip={i18n.str`How the date is going to be displayed`}
/>
- <InputToggle<Preferences>
+ <InputToggle<FormType>
label={i18n.str`Developer mode`}
tooltip={i18n.str`Shows more options and tools that are not intended for a general audience.`}
name="developerMode"
diff --git a/packages/web-util/src/hooks/useNotifications.ts b/packages/web-util/src/hooks/useNotifications.ts
@@ -11,7 +11,6 @@ import {
TranslatedString,
} from "@gnu-taler/taler-util";
import { useEffect, useState } from "preact/hooks";
-import { ButtonHandler } from "../components/Button.js";
import {
InternationalizationAPI,
memoryMap,