aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorNic Eigel <nic@eigel.ch>2024-06-24 01:50:38 +0200
committerNic Eigel <nic@eigel.ch>2024-06-24 01:50:38 +0200
commit0b90a34e7c7c5d9bcca9a2ebe74df9fdfafc6577 (patch)
tree6a1818c24f8fcad09d1756d96e171d32a1f9c0f7 /packages
parentbf94287904c08f2c62acbb5b52c2cc0d1fcb964b (diff)
downloadwallet-core-0b90a34e7c7c5d9bcca9a2ebe74df9fdfafc6577.tar.xz
real-time-auditor
Diffstat (limited to 'packages')
-rw-r--r--packages/auditor-backoffice-ui/README.md2
-rwxr-xr-xpackages/auditor-backoffice-ui/dev.mjs2
-rw-r--r--packages/auditor-backoffice-ui/src/Application.tsx245
-rw-r--r--packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx190
-rw-r--r--packages/auditor-backoffice-ui/src/components/exception/loading.tsx40
-rw-r--r--packages/auditor-backoffice-ui/src/components/form/JumpToElementById.tsx59
-rw-r--r--packages/auditor-backoffice-ui/src/components/form/useGroupField.tsx41
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/FormProvider.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/Input.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/Input.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/InputCurrency.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/InputCurrency.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/InputNumber.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/InputNumber.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/InputSelector.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/InputSelector.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/InputToggle.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/InputToggle.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/InputWithAddon.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/InputWithAddon.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/forms/useField.tsx (renamed from packages/auditor-backoffice-ui/src/components/form/useField.tsx)0
-rw-r--r--packages/auditor-backoffice-ui/src/components/menu/SideBar.tsx265
-rw-r--r--packages/auditor-backoffice-ui/src/components/modal/index.tsx10
-rw-r--r--packages/auditor-backoffice-ui/src/context/backend.ts60
-rw-r--r--packages/auditor-backoffice-ui/src/context/config.ts11
-rw-r--r--packages/auditor-backoffice-ui/src/context/entity.ts (renamed from packages/auditor-backoffice-ui/src/context/instance.ts)29
-rw-r--r--packages/auditor-backoffice-ui/src/custom.d.ts2
-rw-r--r--packages/auditor-backoffice-ui/src/declaration.d.ts1978
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/backend.ts618
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/critical.ts70
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/deposit_confirmations.ts161
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/entity.ts82
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/finance.ts61
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/index.ts176
-rw-r--r--packages/auditor-backoffice-ui/src/hooks/operational.ts83
-rw-r--r--packages/auditor-backoffice-ui/src/index.tsx2
-rw-r--r--packages/auditor-backoffice-ui/src/paths/default/Table.tsx155
-rw-r--r--packages/auditor-backoffice-ui/src/paths/default/index.tsx130
-rw-r--r--packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx346
-rw-r--r--packages/auditor-backoffice-ui/src/paths/details/index.tsx (renamed from packages/auditor-backoffice-ui/src/paths/instance/transfers/update/index.tsx)19
-rw-r--r--packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx214
-rw-r--r--packages/auditor-backoffice-ui/src/paths/finance/index.tsx (renamed from packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/index.tsx)69
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/index.tsx126
-rw-r--r--packages/auditor-backoffice-ui/src/paths/login/index.tsx317
-rw-r--r--packages/auditor-backoffice-ui/src/paths/notfound/index.tsx16
-rw-r--r--packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx72
-rw-r--r--packages/auditor-backoffice-ui/src/paths/operations/index.tsx (renamed from packages/auditor-backoffice-ui/src/paths/instance/products/update/index.tsx)69
-rw-r--r--packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx70
-rw-r--r--packages/auditor-backoffice-ui/src/paths/security/index.tsx (renamed from packages/auditor-backoffice-ui/src/paths/instance/templates/qr/index.tsx)56
-rw-r--r--packages/auditor-backoffice-ui/src/paths/settings/index.tsx55
-rw-r--r--packages/auditor-backoffice-ui/src/stories.tsx48
-rw-r--r--packages/auditor-backoffice-ui/src/sw.js25
-rw-r--r--packages/auditor-backoffice-ui/src/utils/amount.ts7
-rw-r--r--packages/auditor-backoffice-ui/src/utils/table.ts12
-rw-r--r--packages/web-util/src/utils/request.ts1
49 files changed, 2611 insertions, 3383 deletions
diff --git a/packages/auditor-backoffice-ui/README.md b/packages/auditor-backoffice-ui/README.md
index b10fa6a94..03f4403b4 100644
--- a/packages/auditor-backoffice-ui/README.md
+++ b/packages/auditor-backoffice-ui/README.md
@@ -1,4 +1,4 @@
-## AUditor Admin Frontend
+ ## AUditor Admin Frontend
Auditor Admin Frontend is a Single Page Application (SPA) that connects with a running Auditor Backend and lets you audit the exchange.
diff --git a/packages/auditor-backoffice-ui/dev.mjs b/packages/auditor-backoffice-ui/dev.mjs
index 14d5737de..d2299dad4 100755
--- a/packages/auditor-backoffice-ui/dev.mjs
+++ b/packages/auditor-backoffice-ui/dev.mjs
@@ -18,7 +18,7 @@
import { serve } from "@gnu-taler/web-util/node";
import { initializeDev } from "@gnu-taler/web-util/build";
-const devEntryPoints = ["src/stories.tsx", "src/index.tsx"];
+const devEntryPoints = ["src/index.tsx"];
const build = initializeDev({
type: "development",
diff --git a/packages/auditor-backoffice-ui/src/Application.tsx b/packages/auditor-backoffice-ui/src/Application.tsx
index a1a05fc97..3b6aa8dd3 100644
--- a/packages/auditor-backoffice-ui/src/Application.tsx
+++ b/packages/auditor-backoffice-ui/src/Application.tsx
@@ -17,149 +17,150 @@
/**
*
* @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
-import { HttpStatusCode, LibtoolVersion } from "@gnu-taler/taler-util";
+import {HttpStatusCode, LibtoolVersion} from "@gnu-taler/taler-util";
import {
- ErrorType,
- TranslationProvider,
- useTranslationContext,
+ ErrorType,
+ TranslationProvider,
+ useTranslationContext
} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useMemo } from "preact/hooks";
-import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
-import { Loading } from "./components/exception/loading.js";
+import {Fragment, VNode, h, render} from "preact";
+import {useMemo} from "preact/hooks";
+import {ApplicationReadyRoutes} from "./ApplicationReadyRoutes.js";
+import {Loading} from "./components/exception/loading.js";
import {
- NotConnectedAppMenu,
- NotificationCard
+ NotConnectedAppMenu,
+ NotificationCard
} from "./components/menu/index.js";
import {
- BackendContextProvider
+ BackendContextProvider
} from "./context/backend.js";
-import { ConfigContextProvider } from "./context/config.js";
-import { useBackendConfig } from "./hooks/backend.js";
+import {ConfigContextProvider} from "./context/config.js";
+import {useBackendConfig} from "./hooks/backend.js";
import { strings } from "./i18n/strings.js";
export function Application(): VNode {
- return (
- <BackendContextProvider>
- <TranslationProvider source={strings}>
- <ApplicationStatusRoutes />
- </TranslationProvider>
- </BackendContextProvider>
- );
+ return (
+ <BackendContextProvider>
+ <TranslationProvider source={strings}>
+ <ApplicationStatusRoutes/>
+ </TranslationProvider>
+ </BackendContextProvider>
+ );
}
/**
* Check connection testing against /config
- *
- * @returns
+ *
+ * @returns
*/
function ApplicationStatusRoutes(): VNode {
- const result = useBackendConfig();
- const { i18n } = useTranslationContext();
+ const result = useBackendConfig();
+ const {i18n} = useTranslationContext();
- const { currency, version } = result.ok && result.data
- ? result.data
- : { currency: "unknown", version: "unknown" };
- const ctx = useMemo(() => ({ currency, version }), [currency, version]);
+ const configData = result.ok && result.data
+ ? result.data
+ : undefined;
+ const ctx = useMemo(() => (configData), [configData]);
- if (!result.ok) {
- if (result.loading) return <Loading />;
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- ) {
- return (
- <Fragment>
- <NotConnectedAppMenu title="Login" />
- <NotificationCard
- notification={{
- message: i18n.str`Checking the /config endpoint got authorization error`,
- type: "ERROR",
- description: `The /config endpoint of the backend server should be accessible`,
- }}
- />
- </Fragment>
- );
+ if (!result.ok) {
+ if (result.loading) return <Loading/>;
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.Unauthorized
+ ) {
+ return (
+ <Fragment>
+ <NotConnectedAppMenu title="Login"/>
+ <NotificationCard
+ notification={{
+ message: i18n.str`Checking the /config endpoint got authorization error`,
+ type: "ERROR",
+ description: `The /config endpoint of the backend server should be accessible`,
+ }}
+ />
+ </Fragment>
+ );
+ }
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.NotFound
+ ) {
+ return (
+ <Fragment>
+ <NotConnectedAppMenu title="Error"/>
+ <NotificationCard
+ notification={{
+ message: i18n.str`Could not find /config endpoint on this URL`,
+ type: "ERROR",
+ description: `Check the URL or contact the system administrator.`,
+ }}
+ />
+ </Fragment>
+ );
+ }
+ if (result.type === ErrorType.SERVER) {
+ <Fragment>
+ <NotConnectedAppMenu title="Error"/>
+ <NotificationCard
+ notification={{
+ message: i18n.str`Server response with an error code`,
+ type: "ERROR",
+ description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
+ }}
+ />
+ </Fragment>;
+ }
+ if (result.type === ErrorType.UNREADABLE) {
+ <Fragment>
+ <NotConnectedAppMenu title="Error"/>
+ <NotificationCard
+ notification={{
+ message: i18n.str`Response from server is unreadable, http status: ${result.status}`,
+ type: "ERROR",
+ description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
+ }}
+ />
+ </Fragment>;
+ }
+ return (
+ <Fragment>
+ <NotConnectedAppMenu title="Error"/>
+ <NotificationCard
+ notification={{
+ message: i18n.str`Unexpected Error`,
+ type: "ERROR",
+ description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
+ }}
+ />
+ </Fragment>
+ );
}
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- ) {
- return (
- <Fragment>
- <NotConnectedAppMenu title="Error" />
- <NotificationCard
- notification={{
- message: i18n.str`Could not find /config endpoint on this URL`,
- type: "ERROR",
- description: `Check the URL or contact the system administrator.`,
- }}
- />
+
+ const SUPPORTED_VERSION = "1:0:1"
+ if (result.data && !LibtoolVersion.compare(
+ SUPPORTED_VERSION,
+ result.data.version,
+ )?.compatible) {
+ return <Fragment>
+ <NotConnectedAppMenu title="Error"/>
+ <NotificationCard
+ notification={{
+ message: i18n.str`Incompatible version`,
+ type: "ERROR",
+ description: i18n.str`Auditor backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`,
+ }}
+ />
</Fragment>
- );
- }
- if (result.type === ErrorType.SERVER) {
- <Fragment>
- <NotConnectedAppMenu title="Error" />
- <NotificationCard
- notification={{
- message: i18n.str`Server response with an error code`,
- type: "ERROR",
- description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
- }}
- />
- </Fragment>;
- }
- if (result.type === ErrorType.UNREADABLE) {
- <Fragment>
- <NotConnectedAppMenu title="Error" />
- <NotificationCard
- notification={{
- message: i18n.str`Response from server is unreadable, http status: ${result.status}`,
- type: "ERROR",
- description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
- }}
- />
- </Fragment>;
}
+
return (
- <Fragment>
- <NotConnectedAppMenu title="Error" />
- <NotificationCard
- notification={{
- message: i18n.str`Unexpected Error`,
- type: "ERROR",
- description: i18n.str`Got message "${result.message}" from ${result.info?.url}`,
- }}
- />
- </Fragment>
+ <div class="has-navbar-fixed-top">
+ <ConfigContextProvider value={ctx!}>
+ <ApplicationReadyRoutes/>
+ </ConfigContextProvider>
+ </div>
);
- }
-
- const SUPPORTED_VERSION = "18:0:1"
- if (result.data && !LibtoolVersion.compare(
- SUPPORTED_VERSION,
- result.data.version,
- )?.compatible) {
- return <Fragment>
- <NotConnectedAppMenu title="Error" />
- <NotificationCard
- notification={{
- message: i18n.str`Incompatible version`,
- type: "ERROR",
- description: i18n.str`Merchant backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`,
- }}
- />
- </Fragment>
- }
-
- return (
- <div class="has-navbar-fixed-top">
- <ConfigContextProvider value={ctx}>
- <ApplicationReadyRoutes />
- </ConfigContextProvider>
- </div>
- );
-}
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx b/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx
index 414eee39d..576792d6f 100644
--- a/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx
+++ b/packages/auditor-backoffice-ui/src/ApplicationReadyRoutes.tsx
@@ -17,159 +17,73 @@
/**
*
* @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { createHashHistory } from "history";
-import { Fragment, VNode, h } from "preact";
-import { Route, Router, route } from "preact-router";
-import { useState } from "preact/hooks";
-import { InstanceRoutes } from "./InstanceRoutes.js";
+import {ErrorType, useTranslationContext} from "@gnu-taler/web-util/browser";
+import {createHashHistory} from "history";
+import {Fragment, VNode, h} from "preact";
+import {Route, Router, route} from "preact-router";
+import {useEffect, useErrorBoundary, useState} from "preact/hooks";
+import {InstanceRoutes} from "./InstanceRoutes.js";
import {
- NotConnectedAppMenu,
- NotYetReadyAppMenu,
- NotificationCard,
+ NotConnectedAppMenu,
+ NotYetReadyAppMenu,
+ NotificationCard,
} from "./components/menu/index.js";
-import { useBackendContext } from "./context/backend.js";
-import { LoginToken } from "./declaration.js";
-import { useBackendInstancesTestForAdmin } from "./hooks/backend.js";
+import { useBackendContext, useBackendTokenContext } from "./context/backend.js";
+import {Settings} from "./paths/settings/index.js";
+import { useBackendConfig, useBackendToken } from "./hooks/backend.js";
+import { Loading } from "./components/exception/loading.js";
import { LoginPage } from "./paths/login/index.js";
-import { Settings } from "./paths/settings/index.js";
-import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
/**
* Check if admin against /management/instances
- * @returns
+ * @returns
*/
export function ApplicationReadyRoutes(): VNode {
- const { i18n } = useTranslationContext();
- const [unauthorized, setUnauthorized] = useState(false)
- const {
- url: backendURL,
- updateToken,
- alreadyTriedLogin,
- } = useBackendContext();
-
- function updateLoginStatus(token: LoginToken | undefined) {
- updateToken(token)
- setUnauthorized(false)
- }
-
- const result = useBackendInstancesTestForAdmin();
-
- const clearTokenAndGoToRoot = () => {
- route("/");
- };
- const [showSettings, setShowSettings] = useState(false)
- const unauthorizedAdmin = !result.loading
- && !result.ok
- && result.type === ErrorType.CLIENT
- && result.status === HttpStatusCode.Unauthorized;
-
- if (!alreadyTriedLogin && !result.ok) {
- return (
- <Fragment>
- <NotConnectedAppMenu title="Welcome!" />
- <LoginPage onConfirm={updateToken} />
- </Fragment>
- );
- }
-
- if (showSettings) {
- return <Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
- <Settings onClose={() => setShowSettings(false)} />
- </Fragment>
- }
-
- if (result.loading) {
- return <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Loading..." isPasswordOk={false} />;
- }
-
- let admin = result.ok || unauthorizedAdmin;
- let instanceNameByBackendURL: string | undefined;
+ const {i18n} = useTranslationContext();
+ const [unauthorized, setUnauthorized] = useState(false)
+ const [backendToken, setToken] = useState(false)
+ const { url: backendURL} = useBackendContext();
+ const { token } = useBackendTokenContext();
+
+ //TODO FIX bearer
+ const result = useBackendToken();
+ if (result.loading) return <Loading/>;
+ if (!result.ok) {
+ return (
+ <LoginPage />
+ );
+ }
+ const [showSettings, setShowSettings] = useState(false)
- if (!admin) {
- // * the testing against admin endpoint failed and it's not
- // an authorization problem
- // * merchant backend will return this SPA under the main
- // endpoint or /instance/<id> endpoint
- // => trying to infer the instance id
- const path = new URL(backendURL).pathname;
- const match = INSTANCE_ID_LOOKUP.exec(path);
- if (!match || !match[1]) {
- // this should be rare because
- // query to /config is ok but the URL
- // does not match our pattern
- return (
- <Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Error" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
- <NotificationCard
- notification={{
- message: i18n.str`Couldn't access the server.`,
- description: i18n.str`Could not infer instance id from url ${backendURL}`,
- type: "ERROR",
- }}
- />
- {/* <ConnectionPage onConfirm={changeBackend} /> */}
+ if (showSettings) {
+ return <Fragment>
+ <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings"/>
+ <Settings onClose={() => setShowSettings(false)}/>
</Fragment>
- );
}
- instanceNameByBackendURL = match[1];
- }
-
- if (unauthorized || unauthorizedAdmin) {
- return <Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Login" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
- <NotificationCard
- notification={{
- message: i18n.str`Access denied`,
- description: i18n.str`Check your token is valid`,
- type: "ERROR",
- }}
- />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- }
-
- const history = createHashHistory();
- return (
- <Router history={history}>
- <Route
- default
- component={DefaultMainRoute}
- admin={admin}
- onUnauthorized={() => setUnauthorized(true)}
- onLoginPass={() => {
- setUnauthorized(false)
- }}
- instanceNameByBackendURL={instanceNameByBackendURL}
- />
- </Router>
- );
+ const history = createHashHistory();
+ return (
+ <Router history={history}>
+ <Route
+ default
+ component={DefaultMainRoute}
+ />
+ </Router>
+ );
}
function DefaultMainRoute({
- instance,
- admin,
- onUnauthorized,
- onLoginPass,
- instanceNameByBackendURL,
- url, //from preact-router
-}: any): VNode {
- const [instanceName, setInstanceName] = useState(
- instanceNameByBackendURL || instance || "default",
- );
+ url, //from preact-router
+ }: any): VNode {
+ //TODO
+ url = "app/#" + url;
- return (
- <InstanceRoutes
- admin={admin}
- path={url}
- onUnauthorized={onUnauthorized}
- onLoginPass={onLoginPass}
- id={instanceName}
- setInstanceName={setInstanceName}
- />
- );
+ return (
+ <InstanceRoutes
+ path={url}
+ />
+ );
}
diff --git a/packages/auditor-backoffice-ui/src/components/exception/loading.tsx b/packages/auditor-backoffice-ui/src/components/exception/loading.tsx
index 5c249f79d..11b62c124 100644
--- a/packages/auditor-backoffice-ui/src/components/exception/loading.tsx
+++ b/packages/auditor-backoffice-ui/src/components/exception/loading.tsx
@@ -22,27 +22,27 @@
import { h, VNode } from "preact";
export function Loading(): VNode {
- return (
- <div
- class="columns is-centered is-vcentered"
- style={{
- height: "calc(100% - 3rem)",
- position: "absolute",
- width: "100%",
- }}
- >
- <Spinner />
- </div>
- );
+ return (
+ <div
+ class="columns is-centered is-vcentered"
+ style={{
+ height: "calc(100% - 3rem)",
+ position: "absolute",
+ width: "100%",
+ }}
+ >
+ <Spinner />
+ </div>
+ );
}
export function Spinner(): VNode {
- return (
- <div class="lds-ring">
- <div />
- <div />
- <div />
- <div />
- </div>
- );
+ return (
+ <div class="lds-ring">
+ <div />
+ <div />
+ <div />
+ <div />
+ </div>
+ );
}
diff --git a/packages/auditor-backoffice-ui/src/components/form/JumpToElementById.tsx b/packages/auditor-backoffice-ui/src/components/form/JumpToElementById.tsx
deleted file mode 100644
index a0e1d6ae4..000000000
--- a/packages/auditor-backoffice-ui/src/components/form/JumpToElementById.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { TranslatedString } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-
-export function JumpToElementById({ testIfExist, onSelect, placeholder, description }: { placeholder: TranslatedString, description: TranslatedString, testIfExist: (id: string) => Promise<any>, onSelect: (id: string) => void }): VNode {
- const { i18n } = useTranslationContext()
-
- const [error, setError] = useState<string | undefined>(
- undefined,
- );
-
- const [id, setId] = useState<string>()
- async function check(currentId: string | undefined): Promise<void> {
- if (!currentId) {
- setError(i18n.str`missing id`);
- return;
- }
- try {
- await testIfExist(currentId);
- onSelect(currentId);
- setError(undefined);
- } catch {
- setError(i18n.str`not found`);
- }
- }
-
- return <div class="level">
- <div class="level-left">
- <div class="level-item">
- <div class="field has-addons">
- <div class="control">
- <input
- class={error ? "input is-danger" : "input"}
- type="text"
- value={id ?? ""}
- onChange={(e) => setId(e.currentTarget.value)}
- placeholder={placeholder}
- />
- {error && <p class="help is-danger">{error}</p>}
- </div>
- <span
- class="has-tooltip-bottom"
- data-tooltip={description}
- >
- <button
- class="button"
- onClick={(e) => check(id)}
- >
- <span class="icon">
- <i class="mdi mdi-arrow-right" />
- </span>
- </button>
- </span>
- </div>
- </div>
- </div>
- </div>
-}
diff --git a/packages/auditor-backoffice-ui/src/components/form/useGroupField.tsx b/packages/auditor-backoffice-ui/src/components/form/useGroupField.tsx
deleted file mode 100644
index 4fbfc4a75..000000000
--- a/packages/auditor-backoffice-ui/src/components/form/useGroupField.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- This file is part of GNU Taler
- (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
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { useFormContext } from "./FormProvider.js";
-
-interface Use {
- hasError?: boolean;
-}
-
-export function useGroupField<T>(name: keyof T): Use {
- const f = useFormContext<T>();
- if (!f) return {};
-
- return {
- hasError: readField(f.errors, String(name)),
- };
-}
-
-const readField = (object: any, name: string) => {
- return name
- .split(".")
- .reduce((prev, current) => prev && prev[current], object);
-};
diff --git a/packages/auditor-backoffice-ui/src/components/form/FormProvider.tsx b/packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx
index a5f3c1d2f..a5f3c1d2f 100644
--- a/packages/auditor-backoffice-ui/src/components/form/FormProvider.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/FormProvider.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/Input.tsx b/packages/auditor-backoffice-ui/src/components/forms/Input.tsx
index 899061c35..899061c35 100644
--- a/packages/auditor-backoffice-ui/src/components/form/Input.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/Input.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/InputCurrency.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputCurrency.tsx
index c1359e641..c1359e641 100644
--- a/packages/auditor-backoffice-ui/src/components/form/InputCurrency.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/InputCurrency.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/InputNumber.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputNumber.tsx
index 10b28cd93..10b28cd93 100644
--- a/packages/auditor-backoffice-ui/src/components/form/InputNumber.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/InputNumber.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/InputSelector.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputSelector.tsx
index f567f7247..f567f7247 100644
--- a/packages/auditor-backoffice-ui/src/components/form/InputSelector.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/InputSelector.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/InputToggle.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputToggle.tsx
index 89b815b4b..89b815b4b 100644
--- a/packages/auditor-backoffice-ui/src/components/form/InputToggle.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/InputToggle.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/auditor-backoffice-ui/src/components/forms/InputWithAddon.tsx
index b8cd4c2d2..b8cd4c2d2 100644
--- a/packages/auditor-backoffice-ui/src/components/form/InputWithAddon.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/InputWithAddon.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/form/useField.tsx b/packages/auditor-backoffice-ui/src/components/forms/useField.tsx
index 49bba4984..49bba4984 100644
--- a/packages/auditor-backoffice-ui/src/components/form/useField.tsx
+++ b/packages/auditor-backoffice-ui/src/components/forms/useField.tsx
diff --git a/packages/auditor-backoffice-ui/src/components/menu/SideBar.tsx b/packages/auditor-backoffice-ui/src/components/menu/SideBar.tsx
index e3387d597..0b662d8de 100644
--- a/packages/auditor-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/auditor-backoffice-ui/src/components/menu/SideBar.tsx
@@ -15,57 +15,20 @@
*/
/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
-import { useBackendContext } from "../../context/backend.js";
import { useConfigContext } from "../../context/config.js";
-import { useInstanceKYCDetails } from "../../hooks/instance.js";
-import { LangSelector } from "./LangSelector.js";
-const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
-const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
-
-interface Props {
- onLogout: () => void;
- onShowSettings: () => void;
- mobile?: boolean;
- instance: string;
- admin?: boolean;
- mimic?: boolean;
- isPasswordOk: boolean;
-}
-
-export function Sidebar({
- mobile,
- instance,
- onShowSettings,
- onLogout,
- admin,
- mimic,
- isPasswordOk
-}: Props): VNode {
- const config = useConfigContext();
- const { url: backendURL } = useBackendContext()
+export function Sidebar(props: any): VNode {
+ const configData = useConfigContext();
const { i18n } = useTranslationContext();
- const kycStatus = useInstanceKYCDetails();
- const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
+ console.log(configData);
return (
<aside class="aside is-placed-left is-expanded" style={{ overflowY: "scroll" }}>
- {mobile && (
- <div
- class="footer"
- onClick={(e) => {
- return e.stopImmediatePropagation();
- }}
- >
- <LangSelector />
- </div>
- )}
<div class="aside-tools">
<div class="aside-tools-label">
<div>
@@ -75,210 +38,66 @@ export function Sidebar({
class="is-size-7 has-text-right"
style={{ lineHeight: 0, marginTop: -10 }}
>
- {VERSION} ({config.version})
+ (Version {configData.version})
</div>
</div>
</div>
<div class="menu is-menu-main">
- {instance ? (
- <Fragment>
- <ul class="menu-list">
- <li>
- <a href={"/orders"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-cash-register" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Orders</i18n.Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/inventory"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-shopping" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Inventory</i18n.Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/transfers"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-arrow-left-right" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Transfers</i18n.Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/templates"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-newspaper" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Templates</i18n.Translate>
- </span>
- </a>
- </li>
- {needKYC && (
- <li>
- <a href={"/kyc"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-account-check" />
- </span>
- <span class="menu-item-label">KYC Status</span>
- </a>
- </li>
- )}
- </ul>
- <p class="menu-label">
- <i18n.Translate>Configuration</i18n.Translate>
- </p>
- <ul class="menu-list">
- <li>
- <a href={"/bank"} class="has-icon">
+ <Fragment>
+ <ul class="menu-list">
+ <li>
+ <a href={"/key-figures"} class="has-icon">
<span class="icon">
<i class="mdi mdi-bank" />
</span>
- <span class="menu-item-label">
- <i18n.Translate>Bank account</i18n.Translate>
+ <span class="menu-item-label">
+ <i18n.Translate>Key figures</i18n.Translate>
</span>
- </a>
- </li>
- <li>
- <a href={"/otp-devices"} class="has-icon">
+ </a>
+ </li>
+ <li>
+ <a href={"/critical-errors"} class="has-icon">
<span class="icon">
- <i class="mdi mdi-lock" />
+ <i class="mdi mdi-alert" />
</span>
- <span class="menu-item-label">
- <i18n.Translate>OTP Devices</i18n.Translate>
+ <span class="menu-item-label">
+ <i18n.Translate>Critical errors</i18n.Translate>
</span>
- </a>
- </li>
- <li>
- <a href={"/reserves"} class="has-icon">
+ </a>
+ </li>
+ <li>
+ <a href={"/operating-status"} class="has-icon">
<span class="icon">
- <i class="mdi mdi-cash" />
+ <i class="mdi mdi-close-network" />
+ </span>
+ <span class="menu-item-label">
+ <i18n.Translate>Operating status</i18n.Translate>
</span>
- <span class="menu-item-label">Reserves</span>
- </a>
- </li>
- <li>
- <a href={"/webhooks"} class="has-icon">
+ </a>
+ </li>
+ <li>
+ <a href={"/detail-view"} class="has-icon">
<span class="icon">
- <i class="mdi mdi-newspaper" />
+ <i class="mdi mdi-format-wrap-tight" />
</span>
- <span class="menu-item-label">
- <i18n.Translate>Webhooks</i18n.Translate>
+ <span class="menu-item-label">
+ <i18n.Translate>Inconsistencies</i18n.Translate>
</span>
- </a>
- </li>
- <li>
- <a href={"/settings"} class="has-icon">
+ </a>
+ </li>
+ <li>
+ <a href={"/settings"} class="has-icon">
<span class="icon">
<i class="mdi mdi-square-edit-outline" />
</span>
- <span class="menu-item-label">
+ <span class="menu-item-label">
<i18n.Translate>Settings</i18n.Translate>
</span>
- </a>
- </li>
- <li>
- <a href={"/token"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-security" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Access token</i18n.Translate>
- </span>
- </a>
- </li>
- </ul>
- </Fragment>
- ) : undefined}
- <p class="menu-label">
- <i18n.Translate>Connection</i18n.Translate>
- </p>
- <ul class="menu-list">
- <li>
- <a class="has-icon is-state-info is-hoverable"
- onClick={(): void => onShowSettings()}
- >
- <span class="icon">
- <i class="mdi mdi-newspaper" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Interface</i18n.Translate>
- </span>
- </a>
- </li>
- <li>
- <div>
- <span style={{ width: "3rem" }} class="icon">
- <i class="mdi mdi-web" />
- </span>
- <span class="menu-item-label">
- {new URL(backendURL).hostname}
- </span>
- </div>
- </li>
- <li>
- <div>
- <span style={{ width: "3rem" }} class="icon">
- ID
- </span>
- <span class="menu-item-label">
- {!instance ? "default" : instance}
- </span>
- </div>
- </li>
- {admin && !mimic && (
- <Fragment>
- <p class="menu-label">
- <i18n.Translate>Instances</i18n.Translate>
- </p>
- <li>
- <a href={"/instance/new"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-plus" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>New</i18n.Translate>
- </span>
- </a>
- </li>
- <li>
- <a href={"/instances"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-format-list-bulleted" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>List</i18n.Translate>
- </span>
- </a>
- </li>
- </Fragment>
- )}
- {isPasswordOk ?
- <li>
- <a
- class="has-icon is-state-info is-hoverable"
- onClick={(): void => onLogout()}
- >
- <span class="icon">
- <i class="mdi mdi-logout default" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Log out</i18n.Translate>
- </span>
</a>
- </li> : undefined
- }
- </ul>
+ </li>
+ </ul>
+ </Fragment>
</div>
</aside>
);
-}
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/components/modal/index.tsx b/packages/auditor-backoffice-ui/src/components/modal/index.tsx
index c684ba7a3..ab2834d86 100644
--- a/packages/auditor-backoffice-ui/src/components/modal/index.tsx
+++ b/packages/auditor-backoffice-ui/src/components/modal/index.tsx
@@ -22,11 +22,11 @@
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { useInstanceContext } from "../../context/instance.js";
+import { useEntityContext } from "../../context/entity.js";
import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
import { Spinner } from "../exception/loading.js";
-import { FormProvider } from "../form/FormProvider.js";
-import { Input } from "../form/Input.js";
+import { FormProvider } from "../forms/FormProvider.js";
+import { Input } from "../forms/Input.js";
interface Props {
active?: boolean;
@@ -310,9 +310,9 @@ export function UpdateTokenModal({
(k) => (errors as any)[k] !== undefined,
);
- const instance = useInstanceContext();
+ const instance = useEntityContext();
- const text = i18n.str`You are updating the access token from instance with id ${instance.id}`;
+ const text = i18n.str`You are updating the access token from instance with id `;
return (
<ClearConfirmModal
diff --git a/packages/auditor-backoffice-ui/src/context/backend.ts b/packages/auditor-backoffice-ui/src/context/backend.ts
index b13b92c42..ce321c3e6 100644
--- a/packages/auditor-backoffice-ui/src/context/backend.ts
+++ b/packages/auditor-backoffice-ui/src/context/backend.ts
@@ -17,54 +17,54 @@
/**
*
* @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
-import { useMemoryStorage } from "@gnu-taler/web-util/browser";
import { createContext, h, VNode } from "preact";
import { useContext } from "preact/hooks";
-import { LoginToken } from "../declaration.js";
-import { useBackendDefaultToken, useBackendURL } from "../hooks/index.js";
+import { useBackendURL } from "../hooks/index.js";
interface BackendContextType {
- url: string,
- alreadyTriedLogin: boolean;
- token?: LoginToken;
- updateToken: (token: LoginToken | undefined) => void;
+ url: string,
}
const BackendContext = createContext<BackendContextType>({
- url: "",
- alreadyTriedLogin: false,
- token: undefined,
- updateToken: () => null,
+ url: "",
});
function useBackendContextState(
- defaultUrl?: string,
+ defaultUrl?: string,
): BackendContextType {
-const [url] = useBackendURL(defaultUrl);
- //const url = "http://localhost:8081";
- const [token, updateToken] = useBackendDefaultToken();
-
- return {
- url,
- token,
- alreadyTriedLogin: token !== undefined,
- updateToken,
- };
+ const [url] = useBackendURL(defaultUrl);
+
+ return {
+ url,
+ };
}
export const BackendContextProvider = ({
- children,
- defaultUrl,
-}: {
- children: any;
- defaultUrl?: string;
+ children,
+ defaultUrl,
+ }: {
+ children: any;
+ defaultUrl?: string;
}): VNode => {
- const value = useBackendContextState(defaultUrl);
+ const value = useBackendContextState(defaultUrl);
- return h(BackendContext.Provider, { value, children });
+ return h(BackendContext.Provider, { value, children });
};
+
+
export const useBackendContext = (): BackendContextType =>
- useContext(BackendContext);
+ useContext(BackendContext);
+
+interface BackendTokenType {
+ token: string;
+}
+
+const BackendTokenContext = createContext<BackendTokenType>({} as any);
+
+export const BackendTokenContextProvider = BackendTokenContext.Provider;
+
+export const useBackendTokenContext = (): BackendTokenType => useContext(BackendTokenContext); \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/context/config.ts b/packages/auditor-backoffice-ui/src/context/config.ts
index def45ea64..58ee5a594 100644
--- a/packages/auditor-backoffice-ui/src/context/config.ts
+++ b/packages/auditor-backoffice-ui/src/context/config.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2024 Taler Systems S.A.
+ (C) 2021-2023 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
@@ -21,12 +21,9 @@
import { createContext } from "preact";
import { useContext } from "preact/hooks";
+import { AuditorBackend } from "../declaration.js";
-interface Type {
- currency: string;
- version: string;
-}
-const Context = createContext<Type>(null!);
+const Context = createContext<AuditorBackend.VersionResponse>(null!);
export const ConfigContextProvider = Context.Provider;
-export const useConfigContext = (): Type => useContext(Context);
+export const useConfigContext = (): AuditorBackend.VersionResponse => useContext(Context);
diff --git a/packages/auditor-backoffice-ui/src/context/instance.ts b/packages/auditor-backoffice-ui/src/context/entity.ts
index 5800ade7e..a5f87ee02 100644
--- a/packages/auditor-backoffice-ui/src/context/instance.ts
+++ b/packages/auditor-backoffice-ui/src/context/entity.ts
@@ -17,20 +17,31 @@
/**
*
* @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
import { createContext } from "preact";
import { useContext } from "preact/hooks";
-import { LoginToken } from "../declaration.js";
-interface Type {
- id: string;
- token?: LoginToken;
- admin?: boolean;
- changeToken: (t?: LoginToken) => void;
+interface EntityType {
+ title: string;
+ path: string;
+ endpoint: string;
+ entity: any;
}
-const Context = createContext<Type>({} as any);
+const EntityContext = createContext<EntityType>({} as any);
-export const InstanceContextProvider = Context.Provider;
-export const useInstanceContext = (): Type => useContext(Context);
+export const EntityContextProvider = EntityContext.Provider;
+
+export const useEntityContext = (): EntityType => useContext(EntityContext);
+
+interface EntityDataType {
+ data: any;
+}
+
+const EntityDataContext = createContext<EntityDataType>({} as any);
+
+export const EntityDataContextProvider = EntityDataContext.Provider;
+
+export const useEntityDataContext = (): EntityDataType => useContext(EntityDataContext);
diff --git a/packages/auditor-backoffice-ui/src/custom.d.ts b/packages/auditor-backoffice-ui/src/custom.d.ts
index 34522a2dd..e693c2951 100644
--- a/packages/auditor-backoffice-ui/src/custom.d.ts
+++ b/packages/auditor-backoffice-ui/src/custom.d.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2024 Taler Systems S.A.
+ (C) 2021-2023 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/auditor-backoffice-ui/src/declaration.d.ts b/packages/auditor-backoffice-ui/src/declaration.d.ts
index 0c6f599f7..a8cdee53c 100644
--- a/packages/auditor-backoffice-ui/src/declaration.d.ts
+++ b/packages/auditor-backoffice-ui/src/declaration.d.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021-2024 Taler Systems S.A.
+ (C) 2021-2023 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
@@ -17,6 +17,7 @@
/**
*
* @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
type HashCode = string;
@@ -25,7 +26,7 @@ type EddsaSignature = string;
type WireTransferIdentifierRawP = string;
type RelativeTime = TalerProtocolDuration;
type ImageDataUrl = string;
-type MerchantUserType = "business" | "individual";
+type AuditorUserType = "business" | "individual";
export interface WithId {
@@ -38,9 +39,11 @@ interface Timestamp {
// never happen.
t_s: number | "never";
}
+
interface TalerProtocolDuration {
d_us: number | "forever";
}
+
interface Duration {
d_ms: number | "forever";
}
@@ -53,263 +56,51 @@ type Amount = string;
type UUID = string;
type Integer = number;
-interface WireAccount {
- // payto:// URI identifying the account and wire method
- payto_uri: string;
-
- // URI to convert amounts from or to the currency used by
- // this wire account of the exchange. Missing if no
- // conversion is applicable.
- conversion_url?: string;
-
- // Restrictions that apply to bank accounts that would send
- // funds to the exchange (crediting this exchange bank account).
- // Optional, empty array for unrestricted.
- credit_restrictions: AccountRestriction[];
-
- // Restrictions that apply to bank accounts that would receive
- // funds from the exchange (debiting this exchange bank account).
- // Optional, empty array for unrestricted.
- debit_restrictions: AccountRestriction[];
-
- // Signature using the exchange's offline key over
- // a TALER_MasterWireDetailsPS
- // with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
- master_sig: EddsaSignature;
-}
-
-type AccountRestriction = RegexAccountRestriction | DenyAllAccountRestriction;
-
-// Account restriction that disables this type of
-// account for the indicated operation categorically.
-interface DenyAllAccountRestriction {
- type: "deny";
-}
-
-// Accounts interacting with this type of account
-// restriction must have a payto://-URI matching
-// the given regex.
-interface RegexAccountRestriction {
- type: "regex";
-
- // Regular expression that the payto://-URI of the
- // partner account must follow. The regular expression
- // should follow posix-egrep, but without support for character
- // classes, GNU extensions, back-references or intervals. See
- // https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
- // for a description of the posix-egrep syntax. Applications
- // may support regexes with additional features, but exchanges
- // must not use such regexes.
- payto_regex: string;
-
- // Hint for a human to understand the restriction
- // (that is hopefully easier to comprehend than the regex itself).
- human_hint: string;
-
- // Map from IETF BCP 47 language tags to localized
- // human hints.
- human_hint_i18n?: { [lang_tag: string]: string };
-}
-interface LoginToken {
- token: string,
- expiration: Timestamp,
-}
-// token used to get loginToken
-// must forget after used
-declare const __ac_token: unique symbol;
-type AccessToken = string & {
- [__ac_token]: true;
-};
-
-export namespace ExchangeBackend {
- interface WireResponse {
- // Master public key of the exchange, must match the key returned in /keys.
- master_public_key: EddsaPublicKey;
-
- // Array of wire accounts operated by the exchange for
- // incoming wire transfers.
- accounts: WireAccount[];
-
- // Object mapping names of wire methods (i.e. "sepa" or "x-taler-bank")
- // to wire fees.
- fees: { method: AggregateTransferFee };
- }
- interface AggregateTransferFee {
- // Per transfer wire transfer fee.
- wire_fee: Amount;
-
- // Per transfer closing fee.
- closing_fee: Amount;
-
- // What date (inclusive) does this fee go into effect?
- // The different fees must cover the full time period in which
- // any of the denomination keys are valid without overlap.
- start_date: Timestamp;
-
- // What date (exclusive) does this fee stop going into effect?
- // The different fees must cover the full time period in which
- // any of the denomination keys are valid without overlap.
- end_date: Timestamp;
-
- // Signature of TALER_MasterWireFeePS with
- // purpose TALER_SIGNATURE_MASTER_WIRE_FEES.
- sig: EddsaSignature;
- }
-}
export namespace AuditorBackend {
- interface ErrorDetail {
- // Numeric error code unique to the condition.
- // The other arguments are specific to the error value reported here.
- code: number;
+ interface DepositConfirmation {
+ // identifier
+ deposit_confirmation_serial_id: number;
- // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ...
- // Should give a human-readable hint about the error's nature. Optional, may change without notice!
- hint?: string;
+ h_contract_terms: string;
- // Optional detail about the specific input value that failed. May change without notice!
- detail?: string;
+ h_policy: string;
- // Name of the parameter that was bogus (if applicable).
- parameter?: string;
+ h_wire: string;
- // Path to the argument that was bogus (if applicable).
- path?: string;
+ exchange_timestamp: string;
- // Offset of the argument that was bogus (if applicable).
- offset?: string;
+ refund_deadline: string;
- // Index of the argument that was bogus (if applicable).
- index?: string;
+ wire_deadline: string;
- // Name of the object that was bogus (if applicable).
- object?: string;
+ total_without_fee: string;
- // Name of the currency than was problematic (if applicable).
- currency?: string;
+ coin_pubs: string;
- // Expected type (if applicable).
- type_expected?: string;
+ coin_sigs: string;
- // Type that was provided instead (if applicable).
- type_actual?: string;
- }
- interface Exchange {
- // the exchange's base URL
- url: string;
-
- // master public key of the exchange
- master_pub: EddsaPublicKey;
- }
- namespace DepositConfirmation {
- // POST /deposit-confirmation
- interface ProductAddDetail {
- // product ID to use.
- product_id: string;
-
- // Human-readable product description.
- description: string;
+ merchant_pub: string;
- // Map from IETF BCP 47 language tags to localized descriptions
- description_i18n: { [lang_tag: string]: string };
+ merchant_sig: string;
- // unit in which the product is measured (liters, kilograms, packages, etc.)
- unit: string;
+ exchange_pub: string;
- // The price for one unit of the product. Zero is used
- // to imply that this product is not sold separately, or
- // that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable
- // taxes.
- price: Amount;
+ exchange_sig: string;
- // An optional base64-encoded product image
- image: ImageDataUrl;
+ suppressed: string;
- // a list of taxes paid by the merchant for one unit of this product
- taxes: Tax[];
-
- // Number of units of the product in stock in sum in total,
- // including all existing sales ever. Given in product-specific
- // units.
- // A value of -1 indicates "infinite" (i.e. for "electronic" books).
- total_stock: Integer;
-
- // Identifies where the product is in stock.
- address: Location;
-
- // Identifies when we expect the next restocking to happen.
- next_restock?: Timestamp;
-
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
- }
- // PATCH /private/products/$PRODUCT_ID
- interface ProductPatchDetail {
- // Human-readable product description.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized descriptions
- description_i18n: { [lang_tag: string]: string };
-
- // unit in which the product is measured (liters, kilograms, packages, etc.)
- unit: string;
-
- // The price for one unit of the product. Zero is used
- // to imply that this product is not sold separately, or
- // that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable
- // taxes.
- price: Amount;
-
- // An optional base64-encoded product image
- image: ImageDataUrl;
-
- // a list of taxes paid by the merchant for one unit of this product
- taxes: Tax[];
-
- // Number of units of the product in stock in sum in total,
- // including all existing sales ever. Given in product-specific
- // units.
- // A value of -1 indicates "infinite" (i.e. for "electronic" books).
- total_stock: Integer;
-
- // Number of units of the product that were lost (spoiled, stolen, etc.)
- total_lost: Integer;
-
- // Identifies where the product is in stock.
- address: Location;
-
- // Identifies when we expect the next restocking to happen.
- next_restock?: Timestamp;
-
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
- }
-
- // GET /deposit-confirmation
- interface DepositConfirmationList {
- depositConfirmations: DepositConfirmation [];
- }
- interface DepositConfirmation {
- serial_id: string;
- timestamp: string;
- refund_deadline: string;
- wire_deadline: string;
- amount_without_fee: string;
- }
+ ancient: string;
+ }
- // GET /deposit-confirmation/$SERIAL_ID
- interface DepositConfirmationDetail {
- serial_id: string;
- timestamp: string;
- refund_deadline: string;
- wire_deadline: string;
- amount_without_fee: string;
- }
+ interface Config {
+ name: string;
+ version: string;
+ implementation: string;
+ currency: string;
+ auditor_public_key: string;
+ exchange_master_public_key: string;
}
-}
-export namespace MerchantBackend {
interface ErrorDetail {
// Numeric error code unique to the condition.
// The other arguments are specific to the error value reported here.
@@ -347,1447 +138,618 @@ export namespace MerchantBackend {
type_actual?: string;
}
- // Delivery location, loosely modeled as a subset of
- // ISO20022's PostalAddress25.
- interface Tax {
- // the name of the tax
- name: string;
-
- // amount paid in tax
- tax: Amount;
- }
-
- interface Auditor {
- // official name
- name: string;
-
- // Auditor's public key
- auditor_pub: EddsaPublicKey;
-
- // Base URL of the auditor
- url: string;
- }
- interface Exchange {
- // the exchange's base URL
- url: string;
-
- // master public key of the exchange
- master_pub: EddsaPublicKey;
- }
-
- interface Product {
- // merchant-internal identifier for the product.
- product_id?: string;
-
- // Human-readable product description.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized descriptions
- description_i18n?: { [lang_tag: string]: string };
-
- // The number of units of the product to deliver to the customer.
- quantity: Integer;
-
- // The unit in which the product is measured (liters, kilograms, packages, etc.)
- unit: string;
-
- // The price of the product; this is the total price for quantity times unit of this product.
- price?: Amount;
-
- // An optional base64-encoded product image
- image: ImageDataUrl;
-
- // a list of taxes paid by the merchant for this product. Can be empty.
- taxes: Tax[];
-
- // time indicating when this product should be delivered
- delivery_date?: TalerProtocolTimestamp;
-
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
- }
- interface Merchant {
- // label for a location with the business address of the merchant
- address: Location;
-
- // the merchant's legal name of business
- name: string;
-
- // label for a location that denotes the jurisdiction for disputes.
- // Some of the typical fields for a location (such as a street address) may be absent.
- jurisdiction: Location;
- }
-
interface VersionResponse {
// libtool-style representation of the Merchant protocol version, see
// https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
// The format is "current:revision:age".
- version: string;
-
// Name of the protocol.
- name: "taler-merchant";
- // Currency supported by this backend.
+ name: "taler-auditor";
+ version: string;
+
+ // Default (!) currency supported by this backend.
+ // This is the currency that the backend should
+ // suggest by default to the user when entering
+ // amounts. See currencies for a list of
+ // supported currencies and how to render them.
+ implementation: string;
currency: string;
+ auditor_public_key: string;
+ exchange_master_public_key: string;
+
+ // How services should render currencies supported
+ // by this backend. Maps
+ // currency codes (e.g. "EUR" or "KUDOS") to
+ // the respective currency specification.
+ // All currencies in this map are supported by
+ // the backend. Note that the actual currency
+ // specifications are a *hint* for applications
+ // that would like *advice* on how to render amounts.
+ // Applications *may* ignore the currency specification
+ // if they know how to render currencies that they are
+ // used with.
+ //currencies: { currency: CurrencySpecification };
+
+ // Array of exchanges trusted by the merchant.
+ // Since protocol v6.
+ // exchanges: ExchangeConfigInfo[];
}
- interface Location {
- // Nation with its own government.
- country?: string;
-
- // Identifies a subdivision of a country such as state, region, county.
- country_subdivision?: string;
-
- // Identifies a subdivision within a country sub-division.
- district?: string;
-
- // Name of a built-up area, with defined boundaries, and a local government.
- town?: string;
- // Specific location name within the town.
- town_location?: string;
-
- // Identifier consisting of a group of letters and/or numbers that
- // is added to a postal address to assist the sorting of mail.
- post_code?: string;
-
- // Name of a street or thoroughfare.
- street?: string;
-
- // Name of the building or house.
- building_name?: string;
-
- // Number that identifies the position of a building on a street.
- building_number?: string;
-
- // Free-form address lines, should not exceed 7 elements.
- address_lines?: string[];
+ export interface TokenResponse {
+ null;
}
- namespace Instances {
- //POST /private/instances/$INSTANCE/auth
- interface InstanceAuthConfigurationMessage {
- // Type of authentication.
- // "external": The mechant backend does not do
- // any authentication checks. Instead an API
- // gateway must do the authentication.
- // "token": The merchant checks an auth token.
- // See "token" for details.
- method: "external" | "token";
-
- // For method "external", this field is mandatory.
- // The token MUST begin with the string "secret-token:".
- // After the auth token has been set (with method "token"),
- // the value must be provided in a "Authorization: Bearer $token"
- // header.
- token?: string;
- }
- //POST /private/instances
- interface InstanceConfigurationMessage {
- // Name of the merchant instance to create (will become $INSTANCE).
- id: string;
-
- // Merchant name corresponding to this instance.
- name: string;
-
- // Type of the user (business or individual).
- // Defaults to 'business'. Should become mandatory field
- // in the future, left as optional for API compatibility for now.
- user_type?: MerchantUserType;
-
- // Merchant email for customer contact.
- email?: string;
-
- // Merchant public website.
- website?: string;
-
- // Merchant logo.
- logo?: ImageDataUrl;
-
- // "Authentication" header required to authorize management access the instance.
- // Optional, if not given authentication will be disabled for
- // this instance (hopefully authentication checks are still
- // done by some reverse proxy).
- auth: InstanceAuthConfigurationMessage;
- // The merchant's physical address (to be put into contracts).
- address: Location;
-
- // The jurisdiction under which the merchant conducts its business
- // (to be put into contracts).
- jurisdiction: Location;
-
- // Use STEFAN curves to determine default fees?
- // If false, no fees are allowed by default.
- // Can always be overridden by the frontend on a per-order basis.
- use_stefan: boolean;
-
- // If the frontend does NOT specify an execution date, how long should
- // we tell the exchange to wait to aggregate transactions before
- // executing the wire transfer? This delay is added to the current
- // time when we generate the advisory execution time for the exchange.
- default_wire_transfer_delay: RelativeTime;
-
- // If the frontend does NOT specify a payment deadline, how long should
- // offers we make be valid by default?
- default_pay_delay: RelativeTime;
+ namespace Default {
+ interface ObjectResponse {
+ object: AnyEntry[];
}
+ }
- // PATCH /private/instances/$INSTANCE
- interface InstanceReconfigurationMessage {
-
- // Merchant name corresponding to this instance.
- name: string;
-
- // Type of the user (business or individual).
- // Defaults to 'business'. Should become mandatory field
- // in the future, left as optional for API compatibility for now.
- user_type?: MerchantUserType;
-
- // Merchant email for customer contact.
- email?: string;
-
- // Merchant public website.
- website?: string;
-
- // Merchant logo.
- logo?: ImageDataUrl;
-
- // The merchant's physical address (to be put into contracts).
- address: Location;
-
- // The jurisdiction under which the merchant conducts its business
- // (to be put into contracts).
- jurisdiction: Location;
-
- // Use STEFAN curves to determine default fees?
- // If false, no fees are allowed by default.
- // Can always be overridden by the frontend on a per-order basis.
- use_stefan: boolean;
-
- // If the frontend does NOT specify an execution date, how long should
- // we tell the exchange to wait to aggregate transactions before
- // executing the wire transfer? This delay is added to the current
- // time when we generate the advisory execution time for the exchange.
- default_wire_transfer_delay: RelativeTime;
+ namespace AmountArithmeticInconsistency {
- // If the frontend does NOT specify a payment deadline, how long should
- // offers we make be valid by default?
- default_pay_delay: RelativeTime;
+ class ClassAmountArithmeticInconsistency {
+ data: AmountArithmeticInconsistencyDetail[];
}
- // GET /private/instances
- interface InstancesResponse {
- // List of instances that are present in the backend (see Instance)
- instances: Instance[];
+ interface SummaryResponse {
+ amount_arithmetic_inconsistency: AmountArithmeticInconsistencyDetail[];
}
- interface Instance {
- // Merchant name corresponding to this instance.
- name: string;
-
- // Type of the user ("business" or "individual").
- user_type: MerchantUserType;
-
- // Merchant public website.
- website?: string;
-
- // Merchant logo.
- logo?: ImageDataUrl;
-
- // Merchant instance this response is about ($INSTANCE)
- id: string;
-
- // Public key of the merchant/instance, in Crockford Base32 encoding.
- merchant_pub: EddsaPublicKey;
-
- // List of the payment targets supported by this instance. Clients can
- // specify the desired payment target in /order requests. Note that
- // front-ends do not have to support wallets selecting payment targets.
- payment_targets: string[];
-
- // Has this instance been deleted (but not purged)?
- deleted: boolean;
+ interface AmountArithmeticInconsistencyDetail {
+ row_id: number;
+ operation: string;
+ exchange_amount: string;
+ auditor_amount: string;
+ profitable: boolean;
+ suppressed: boolean;
}
+ }
- //GET /private/instances/$INSTANCE
- interface QueryInstancesResponse {
-
- // Merchant name corresponding to this instance.
- name: string;
- // Type of the user ("business" or "individual").
- user_type: MerchantUserType;
-
- // Merchant email for customer contact.
- email?: string;
-
- // Merchant public website.
- website?: string;
-
- // Merchant logo.
- logo?: ImageDataUrl;
-
- // Public key of the merchant/instance, in Crockford Base32 encoding.
- merchant_pub: EddsaPublicKey;
-
- // The merchant's physical address (to be put into contracts).
- address: Location;
-
- // The jurisdiction under which the merchant conducts its business
- // (to be put into contracts).
- jurisdiction: Location;
-
- // Use STEFAN curves to determine default fees?
- // If false, no fees are allowed by default.
- // Can always be overridden by the frontend on a per-order basis.
- use_stefan: boolean;
-
- // If the frontend does NOT specify an execution date, how long should
- // we tell the exchange to wait to aggregate transactions before
- // executing the wire transfer? This delay is added to the current
- // time when we generate the advisory execution time for the exchange.
- default_wire_transfer_delay: RelativeTime;
-
- // If the frontend does NOT specify a payment deadline, how long should
- // offers we make be valid by default?
- default_pay_delay: RelativeTime;
-
- // Authentication configuration.
- // Does not contain the token when token auth is configured.
- auth: {
- method: "external" | "token";
- };
+ namespace BadSigLoss {
+ class ClassBadSigLoss {
+ data: BadSigLossDetail[];
}
- // DELETE /private/instances/$INSTANCE
- interface LoginTokenRequest {
- // Scope of the token (which kinds of operations it will allow)
- scope: "readonly" | "write";
-
- // Server may impose its own upper bound
- // on the token validity duration
- duration?: RelativeTime;
- // Can this token be refreshed?
- // Defaults to false.
- refreshable?: boolean;
+ interface SummaryResponse {
+ amount_arithmetic_inconsistency: BadSigLossDetail[];
}
- interface LoginTokenSuccessResponse {
- // The login token that can be used to access resources
- // that are in scope for some time. Must be prefixed
- // with "Bearer " when used in the "Authorization" HTTP header.
- // Will already begin with the RFC 8959 prefix.
- token: string;
- // Scope of the token (which kinds of operations it will allow)
- scope: "readonly" | "write";
-
- // Server may impose its own upper bound
- // on the token validity duration
- expiration: Timestamp;
-
- // Can this token be refreshed?
- refreshable: boolean;
+ interface BadSigLossDetail
+ {
+ row_id: number;
+ operation: string;
+ loss: string;
+ operation_specific_pub: string;
+ suppressed: boolean;
}
}
- namespace KYC {
- //GET /private/instances/$INSTANCE/kyc
- interface AccountKycRedirects {
- // Array of pending KYCs.
- pending_kycs: MerchantAccountKycRedirect[];
+ namespace Balance {
- // Array of exchanges with no reply.
- timeout_kycs: ExchangeKycTimeout[];
+ class ClassBalance {
+ // List of products that are present in the inventory
+ data: BalanceDetail[];
}
- interface MerchantAccountKycRedirect {
- // URL that the user should open in a browser to
- // proceed with the KYC process (as returned
- // by the exchange's /kyc-check/ endpoint).
- // Optional, missing if the account is blocked
- // due to AML and not due to KYC.
- kyc_url?: string;
- // Base URL of the exchange this is about.
- exchange_url: string;
+ interface SummaryResponse {
+ // List of products that are present in the inventory
+ balances: BalanceDetail[];
+ }
- // AML status of the account.
- aml_status: number;
+ interface BalanceDetail {
+ // identifier
+ row_id: number;
- // Our bank wire account this is about.
- payto_uri: string;
- }
- interface ExchangeKycTimeout {
- // Base URL of the exchange this is about.
- exchange_url: string;
+ balance_key: string;
- // Numeric error code indicating errors the exchange
- // returned, or TALER_EC_INVALID for none.
- exchange_code: number;
+ balance_value: string;
- // HTTP status code returned by the exchange when we asked for
- // information about the KYC status.
- // 0 if there was no response at all.
- exchange_http_status: number;
+ suppressed: boolean;
}
-
}
- namespace BankAccounts {
-
- interface AccountAddDetails {
-
- // payto:// URI of the account.
- payto_uri: string;
-
- // URL from where the merchant can download information
- // about incoming wire transfers to this account.
- credit_facade_url?: string;
-
- // Credentials to use when accessing the credit facade.
- // Never returned on a GET (as this may be somewhat
- // sensitive data). Can be set in POST
- // or PATCH requests to update (or delete) credentials.
- // To really delete credentials, set them to the type: "none".
- credit_facade_credentials?: FacadeCredentials;
-
+ namespace ClosureLag {
+ class ClassClosureLag {
+ // List of products that are present in the inventory
+ data: ClosureLagDetail[];
}
- type FacadeCredentials =
- | NoFacadeCredentials
- | BasicAuthFacadeCredentials;
-
- interface NoFacadeCredentials {
- type: "none";
+ interface SummaryResponse {
+ // List of products that are present in the inventory
+ closure_lags: ClosureLagDetail[];
}
- interface BasicAuthFacadeCredentials {
- type: "basic";
-
- // Username to use to authenticate
- username: string;
-
- // Password to use to authenticate
- password: string;
+ interface ClosureLagDetail {
+ row_id: number;
+ amount: string;
+ deadline: number;
+ wtid: number;
+ account: string;
+ suppressed: boolean;
}
+ }
- interface AccountAddResponse {
- // Hash over the wire details (including over the salt).
- h_wire: HashCode;
-
- // Salt used to compute h_wire.
- salt: HashCode;
+ namespace CoinInconsistency {
+ class ClassCoinInconsistency {
+ data: CoinInconsistencyDetail[];
}
- interface AccountPatchDetails {
-
- // URL from where the merchant can download information
- // about incoming wire transfers to this account.
- credit_facade_url?: string;
-
- // Credentials to use when accessing the credit facade.
- // Never returned on a GET (as this may be somewhat
- // sensitive data). Can be set in POST
- // or PATCH requests to update (or delete) credentials.
- // To really delete credentials, set them to the type: "none".
- credit_facade_credentials?: FacadeCredentials;
+ interface SummaryResponse {
+ amount_arithmetic_inconsistency: CoinInconsistencyDetail[];
}
-
- interface AccountsSummaryResponse {
-
- // List of accounts that are known for the instance.
- accounts: BankAccountEntry[];
+ interface CoinInconsistencyDetail
+ {
+ row_id: number;
+ operation: string;
+ exchange_amount: string;
+ auditor_amount: string;
+ coin_pub: string;
+ profitable: boolean;
+ suppressed: boolean;
}
+ }
- interface BankAccountEntry {
- // payto:// URI of the account.
- payto_uri: string;
-
- // Hash over the wire details (including over the salt)
- h_wire: HashCode;
-
- // salt used to compute h_wire
- salt: HashCode;
-
- // URL from where the merchant can download information
- // about incoming wire transfers to this account.
- credit_facade_url?: string;
-
- // Credentials to use when accessing the credit facade.
- // Never returned on a GET (as this may be somewhat
- // sensitive data). Can be set in POST
- // or PATCH requests to update (or delete) credentials.
- credit_facade_credentials?: FacadeCredentials;
+ namespace DenominationKeyValidityWithdrawInconsistency {
+ class ClassDenominationKeyValidityWithdrawInconsistency {
+ data: DenominationKeyValidityWithdrawInconsistencyDetail[];
+ }
- // true if this account is active,
- // false if it is historic.
- active: boolean;
+ interface SummaryResponse {
+ responseData: DenominationKeyValidityWithdrawInconsistencyDetail[];
}
+ interface DenominationKeyValidityWithdrawInconsistencyDetail
+ {
+ row_id: number;
+ operation: string;
+ loss: string;
+ operation_specific_pub: string;
+ suppressed: boolean;
+ }
}
- namespace Products {
- // POST /private/products
- interface ProductAddDetail {
- // product ID to use.
- product_id: string;
-
- // Human-readable product description.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized descriptions
- description_i18n: { [lang_tag: string]: string };
-
- // unit in which the product is measured (liters, kilograms, packages, etc.)
- unit: string;
-
- // The price for one unit of the product. Zero is used
- // to imply that this product is not sold separately, or
- // that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable
- // taxes.
- price: Amount;
-
- // An optional base64-encoded product image
- image: ImageDataUrl;
-
- // a list of taxes paid by the merchant for one unit of this product
- taxes: Tax[];
-
- // Number of units of the product in stock in sum in total,
- // including all existing sales ever. Given in product-specific
- // units.
- // A value of -1 indicates "infinite" (i.e. for "electronic" books).
- total_stock: Integer;
-
- // Identifies where the product is in stock.
- address: Location;
-
- // Identifies when we expect the next restocking to happen.
- next_restock?: Timestamp;
-
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
+ namespace DenominationPending {
+ class ClassDenominationPending {
+ data: DenominationPendingDetail[];
}
- // PATCH /private/products/$PRODUCT_ID
- interface ProductPatchDetail {
- // Human-readable product description.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized descriptions
- description_i18n: { [lang_tag: string]: string };
- // unit in which the product is measured (liters, kilograms, packages, etc.)
- unit: string;
-
- // The price for one unit of the product. Zero is used
- // to imply that this product is not sold separately, or
- // that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable
- // taxes.
- price: Amount;
-
- // An optional base64-encoded product image
- image: ImageDataUrl;
-
- // a list of taxes paid by the merchant for one unit of this product
- taxes: Tax[];
-
- // Number of units of the product in stock in sum in total,
- // including all existing sales ever. Given in product-specific
- // units.
- // A value of -1 indicates "infinite" (i.e. for "electronic" books).
- total_stock: Integer;
-
- // Number of units of the product that were lost (spoiled, stolen, etc.)
- total_lost: Integer;
-
- // Identifies where the product is in stock.
- address: Location;
-
- // Identifies when we expect the next restocking to happen.
- next_restock?: Timestamp;
-
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
+ interface SummaryResponse {
+ responseData: DenominationPendingDetail[];
}
- // GET /private/products
- interface InventorySummaryResponse {
- // List of products that are present in the inventory
- products: InventoryEntry[];
+ interface DenominationPendingDetail
+ {
+ denom_pub_hash: string;
+ denom_balance: string;
+ denom_loss: string;
+ num_issued: number;
+ denom_risk: string;
+ recoup_loss: string;
+ suppressed: boolean;
}
- interface InventoryEntry {
- // Product identifier, as found in the product.
- product_id: string;
- }
-
- // GET /private/products/$PRODUCT_ID
- interface ProductDetail {
- // Human-readable product description.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized descriptions
- description_i18n: { [lang_tag: string]: string };
-
- // unit in which the product is measured (liters, kilograms, packages, etc.)
- unit: string;
-
- // The price for one unit of the product. Zero is used
- // to imply that this product is not sold separately, or
- // that the price is not fixed, and must be supplied by the
- // front-end. If non-zero, this price MUST include applicable
- // taxes.
- price: Amount;
-
- // An optional base64-encoded product image
- image: ImageDataUrl;
-
- // a list of taxes paid by the merchant for one unit of this product
- taxes: Tax[];
-
- // Number of units of the product in stock in sum in total,
- // including all existing sales ever. Given in product-specific
- // units.
- // A value of -1 indicates "infinite" (i.e. for "electronic" books).
- total_stock: Integer;
-
- // Number of units of the product that have already been sold.
- total_sold: Integer;
-
- // Number of units of the product that were lost (spoiled, stolen, etc.)
- total_lost: Integer;
-
- // Identifies where the product is in stock.
- address: Location;
-
- // Identifies when we expect the next restocking to happen.
- next_restock?: Timestamp;
+ }
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
+ namespace DenominationsWithoutSigs {
+ class ClassDenominationsWithoutSigs {
+ data: DenominationsWithoutSigsDetail[];
}
- // POST /private/products/$PRODUCT_ID/lock
- interface LockRequest {
- // UUID that identifies the frontend performing the lock
- // It is suggested that clients use a timeflake for this,
- // see https://github.com/anthonynsimon/timeflake
- lock_uuid: UUID;
-
- // How long does the frontend intend to hold the lock
- duration: RelativeTime;
-
- // How many units should be locked?
- quantity: Integer;
+ interface SummaryResponse {
+ responseData: DenominationsWithoutSigsDetail[];
}
- // DELETE /private/products/$PRODUCT_ID
+ interface DenominationsWithoutSigsDetail
+ {
+ row_id: number;
+ denompub_h: string;
+ value: string;
+ start_time: number;
+ end_time: number;
+ suppressed: boolean;
+ }
}
- namespace Orders {
- type MerchantOrderStatusResponse =
- | CheckPaymentPaidResponse
- | CheckPaymentClaimedResponse
- | CheckPaymentUnpaidResponse;
- interface CheckPaymentPaidResponse {
- // The customer paid for this contract.
- order_status: "paid";
-
- // Was the payment refunded (even partially)?
- refunded: boolean;
-
- // True if there are any approved refunds that the wallet has
- // not yet obtained.
- refund_pending: boolean;
-
- // Did the exchange wire us the funds?
- wired: boolean;
-
- // Total amount the exchange deposited into our bank account
- // for this contract, excluding fees.
- deposit_total: Amount;
-
- // Numeric error code indicating errors the exchange
- // encountered tracking the wire transfer for this purchase (before
- // we even got to specific coin issues).
- // 0 if there were no issues.
- exchange_ec: number;
-
- // HTTP status code returned by the exchange when we asked for
- // information to track the wire transfer for this purchase.
- // 0 if there were no issues.
- exchange_hc: number;
-
- // Total amount that was refunded, 0 if refunded is false.
- refund_amount: Amount;
-
- // Contract terms.
- contract_terms: ContractTerms;
-
- // The wire transfer status from the exchange for this order if
- // available, otherwise empty array.
- wire_details: TransactionWireTransfer[];
-
- // Reports about trouble obtaining wire transfer details,
- // empty array if no trouble were encountered.
- wire_reports: TransactionWireReport[];
-
- // The refund details for this order. One entry per
- // refunded coin; empty array if there are no refunds.
- refund_details: RefundDetails[];
-
- // Status URL, can be used as a redirect target for the browser
- // to show the order QR code / trigger the wallet.
- order_status_url: string;
+ namespace DepositConfirmation {
+ class ClassDepositConfirmation{
+ data: DepositConfirmationDetail[];
}
- interface CheckPaymentClaimedResponse {
- // A wallet claimed the order, but did not yet pay for the contract.
- order_status: "claimed";
- // Contract terms.
- contract_terms: ContractTerms;
+ interface SummaryResponse {
+ responseData: DepositConfirmationDetail[];
}
- interface CheckPaymentUnpaidResponse {
- // The order was neither claimed nor paid.
- order_status: "unpaid";
-
- // when was the order created
- creation_time: Timestamp;
-
- // Order summary text.
- summary: string;
- // Total amount of the order (to be paid by the customer).
- total_amount: Amount;
-
- // URI that the wallet must process to complete the payment.
- taler_pay_uri: string;
-
- // Alternative order ID which was paid for already in the same session.
- // Only given if the same product was purchased before in the same session.
- already_paid_order_id?: string;
-
- // Fulfillment URL of an already paid order. Only given if under this
- // session an already paid order with a fulfillment URL exists.
- already_paid_fulfillment_url?: string;
-
- // Status URL, can be used as a redirect target for the browser
- // to show the order QR code / trigger the wallet.
- order_status_url: string;
-
- // We do we NOT return the contract terms here because they may not
- // exist in case the wallet did not yet claim them.
+ interface DepositConfirmationDetail {
+ deposit_confirmation_serial_id: number;
+ h_contract_terms: string;
+ h_policy: string;
+ h_wire: string;
+ exchange_timestamp: string;
+ refund_deadline: string;
+ wire_deadline: string;
+ total_without_fee: string;
+ coin_pubs: string;
+ coin_sigs: string;
+ merchant_pub: string;
+ merchant_sig: string;
+ exchange_pub: string;
+ exchange_sig: string;
+ suppressed: string;
+ ancient: string;
}
- interface RefundDetails {
- // Reason given for the refund.
- reason: string;
-
- // When was the refund approved.
- timestamp: Timestamp;
-
- // Set to true if a refund is still available for the wallet for this payment.
- pending: boolean;
+ }
- // Total amount that was refunded (minus a refund fee).
- amount: Amount;
+ namespace Emergency {
+ class ClassEmergency{
+ data: EmergencyDetail[];
}
- interface TransactionWireTransfer {
- // Responsible exchange.
- exchange_url: string;
-
- // 32-byte wire transfer identifier.
- wtid: Base32;
- // Execution time of the wire transfer.
- execution_time: Timestamp;
-
- // Total amount that has been wire transferred
- // to the merchant.
- amount: Amount;
-
- // Was this transfer confirmed by the merchant via the
- // POST /transfers API, or is it merely claimed by the exchange?
- confirmed: boolean;
+ interface SummaryResponse {
+ responseData: EmergencyDetail[];
}
- interface TransactionWireReport {
- // Numerical error code.
- code: number;
-
- // Human-readable error description.
- hint: string;
- // Numerical error code from the exchange.
- exchange_ec: number;
-
- // HTTP status code received from the exchange.
- exchange_hc: number;
+ interface EmergencyDetail
+ {
+ row_id: number;
+ denompub_h: string;
+ denom_risk: string;
+ denom_loss: string;
+ deposit_start: number;
+ deposit_end: number;
+ value: string;
+ }
+ }
- // Public key of the coin for which we got the exchange error.
- coin_pub: CoinPublicKey;
+ namespace EmergencyByCount {
+ class ClassEmergencyByCount{
+ data: EmergencyByCountDetail[];
}
- interface OrderHistory {
- // timestamp-sorted array of all orders matching the query.
- // The order of the sorting depends on the sign of delta.
- orders: OrderHistoryEntry[];
+ interface SummaryResponse {
+ responseData: EmergencyByCountDetail[];
}
- interface OrderHistoryEntry {
- // order ID of the transaction related to this entry.
- order_id: string;
- // row ID of the order in the database
+ interface EmergencyByCountDetail
+ {
row_id: number;
-
- // when the order was created
- timestamp: Timestamp;
-
- // the amount of money the order is for
- amount: Amount;
-
- // the summary of the order
- summary: string;
-
- // whether some part of the order is refundable,
- // that is the refund deadline has not yet expired
- // and the total amount refunded so far is below
- // the value of the original transaction.
- refundable: boolean;
-
- // whether the order has been paid or not
- paid: boolean;
+ denompub_h: string;
+ num_issued: number;
+ num_known: number;
+ risk: string;
+ start: number;
+ deposit_end: number;
+ value: string;
+ suppressed: boolean;
}
+ }
- interface PostOrderRequest {
- // The order must at least contain the minimal
- // order detail, but can override all
- order: Order;
-
- // if set, the backend will then set the refund deadline to the current
- // time plus the specified delay. If it's not set, refunds will not be
- // possible.
- refund_delay?: RelativeTime;
-
- // specifies the payment target preferred by the client. Can be used
- // to select among the various (active) wire methods supported by the instance.
- payment_target?: string;
-
- // specifies that some products are to be included in the
- // order from the inventory. For these inventory management
- // is performed (so the products must be in stock) and
- // details are completed from the product data of the backend.
- inventory_products?: MinimalInventoryProduct[];
-
- // Specifies a lock identifier that was used to
- // lock a product in the inventory. Only useful if
- // manage_inventory is set. Used in case a frontend
- // reserved quantities of the individual products while
- // the shopping card was being built. Multiple UUIDs can
- // be used in case different UUIDs were used for different
- // products (i.e. in case the user started with multiple
- // shopping sessions that were combined during checkout).
- lock_uuids?: UUID[];
-
- // Should a token for claiming the order be generated?
- // False can make sense if the ORDER_ID is sufficiently
- // high entropy to prevent adversarial claims (like it is
- // if the backend auto-generates one). Default is 'true'.
- create_token?: boolean;
-
- // OTP device ID to associate with the order.
- // This parameter is optional.
- otp_id?: string;
+ namespace FeeTimeInconsistency {
+ class ClassFeeTimeInconsistency{
+ data: FeeTimeInconsistencyDetail[];
}
- type Order = MinimalOrderDetail | ContractTerms;
-
- interface MinimalOrderDetail {
- // Amount to be paid by the customer
- amount: Amount;
- // Short summary of the order
- summary: string;
-
- // URL that will show that the order was successful after
- // it has been paid for. Optional. When POSTing to the
- // merchant, the placeholder "${ORDER_ID}" will be
- // replaced with the actual order ID (useful if the
- // order ID is generated server-side and needs to be
- // in the URL).
- fulfillment_url?: string;
+ interface SummaryResponse {
+ responseData: FeeTimeInconsistencyDetail[];
}
- interface MinimalInventoryProduct {
- // Which product is requested (here mandatory!)
- product_id: string;
-
- // How many units of the product are requested
- quantity: Integer;
+ interface FeeTimeInconsistencyDetail
+ {
+ row_id: number;
+ type: string;
+ time: string;
+ diagnostic: string;
+ suppressed: boolean;
}
- interface PostOrderResponse {
- // Order ID of the response that was just created
- order_id: string;
+ }
- // Token that authorizes the wallet to claim the order.
- // Provided only if "create_token" was set to 'true'
- // in the request.
- token?: ClaimToken;
+ namespace HistoricDenominationRevenue {
+ class ClassHistoricDenominationRevenue {
+ data: HistoricDenominationRevenueDetail[];
}
- interface OutOfStockResponse {
- // Product ID of an out-of-stock item
- product_id: string;
-
- // Requested quantity
- requested_quantity: Integer;
-
- // Available quantity (must be below requested_quanitity)
- available_quantity: Integer;
- // When do we expect the product to be again in stock?
- // Optional, not given if unknown.
- restock_expected?: Timestamp;
- }
-
- interface ForgetRequest {
- // Array of valid JSON paths to forgettable fields in the order's
- // contract terms.
- fields: string[];
+ interface SummaryResponse {
+ responseData: HistoricDenominationRevenueDetail[];
}
- interface RefundRequest {
- // Amount to be refunded
- refund: Amount;
- // Human-readable refund justification
- reason: string;
- }
- interface MerchantRefundResponse {
- // URL (handled by the backend) that the wallet should access to
- // trigger refund processing.
- // taler://refund/...
- taler_refund_uri: string;
-
- // Contract hash that a client may need to authenticate an
- // HTTP request to obtain the above URI in a wallet-friendly way.
- h_contract: HashCode;
+ interface HistoricDenominationRevenueDetail
+ {
+ denom_pub_hash: string;
+ revenue_timestamp: number;
+ revenue_balance: string;
+ loss_balance: string;
+ suppressed: boolean;
}
}
- namespace Rewards {
- // GET /private/reserves
- interface RewardReserveStatus {
- // Array of all known reserves (possibly empty!)
- reserves: ReserveStatusEntry[];
+ namespace HistoricReserveSummary {
+ class ClassHistoricReserveSummary {
+ data: HistoricReserveSummaryDetail[];
}
- interface ReserveStatusEntry {
- // Public key of the reserve
- reserve_pub: EddsaPublicKey;
-
- // Timestamp when it was established
- creation_time: Timestamp;
- // Timestamp when it expires
- expiration_time: Timestamp;
-
- // Initial amount as per reserve creation call
- merchant_initial_amount: Amount;
-
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
-
- // Amount picked up so far.
- pickup_amount: Amount;
-
- // Amount approved for rewards that exceeds the pickup_amount.
- committed_amount: Amount;
-
- // Is this reserve active (false if it was deleted but not purged)
- active: boolean;
+ interface SummaryResponse {
+ responseData: HistoricReserveSummaryDetail[];
}
- interface ReserveCreateRequest {
- // Amount that the merchant promises to put into the reserve
- initial_balance: Amount;
-
- // Exchange the merchant intends to use for reward
- exchange_url: string;
-
- // Desired wire method, for example "iban" or "x-taler-bank"
- wire_method: string;
+ interface HistoricReserveSummaryDetail
+ {
+ denom_pub_hash: string;
+ revenue_timestamp: number;
+ revenue_balance: string;
+ loss_balance: string;
+ suppressed: boolean;
}
- interface ReserveCreateConfirmation {
- // Public key identifying the reserve
- reserve_pub: EddsaPublicKey;
+ }
- // Wire accounts of the exchange where to transfer the funds.
- accounts: WireAccount[];
+ namespace MisattributionInInconsistency {
+ class ClassMisattributionInInconsistency {
+ data: MisattributionInInconsistencyDetail[];
}
- interface RewardCreateRequest {
- // Amount that the customer should be reward
- amount: Amount;
-
- // Justification for giving the reward
- justification: string;
- // URL that the user should be directed to after rewarding,
- // will be included in the reward_token.
- next_url: string;
+ interface SummaryResponse {
+ responseData: MisattributionInInconsistencyDetail[];
}
- interface RewardCreateConfirmation {
- // Unique reward identifier for the reward that was created.
- reward_id: HashCode;
- // taler://reward URI for the reward
- taler_reward_uri: string;
-
- // URL that will directly trigger processing
- // the reward when the browser is redirected to it
- reward_status_url: string;
-
- // when does the reward expire
- reward_expiration: Timestamp;
+ interface MisattributionInInconsistencyDetail
+ {
+ row_id: number;
+ amount: string;
+ bank_row: number;
+ reserve_pub: string;
+ suppressed: boolean;
}
+ }
- interface ReserveDetail {
- // Timestamp when it was established.
- creation_time: Timestamp;
-
- // Timestamp when it expires.
- expiration_time: Timestamp;
-
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
-
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
-
- // Amount picked up so far.
- pickup_amount: Amount;
-
- // Amount approved for rewards that exceeds the pickup_amount.
- committed_amount: Amount;
-
- // Array of all rewards created by this reserves (possibly empty!).
- // Only present if asked for explicitly.
- rewards?: RewardStatusEntry[];
-
- // Is this reserve active (false if it was deleted but not purged)?
- active: boolean;
-
- // Array of wire accounts of the exchange that could
- // be used to fill the reserve, can be NULL
- // if the reserve is inactive or was already filled
- accounts?: WireAccount[];
-
- // URL of the exchange hosting the reserve,
- // NULL if the reserve is inactive
- exchange_url: string;
+ namespace Progress {
+ class ClassProgress {
+ data: ProgressDetail[];
}
- interface RewardStatusEntry {
- // Unique identifier for the reward.
- reward_id: HashCode;
-
- // Total amount of the reward that can be withdrawn.
- total_amount: Amount;
-
- // Human-readable reason for why the reward was granted.
- reason: string;
+ interface SummaryResponse {
+ responseData: ProgressDetail[];
}
- interface RewardDetails {
- // Amount that we authorized for this reward.
- total_authorized: Amount;
-
- // Amount that was picked up by the user already.
- total_picked_up: Amount;
-
- // Human-readable reason given when authorizing the reward.
- reason: string;
-
- // Timestamp indicating when the reward is set to expire (may be in the past).
- expiration: Timestamp;
-
- // Reserve public key from which the reward is funded.
- reserve_pub: EddsaPublicKey;
+ interface ProgressDetail
+ {
+ progress_key: string;
+ progress_offset: number;
+ suppressed: boolean;
+ }
+ }
- // Array showing the pickup operations of the wallet (possibly empty!).
- // Only present if asked for explicitly.
- pickups?: PickupDetail[];
+ namespace PurseNotClosedInconsistency {
+ class ClassPurseNotClosedInconsistency {
+ data: PurseNotClosedInconsistencyDetail[];
}
- interface PickupDetail {
- // Unique identifier for the pickup operation.
- pickup_id: HashCode;
- // Number of planchets involved.
- num_planchets: Integer;
+ interface SummaryResponse {
+ responseData: PurseNotClosedInconsistencyDetail[];
+ }
- // Total amount requested for this pickup_id.
- requested_amount: Amount;
+ interface PurseNotClosedInconsistencyDetail
+ {
+ row_id: number;
+ purse_pub: string,
+ amount: string;
+ expiration_date: number;
+ suppressed: boolean;
}
}
- namespace Transfers {
- interface TransferList {
- // list of all the transfers that fit the filter that we know
- transfers: TransferDetails[];
+ namespace Purses {
+ class ClassPurses {
+ data: PursesDetail[];
}
- interface TransferDetails {
- // how much was wired to the merchant (minus fees)
- credit_amount: Amount;
-
- // raw wire transfer identifier identifying the wire transfer (a base32-encoded value)
- wtid: string;
-
- // target account that received the wire transfer
- payto_uri: string;
-
- // base URL of the exchange that made the wire transfer
- exchange_url: string;
- // Serial number identifying the transfer in the merchant backend.
- // Used for filgering via offset.
- transfer_serial_id: number;
-
- // Time of the execution of the wire transfer by the exchange, according to the exchange
- // Only provided if we did get an answer from the exchange.
- execution_time?: Timestamp;
-
- // True if we checked the exchange's answer and are happy with it.
- // False if we have an answer and are unhappy, missing if we
- // do not have an answer from the exchange.
- verified?: boolean;
-
- // True if the merchant uses the POST /transfers API to confirm
- // that this wire transfer took place (and it is thus not
- // something merely claimed by the exchange).
- confirmed?: boolean;
+ interface SummaryResponse {
+ responseData: PursesDetail[];
}
- interface TransferInformation {
- // how much was wired to the merchant (minus fees)
- credit_amount: Amount;
-
- // raw wire transfer identifier identifying the wire transfer (a base32-encoded value)
- wtid: WireTransferIdentifierRawP;
-
- // target account that received the wire transfer
- payto_uri: string;
-
- // base URL of the exchange that made the wire transfer
- exchange_url: string;
+ interface PursesDetail
+ {
+ auditor_purses_rowid: number;
+ purse_pub: string;
+ balance: string;
+ target: string,
+ expiration_date: number;
+ suppressed: boolean;
}
}
- namespace OTP {
- interface OtpDeviceAddDetails {
- // Device ID to use.
- otp_device_id: string;
-
- // Human-readable description for the device.
- otp_device_description: string;
-
- // A base64-encoded key
- otp_key: string;
-
- // Algorithm for computing the POS confirmation.
- otp_algorithm: Integer;
-
- // Counter for counter-based OTP devices.
- otp_ctr?: Integer;
+ namespace RefreshesHanging {
+ class ClassRefreshesHanging {
+ data: RefreshesHangingDetail[];
}
- interface OtpDevicePatchDetails {
- // Human-readable description for the device.
- otp_device_description: string;
-
- // A base64-encoded key
- otp_key: string | undefined;
-
- // Algorithm for computing the POS confirmation.
- otp_algorithm: Integer;
-
- // Counter for counter-based OTP devices.
- otp_ctr?: Integer;
+ interface SummaryResponse {
+ responseData: RefreshesHangingDetail[];
}
- interface OtpDeviceSummaryResponse {
- // Array of devices that are present in our backend.
- otp_devices: OtpDeviceEntry[];
+ interface RefreshesHangingDetail
+ {
+ row_id: number;
+ amount: string;
+ coin_pub: string;
+ suppressed: boolean;
}
- interface OtpDeviceEntry {
- // Device identifier.
- otp_device_id: string;
+ }
- // Human-readable description for the device.
- device_description: string;
+ namespace ReserveBalanceInsufficientInconsistency {
+ class ClassReserveBalanceInsufficientInconsistency {
+ data: ReserveBalanceInsufficientInconsistencyDetail[];
}
- interface OtpDeviceDetails {
- // Human-readable description for the device.
- device_description: string;
-
- // Algorithm for computing the POS confirmation.
- otp_algorithm: Integer;
-
- // Counter for counter-based OTP devices.
- otp_ctr?: Integer;
+ interface SummaryResponse {
+ responseData: ReserveBalanceInsufficientInconsistencyDetail[];
}
-
+ interface ReserveBalanceInsufficientInconsistencyDetail
+ {
+ row_id: number;
+ reserve_pub: string;
+ inconsistency_gain: boolean;
+ inconsistency_amount: string;
+ suppressed: boolean;
+ }
}
- namespace Template {
- interface TemplateAddDetails {
- // Template ID to use.
- template_id: string;
- // Human-readable description for the template.
- template_description: string;
-
- // OTP device ID.
- // This parameter is optional.
- otp_id?: string;
-
- // Additional information in a separate template.
- template_contract: TemplateContractDetails;
+ namespace ReserveBalanceSummaryWrongInconsistency {
+ class ClassReserveBalanceSummaryWrongInconsistency {
+ data: ReserveBalanceSummaryWrongInconsistencyDetail[];
}
- interface TemplateContractDetails {
- // Human-readable summary for the template.
- summary?: string;
-
- // The price is imposed by the merchant and cannot be changed by the customer.
- // This parameter is optional.
- amount?: Amount;
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age: Integer;
-
- // The time the customer need to pay before his order will be deleted.
- // It is deleted if the customer did not pay and if the duration is over.
- pay_duration: RelativeTime;
+ interface SummaryResponse {
+ responseData: ReserveBalanceSummaryWrongInconsistencyDetail[];
}
- interface TemplatePatchDetails {
- // Human-readable description for the template.
- template_description: string;
-
- // OTP device ID.
- // This parameter is optional.
- otp_id?: string;
- // Additional information in a separate template.
- template_contract: TemplateContractDetails;
+ interface ReserveBalanceSummaryWrongInconsistencyDetail
+ {
+ row_id: number;
+ reserve_pub: string;
+ exchange_amount: string;
+ auditor_amount: string;
+ suppressed: boolean;
}
+ }
- interface TemplateSummaryResponse {
- // List of templates that are present in our backend.
- templates: TemplateEntry[];
+ namespace ReserveInInconsistency {
+ class ClassReserveInInconsistency {
+ data: ReserveInInconsistencyDetail[];
}
- interface TemplateEntry {
- // Template identifier, as found in the template.
- template_id: string;
-
- // Human-readable description for the template.
- template_description: string;
+ interface SummaryResponse {
+ responseData: ReserveInInconsistencyDetail[];
}
- interface TemplateDetails {
- // Human-readable description for the template.
- template_description: string;
-
- // OTP device ID.
- // This parameter is optional.
- otp_id?: string;
-
- // Additional information in a separate template.
- template_contract: TemplateContractDetails;
+ interface ReserveInInconsistencyDetail
+ {
+ row_id: number;
+ amount_exchange_expected: string;
+ amount_wired: string;
+ reserve_pub: string;
+ timestamp: number;
+ account: string;
+ diagnostic: string;
+ suppressed: boolean;
}
+ }
- interface UsingTemplateDetails {
- // Subject of the template
- summary?: string;
+ namespace ReserveNotClosedInconsistency {
+ class ClassReserveNotClosedInconsistency {
+ data: ReserveNotClosedInconsistencyDetail[];
+ }
- // The amount entered by the customer.
- amount?: Amount;
+ interface SummaryResponse {
+ responseData: ReserveNotClosedInconsistencyDetail[];
}
- interface UsingTemplateResponse {
- // After enter the request. The user will be pay with a taler URL.
- order_id: string;
- token: string;
+ interface ReserveNotClosedInconsistencyDetail
+ {
+ row_id: number;
+ reserve_pub: string;
+ balance: string;
+ expiration_time: number;
+ diagnostic: string;
+ suppressed: boolean;
}
}
- namespace Webhooks {
- type MerchantWebhookType = "pay" | "refund";
- interface WebhookAddDetails {
- // Webhook ID to use.
- webhook_id: string;
-
- // The event of the webhook: why the webhook is used.
- event_type: MerchantWebhookType;
-
- // URL of the webhook where the customer will be redirected.
- url: string;
+ namespace Reserves {
+ class ClassReserves{
+ data: ReservesDetail[];
+ }
- // Method used by the webhook
- http_method: string;
+ interface SummaryResponse {
+ responseData: ReservesDetail[];
+ }
- // Header template of the webhook
- header_template?: string;
+ interface ReservesDetail
+ {
+ auditor_reserves_rowid: number;
+ reserve_pub: string;
+ reserve_balance: string;
+ reserve_loss: string;
+ withdraw_fee_balance: string;
+ close_fee_balance: string;
+ purse_fee_balance: string;
+ open_fee_balance: string;
+ history_fee_balance: string;
+ expiration_date: number;
+ origin_account: string;
+ suppressed: boolean;
+ }
+ }
- // Body template by the webhook
- body_template?: string;
+ namespace RowInconsistency {
+ class ClassRowInconsistency {
+ data: RowInconsistencyDetail[];
}
- interface WebhookPatchDetails {
- // The event of the webhook: why the webhook is used.
- event_type: string;
- // URL of the webhook where the customer will be redirected.
- url: string;
+ interface SummaryResponse {
+ responseData: RowInconsistencyDetail[];
+ }
- // Method used by the webhook
- http_method: string;
+ interface RowInconsistencyDetail
+ {
+ row_id: number;
+ row_table: string;
+ diagnostic: string;
+ suppressed: boolean;
+ }
+ }
- // Header template of the webhook
- header_template?: string;
+ namespace RowMinorInconsistency {
+ class ClassRowMinorInconsistency {
+ data: RowMinorInconsistencyDetail[];
+ }
- // Body template by the webhook
- body_template?: string;
+ interface SummaryResponse {
+ responseData: RowMinorInconsistencyDetail[];
}
- interface WebhookSummaryResponse {
- // List of webhooks that are present in our backend.
- webhooks: WebhookEntry[];
+
+ interface RowMinorInconsistencyDetail
+ {
+ row_id: number;
+ row_table: string;
+ diagnostic: string;
+ suppressed: boolean;
}
- interface WebhookEntry {
- // Webhook identifier, as found in the webhook.
- webhook_id: string;
+ }
- // The event of the webhook: why the webhook is used.
- event_type: string;
+ namespace WireFormatInconsistency {
+ class ClassWireFormatInconsistency {
+ data: WireFormatInconsistencyDetail[];
}
- interface WebhookDetails {
- // The event of the webhook: why the webhook is used.
- event_type: string;
- // URL of the webhook where the customer will be redirected.
- url: string;
+ interface SummaryResponse {
+ responseData: WireFormatInconsistencyDetail[];
+ }
- // Method used by the webhook
- http_method: string;
+ interface WireFormatInconsistencyDetail
+ {
+ row_id: number;
+ amount: string;
+ wire_offset: string;
+ diagnostic: string;
+ suppressed: boolean;
+ }
+ }
- // Header template of the webhook
- header_template?: string;
+ namespace WireOutInconsistency {
+ class ClassWireOutInconsistency{
+ data: WireOutInconsistencyDetail[];
+ }
- // Body template by the webhook
- body_template?: string;
+ interface SummaryResponse {
+ responseData: WireOutInconsistencyDetail[];
}
- }
- interface ContractTerms {
- // Human-readable description of the whole purchase
- summary: string;
-
- // Map from IETF BCP 47 language tags to localized summaries
- summary_i18n?: { [lang_tag: string]: string };
-
- // Unique, free-form identifier for the proposal.
- // Must be unique within a merchant instance.
- // For merchants that do not store proposals in their DB
- // before the customer paid for them, the order_id can be used
- // by the frontend to restore a proposal from the information
- // encoded in it (such as a short product identifier and timestamp).
- order_id: string;
-
- // Total price for the transaction.
- // The exchange will subtract deposit fees from that amount
- // before transferring it to the merchant.
- amount: Amount;
-
- // The URL for this purchase. Every time is is visited, the merchant
- // will send back to the customer the same proposal. Clearly, this URL
- // can be bookmarked and shared by users.
- fulfillment_url?: string;
-
- // Maximum total deposit fee accepted by the merchant for this contract
- max_fee: Amount;
-
- // List of products that are part of the purchase (see Product).
- products: Product[];
-
- // Time when this contract was generated
- timestamp: TalerProtocolTimestamp;
-
- // After this deadline has passed, no refunds will be accepted.
- refund_deadline: TalerProtocolTimestamp;
-
- // After this deadline, the merchant won't accept payments for the contact
- pay_deadline: TalerProtocolTimestamp;
-
- // Transfer deadline for the exchange. Must be in the
- // deposit permissions of coins used to pay for this order.
- wire_transfer_deadline: TalerProtocolTimestamp;
-
- // Merchant's public key used to sign this proposal; this information
- // is typically added by the backend Note that this can be an ephemeral key.
- merchant_pub: EddsaPublicKey;
-
- // Base URL of the (public!) merchant backend API.
- // Must be an absolute URL that ends with a slash.
- merchant_base_url: string;
-
- // More info about the merchant, see below
- merchant: Merchant;
-
- // The hash of the merchant instance's wire details.
- h_wire: HashCode;
-
- // Wire transfer method identifier for the wire method associated with h_wire.
- // The wallet may only select exchanges via a matching auditor if the
- // exchange also supports this wire method.
- // The wire transfer fees must be added based on this wire transfer method.
- wire_method: string;
-
- // Any exchanges audited by these auditors are accepted by the merchant.
- auditors: Auditor[];
-
- // Exchanges that the merchant accepts even if it does not accept any auditors that audit them.
- exchanges: Exchange[];
-
- // Delivery location for (all!) products.
- delivery_location?: Location;
-
- // Time indicating when the order should be delivered.
- // May be overwritten by individual products.
- delivery_date?: TalerProtocolTimestamp;
-
- // Nonce generated by the wallet and echoed by the merchant
- // in this field when the proposal is generated.
- nonce: string;
-
- // Specifies for how long the wallet should try to get an
- // automatic refund for the purchase. If this field is
- // present, the wallet should wait for a few seconds after
- // the purchase and then automatically attempt to obtain
- // a refund. The wallet should probe until "delay"
- // after the payment was successful (i.e. via long polling
- // or via explicit requests with exponential back-off).
- //
- // In particular, if the wallet is offline
- // at that time, it MUST repeat the request until it gets
- // one response from the merchant after the delay has expired.
- // If the refund is granted, the wallet MUST automatically
- // recover the payment. This is used in case a merchant
- // knows that it might be unable to satisfy the contract and
- // desires for the wallet to attempt to get the refund without any
- // customer interaction. Note that it is NOT an error if the
- // merchant does not grant a refund.
- auto_refund?: RelativeTime;
-
- // Extra data that is only interpreted by the merchant frontend.
- // Useful when the merchant needs to store extra information on a
- // contract without storing it separately in their database.
- extra?: any;
-
- // Minimum age buyer must have (in years). Default is 0.
- minimum_age?: Integer;
+ interface WireOutInconsistencyDetail
+ {
+ row_id: number;
+ destination_account: string;
+ expected: string;
+ claimed: string;
+ suppressed: boolean;
+ }
}
-}
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/hooks/backend.ts b/packages/auditor-backoffice-ui/src/hooks/backend.ts
index 5ed06bf78..4b0a5a828 100644
--- a/packages/auditor-backoffice-ui/src/hooks/backend.ts
+++ b/packages/auditor-backoffice-ui/src/hooks/backend.ts
@@ -17,461 +17,245 @@
/**
*
* @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
*/
-import { AbsoluteTime, HttpStatusCode } from "@gnu-taler/taler-util";
import {
- ErrorType,
- HttpError,
- HttpResponse,
- HttpResponseOk,
- RequestError,
- RequestOptions,
- useApiContext,
+ HttpResponse,
+ HttpResponseOk,
+ RequestError,
+ RequestOptions,
+ useApiContext,
} from "@gnu-taler/web-util/browser";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import { useSWRConfig } from "swr";
+import {useCallback, useEffect, useState} from "preact/hooks";
+import {useSWRConfig} from "swr";
import { useBackendContext } from "../context/backend.js";
-import { useInstanceContext } from "../context/instance.js";
-import { AccessToken, LoginToken, MerchantBackend, Timestamp } from "../declaration.js";
-
+import { AuditorBackend } from "../declaration.js";
export function useMatchMutate(): (
- re?: RegExp,
- value?: unknown,
+ re?: RegExp,
+ value?: unknown,
) => Promise<any> {
- const { cache, mutate } = useSWRConfig();
+ const {cache, mutate} = useSWRConfig();
- if (!(cache instanceof Map)) {
- throw new Error(
- "matchMutate requires the cache provider to be a Map instance",
- );
- }
-
- return function matchRegexMutate(re?: RegExp) {
- return mutate((key) => {
- // evict if no key or regex === all
- if (!key || !re) return true
- // match string
- if (typeof key === 'string' && re.test(key)) return true
- // record or object have the path at [0]
- if (typeof key === 'object' && re.test(key[0])) return true
- //key didn't match regex
- return false
- }, undefined, {
- revalidate: true,
- });
- };
+ if (!(cache instanceof Map)) {
+ throw new Error(
+ "matchMutate requires the cache provider to be a Map instance",
+ );
+ }
+
+ return function matchRegexMutate(re?: RegExp) {
+ return mutate((key) => {
+ // evict if no key or regex === all
+ if (!key || !re) return true
+ // match string
+ if (typeof key === 'string' && re.test(key)) return true
+ // record or object have the path at [0]
+ if (typeof key === 'object' && re.test(key[0])) return true
+ //key didn't match regex
+ return false
+ }, undefined, {
+ revalidate: true,
+ });
+ };
}
-export function useBackendInstancesTestForAdmin(): HttpResponse<
- MerchantBackend.Instances.InstancesResponse,
- MerchantBackend.ErrorDetail
+const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
+const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
+
+export function useBackendConfig(): HttpResponse<
+ AuditorBackend.VersionResponse | undefined,
+ RequestError<AuditorBackend.ErrorDetail>
> {
- const { request } = useBackendBaseRequest();
+ const {request} = useBackendBaseRequest();
- type Type = MerchantBackend.Instances.InstancesResponse;
+ type Type = AuditorBackend.VersionResponse;
+ type State = { data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>, timer: number }
+ const [result, setResult] = useState<State>({data: {loading: true}, timer: 0});
- const [result, setResult] = useState<
- HttpResponse<Type, MerchantBackend.ErrorDetail>
- >({ loading: true });
+ useEffect(() => {
+ if (result.timer) {
+ clearTimeout(result.timer);
+ }
- useEffect(() => {
- request<Type>(`/management/instances`)
- .then((data) => setResult(data))
- .catch((error: RequestError<MerchantBackend.ErrorDetail>) =>
- setResult(error.cause),
- );
- }, [request]);
+ function tryConfig(): void {
+ request<Type>(`/config`)
+ .then((data) => {
+ const timer: any = setTimeout(() => {
+ tryConfig();
+ }, CHECK_CONFIG_INTERVAL_OK);
+ setResult({data, timer});
+ })
+ .catch((error) => {
+ const timer: any = setTimeout(() => {
+ tryConfig();
+ }, CHECK_CONFIG_INTERVAL_FAIL);
+ const data = error.cause;
+ setResult({data, timer});
+ });
+ }
- return result;
-}
+ tryConfig();
+ }, [request]);
-const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
-const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
+ return result.data;
+}
-export function useBackendConfig(): HttpResponse<
- MerchantBackend.VersionResponse | undefined,
- RequestError<MerchantBackend.ErrorDetail>
+export function useBackendToken(): HttpResponse<
+ AuditorBackend.VersionResponse,
+ RequestError<AuditorBackend.ErrorDetail>
> {
- const { request } = useBackendBaseRequest();
+ const {request} = useBackendBaseRequest();
- type Type = MerchantBackend.VersionResponse;
- type State = { data: HttpResponse<Type, RequestError<MerchantBackend.ErrorDetail>>, timer: number }
- const [result, setResult] = useState<State>({ data: { loading: true }, timer: 0 });
+ type Type = AuditorBackend.VersionResponse;
+ type State = { data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>, timer: number }
+ const [result, setResult] = useState<State>({data: {loading: true}, timer: 0});
- useEffect(() => {
- if (result.timer) {
- clearTimeout(result.timer)
- }
- function tryConfig(): void {
- request<Type>(`/config`)
- .then((data) => {
- const timer: any = setTimeout(() => {
- tryConfig()
- }, CHECK_CONFIG_INTERVAL_OK)
- setResult({ data, timer })
- })
- .catch((error) => {
- const timer: any = setTimeout(() => {
- tryConfig()
- }, CHECK_CONFIG_INTERVAL_FAIL)
- const data = error.cause
- setResult({ data, timer })
- });
- }
- tryConfig()
- }, [request]);
+ useEffect(() => {
+ if (result.timer) {
+ clearTimeout(result.timer);
+ }
+
+ function tryToken(): void {
+ request<Type>(`/monitoring/balances`)
+ .then((data) => {
+ const timer: any = setTimeout(() => {
+ tryToken();
+ }, CHECK_CONFIG_INTERVAL_OK);
+ setResult({data, timer});
+ })
+ .catch((error) => {
+ const timer: any = setTimeout(() => {
+ tryToken();
+ }, CHECK_CONFIG_INTERVAL_FAIL);
+ const data = error.cause;
+ setResult({data, timer});
+ });
+ }
+
+ tryToken();
+ }, [request]);
- return result.data;
+ return result.data;
}
interface useBackendInstanceRequestType {
- request: <T>(
- endpoint: string,
- options?: RequestOptions,
- ) => Promise<HttpResponseOk<T>>;
- fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
- reserveDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
- rewardsDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
- multiFetcher: <T>(params: [url: string[]]) => Promise<HttpResponseOk<T>[]>;
- orderFetcher: <T>(
- params: [endpoint: string,
- paid?: YesOrNo,
- refunded?: YesOrNo,
- wired?: YesOrNo,
- searchDate?: Date,
- delta?: number,]
- ) => Promise<HttpResponseOk<T>>;
- transferFetcher: <T>(
- params: [endpoint: string,
- payto_uri?: string,
- verified?: string,
- position?: string,
- delta?: number,]
- ) => Promise<HttpResponseOk<T>>;
- templateFetcher: <T>(
- params: [endpoint: string,
- position?: string,
- delta?: number]
- ) => Promise<HttpResponseOk<T>>;
- webhookFetcher: <T>(
- params: [endpoint: string,
- position?: string,
- delta?: number]
- ) => Promise<HttpResponseOk<T>>;
-}
-interface useBackendBaseRequestType {
- request: <T>(
- endpoint: string,
- options?: RequestOptions,
- ) => Promise<HttpResponseOk<T>>;
-}
-type YesOrNo = "yes" | "no";
-type LoginResult = {
- valid: true;
- token: string;
- expiration: Timestamp;
-} | {
- valid: false;
- cause: HttpError<{}>;
+ request: <T>(
+ endpoint: string,
+ options?: RequestOptions,
+ ) => Promise<HttpResponseOk<T>>;
+ fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
+ multiFetcher: <T>(params: string[]) => Promise<HttpResponseOk<T>[]>;
+ depositConfirmationFetcher: <T>(
+ params: [
+ endpoint: string,
+ ],
+ ) => Promise<HttpResponseOk<T>>;
}
-export function useCredentialsChecker() {
- const { request } = useApiContext();
- //check against instance details endpoint
- //while merchant backend doesn't have a login endpoint
- async function requestNewLoginToken(
- baseUrl: string,
- token: AccessToken,
- ): Promise<LoginResult> {
- const data: MerchantBackend.Instances.LoginTokenRequest = {
- scope: "write",
- duration: {
- d_us: "forever"
- },
- refreshable: true,
- }
- try {
- const response = await request<MerchantBackend.Instances.LoginTokenSuccessResponse>(baseUrl, `/private/token`, {
- method: "POST",
- token,
- data
- });
- return { valid: true, token: response.data.token, expiration: response.data.expiration };
- } catch (error) {
- if (error instanceof RequestError) {
- return { valid: false, cause: error.cause };
- }
-
- return {
- valid: false, cause: {
- type: ErrorType.UNEXPECTED,
- loading: false,
- info: {
- hasToken: true,
- status: 0,
- options: {},
- url: `/private/token`,
- payload: {}
- },
- exception: error,
- message: (error instanceof Error ? error.message : "unpexepected error")
- }
- };
- }
- };
-
- async function refreshLoginToken(
- baseUrl: string,
- token: LoginToken
- ): Promise<LoginResult> {
-
- if (AbsoluteTime.isExpired(AbsoluteTime.fromProtocolTimestamp(token.expiration))) {
- return {
- valid: false, cause: {
- type: ErrorType.CLIENT,
- status: HttpStatusCode.Unauthorized,
- message: "login token expired, login again.",
- info: {
- hasToken: true,
- status: 401,
- options: {},
- url: `/private/token`,
- payload: {}
- },
- payload: {}
- },
- }
- }
+interface useBackendBaseRequestType {
- return requestNewLoginToken(baseUrl, token.token as AccessToken)
- }
- return { requestNewLoginToken, refreshLoginToken }
+ request: <T>(
+ endpoint: string,
+ options?: RequestOptions
+ ) => Promise<HttpResponseOk<T>>;
}
+type YesOrNo = "yes" | "no";
+
/**
*
* @param root the request is intended to the base URL and no the instance URL
* @returns request handler to
*/
+//TODO: Add token
export function useBackendBaseRequest(): useBackendBaseRequestType {
- const { url: backend, token: loginToken } = useBackendContext();
- const { request: requestHandler } = useApiContext();
- const token = loginToken?.token;
-
- const request = useCallback(
- function requestImpl<T>(
- endpoint: string,
- options: RequestOptions = {},
- ): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(backend, endpoint, { ...options, token }).then(res => {
- return res
- }).catch(err => {
- throw err
- });
- },
- [backend, token],
- );
-
- return { request };
+ const {url: backend} = useBackendContext();
+ const {request: requestHandler} = useApiContext();
+ //const { token } = useBackendTokenContext();
+ const token = "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0";
+
+
+ const request = useCallback(
+ function requestImpl<T>(
+ endpoint: string,
+ //todo: remove
+ options: RequestOptions = {},
+ ): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(backend, endpoint, {...options, token}).then(res => {
+ return res;
+ }).catch(err => {
+ throw err;
+ });
+ },
+ [backend],
+ );
+
+ return {request};
}
-export function useBackendInstanceRequest(): useBackendInstanceRequestType {
- const { url: rootBackendUrl, token: rootToken } = useBackendContext();
- const { token: instanceToken, id, admin } = useInstanceContext();
- const { request: requestHandler } = useApiContext();
-
- const { baseUrl, token: loginToken } = !admin
- ? { baseUrl: rootBackendUrl, token: rootToken }
- : { baseUrl: `${rootBackendUrl}/instances/${id}`, token: instanceToken };
-
- const token = loginToken?.token;
-
- const request = useCallback(
- function requestImpl<T>(
- endpoint: string,
- options: RequestOptions = {},
- ): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(baseUrl, endpoint, { token, ...options });
- },
- [baseUrl, token],
- );
-
- const multiFetcher = useCallback(
- function multiFetcherImpl<T>(
- args: [endpoints: string[]],
- ): Promise<HttpResponseOk<T>[]> {
- const [endpoints] = args
- return Promise.all(
- endpoints.map((endpoint) =>
- requestHandler<T>(baseUrl, endpoint, { token }),
- ),
- );
- },
- [baseUrl, token],
- );
-
- const fetcher = useCallback(
- function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(baseUrl, endpoint, { token });
- },
- [baseUrl, token],
- );
-
- const orderFetcher = useCallback(
- function orderFetcherImpl<T>(
- args: [endpoint: string,
- paid?: YesOrNo,
- refunded?: YesOrNo,
- wired?: YesOrNo,
- searchDate?: Date,
- delta?: number,]
- ): Promise<HttpResponseOk<T>> {
- const [endpoint, paid, refunded, wired, searchDate, delta] = args
- const date_s =
- delta && delta < 0 && searchDate
- ? Math.floor(searchDate.getTime() / 1000) + 1
- : searchDate !== undefined ? Math.floor(searchDate.getTime() / 1000) : undefined;
- const params: any = {};
- if (paid !== undefined) params.paid = paid;
- if (delta !== undefined) params.delta = delta;
- if (refunded !== undefined) params.refunded = refunded;
- if (wired !== undefined) params.wired = wired;
- if (date_s !== undefined) params.date_s = date_s;
- if (delta === 0) {
- //in this case we can already assume the response
- //and avoid network
- return Promise.resolve({
- ok: true,
- data: { orders: [] } as T,
- })
- }
- return requestHandler<T>(baseUrl, endpoint, { params, token });
- },
- [baseUrl, token],
- );
-
- const reserveDetailFetcher = useCallback(
- function reserveDetailFetcherImpl<T>(
- endpoint: string,
- ): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(baseUrl, endpoint, {
- params: {
- rewards: "yes",
+
+export function useBackendRequest(): useBackendInstanceRequestType {
+ const {url: rootBackendUrl} = useBackendContext();
+ // const {id} = useInstanceContext();
+ const {request: requestHandler} = useApiContext();
+
+ //TODO: check
+ const baseUrl = "http://localhost:8083/";
+ const token = "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0";
+
+
+
+
+ const request = useCallback(
+ function requestImpl<T>(
+ endpoint: string,
+ options: RequestOptions = {},
+ ): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(baseUrl, endpoint, {...options, token});
},
- token,
- });
- },
- [baseUrl, token],
- );
-
- const rewardsDetailFetcher = useCallback(
- function rewardsDetailFetcherImpl<T>(
- endpoint: string,
- ): Promise<HttpResponseOk<T>> {
- return requestHandler<T>(baseUrl, endpoint, {
- params: {
- pickups: "yes",
+ [baseUrl],
+ );
+
+ const multiFetcher = useCallback(
+ function multiFetcherImpl<T>(
+ params: string[],
+ options: RequestOptions = {},
+ ): Promise<HttpResponseOk<T>[]> {
+ return Promise.all(
+ params.map((endpoint) =>
+ requestHandler<T>(baseUrl, endpoint, {...options, token}),
+ ),
+ );
},
- token,
- });
- },
- [baseUrl, token],
- );
-
- const transferFetcher = useCallback(
- function transferFetcherImpl<T>(
- args: [endpoint: string,
- payto_uri?: string,
- verified?: string,
- position?: string,
- delta?: number,]
- ): Promise<HttpResponseOk<T>> {
- const [endpoint, payto_uri, verified, position, delta] = args
- const params: any = {};
- if (payto_uri !== undefined) params.payto_uri = payto_uri;
- if (verified !== undefined) params.verified = verified;
- if (delta === 0) {
- //in this case we can already assume the response
- //and avoid network
- return Promise.resolve({
- ok: true,
- data: { transfers: [] } as T,
- })
- }
- if (delta !== undefined) {
- params.limit = delta;
- }
- if (position !== undefined) params.offset = position;
-
- return requestHandler<T>(baseUrl, endpoint, { params, token });
- },
- [baseUrl, token],
- );
-
- const templateFetcher = useCallback(
- function templateFetcherImpl<T>(
- args: [endpoint: string,
- position?: string,
- delta?: number,]
- ): Promise<HttpResponseOk<T>> {
- const [endpoint, position, delta] = args
- const params: any = {};
- if (delta === 0) {
- //in this case we can already assume the response
- //and avoid network
- return Promise.resolve({
- ok: true,
- data: { templates: [] } as T,
- })
- }
- if (delta !== undefined) {
- params.limit = delta;
- }
- if (position !== undefined) params.offset = position;
-
- return requestHandler<T>(baseUrl, endpoint, { params, token });
- },
- [baseUrl, token],
- );
-
- const webhookFetcher = useCallback(
- function webhookFetcherImpl<T>(
- args: [endpoint: string,
- position?: string,
- delta?: number,]
- ): Promise<HttpResponseOk<T>> {
- const [endpoint, position, delta] = args
- const params: any = {};
- if (delta === 0) {
- //in this case we can already assume the response
- //and avoid network
- return Promise.resolve({
- ok: true,
- data: { webhooks: [] } as T,
- })
- }
- if (delta !== undefined) {
- params.limit = delta;
- }
- if (position !== undefined) params.offset = position;
-
- return requestHandler<T>(baseUrl, endpoint, { params, token });
- },
- [baseUrl, token],
- );
-
- return {
- request,
- fetcher,
- multiFetcher,
- orderFetcher,
- reserveDetailFetcher,
- rewardsDetailFetcher,
- transferFetcher,
- templateFetcher,
- webhookFetcher,
- };
-}
+ [baseUrl],
+ );
+
+ const fetcher = useCallback(
+ function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
+ return requestHandler<T>(baseUrl, endpoint, {token});
+ },
+ [baseUrl],
+ );
+
+ const depositConfirmationFetcher = useCallback(
+ function orderFetcherImpl<T>(
+ args: [endpoint: string,
+ ],
+ ): Promise<HttpResponseOk<T>> {
+ const [endpoint] = args;
+ const params: any = {"token": "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"};
+ return requestHandler<T>(baseUrl, endpoint, {params, token});
+ },
+ [baseUrl],
+ );
+
+
+ return {
+ request,
+ fetcher,
+ depositConfirmationFetcher,
+ multiFetcher
+ };
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/hooks/critical.ts b/packages/auditor-backoffice-ui/src/hooks/critical.ts
new file mode 100644
index 000000000..6a25d3037
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/hooks/critical.ts
@@ -0,0 +1,70 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+ HttpResponse,
+ HttpResponseOk,
+ HttpResponsePaginated,
+ RequestError,
+} from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { AuditorBackend, WithId } from "../declaration.js";
+import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
+import { useBackendRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook, useSWRConfig } from "swr";
+
+const useSWR = _useSWR as unknown as SWRHook;
+
+type YesOrNo = "yes" | "no";
+
+export interface HelperDashboardFilter {
+ finance?: YesOrNo;
+ security?: YesOrNo;
+ operating?: YesOrNo;
+ detail?: YesOrNo;
+}
+
+export function getCriticalData(
+ args?: HelperDashboardFilter,
+ updateFilter?: (d: Date) => void,
+): HttpResponse<any, AuditorBackend.ErrorDetail> {
+ const { multiFetcher } = useBackendRequest();
+ const endpoints = [
+ "monitoring/fee-time-inconsistency",
+ "monitoring/emergency",
+ "monitoring/emergency-by-count",
+ "monitoring/reserve-balance-insufficient-inconsistency",
+ ];
+
+
+ const { data: list, error: listError } = useSWR<
+ HttpResponseOk<any>[], RequestError<AuditorBackend.ErrorDetail>
+ >(endpoints, multiFetcher, {
+ refreshInterval: 60,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ if (listError) return listError.cause;
+
+ if (list) {
+ return { ok: true, data: [list] };
+ }
+ return { loading: true };
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/hooks/deposit_confirmations.ts b/packages/auditor-backoffice-ui/src/hooks/deposit_confirmations.ts
deleted file mode 100644
index e4ec9a2f2..000000000
--- a/packages/auditor-backoffice-ui/src/hooks/deposit_confirmations.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- This file is part of GNU Taler
- (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
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-import {
- HttpResponse,
- HttpResponseOk,
- RequestError,
-} from "@gnu-taler/web-util/browser";
-import { AuditorBackend, MerchantBackend, WithId } from "../declaration.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
-
-// FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import _useSWR, { SWRHook, useSWRConfig } from "swr";
-const useSWR = _useSWR as unknown as SWRHook;
-
-export interface DepositConfirmationAPI {
- getDepositConfirmation: (
- id: string,
- ) => Promise<void>;
- createDepositConfirmation: (
- data: MerchantBackend.Products.ProductAddDetail,
- ) => Promise<void>;
- updateDepositConfirmation: (
- id: string,
- data: MerchantBackend.Products.ProductPatchDetail,
- ) => Promise<void>;
- deleteDepositConfirmation: (id: string) => Promise<void>;
-}
-
-export function useDepositConfirmationAPI(): DepositConfirmationAPI {
- const mutateAll = useMatchMutate();
- const { mutate } = useSWRConfig();
-
- const { request } = useBackendInstanceRequest();
-
- const createDepositConfirmation = async (
- data: MerchantBackend.Products.ProductAddDetail,
- ): Promise<void> => {
- const res = await request(`/private/products`, {
- method: "POST",
- data,
- });
-
- return await mutateAll(/.*\/private\/products.*/);
- };
-
- const updateDepositConfirmation = async (
- productId: string,
- data: MerchantBackend.Products.ProductPatchDetail,
- ): Promise<void> => {
- const r = await request(`/private/products/${productId}`, {
- method: "PATCH",
- data,
- });
-
- return await mutateAll(/.*\/private\/products.*/);
- };
-
- const deleteDepositConfirmation = async (productId: string): Promise<void> => {
- await request(`/private/products/${productId}`, {
- method: "DELETE",
- });
- await mutate([`/private/products`]);
- };
-
- const getDepositConfirmation = async (
- serialId: string,
- ): Promise<void> => {
- await request(`/deposit-confirmation/${serialId}`, {
- method: "GET",
- });
-
- return
- };
-
- return {createDepositConfirmation, updateDepositConfirmation, deleteDepositConfirmation, getDepositConfirmation};
-}
-
-export function useDepositConfirmation(): HttpResponse<
- (AuditorBackend.DepositConfirmation.DepositConfirmationDetail & WithId)[],
- AuditorBackend.ErrorDetail
-> {
- const { fetcher, multiFetcher } = useBackendInstanceRequest();
-
- const { data: list, error: listError } = useSWR<
- HttpResponseOk<AuditorBackend.DepositConfirmation.DepositConfirmationList>,
- RequestError<AuditorBackend.ErrorDetail>
- >([`/deposit-confirmation`], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- });
-
- const paths = (list?.data.depositConfirmations || []).map(
- (p) => `/deposit-confirmation/${p.serial_id}`,
- );
- const { data: depositConfirmations, error: depositConfirmationError } = useSWR<
- HttpResponseOk<AuditorBackend.DepositConfirmation.DepositConfirmationDetail>[],
- RequestError<AuditorBackend.ErrorDetail>
- >([paths], multiFetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- });
-
- if (listError) return listError.cause;
- if (depositConfirmationError) return depositConfirmationError.cause;
-
- if (depositConfirmations) {
- const dataWithId = depositConfirmations.map((d) => {
- //take the id from the queried url
- return {
- ...d.data,
- id: d.info?.url.replace(/.*\/deposit-confirmation\//, "") || "",
- };
- });
- return { ok: true, data: dataWithId };
- }
- return { loading: true };
-}
-
-export function useDepositConfirmationDetails(
- serialId: string,
-): HttpResponse<
- AuditorBackend.DepositConfirmation.DepositConfirmationDetail,
- AuditorBackend.ErrorDetail
-> {
- const { fetcher } = useBackendInstanceRequest();
-
- const { data, error, isValidating } = useSWR<
- HttpResponseOk<AuditorBackend.DepositConfirmation.DepositConfirmationDetail>,
- RequestError<AuditorBackend.ErrorDetail>
- >([`/deposit-confirmation/${serialId}`], fetcher, {
- refreshInterval: 0,
- refreshWhenHidden: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false,
- refreshWhenOffline: false,
- });
-
- if (isValidating) return { loading: true, data: data?.data };
- if (data) return data;
- if (error) return error.cause;
- return { loading: true };
-}
diff --git a/packages/auditor-backoffice-ui/src/hooks/entity.ts b/packages/auditor-backoffice-ui/src/hooks/entity.ts
new file mode 100644
index 000000000..ae62da35e
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/hooks/entity.ts
@@ -0,0 +1,82 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+ HttpResponse,
+ HttpResponseOk,
+ RequestError,
+} from "@gnu-taler/web-util/browser";
+import { AuditorBackend, WithId } from "../declaration.js";
+import { useBackendRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook, useSWRConfig } from "swr";
+import { useEntityContext } from "../context/entity.js";
+
+const useSWR = _useSWR as unknown as SWRHook;
+
+type YesOrNo = "yes" | "no";
+
+interface Props {
+ endpoint: string;
+ entity: any;
+}
+
+export function getEntityList({ endpoint, entity }: Props): HttpResponse<any, AuditorBackend.ErrorDetail> {
+ const { fetcher } = useBackendRequest();
+
+ const { data: list, error: listError } = useSWR<
+ HttpResponseOk<typeof entity>,
+ RequestError<AuditorBackend.ErrorDetail>
+ >([`monitoring/` + endpoint], fetcher, {
+ refreshInterval: 0,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ if (listError) return listError.cause;
+
+ if (list?.data != null) {
+ return { ok: true, data: [list?.data] };
+ }
+ return { loading: true };
+}
+export interface EntityAPI {
+ updateEntity: (
+ id: string
+ ) => Promise<void>;
+}
+
+export function useEntityAPI(): EntityAPI {
+ const mutateAll = useMatchMutate();
+ const { request } = useBackendRequest();
+ const { endpoint } = useEntityContext();
+ const data = {"suppressed": true};
+
+ const updateEntity = async (
+ id: string,
+ ): Promise<void> => {
+ const r = await request(`monitoring/${endpoint}/${id}`, {
+ method: "PATCH",
+ data,
+ });
+
+ return await mutateAll(/.*\/monitoring.*/);
+ };
+
+ return { updateEntity };
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/hooks/finance.ts b/packages/auditor-backoffice-ui/src/hooks/finance.ts
new file mode 100644
index 000000000..97bf2577f
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/hooks/finance.ts
@@ -0,0 +1,61 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+ HttpResponse,
+ HttpResponseOk,
+ RequestError,
+} from "@gnu-taler/web-util/browser";
+import { AuditorBackend, WithId } from "../declaration.js";
+import { useBackendRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook, useSWRConfig } from "swr";
+
+const useSWR = _useSWR as unknown as SWRHook;
+
+
+export function getKeyFiguresData(): HttpResponse<any, AuditorBackend.ErrorDetail> {
+ const { multiFetcher } = useBackendRequest();
+ const endpoints = [
+ "monitoring/misattribution-in-inconsistency",
+ "monitoring/coin-inconsistency",
+ "monitoring/reserve-in-inconsistency",
+ "monitoring/bad-sig-losses",
+ "monitoring/balances",
+ "monitoring/amount-arithmetic-inconsistency",
+ "monitoring/wire-format-inconsistency",
+ "monitoring/wire-out-inconsistency",
+ "monitoring/reserve-balance-summary-wrong-inconsistency",
+
+ ];
+
+ const { data: list, error: listError } = useSWR<
+ HttpResponseOk<any>[], RequestError<AuditorBackend.ErrorDetail>
+ >(endpoints, multiFetcher, {
+ refreshInterval: 60,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ if (listError) return listError.cause;
+
+ if (list) {
+ return { ok: true, data: [list] };
+ }
+ return { loading: true };
+}
diff --git a/packages/auditor-backoffice-ui/src/hooks/index.ts b/packages/auditor-backoffice-ui/src/hooks/index.ts
index 61afbc94a..cf1c57771 100644
--- a/packages/auditor-backoffice-ui/src/hooks/index.ts
+++ b/packages/auditor-backoffice-ui/src/hooks/index.ts
@@ -14,138 +14,66 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import { buildCodecForObject, codecForMap, codecForString, codecForTimestamp } from "@gnu-taler/taler-util";
-import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
-import { LoginToken } from "../declaration.js";
+import {StateUpdater, useState} from "preact/hooks";
import { ValueOrFunction } from "../utils/types.js";
-import { useMatchMutate } from "./backend.js";
-
-const calculateRootPath = () => {
- const rootPath =
- typeof window !== undefined
- ? window.location.origin + window.location.pathname
- : "/";
-
- /**
- * By default, merchant backend serves the html content
- * from the /webui root. This should cover most of the
- * cases and the rootPath will be the merchant backend
- * URL where the instances are
- */
- return rootPath.replace("/webui/", "");
-};
-
-const loginTokenCodec = buildCodecForObject<LoginToken>()
- .property("token", codecForString())
- .property("expiration", codecForTimestamp)
- .build("loginToken")
-const TOKENS_KEY = buildStorageKey("auditor-token", codecForMap(loginTokenCodec));
-
export function useBackendURL(
- url?: string,
+ url?: string,
): [string, StateUpdater<string>] {
- const [value, setter] = useSimpleLocalStorage(
- "auditor-base-url",
- url || calculateRootPath(),
- );
-
- const checkedSetter = (v: ValueOrFunction<string>) => {
- return setter((p) => (v instanceof Function ? v(p ?? "") : v).replace(/\/$/, ""));
- };
-
- return [value!, checkedSetter];
-}
+ const [value, setter] = useSimpleLocalStorage(
+ "auditor-base-url",
+ url || calculateRootPath(),
+ );
-export function useBackendDefaultToken(
-): [LoginToken | undefined, ((d: LoginToken | undefined) => void)] {
- const { update: setToken, value: tokenMap, reset } = useLocalStorage(TOKENS_KEY, {})
+ const checkedSetter = (v: ValueOrFunction<string>) => {
+ return setter((p) => (v instanceof Function ? v(p ?? "") : v).replace(/\/$/, ""));
+ };
- const tokenOfDefaultInstance = tokenMap["default"]
- const clearCache = useMatchMutate()
- useEffect(() => {
- clearCache()
- }, [tokenOfDefaultInstance])
-
- function updateToken(
- value: (LoginToken | undefined)
- ): void {
- if (value === undefined) {
- reset()
- } else {
- const res = { ...tokenMap, "default": value }
- setToken(res)
- }
- }
- return [tokenMap["default"], updateToken];
-}
-
-export function useBackendInstanceToken(
- id: string,
-): [LoginToken | undefined, ((d: LoginToken | undefined) => void)] {
- const { update: setToken, value: tokenMap, reset } = useLocalStorage(TOKENS_KEY, {})
- const [defaultToken, defaultSetToken] = useBackendDefaultToken();
-
- // instance named 'default' use the default token
- if (id === "default") {
- return [defaultToken, defaultSetToken];
- }
- function updateToken(
- value: (LoginToken | undefined)
- ): void {
- if (value === undefined) {
- reset()
- } else {
- const res = { ...tokenMap, [id]: value }
- setToken(res)
- }
- }
-
- return [tokenMap[id], updateToken];
+ return [value!, checkedSetter];
}
-export function useLang(initial?: string): [string, StateUpdater<string>] {
- const browserLang =
- typeof window !== "undefined"
- ? navigator.language || (navigator as any).userLanguage
- : undefined;
- const defaultLang = (browserLang || initial || "en").substring(0, 2);
- return useSimpleLocalStorage("lang-preference", defaultLang) as [string, StateUpdater<string>];
-}
+const calculateRootPath = () => {
+ const rootPath =
+ typeof window !== undefined
+ ? window.location.origin + window.location.pathname
+ : "/";
+
+ /**
+ * By default, auditor backend serves the html content
+ * from the /webui root. This should cover most of the
+ * cases and the rootPath will be the auditor backend
+ * URL where the instances are
+ */
+ return rootPath.replace("/webui/", "");
+};
export function useSimpleLocalStorage(
- key: string,
- initialValue?: string,
+ key: string,
+ initialValue?: string,
): [string | undefined, StateUpdater<string | undefined>] {
- const [storedValue, setStoredValue] = useState<string | undefined>(
- (): string | undefined => {
- return typeof window !== "undefined"
- ? window.localStorage.getItem(key) || initialValue
- : initialValue;
- },
- );
-
- const setValue = (
- value?: string | ((val?: string) => string | undefined),
- ) => {
- setStoredValue((p) => {
- const toStore = value instanceof Function ? value(p) : value;
- if (typeof window !== "undefined") {
- if (!toStore) {
- window.localStorage.removeItem(key);
- } else {
- window.localStorage.setItem(key, toStore);
- }
- }
- return toStore;
- });
- };
-
- return [storedValue, setValue];
-}
+ const [storedValue, setStoredValue] = useState<string | undefined>(
+ (): string | undefined => {
+ return typeof window !== "undefined"
+ ? window.localStorage.getItem(key) || initialValue
+ : initialValue;
+ },
+ );
+
+ const setValue = (
+ value?: string | ((val?: string) => string | undefined),
+ ) => {
+ setStoredValue((p) => {
+ const toStore = value instanceof Function ? value(p) : value;
+ if (typeof window !== "undefined") {
+ if (!toStore) {
+ window.localStorage.removeItem(key);
+ } else {
+ window.localStorage.setItem(key, toStore);
+ }
+ }
+ return toStore;
+ });
+ };
+
+ return [storedValue, setValue];
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/hooks/operational.ts b/packages/auditor-backoffice-ui/src/hooks/operational.ts
new file mode 100644
index 000000000..89524f24e
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/hooks/operational.ts
@@ -0,0 +1,83 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+import {
+ HttpResponse,
+ HttpResponseOk,
+ RequestError,
+} from "@gnu-taler/web-util/browser";
+import { AuditorBackend, WithId } from "../declaration.js";
+import { useBackendRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook, useSWRConfig } from "swr";
+
+const useSWR = _useSWR as unknown as SWRHook;
+
+type YesOrNo = "yes" | "no";
+
+export interface HelperDashboardFilter {
+ finance?: YesOrNo;
+ security?: YesOrNo;
+ operating?: YesOrNo;
+ detail?: YesOrNo;
+}
+
+export function getOperationData(
+ args?: HelperDashboardFilter,
+ updateFilter?: (d: Date) => void,
+): HttpResponse<any, AuditorBackend.ErrorDetail> {
+ const { multiFetcher } = useBackendRequest();
+ const endpoints = [
+ "monitoring/row-inconsistency",
+ "monitoring/purse-not-closed-inconsistencies",
+ "monitoring/reserve-not-closed-inconsistency",
+ "monitoring/denominations-without-sigs",
+ "monitoring/deposit-confirmation",
+ "monitoring/denomination-key-validity-withdraw-inconsistency",
+ "monitoring/refreshes-hanging",
+ //TODO fix endpoint
+ // "monitoring/closure-lags",
+ // "monitoring/row-minor-inconsistencies",
+ // "monitoring/historic-denomination-revenue",
+ // "monitoring/denomination-pending",
+ "monitoring/historic-reserve-summary",
+
+ ];
+
+
+ const { data: list, error: listError } = useSWR<
+ HttpResponseOk<any>[], RequestError<AuditorBackend.ErrorDetail>
+ >(endpoints, multiFetcher, {
+ refreshInterval: 60,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ if (listError) return listError.cause;
+
+ if (list) {
+ return { ok: true, data: [list] };
+ }
+ return { loading: true };
+}
+
+export interface EntityAPI {
+ updateEntity: (
+ id: string,
+ ) => Promise<void>;
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/index.tsx b/packages/auditor-backoffice-ui/src/index.tsx
index 7fdf7c1c3..fc956e8aa 100644
--- a/packages/auditor-backoffice-ui/src/index.tsx
+++ b/packages/auditor-backoffice-ui/src/index.tsx
@@ -21,4 +21,4 @@ import "./scss/main.scss";
const app = document.getElementById("app");
-render(<Application />, app as any);
+render(<Application />, app as any); \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/default/Table.tsx b/packages/auditor-backoffice-ui/src/paths/default/Table.tsx
new file mode 100644
index 000000000..9bb75907d
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/paths/default/Table.tsx
@@ -0,0 +1,155 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import { StateUpdater, useState } from "preact/hooks";
+import { useEntityContext, useEntityDataContext } from "../../context/entity.js";
+
+interface Props {
+ onSuppress: (id: any) => void;
+}
+
+export function CardTable({onSuppress}: Props): any {
+
+ const data = useEntityDataContext();
+ const [rowSelection, rowSelectionHandler] = useState<string | undefined>(
+ undefined,
+ );
+ const { i18n } = useTranslationContext();
+ const { title, endpoint, entity } = useEntityContext();
+
+ return (
+ <div class="card has-table">
+ <header class="card-header">
+ <p class="card-header-title">
+ <span class="icon">
+ <i class="mdi mdi-shopping" />
+ </span>
+ <i18n.Translate>{title}</i18n.Translate>
+ </p>
+ <div class="card-header-icon" aria-label="more options">
+ </div>
+ </header>
+ <div class="card-content">
+ <div class="b-table has-pagination">
+ <div class="table-wrapper has-mobile-cards">
+ {(data.data[0][endpoint] !== undefined && data.data[0][endpoint].length != 0) ? (
+ <Table
+ data={data.data[0][endpoint]}
+ onSuppress={onSuppress}
+ />
+ ) : (
+ <EmptyTable />
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+
+interface TableProps {
+ data: any;
+ onSuppress: (id: any) => void;
+}
+
+function Table({
+ data,
+ onSuppress,
+ }: TableProps): VNode {
+ const { i18n } = useTranslationContext();
+ const { entity } = useEntityContext();
+ type Entity = typeof entity;
+ let count = 0;
+
+ return (
+ <div class="table-container">
+ <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+ <thead>
+ <tr>
+ {Object.keys(data[0]).map((i: Entity) => {
+ const paramName = i[0].toUpperCase() + i.replace("_", " ").slice(1, i.count);
+ return (
+ <Fragment key={count.toString() + i}>
+ <th>
+ <i18n.Translate>{paramName}</i18n.Translate>
+ </th>
+ </Fragment>);
+ })}
+ </tr>
+ </thead>
+ <tbody>
+ {data.map((key: Entity, value: string) => {
+ return (
+ <tr>
+ {Object.keys(data[0]).map((i: Entity) => {
+ return (
+ <Fragment>
+ <td>
+ {(key[i] == false) ? "false" : key[i]}
+ </td>
+ </Fragment>
+ );
+ })}
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <span
+ class="has-tooltip-bottom"
+ data-tooltip={i18n.str`suppress`}
+ >
+ <button
+ class="button is-small is-success "
+ type="button"
+ onClick={(): void => onSuppress(key["row_id"])}
+ >
+ {<i18n.Translate>Suppress</i18n.Translate>}
+ </button>
+ </span>
+ </div>
+ </td>
+ </tr>
+ );
+ })
+ }
+ </tbody>
+ </table>
+ </div>
+ );
+}
+
+function EmptyTable(): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="content has-text-grey has-text-centered">
+ <p>
+ <span class="icon is-large">
+ <i class="mdi mdi-emoticon-happy mdi-48px" />
+ </span>
+ </p>
+ <p>
+ <i18n.Translate>
+ There are no entries yet
+ </i18n.Translate>
+ </p>
+ </div>
+ );
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/default/index.tsx b/packages/auditor-backoffice-ui/src/paths/default/index.tsx
new file mode 100644
index 000000000..1b7758190
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/paths/default/index.tsx
@@ -0,0 +1,130 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * @author Nic Eigel
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import {
+ ErrorType,
+ HttpError,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../../components/exception/loading.js";
+import { NotificationCard } from "../../components/menu/index.js";
+import { AuditorBackend, WithId } from "../../declaration.js";
+import { Notification } from "../../utils/types.js";
+import { CardTable } from "./Table.js";
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { EntityDataContextProvider, useEntityContext } from "../../context/entity.js";
+import { getEntityList, useEntityAPI } from "../../hooks/entity.js";
+import { useMemo } from "preact/hooks";
+import { ConfirmModal, DeleteModal } from "../../components/modal/index.js";
+import { route } from "preact-router";
+import { Paths } from "../../InstanceRoutes.js";
+
+
+interface Props {
+ onNotFound: () => VNode;
+ onLoadError: (e: HttpError<AuditorBackend.ErrorDetail>) => VNode;
+}
+
+export default function DefaultList({
+ onLoadError,
+ onNotFound,
+ }: Props): VNode {
+ const { endpoint, entity } = useEntityContext();
+ const result = getEntityList({ endpoint, entity });
+ const { updateEntity } = useEntityAPI();
+ const [suppressing, setSuppressing] =
+ useState<typeof entity & WithId | null>(null);
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+
+ if (result.loading) return <Loading />;
+ if (!result.ok) {
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.Unauthorized
+ )
+ return onNotFound();
+ return onLoadError(result);
+ }
+
+ let data = result.data;
+ const value = useMemo(
+ () => ({ data }),
+ [data],
+ );
+
+ function onReturn(): void {
+ route(Paths.detail_view);
+ }
+
+ return (
+
+ <section class="section is-main-section">
+ <button
+ class="button is-fullwidth"
+ onClick={onReturn}
+ >Back
+ </button><br />
+
+ <NotificationCard notification={notif} />
+
+ <EntityDataContextProvider value={value}>
+ <CardTable
+ onSuppress={(e: typeof entity & WithId) =>
+ setSuppressing(e)
+ }
+ />
+ </EntityDataContextProvider>
+
+ {suppressing && (
+ <ConfirmModal
+ label={`Suppress row`}
+ description={`Suppress the row`}
+ danger
+ active
+ onCancel={() => setSuppressing(null)}
+ onConfirm={async (): Promise<void> => {
+ try {
+ await updateEntity(suppressing);
+ setNotif({
+ message: i18n.str`Entity row with id: ${suppressing} has been suppressed`,
+ type: "SUCCESS",
+ });
+ } catch (error) {
+ setNotif({
+ message: i18n.str`Failed to suppress row`,
+ type: "ERROR",
+ description: error instanceof Error ? error.message : undefined,
+ });
+ }
+ setSuppressing(null);
+ }}
+ >
+ <p class="warning">
+ Suppressing a row <b>cannot be undone</b> in this GUI.
+ </p>
+ </ConfirmModal>
+ )}
+ </section>
+ );
+}
diff --git a/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx
new file mode 100644
index 000000000..60ae7b578
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/paths/details/ListPage.tsx
@@ -0,0 +1,346 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode, Fragment } from "preact";
+import { route, Route } from "preact-router";
+import { Paths, Redirect } from "../../InstanceRoutes.js";
+import { AuditorBackend } from "../../declaration.js";
+
+export interface ListPageProps {
+ onShowAll: () => void;
+ onShowNotPaid: () => void;
+ onShowPaid: () => void;
+ onShowRefunded: () => void;
+ onShowNotWired: () => void;
+ onShowWired: () => void;
+ onCopyURL: (id: string) => void;
+ isAllActive: string;
+ isPaidActive: string;
+ isNotPaidActive: string;
+ isRefundedActive: string;
+ isNotWiredActive: string;
+ isWiredActive: string;
+
+ jumpToDate?: Date;
+ onSelectDate: (date?: Date) => void;
+
+ onLoadMoreBefore?: () => void;
+ hasMoreBefore?: boolean;
+ hasMoreAfter?: boolean;
+ onLoadMoreAfter?: () => void;
+
+ onCreate: () => void;
+}
+
+export function ListPage(): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Fragment>
+
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.amount_arithmethic_inconsistency_list) }
+ value={"Amount arithmetic inconsistencies"}
+ >Amount arithmetic inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.bad_sig_losses_list) }
+ value={"Bad signature losses"}
+ >Bad signature losses
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.closure_lag_list) }
+ >Closure Lags
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.coin_inconsistency_list) }
+ >Coin inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.denomination_key_validity_withdraw_inconsistency_list) }
+ >Denominations key validity
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.denomination_without_sig_list) }
+ >Denominations without signature
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.denomination_pending_list) }
+ >Denominations pending
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.deposit_confirmation_list) }
+ >Deposit confirmations
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.emergency_list) }
+ >Emergencies
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.emergency_by_count_list) }
+ >Emergencies by count
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.fee_time_inconsistency_list) }
+ >Fee time inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.misattribution_in_inconsistency_list) }
+ >Misattribution in inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.purse_not_closed_inconsistency_list) }
+ >Purses not closed
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.purse_list) }
+ >Purses
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.refresh_hanging_list) }
+ >Refreshes hanging
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.reserve_balance_insufficient_inconsistency_list) }
+ >Reserve balances insufficient
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.reserve_balance_summary_wrong_inconsistency_list) }
+ >Reserve balances summary wrong
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.reserve_in_inconsistency_list) }
+ >Reserves in
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.reserve_not_closed_inconsistency_list) }
+ >Reserves not closed
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.reserves_list) }
+ >Reserves
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.row_inconsistency_list) }
+ >Row inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.row_minor_inconsistency_list) }
+ >Row minor inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.wire_format_inconsistency_list) }
+ >Wire format inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="column">
+ <div class="card">
+ <div class="card-body">
+ <button
+ class="button is-fullwidth"
+ onClick={(e) => route(Paths.wire_out_inconsistency_list) }
+ >Wire out inconsistencies
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </Fragment>
+ );
+}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/details/index.tsx
index 719f99209..f99dae7e5 100644
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/update/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/details/index.tsx
@@ -16,11 +16,24 @@
/**
*
+ * @author Nic Eigel
* @author Sebastian Javier Marchano (sebasjm)
*/
import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { NotificationCard } from "../../components/menu/index.js";
+import { Notification } from "../../utils/types.js";
+import { ListPage } from "./ListPage.js";
-export default function UpdateTransfer(): VNode {
- return <div>order transfer page</div>;
-}
+export default function DetailsDashboard(): VNode {
+
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+
+ return (
+ <section class="section is-main-section">
+ <NotificationCard notification={notif} />
+ <ListPage />
+ </section>
+ );
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx
new file mode 100644
index 000000000..88ca6bcfd
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/paths/finance/ListPage.tsx
@@ -0,0 +1,214 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Nic Eigel
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode, Fragment } from "preact";
+
+export function ListPage(data: any): VNode {
+ const { i18n } = useTranslationContext();
+
+ let balances = data.data.data[0][4].data.balances;
+ let coinBalances = [
+ "Total recoup loss",
+ "Coin refund fee revenue",
+ "Coin deposit fee revenue",
+ "Coin melt fee revenue",
+ "Coin irregular loss",
+ "Coins reported emergency risk by amount",
+ "Coins emergencies loss by count",
+ "Coins emergencies loss",
+ "Coins total arithmetic delta minus",
+ "Coins total arithmetic delta plus",
+ "Total escrowed",
+ "Total refresh hanging",
+ ];
+ let reserveBalances = [
+ "Total balance summary delta minus",
+ "Total balance reserve not closed",
+ "Reserves total arithmetic delta minus",
+ "Reserves total arithmetic delta plus",
+ "Reserves total bad signature loss",
+ "Reserves history fee revenue",
+ "Reserves open fee revenue",
+ ];
+ let i = 0;
+
+ return (
+ <Fragment>
+ <div class="columns">
+ <div class="column is-half">
+ <div class="columns">
+ <div class="column">
+ <div class="card">
+ <div class="card-content">
+ <table class="table is-striped is-fullwidth is-dark">
+ <tbody>
+ <tr>
+ <th>Finding</th>
+ <td class="has-text-right"><b>Count</b></td>
+ <td class="has-text-right"><b>Gain/Loss</b></td>
+ </tr>
+ {
+ data["data"]["data"][0].map((x: any) => {
+ const key = Object.keys(x.data)[0];
+ let value = Object.values(x.data)[0];
+ const paramName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length);
+ if (key == "balances") {
+ //TODO fix
+ let gains = 0;
+ if (value == null)
+ value = 0;
+ else
+ value = Object.keys(value).length;
+
+ return (
+ <tr class="is-link">
+ <td>{paramName}</td>
+ <td class="has-text-right"><p
+ class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td>
+ <td class="has-text-right"><p
+ class={gains == 0 ? "text-success" : "text-danger"}>{String(gains)}</p></td>
+ </tr>
+ );
+ } else {
+ <tr class="is-link">
+ <td>{paramName}</td>
+ <td class="has-text-right"><p
+ class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td>
+ <td class="has-text-right"><p>{
+ //TODO
+ }</p></td>
+ </tr>;
+ }
+ })
+ }
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="card">
+ <div class="card-content">
+ <table class="table is-striped is-fullwidth is-dark">
+ <tbody>
+ <tr>
+ <th>Summary</th>
+ <td class="has-text-right"><b>Value</b></td>
+ </tr>
+ <tr>
+ <td>Total gain/loss</td>
+ <td class="has-text-right">{
+ //TODO fix
+ }</td>
+ </tr>
+ <tr>
+ <td>Pending gain/loss</td>
+ <td class="has-text-right">{
+ //TODO fix
+ }</td>
+ </tr>
+ <tr>
+ <td>Transaction count</td>
+ <td class="has-text-right">{
+ //TODO fix
+ }</td>
+ </tr>
+ <tr>
+ <td>Transactions pending</td>
+ <td class="has-text-right">{
+ //TODO fix
+ }</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="column is-half">
+ <div class="card">
+ <div class="card-content">
+ <p class="has-text-weight-bold">Helper coin</p>
+ <table class="table is-striped is-fullwidth is-dark">
+ <tbody>
+ <tr>
+ <th>Balance</th>
+ <td><b>Value</b></td>
+ </tr>
+ {
+ balances.map((x: any) => {
+ let key = x.balance_key;
+ let balanceName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length);
+
+ if(coinBalances.includes(balanceName))
+ {
+ let value = balances[i].balance_value.replace(":", " ");
+ i=i+1;
+ return (
+ <tr class="is-link">
+ <td>{balanceName}</td>
+ <td><p>{value}</p></td>
+ </tr>
+ );
+ } else {
+ return null;
+ }
+ })
+ }
+ </tbody>
+ </table>
+ <p class="has-text-weight-bold">Helper reserve</p>
+ <table class="table is-striped is-fullwidth is-dark">
+ <tbody>
+ <tr>
+ <th>Balance</th>
+ <td><b>Value</b></td>
+ </tr>
+ {
+ balances.map((x: any) => {
+ let key = x.balance_key;
+ let balanceName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length);
+
+ if(reserveBalances.includes(balanceName))
+ {
+ let value = balances[i].balance_value.replace(":", " ");
+ i = i+1;
+ return (
+ <tr class="is-link">
+ <td>{balanceName}</td>
+ <td><p>{value}</p></td>
+ </tr>
+ );
+ } else {
+ return null;
+ }
+ })
+ }
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </Fragment>
+ );
+}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/finance/index.tsx
index 2d3e7bd6b..b0d07aa0f 100644
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/finance/index.tsx
@@ -16,43 +16,41 @@
/**
*
+ * @author Nic Eigel
* @author Sebastian Javier Marchano (sebasjm)
*/
import {
ErrorType,
- HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { Loading } from "../../../../components/exception/loading.js";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useProductAPI, useProductDetails } from "../../../../hooks/product.js";
-import { Notification } from "../../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
+import { Loading } from "../../components/exception/loading.js";
+import { NotificationCard } from "../../components/menu/index.js";
+import { Notification } from "../../utils/types.js";
+import { ListPage } from "./ListPage.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { getKeyFiguresData } from "../../hooks/finance.js";
+
-export type Entity = MerchantBackend.Products.ProductAddDetail;
interface Props {
- onBack?: () => void;
- onConfirm: () => void;
onUnauthorized: () => VNode;
onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- pid: string;
+ onSelect: (id: string) => void;
+ onCreate: () => void;
}
-export default function UpdateProduct({
- pid,
- onConfirm,
- onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
-}: Props): VNode {
- const { updateProduct } = useProductAPI();
- const result = useProductDetails(pid);
+
+export default function FinanceDashboard({
+ onUnauthorized,
+ // onLoadError,
+ onCreate,
+ onSelect,
+ onNotFound,
+ }: Props): VNode {
+
+ const result = getKeyFiguresData();
+
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
@@ -69,27 +67,14 @@ export default function UpdateProduct({
result.status === HttpStatusCode.NotFound
)
return onNotFound();
- return onLoadError(result);
+ else
+ return onNotFound();
}
return (
- <Fragment>
+ <section class="section is-main-section">
<NotificationCard notification={notif} />
- <UpdatePage
- product={{ ...result.data, product_id: pid }}
- onBack={onBack}
- onUpdate={(data) => {
- return updateProduct(pid, data)
- .then(onConfirm)
- .catch((error) => {
- setNotif({
- message: i18n.str`could not create product`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
+ <ListPage data={result} />
+ </section>
);
-}
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/index.tsx
deleted file mode 100644
index a99cfd2ef..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/index.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- This file is part of GNU Taler
- (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
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- * @author Nic Eigel
- */
-
-import {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Loading } from "../../../../components/exception/loading.js";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { AuditorBackend, WithId } from "../../../../declaration.js";
-import {
- useDepositConfirmation,
- useDepositConfirmationAPI,
-} from "../../../../hooks/deposit_confirmations.js";
-import { Notification } from "../../../../utils/types.js";
-import { CardTable } from "./Table.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ConfirmModal, DeleteModal } from "../../../../components/modal/index.js";
-import { JumpToElementById } from "../../../../components/form/JumpToElementById.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onCreate: () => void;
- onSelect: (id: string) => void;
- onLoadError: (e: HttpError<AuditorBackend.ErrorDetail>) => VNode;
-}
-export default function DepositConfirmationList({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const result = useDepositConfirmation();
- const { deleteDepositConfirmation, updateDepositConfirmation, getDepositConfirmation } = useDepositConfirmationAPI();
- const [deleting, setDeleting] =
- useState<AuditorBackend.DepositConfirmation.DepositConfirmationDetail & WithId | null>(null);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- 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);
- }
-
- return (
- <section class="section is-main-section">
- <NotificationCard notification={notif} />
-
- <JumpToElementById
- testIfExist={getDepositConfirmation}
- onSelect={onSelect}
- description={i18n.str`jump to deposit_confirmation with the given serial ID`}
- placeholder={i18n.str`serial id`}
- />
-
- {deleting && (
- <ConfirmModal
- label={`Delete deposit-confirmation`}
- description={`Delete the deposit-cofirmation "${deleting.serial_id}"`}
- danger
- active
- onCancel={() => setDeleting(null)}
- onConfirm={async (): Promise<void> => {
- try {
- await deleteDepositConfirmation(deleting.serial_id);
- setNotif({
- message: i18n.str`Deposit-confirmation "${deleting.serial_id}" (ID: ${deleting.serial_id}) has been deleted`,
- type: "SUCCESS",
- });
- } catch (error) {
- setNotif({
- message: i18n.str`Failed to delete deposit-confirmation`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- }
- setDeleting(null);
- }}
- >
- <p>
- If you delete the deposit-confirmation (ID:{" "}
- <b>{deleting.serial_id}</b>), the stock and related information will be lost
- </p>
- <p class="warning">
- Deleting a deposit-confirmation <b>cannot be undone</b>.
- </p>
- </ConfirmModal>
- )}
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/login/index.tsx b/packages/auditor-backoffice-ui/src/paths/login/index.tsx
index f8990d377..c99dc6050 100644
--- a/packages/auditor-backoffice-ui/src/paths/login/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/login/index.tsx
@@ -16,187 +16,198 @@
/**
*
+ * @author Nic Eigel
* @author Sebastian Javier Marchano (sebasjm)
*/
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { ComponentChildren, h, VNode } from "preact";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import { useBackendContext } from "../../context/backend.js";
-import { useInstanceContext } from "../../context/instance.js";
-import { AccessToken, LoginToken } from "../../declaration.js";
-import { useCredentialsChecker } from "../../hooks/backend.js";
-
-interface Props {
- onConfirm: (token: LoginToken | undefined) => void;
-}
-
-function normalizeToken(r: string): AccessToken {
- return `secret-token:${r}` as AccessToken;
-}
-
-export function LoginPage({ onConfirm }: Props): VNode {
- const { url: backendURL } = useBackendContext();
- const { admin, id } = useInstanceContext();
- const { requestNewLoginToken } = useCredentialsChecker();
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import { useCallback, useState } from "preact/hooks";
+import { useBackendContext, useBackendTokenContext } from "../../context/backend.js";
+import { NotificationCard } from "../../components/menu/index.js";
+import { Notification } from "../../utils/types.js";
+import { useBackendToken } from "../../hooks/backend.js";
+import { Route } from "preact-router";
+import { Paths, Redirect } from "../../InstanceRoutes.js";
+
+export function LoginPage(): VNode {
const [token, setToken] = useState("");
-
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
-
-
const doLogin = useCallback(async function doLoginImpl() {
- const secretToken = normalizeToken(token);
- const baseUrl = id === undefined ? backendURL : `${backendURL}/instances/${id}`
- const result = await requestNewLoginToken(baseUrl, secretToken);
- if (result.valid) {
- const { token, expiration } = result
- onConfirm({ token, expiration });
+
+ const result = useBackendToken();
+ if (!result.ok) {
+ }
+ if (result.ok) {
+ //TODO fixme
+ const { token } = useBackendTokenContext();
+ /* return (
+ <Route path="/" component={Redirect} to={Paths.key_figures}/>
+ );*/
} else {
- onConfirm(undefined);
+ setNotif({
+ message: "Your password is incorrect",
+ type: "ERROR",
+ });
}
- }, [id, token])
-
- if (admin && id !== "default") {
- //admin trying to access another instance
- return (<div class="columns is-centered" style={{ margin: "auto" }}>
- <div class="column is-two-thirds ">
- <div class="modal-card" style={{ width: "100%", margin: 0 }}>
- <header
- class="modal-card-head"
- style={{ border: "1px solid", borderBottom: 0 }}
- >
- <p class="modal-card-title">{i18n.str`Login required`}</p>
- </header>
- <section
- class="modal-card-body"
- style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
- >
- <p>
- <i18n.Translate>Need the access token for the instance.</i18n.Translate>
- </p>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Access Token</i18n.Translate>
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <p class="control is-expanded">
- <input
- class="input"
- type="password"
- placeholder={"current access token"}
- name="token"
- onKeyPress={(e) =>
- e.keyCode === 13
- ? doLogin()
- : null
- }
- value={token}
- onInput={(e): void => setToken(e?.currentTarget.value)}
- />
- </p>
- </div>
+ }, [token]);
+
+ return (
+ <Route path="/" component={Redirect} to={Paths.key_figures}/>
+ );
+
+ return (
+ <div class="columns is-centered" style={{ margin: "auto" }}>
+ <div class="column is-two-thirds ">
+ <div class="modal-card" style={{ width: "100%", margin: 0 }}>
+ <header
+ class="modal-card-head"
+ style={{ border: "1px solid", borderBottom: 0 }}
+ >
+ <p class="modal-card-title">{i18n.str`Token required`}</p>
+ </header>
+ <section
+ class="modal-card-body"
+ style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
+ >
+
+ <p>
+ <i18n.Translate>Need the access token for the API.</i18n.Translate>
+ </p>
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <i18n.Translate>Access Token</i18n.Translate>
+ </label>
+ </div>
+ <div class="field-body">
+ <div class="field">
+ <p class="control is-expanded">
+ <input
+ class="input"
+ type="password"
+ placeholder={"current access token"}
+ name="token"
+ onKeyPress={(e) =>
+ e.keyCode === 13
+ ? doLogin()
+ : null
+ }
+ value={token}
+ onInput={(e): void => setToken(e?.currentTarget.value)}
+ />
+ </p>
</div>
</div>
- </section>
- <footer
- class="modal-card-foot "
- style={{
- justifyContent: "flex-end",
- border: "1px solid",
- borderTop: 0,
- }}
+ </div>
+ </section>
+ <footer
+ class="modal-card-foot "
+ style={{
+ justifyContent: "flex-end",
+ border: "1px solid",
+ borderTop: 0,
+ }}
+ >
+ <AsyncButton
+ onClick={() => doLogin()}
>
- <AsyncButton
- onClick={doLogin}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </footer>
- </div>
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </footer>
</div>
- </div>)
- }
+ </div>
+ </div>);
- return (
- <div class="columns is-centered" style={{ margin: "auto" }}>
- <div class="column is-two-thirds ">
- <div class="modal-card" style={{ width: "100%", margin: 0 }}>
- <header
- class="modal-card-head"
- style={{ border: "1px solid", borderBottom: 0 }}
- >
- <p class="modal-card-title">{i18n.str`Login required`}</p>
- </header>
- <section
- class="modal-card-body"
- style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
- >
- <i18n.Translate>Please enter your access token.</i18n.Translate>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Access Token</i18n.Translate>
- </label>
- </div>
- <div class="field-body">
- <div class="field">
- <p class="control is-expanded">
- <input
- class="input"
- type="password"
- placeholder={"current access token"}
- name="token"
- onKeyPress={(e) =>
- e.keyCode === 13
- ? doLogin()
- : null
- }
- value={token}
- onInput={(e): void => setToken(e?.currentTarget.value)}
- />
- </p>
+ return (<Fragment>
+ <NotificationCard notification={notif} />
+ <div class="columns is-centered" style={{ margin: "auto" }}>
+ <div class="column is-two-thirds ">
+ <div class="modal-card" style={{ width: "100%", margin: 0 }}>
+ <header
+ class="modal-card-head"
+ style={{ border: "1px solid", borderBottom: 0 }}
+ >
+ <p class="modal-card-title">{i18n.str`Login required`}</p>
+ </header>
+ <section
+ class="modal-card-body"
+ style={{ border: "1px solid", borderTop: 0, borderBottom: 0 }}
+ >
+ <i18n.Translate>Please enter your access token.</i18n.Translate>
+
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <i18n.Translate>Access Token</i18n.Translate>
+ </label>
+ </div>
+ <div class="field-body">
+
+ <div class="field">
+ <p class="control is-expanded">
+ <input
+ class="input"
+ type="password"
+ placeholder={"current access token"}
+ name="token"
+ onKeyPress={(e) =>
+ e.keyCode === 13
+ ? doLogin()
+ : null
+ }
+ value={token}
+ onInput={(e): void => setToken(e?.currentTarget.value)}
+ />
+ </p>
+ </div>
</div>
</div>
- </div>
- </section>
- <footer
- class="modal-card-foot "
- style={{
- justifyContent: "space-between",
- border: "1px solid",
- borderTop: 0,
- }}
- >
- <div />
- <AsyncButton
- type="is-info"
- onClick={doLogin}
+ </section>
+ <footer
+ class="modal-card-foot "
+ style={{
+ justifyContent: "space-between",
+ border: "1px solid",
+ borderTop: 0,
+ }}
>
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </footer>
+ <div />
+ <AsyncButton
+ type="is-info"
+ onClick={doLogin}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+
+ </footer>
+ </div>
</div>
</div>
- </div>
+ </Fragment>
+
);
}
-function AsyncButton({ onClick, disabled, type = "", children }: { type?: string, disabled?: boolean, onClick: () => Promise<void>, children: ComponentChildren }): VNode {
- const [running, setRunning] = useState(false)
+function AsyncButton({ onClick, disabled, type = "", children }: {
+ type?: string,
+ disabled?: boolean,
+ onClick: () => Promise<void>,
+ children: ComponentChildren
+}): VNode {
+ const [running, setRunning] = useState(false);
return <button class={"button " + type} disabled={disabled || running} onClick={() => {
- setRunning(true)
+ setRunning(true);
onClick().then(() => {
- setRunning(false)
+ setRunning(false);
}).catch(() => {
- setRunning(false)
- })
+ setRunning(false);
+ });
}}>
{children}
- </button>
+ </button>;
}
diff --git a/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx b/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx
index 68adb79bf..114b95219 100644
--- a/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/notfound/index.tsx
@@ -23,12 +23,12 @@ import { h, VNode } from "preact";
import { Link } from "preact-router";
export default function NotFoundPage(): VNode {
- return (
- <div>
- <p>That page doesn&apos;t exist.</p>
- <Link href="/">
- <h4>Back to Home</h4>
- </Link>
- </div>
- );
+ return (
+ <div>
+ <p>That page doesn&apos;t exist.</p>
+ <Link href="/">
+ <h4>Back to Home</h4>
+ </Link>
+ </div>
+ );
}
diff --git a/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx
new file mode 100644
index 000000000..7f0579b2b
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/paths/operations/ListPage.tsx
@@ -0,0 +1,72 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Nic Eigel
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode, Fragment } from "preact";
+
+export function ListPage(data: any): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Fragment>
+ <div class="columns is-fullwidth">
+ <div class="column is-fullwidth">
+ <div class="card">
+ <div class="card-content">
+ <table class="table is-striped is-fullwidth">
+ <tbody>
+ <tr>
+ <th>Finding</th>
+ <td class="has-text-right"><b>Count</b></td>
+ <td class="has-text-right"><b>Time difference (s)</b></td>
+ <td class="has-text-right"><b>Diagnostic</b></td>
+ </tr>
+ {
+ data["data"]["data"][0].map((x: any) => {
+ const key = Object.keys(x.data)[0];
+ let value = Object.values(x.data)[0];
+ console.log(value);
+ if (!!value)
+ value = 0;
+ const paramName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length);
+ return (
+ <tr class="is-link">
+ <td>{paramName}</td>
+ <td className="has-text-right"><p
+ class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td>
+ <td className="has-text-right">{//TODO
+ }</td>
+ <td>{//TODO
+ }</td>
+ </tr>
+ );
+ })
+ }
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </Fragment>
+ );
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/operations/index.tsx
index 2d3e7bd6b..c05b271fe 100644
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/update/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/operations/index.tsx
@@ -16,43 +16,41 @@
/**
*
+ * @author Nic Eigel
* @author Sebastian Javier Marchano (sebasjm)
*/
import {
ErrorType,
- HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { Loading } from "../../../../components/exception/loading.js";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useProductAPI, useProductDetails } from "../../../../hooks/product.js";
-import { Notification } from "../../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
+import { Loading } from "../../components/exception/loading.js";
+import { NotificationCard } from "../../components/menu/index.js";
+import { Notification } from "../../utils/types.js";
+import { ListPage } from "./ListPage.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { getOperationData } from "../../hooks/operational.js";
+
-export type Entity = MerchantBackend.Products.ProductAddDetail;
interface Props {
- onBack?: () => void;
- onConfirm: () => void;
onUnauthorized: () => VNode;
onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- pid: string;
+ onSelect: (id: string) => void;
+ onCreate: () => void;
}
-export default function UpdateProduct({
- pid,
- onConfirm,
- onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
-}: Props): VNode {
- const { updateProduct } = useProductAPI();
- const result = useProductDetails(pid);
+
+export default function OperationsDashboard({
+ onUnauthorized,
+ // onLoadError,
+ onCreate,
+ onSelect,
+ onNotFound,
+ }: Props): VNode {
+
+ const result = getOperationData();
+
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
@@ -69,27 +67,14 @@ export default function UpdateProduct({
result.status === HttpStatusCode.NotFound
)
return onNotFound();
- return onLoadError(result);
+ else
+ return onNotFound();
}
return (
- <Fragment>
+ <section class="section is-main-section">
<NotificationCard notification={notif} />
- <UpdatePage
- product={{ ...result.data, product_id: pid }}
- onBack={onBack}
- onUpdate={(data) => {
- return updateProduct(pid, data)
- .then(onConfirm)
- .catch((error) => {
- setNotif({
- message: i18n.str`could not create product`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
+ <ListPage data={result} />
+ </section>
);
-}
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx
new file mode 100644
index 000000000..74f83bd4a
--- /dev/null
+++ b/packages/auditor-backoffice-ui/src/paths/security/ListPage.tsx
@@ -0,0 +1,70 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Nic Eigel
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode, Fragment } from "preact";
+
+export function ListPage(data: any): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Fragment>
+ <div class="columns is-fullwidth">
+ <div class="column is-fullwidth">
+ <div class="card">
+ <div class="card-content">
+ <table class="table is-striped is-fullwidth">
+ <tbody>
+ <tr>
+ <th>Finding</th>
+ <td class="has-text-right"><b>Count</b></td>
+ <td class="has-text-right"><b>Expiration dates</b></td>
+ </tr>
+ {
+ data["data"]["data"][0].map((x: any) => {
+ const key = Object.keys(x.data)[0];
+ let value = Object.values(x.data)[0];
+ console.log(value);
+ if (!!value)
+ value = 0;
+ const paramName = key[0].toUpperCase() + key.split("_").join(" ").split("-").join(" ").slice(1, key.length);
+ return (
+ <tr class="is-link">
+ <td>{paramName}</td>
+ <td class="has-text-right"><p
+ class={value == 0 ? "text-success" : "text-danger"}>{String(value)}</p></td>
+ <td class="has-text-right">{
+ //TODO
+ }</td>
+ </tr>
+ );
+ })
+ }
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </Fragment>
+ );
+}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/index.tsx b/packages/auditor-backoffice-ui/src/paths/security/index.tsx
index 65ccc2dcc..99c98a5e7 100644
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/security/index.tsx
@@ -16,46 +16,43 @@
/**
*
+ * @author Nic Eigel
* @author Sebastian Javier Marchano (sebasjm)
*/
import {
ErrorType,
- HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { Loading } from "../../../../components/exception/loading.js";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import {
- useTemplateAPI,
- useTemplateDetails,
-} from "../../../../hooks/templates.js";
-import { Notification } from "../../../../utils/types.js";
-import { QrPage } from "./QrPage.js";
+import { Loading } from "../../components/exception/loading.js";
+import { NotificationCard } from "../../components/menu/index.js";
+import { Notification } from "../../utils/types.js";
+import { ListPage } from "./ListPage.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-export type Entity = MerchantBackend.Transfers.TransferInformation;
+import { getCriticalData } from "../../hooks/critical.js";
interface Props {
- onBack?: () => void;
onUnauthorized: () => VNode;
onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- tid: string;
+ onSelect: (id: string) => void;
+ onCreate: () => void;
}
-export default function TemplateQrPage({
- tid,
- onBack,
- onLoadError,
- onNotFound,
- onUnauthorized,
-}: Props): VNode {
- const result = useTemplateDetails(tid);
+export default function SecurityDashboard({
+ onUnauthorized,
+ // onLoadError,
+ onCreate,
+ onSelect,
+ onNotFound,
+ }: Props): VNode {
+
+ const result = getCriticalData();
+
const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+
if (result.loading) return <Loading />;
if (!result.ok) {
if (
@@ -68,13 +65,14 @@ export default function TemplateQrPage({
result.status === HttpStatusCode.NotFound
)
return onNotFound();
- return onLoadError(result);
+ else
+ return onNotFound();
}
return (
- <>
+ <section class="section is-main-section">
<NotificationCard notification={notif} />
- <QrPage contract={result.data.template_contract} id={tid} onBack={onBack} />
- </>
+ <ListPage data={result} />
+ </section>
);
-}
+} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/settings/index.tsx b/packages/auditor-backoffice-ui/src/paths/settings/index.tsx
index 093c3d09d..77a56a794 100644
--- a/packages/auditor-backoffice-ui/src/paths/settings/index.tsx
+++ b/packages/auditor-backoffice-ui/src/paths/settings/index.tsx
@@ -1,8 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ * @author Nic Eigel
+ */
+
import { useLang, useTranslationContext } from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
-import { FormErrors, FormProvider } from "../../components/form/FormProvider.js";
-import { InputSelector } from "../../components/form/InputSelector.js";
-import { InputToggle } from "../../components/form/InputToggle.js";
+import { FormErrors, FormProvider } from "../../components/forms/FormProvider.js";
import { LangSelector } from "../../components/menu/LangSelector.js";
import { Settings, useSettings } from "../../hooks/useSettings.js";
@@ -16,7 +36,7 @@ function getBrowserLang(): string | undefined {
export function Settings({ onClose }: { onClose?: () => void }): VNode {
const { i18n } = useTranslationContext()
const borwserLang = getBrowserLang()
- //const { update } = useLang()
+ const { update } = useLang(undefined, {})
const [value, updateValue] = useSettings()
const errors: FormErrors<Settings> = {
@@ -60,38 +80,13 @@ export function Settings({ onClose }: { onClose?: () => void }): VNode {
data-tooltip={i18n.str`generate random secret key`}
class="button is-info mr-2"
onClick={(e) => {
- //update(borwserLang.substring(0, 2))
+ update(borwserLang.substring(0, 2))
}}
>
<i18n.Translate>Set default</i18n.Translate>
</button>}
</div>
</div>
- <InputToggle<Settings>
- label={i18n.str`Advance order creation`}
- tooltip={i18n.str`Shows more options in the order creation form`}
- name="advanceOrderMode"
- />
- <InputSelector<Settings>
- name="dateFormat"
- label={i18n.str`Date format`}
- expand={true}
- help={
- value.dateFormat === "dmy" ? "31/12/2001" : value.dateFormat === "mdy" ? "12/31/2001" : value.dateFormat === "ymd" ? "2001/12/31" : ""
- }
- toStr={(e) => {
- if (e === "ymd") return "year month day"
- if (e === "mdy") return "month day year"
- if (e === "dmy") return "day month year"
- return "choose one"
- }}
- values={[
- "ymd",
- "mdy",
- "dmy",
- ]}
- tooltip={i18n.str`how the date is going to be displayed`}
- />
</FormProvider>
</div>
</div>
diff --git a/packages/auditor-backoffice-ui/src/stories.tsx b/packages/auditor-backoffice-ui/src/stories.tsx
deleted file mode 100644
index 8bb06b8cb..000000000
--- a/packages/auditor-backoffice-ui/src/stories.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- This file is part of GNU Taler
- (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
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-import { strings } from "./i18n/strings.js";
-
-import * as admin from "./paths/admin/index.stories.js";
-import * as instance from "./paths/instance/index.stories.js";
-import * as components from "./components/index.stories.js";
-
-import { renderStories } from "@gnu-taler/web-util/browser";
-
-import "./scss/main.scss";
-
-function SortStories(a: any, b: any): number {
- return (a?.order ?? 0) - (b?.order ?? 0);
-}
-
-function main(): void {
- renderStories(
- { admin, instance, components },
- {
- strings,
- },
- );
-}
-
-if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", main);
-} else {
- main();
-}
diff --git a/packages/auditor-backoffice-ui/src/sw.js b/packages/auditor-backoffice-ui/src/sw.js
deleted file mode 100644
index bf52db6fa..000000000
--- a/packages/auditor-backoffice-ui/src/sw.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- This file is part of GNU Taler
- (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
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-// import { getFiles, setupPrecaching, setupRouting } from 'preact-cli/sw/';
-
-// setupRouting();
-// setupPrecaching(getFiles());
diff --git a/packages/auditor-backoffice-ui/src/utils/amount.ts b/packages/auditor-backoffice-ui/src/utils/amount.ts
index fda997619..0796087ac 100644
--- a/packages/auditor-backoffice-ui/src/utils/amount.ts
+++ b/packages/auditor-backoffice-ui/src/utils/amount.ts
@@ -12,13 +12,13 @@
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
+ *//*
import {
amountFractionalBase,
AmountJson,
Amounts,
} from "@gnu-taler/taler-util";
-import { MerchantBackend } from "../declaration.js";
+import { AuditorBackend } from "../declaration.js";
/**
* merge refund with the same description and a difference less than one minute
@@ -26,7 +26,7 @@ import { MerchantBackend } from "../declaration.js";
* @param cur new refund to add to the list
* @returns list with the new refund, may be merged with the last
*/
-export function mergeRefunds(
+/*export function mergeRefunds(
prev: MerchantBackend.Orders.RefundDetails[],
cur: MerchantBackend.Orders.RefundDetails,
): MerchantBackend.Orders.RefundDetails[] {
@@ -69,3 +69,4 @@ export function rate(a: AmountJson, b: AmountJson): number {
function toFloat(amount: AmountJson): number {
return amount.value + amount.fraction / amountFractionalBase;
}
+*/ \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/utils/table.ts b/packages/auditor-backoffice-ui/src/utils/table.ts
index 306328aa1..1322ad804 100644
--- a/packages/auditor-backoffice-ui/src/utils/table.ts
+++ b/packages/auditor-backoffice-ui/src/utils/table.ts
@@ -13,14 +13,14 @@
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-
+/*
import { WithId } from "../declaration.js";
-
+*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
-
+/*
export interface Actions<T extends WithId> {
element: T;
type: "DELETE" | "UPDATE";
@@ -40,7 +40,7 @@ export function buildActions<T extends WithId>(
.filter(notEmpty)
.map((id) => ({ element: id, type: action }));
}
-
+*/
/**
* For any object or array, return the same object if is not empty.
* not empty:
@@ -48,10 +48,10 @@ export function buildActions<T extends WithId>(
* - for objects: at least one property not undefined
* @param obj
* @returns
- */
+ *//*
export function undefinedIfEmpty<
T extends Record<string, unknown> | Array<unknown>,
>(obj: T | undefined): T | undefined {
if (obj === undefined) return undefined;
return Object.values(obj).some((v) => v !== undefined) ? obj : undefined;
-}
+}*/
diff --git a/packages/web-util/src/utils/request.ts b/packages/web-util/src/utils/request.ts
index 23d3af468..944e65945 100644
--- a/packages/web-util/src/utils/request.ts
+++ b/packages/web-util/src/utils/request.ts
@@ -51,6 +51,7 @@ export async function defaultRequestHandler<T>(
`${options.basicAuth.username}:${options.basicAuth.password}`,
)}`;
}
+
requestHeaders["Content-Type"] =
!options.contentType || options.contentType === "json" ? "application/json" : "text/plain";