summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-10-06 10:38:09 -0300
committerSebastian <sebasjm@gmail.com>2023-10-06 10:38:23 -0300
commit98013322db6b912ac50d72353dbd8b5f7fbc0435 (patch)
tree184b99e08c69f131fe26628bbb106633d7d66013
parent97d7be7503168f4f3bbd05905d32aa76ca1636b2 (diff)
downloadwallet-core-98013322db6b912ac50d72353dbd8b5f7fbc0435.tar.gz
wallet-core-98013322db6b912ac50d72353dbd8b5f7fbc0435.tar.bz2
wallet-core-98013322db6b912ac50d72353dbd8b5f7fbc0435.zip
backoffice ui
-rw-r--r--packages/merchant-backoffice-ui/package.json2
-rw-r--r--packages/merchant-backoffice-ui/src/Application.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/InstanceRoutes.tsx24
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx3
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx59
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/index.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/backend.ts34
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/index.ts8
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/templates.ts112
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/webhooks.ts4
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx51
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx31
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx5
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx56
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx8
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx16
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx11
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx1
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/login/index.tsx23
26 files changed, 256 insertions, 232 deletions
diff --git a/packages/merchant-backoffice-ui/package.json b/packages/merchant-backoffice-ui/package.json
index 66e03a65c..23b258792 100644
--- a/packages/merchant-backoffice-ui/package.json
+++ b/packages/merchant-backoffice-ui/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@gnu-taler/merchant-backoffice-ui",
- "version": "0.1.0",
+ "version": "0.9.3-dev.27",
"license": "AGPL-3.0-or-later",
"type": "module",
"scripts": {
diff --git a/packages/merchant-backoffice-ui/src/Application.tsx b/packages/merchant-backoffice-ui/src/Application.tsx
index f0a7de53b..4210192ae 100644
--- a/packages/merchant-backoffice-ui/src/Application.tsx
+++ b/packages/merchant-backoffice-ui/src/Application.tsx
@@ -64,7 +64,7 @@ function ApplicationStatusRoutes(): VNode {
const result = useBackendConfig();
const { i18n } = useTranslationContext();
- const { currency, version } = result.ok
+ const { currency, version } = result.ok && result.data
? result.data
: { currency: "unknown", version: "unknown" };
const ctx = useMemo(() => ({ currency, version }), [currency, version]);
diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
index f5372db8d..95be49c9d 100644
--- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
@@ -80,7 +80,7 @@ import { dateFormatForSettings, useSettings } from "./hooks/useSettings.js";
export enum InstancePaths {
error = "/error",
- server = "/server",
+ settings = "/settings",
token = "/token",
bank_list = "/bank",
@@ -118,7 +118,7 @@ export enum InstancePaths {
validators_update = "/validators/:vid/update",
validators_new = "/validators/new",
- settings = "/interface",
+ interface = "/interface",
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -255,7 +255,7 @@ export function InstanceRoutes({
instance={id}
admin={admin}
onShowSettings={() => {
- route(InstancePaths.settings)
+ route(InstancePaths.interface)
}}
path={path}
onLogout={clearTokenAndGoToRoot}
@@ -320,7 +320,7 @@ export function InstanceRoutes({
* Update instance page
*/}
<Route
- path={InstancePaths.server}
+ path={InstancePaths.settings}
component={InstanceUpdatePage}
onBack={() => {
route(`/`);
@@ -353,7 +353,7 @@ export function InstanceRoutes({
path={InstancePaths.inventory_list}
component={ProductListPage}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onCreate={() => {
route(InstancePaths.inventory_new);
}}
@@ -392,7 +392,7 @@ export function InstanceRoutes({
path={InstancePaths.bank_list}
component={BankAccountListPage}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onCreate={() => {
route(InstancePaths.bank_new);
}}
@@ -437,7 +437,7 @@ export function InstanceRoutes({
route(InstancePaths.order_details.replace(":oid", id));
}}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
/>
<Route
@@ -468,7 +468,7 @@ export function InstanceRoutes({
component={TransferListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onCreate={() => {
route(InstancePaths.transfers_new);
}}
@@ -491,7 +491,7 @@ export function InstanceRoutes({
component={WebhookListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onCreate={() => {
route(InstancePaths.webhooks_new);
}}
@@ -530,7 +530,7 @@ export function InstanceRoutes({
component={ValidatorListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onCreate={() => {
route(InstancePaths.validators_new);
}}
@@ -569,7 +569,7 @@ export function InstanceRoutes({
component={TemplateListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onCreate={() => {
route(InstancePaths.templates_new);
}}
@@ -638,7 +638,7 @@ export function InstanceRoutes({
component={ReservesListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
onSelect={(id: string) => {
route(InstancePaths.reserves_details.replace(":rid", id));
}}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
index 34feec202..d7b490a5d 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
@@ -107,8 +107,9 @@ export function InputWithAddon<T>({
{error && <p class="help is-danger">{error}</p>}
<span class="has-text-grey">{help}</span>
</div>
- {side}
+ {expand ? <div>{side}</div> : side}
</div>
+
</div>
);
}
diff --git a/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx b/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx
new file mode 100644
index 000000000..2ff23dfd3
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/form/JumpToElementById.tsx
@@ -0,0 +1,59 @@
+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, palceholder, description }: { palceholder: 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={palceholder}
+ />
+ {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/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
index 402134096..6905cb4d0 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -177,12 +177,12 @@ export function Sidebar({
</a>
</li>
<li>
- <a href={"/server"} class="has-icon">
+ <a href={"/settings"} class="has-icon">
<span class="icon">
<i class="mdi mdi-square-edit-outline" />
</span>
<span class="menu-item-label">
- <i18n.Translate>Server</i18n.Translate>
+ <i18n.Translate>Settings</i18n.Translate>
</span>
</a>
</li>
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
index b8ac2c9ab..0e881d7a5 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -24,7 +24,7 @@ import { Sidebar } from "./SideBar.js";
function getInstanceTitle(path: string, id: string): string {
switch (path) {
- case InstancePaths.server:
+ case InstancePaths.settings:
return `${id}: Settings`;
case InstancePaths.order_list:
return `${id}: Orders`;
@@ -64,9 +64,7 @@ function getInstanceTitle(path: string, id: string): string {
return `${id}: Templates`;
case InstancePaths.templates_use:
return `${id}: Use template`;
- case InstancePaths.settings:
- return `${id}: Interface`;
- case InstancePaths.settings:
+ case InstancePaths.interface:
return `${id}: Interface`;
default:
return "";
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index eaeede103..4515f0557 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -91,7 +91,7 @@ const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
export function useBackendConfig(): HttpResponse<
- MerchantBackend.VersionResponse,
+ MerchantBackend.VersionResponse | undefined,
RequestError<MerchantBackend.ErrorDetail>
> {
const { request } = useBackendBaseRequest();
@@ -340,6 +340,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
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],
@@ -385,6 +393,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
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;
}
@@ -403,6 +419,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
): 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;
}
@@ -421,6 +445,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
): 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;
}
diff --git a/packages/merchant-backoffice-ui/src/hooks/index.ts b/packages/merchant-backoffice-ui/src/hooks/index.ts
index 498e4eb78..3c8ef15ed 100644
--- a/packages/merchant-backoffice-ui/src/hooks/index.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/index.ts
@@ -32,7 +32,13 @@ const calculateRootPath = () => {
? window.location.origin + window.location.pathname
: "/";
- return rootPath.replace("webui/","");
+ /**
+ * 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>()
diff --git a/packages/merchant-backoffice-ui/src/hooks/templates.ts b/packages/merchant-backoffice-ui/src/hooks/templates.ts
index 56cdd3046..ee8728cc8 100644
--- a/packages/merchant-backoffice-ui/src/hooks/templates.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/templates.ts
@@ -82,10 +82,19 @@ export function useTemplateAPI(): TemplateAPI {
return res;
};
+ const testTemplateExist = async (
+ templateId: string,
+ ): Promise<HttpResponseOk<void>> => {
+ const res = await request<void>(`/private/templates/${templateId}`, { method: "GET", });
+ return res;
+ };
+
+
return {
createTemplate,
updateTemplate,
deleteTemplate,
+ testTemplateExist,
createOrderFromTemplate,
};
}
@@ -98,6 +107,9 @@ export interface TemplateAPI {
id: string,
data: MerchantBackend.Template.TemplatePatchDetails,
) => Promise<HttpResponseOk<void>>;
+ testTemplateExist: (
+ id: string
+ ) => Promise<HttpResponseOk<void>>;
deleteTemplate: (id: string) => Promise<HttpResponseOk<void>>;
createOrderFromTemplate: (
id: string,
@@ -119,11 +131,11 @@ export function useInstanceTemplates(
> {
const { templateFetcher } = useBackendInstanceRequest();
- // const [pageBefore, setPageBefore] = useState(1);
+ const [pageBefore, setPageBefore] = useState(1);
const [pageAfter, setPageAfter] = useState(1);
const totalAfter = pageAfter * PAGE_SIZE;
- // const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 0;
+ const totalBefore = args?.position ? pageBefore * PAGE_SIZE : 0;
/**
* FIXME: this can be cleaned up a little
@@ -131,20 +143,20 @@ export function useInstanceTemplates(
* the logic of double query should be inside the orderFetch so from the hook perspective and cache
* is just one query and one error status
*/
- // const {
- // data: beforeData,
- // error: beforeError,
- // isValidating: loadingBefore,
- // } = useSWR<HttpResponseOk<MerchantBackend.Template.TemplateSummaryResponse>, HttpError>(
- // [
- // `/private/templates`,
- // token,
- // url,
- // args?.position,
- // totalBefore,
- // ],
- // templateFetcher,
- // );
+ const {
+ data: beforeData,
+ error: beforeError,
+ isValidating: loadingBefore,
+ } = useSWR<
+ HttpResponseOk<MerchantBackend.Template.TemplateSummaryResponse>,
+ RequestError<MerchantBackend.ErrorDetail>>(
+ [
+ `/private/templates`,
+ args?.position,
+ totalBefore,
+ ],
+ templateFetcher,
+ );
const {
data: afterData,
error: afterError,
@@ -155,9 +167,13 @@ export function useInstanceTemplates(
>([`/private/templates`, args?.position, -totalAfter], templateFetcher);
//this will save last result
- // const [lastBefore, setLastBefore] = useState<
- // HttpResponse<MerchantBackend.Template.TemplateSummaryResponse, MerchantBackend.ErrorDetail>
- // >({ loading: true });
+ const [lastBefore, setLastBefore] = useState<
+ HttpResponse<
+ MerchantBackend.Template.TemplateSummaryResponse,
+ MerchantBackend.ErrorDetail
+ >
+ >({ loading: true });
+
const [lastAfter, setLastAfter] = useState<
HttpResponse<
MerchantBackend.Template.TemplateSummaryResponse,
@@ -166,19 +182,18 @@ export function useInstanceTemplates(
>({ loading: true });
useEffect(() => {
if (afterData) setLastAfter(afterData);
- // if (beforeData) setLastBefore(beforeData);
- }, [afterData /*, beforeData*/]);
+ if (beforeData) setLastBefore(beforeData);
+ }, [afterData, beforeData]);
- // if (beforeError) return beforeError;
+ if (beforeError) return beforeError.cause;
if (afterError) return afterError.cause;
// if the query returns less that we ask, then we have reach the end or beginning
const isReachingEnd =
afterData && afterData.data.templates.length < totalAfter;
- const isReachingStart = false;
- // args?.position === undefined
- // ||
- // (beforeData && beforeData.data.templates.length < totalBefore);
+ const isReachingStart = args?.position === undefined
+ ||
+ (beforeData && beforeData.data.templates.length < totalBefore);
const pagination = {
isReachingEnd,
@@ -188,37 +203,36 @@ export function useInstanceTemplates(
if (afterData.data.templates.length < MAX_RESULT_SIZE) {
setPageAfter(pageAfter + 1);
} else {
- const from = `${
- afterData.data.templates[afterData.data.templates.length - 1]
- .template_id
- }`;
+ const from = `${afterData.data.templates[afterData.data.templates.length - 1]
+ .template_id
+ }`;
if (from && updatePosition) updatePosition(from);
}
},
loadMorePrev: () => {
- // if (!beforeData || isReachingStart) return;
- // if (beforeData.data.templates.length < MAX_RESULT_SIZE) {
- // setPageBefore(pageBefore + 1);
- // } else if (beforeData) {
- // const from = `${beforeData.data.templates[beforeData.data.templates.length - 1]
- // .template_id
- // }`;
- // if (from && updatePosition) updatePosition(from);
- // }
+ if (!beforeData || isReachingStart) return;
+ if (beforeData.data.templates.length < MAX_RESULT_SIZE) {
+ setPageBefore(pageBefore + 1);
+ } else if (beforeData) {
+ const from = `${beforeData.data.templates[beforeData.data.templates.length - 1]
+ .template_id
+ }`;
+ if (from && updatePosition) updatePosition(from);
+ }
},
};
- const templates = !afterData ? [] : (afterData || lastAfter).data.templates;
- // const templates =
- // !beforeData || !afterData
- // ? []
- // : (beforeData || lastBefore).data.templates
- // .slice()
- // .reverse()
- // .concat((afterData || lastAfter).data.templates);
- if (loadingAfter /* || loadingBefore */)
+ // const templates = !afterData ? [] : (afterData || lastAfter).data.templates;
+ const templates =
+ !beforeData || !afterData
+ ? []
+ : (beforeData || lastBefore).data.templates
+ .slice()
+ .reverse()
+ .concat((afterData || lastAfter).data.templates);
+ if (loadingAfter || loadingBefore)
return { loading: true, data: { templates } };
- if (/*beforeData &&*/ afterData) {
+ if (beforeData && afterData) {
return { ok: true, data: { templates }, ...pagination };
}
return { loading: true };
diff --git a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
index c91fff8b4..ad6bf96e2 100644
--- a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
@@ -93,9 +93,11 @@ export function useInstanceWebhooks(
> {
const { webhookFetcher } = useBackendInstanceRequest();
+ const [pageBefore, setPageBefore] = useState(1);
const [pageAfter, setPageAfter] = useState(1);
const totalAfter = pageAfter * PAGE_SIZE;
+ const totalBefore = args?.position ? pageBefore * PAGE_SIZE : 0;
const {
data: afterData,
@@ -120,7 +122,7 @@ export function useInstanceWebhooks(
const isReachingEnd =
afterData && afterData.data.webhooks.length < totalAfter;
- const isReachingStart = false;
+ const isReachingStart = true;
const pagination = {
isReachingEnd,
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
index c29a6fa6e..9f80719a1 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
@@ -21,7 +21,7 @@
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
-import { h, VNode } from "preact";
+import { h, VNode, Fragment } from "preact";
import { useState } from "preact/hooks";
import { DatePicker } from "../../../../components/picker/DatePicker.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
@@ -29,8 +29,6 @@ import { CardTable } from "./Table.js";
import { dateFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
export interface ListPageProps {
- errorOrderId: string | undefined;
-
onShowAll: () => void;
onShowNotPaid: () => void;
onShowPaid: () => void;
@@ -56,17 +54,18 @@ export interface ListPageProps {
onSelectOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void;
onRefundOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void;
- onSearchOrderById: (id: string) => void;
onCreate: () => void;
}
export function ListPage({
+ hasMoreAfter,
+ hasMoreBefore,
+ onLoadMoreAfter,
+ onLoadMoreBefore,
orders,
- errorOrderId,
isAllActive,
onSelectOrder,
onRefundOrder,
- onSearchOrderById,
jumpToDate,
onCopyURL,
onShowAll,
@@ -86,42 +85,10 @@ export function ListPage({
const { i18n } = useTranslationContext();
const dateTooltip = i18n.str`select date to show nearby orders`;
const [pickDate, setPickDate] = useState(false);
- const [orderId, setOrderId] = useState<string>("");
const [settings] = useSettings();
return (
- <section class="section is-main-section">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <div class="field has-addons">
- <div class="control">
- <input
- class={errorOrderId ? "input is-danger" : "input"}
- type="text"
- value={orderId}
- onChange={(e) => setOrderId(e.currentTarget.value)}
- placeholder={i18n.str`order id`}
- />
- {errorOrderId && <p class="help is-danger">{errorOrderId}</p>}
- </div>
- <span
- class="has-tooltip-bottom"
- data-tooltip={i18n.str`jump to order with the given order ID`}
- >
- <button
- class="button"
- onClick={(e) => onSearchOrderById(orderId)}
- >
- <span class="icon">
- <i class="mdi mdi-arrow-right" />
- </span>
- </button>
- </span>
- </div>
- </div>
- </div>
- </div>
+ <Fragment>
<div class="columns">
<div class="column is-two-thirds">
<div class="tabs" style={{ overflow: "inherit" }}>
@@ -249,7 +216,11 @@ export function ListPage({
onCopyURL={onCopyURL}
onSelect={onSelectOrder}
onRefund={onRefundOrder}
+ hasMoreAfter={hasMoreAfter}
+ hasMoreBefore={hasMoreBefore}
+ onLoadMoreAfter={onLoadMoreAfter}
+ onLoadMoreBefore={onLoadMoreBefore}
/>
- </section>
+ </Fragment>
);
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
index 608c9b20d..b2806bb79 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
@@ -140,10 +140,9 @@ function Table({
const [settings] = useSettings();
return (
<div class="table-container">
- {onLoadMoreBefore && (
+ {hasMoreBefore && (
<button
class="button is-fullwidth"
- disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<i18n.Translate>load newer orders</i18n.Translate>
@@ -218,10 +217,9 @@ function Table({
})}
</tbody>
</table>
- {onLoadMoreAfter && (
+ {hasMoreAfter && (
<button
class="button is-fullwidth"
- disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<i18n.Translate>load older orders</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
index 48f77e3d3..34c7d348a 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
@@ -39,6 +39,7 @@ import { Notification } from "../../../../utils/types.js";
import { ListPage } from "./ListPage.js";
import { RefundModal } from "./Table.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { JumpToElementById } from "../../../../components/form/JumpToElementById.js";
interface Props {
onUnauthorized: () => VNode;
@@ -69,9 +70,6 @@ export default function OrderList({
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
- const [errorOrderId, setErrorOrderId] = useState<string | undefined>(
- undefined,
- );
if (result.loading) return <Loading />;
if (!result.ok) {
@@ -100,24 +98,17 @@ export default function OrderList({
? "is-active"
: "";
- async function testIfOrderExistAndSelect(orderId: string): Promise<void> {
- if (!orderId) {
- setErrorOrderId(i18n.str`Enter an order id`);
- return;
- }
- try {
- await getPaymentURL(orderId);
- onSelect(orderId);
- setErrorOrderId(undefined);
- } catch {
- setErrorOrderId(i18n.str`order not found`);
- }
- }
-
return (
- <Fragment>
+ <section class="section is-main-section">
<NotificationCard notification={notif} />
+ <JumpToElementById
+ testIfExist={getPaymentURL}
+ onSelect={onSelect}
+ description={i18n.str`jump to order with the given product ID`}
+ palceholder={i18n.str`order id`}
+ />
+
<ListPage
orders={result.data.orders.map((o) => ({ ...o, id: o.order_id }))}
onLoadMoreBefore={result.loadMorePrev}
@@ -126,7 +117,6 @@ export default function OrderList({
hasMoreAfter={!result.isReachingEnd}
onSelectOrder={(order) => onSelect(order.id)}
onRefundOrder={(value) => setOrderToBeRefunded(value)}
- errorOrderId={errorOrderId}
isAllActive={isAllActive}
isNotWiredActive={isNotWiredActive}
isWiredActive={isWiredActive}
@@ -138,7 +128,6 @@ export default function OrderList({
getPaymentURL(id).then((resp) => copyToClipboard(resp.data))
}
onCreate={onCreate}
- onSearchOrderById={testIfOrderExistAndSelect}
onSelectDate={setNewDate}
onShowAll={() => setFilter({})}
onShowNotPaid={() => setFilter({ paid: "no" })}
@@ -190,7 +179,7 @@ export default function OrderList({
}}
/>
)}
- </Fragment>
+ </section>
);
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
index db73217ed..275f855cb 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
@@ -244,7 +244,10 @@ function Table({
}
style={{ cursor: "pointer" }}
>
+ <span style={{"whiteSpace":"nowrap"}}>
+
{i.total_sold} {i.unit}
+ </span>
</td>
<td class="is-actions-cell right-sticky">
<div class="buttons is-right">
@@ -341,7 +344,7 @@ function FastProductWithInfiniteStockUpdateForm({
<div class="buttons mt-5">
- <button class="button " onClick={onCancel}>
+ <button class="button mt-5" onClick={onCancel}>
<i18n.Translate>Clone</i18n.Translate>
</button>
</div>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
index 274a7c2ea..942b5d0ac 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
@@ -37,6 +37,7 @@ 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;
@@ -74,60 +75,17 @@ export default function ProductList({
return onNotFound();
return onLoadError(result);
}
- const [errorId, setErrorId] = useState<string | undefined>(
- undefined,
- );
-
- const [productId, setProductId] = useState<string>()
- async function testIfProductExistAndSelect(orderId: string | undefined): Promise<void> {
- if (!orderId) {
- setErrorId(i18n.str`Enter a product id`);
- return;
- }
- try {
- await getProduct(orderId);
- onSelect(orderId);
- setErrorId(undefined);
- } catch {
- setErrorId(i18n.str`product not found`);
- }
- }
return (
<section class="section is-main-section">
<NotificationCard notification={notif} />
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <div class="field has-addons">
- <div class="control">
- <input
- class={errorId ? "input is-danger" : "input"}
- type="text"
- value={productId ?? ""}
- onChange={(e) => setProductId(e.currentTarget.value)}
- placeholder={i18n.str`product id`}
- />
- {errorId && <p class="help is-danger">{errorId}</p>}
- </div>
- <span
- class="has-tooltip-bottom"
- data-tooltip={i18n.str`jump to product with the given product ID`}
- >
- <button
- class="button"
- onClick={(e) => testIfProductExistAndSelect(productId)}
- >
- <span class="icon">
- <i class="mdi mdi-arrow-right" />
- </span>
- </button>
- </span>
- </div>
- </div>
- </div>
- </div>
+ <JumpToElementById
+ testIfExist={getProduct}
+ onSelect={onSelect}
+ description={i18n.str`jump to product with the given product ID`}
+ palceholder={i18n.str`product id`}
+ />
<CardTable
instances={result.data}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
index ab1d5ef91..bf6062c34 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
@@ -49,7 +49,6 @@ export function ListPage({
const { i18n } = useTranslationContext();
return (
- <section class="section is-main-section">
<CardTable
templates={templates.map((o) => ({
...o,
@@ -65,6 +64,5 @@ export function ListPage({
onLoadMoreAfter={onLoadMoreAfter}
hasMoreAfter={!onLoadMoreAfter}
/>
- </section>
);
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
index 3bea9abe8..9fdf4ead9 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
@@ -136,11 +136,10 @@ function Table({
const { i18n } = useTranslationContext();
return (
<div class="table-container">
- {onLoadMoreBefore && (
+ {hasMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more templates before the first one`}
- disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<i18n.Translate>load newer templates</i18n.Translate>
@@ -188,7 +187,7 @@ function Table({
data-tooltip={i18n.str`use template to create new order`}
onClick={() => onNewOrder(i)}
>
- New order
+ Use template
</button>
<button
class="button is-info is-small has-tooltip-left"
@@ -204,11 +203,10 @@ function Table({
})}
</tbody>
</table>
- {onLoadMoreAfter && (
+ {hasMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more templates after the last one`}
- disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<i18n.Translate>load older templates</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index 3c9bb231c..b9767442f 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -35,8 +35,9 @@ import {
} from "../../../../hooks/templates.js";
import { Notification } from "../../../../utils/types.js";
import { ListPage } from "./ListPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
import { ConfirmModal } from "../../../../components/modal/index.js";
+import { JumpToElementById } from "../../../../components/form/JumpToElementById.js";
interface Props {
onUnauthorized: () => VNode;
@@ -60,7 +61,7 @@ export default function ListTemplates({
const [position, setPosition] = useState<string | undefined>(undefined);
const { i18n } = useTranslationContext();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { deleteTemplate } = useTemplateAPI();
+ const { deleteTemplate, testTemplateExist } = useTemplateAPI();
const result = useInstanceTemplates({ position }, (id) => setPosition(id));
const [deleting, setDeleting] =
useState<MerchantBackend.Template.TemplateEntry | null>(null);
@@ -81,9 +82,16 @@ export default function ListTemplates({
}
return (
- <Fragment>
+ <section class="section is-main-section">
<NotificationCard notification={notif} />
+ <JumpToElementById
+ testIfExist={testTemplateExist}
+ onSelect={onSelect}
+ description={i18n.str`jump to template with the given template ID`}
+ palceholder={i18n.str`template id`}
+ />
+
<ListPage
templates={result.data.templates}
onLoadMoreBefore={
@@ -139,6 +147,6 @@ export default function ListTemplates({
</p>
</ConfirmModal>
)}
- </Fragment>
+ </section>
);
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
index db2533ecc..02b12c4c2 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
@@ -73,7 +73,7 @@ export function ListPage({
>
<InputSelector
name="payto_uri"
- label={i18n.str`Address`}
+ label={i18n.str`Account URI`}
values={accounts}
placeholder={i18n.str`Select one account`}
tooltip={i18n.str`filter by account address`}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
index 1c464cbc7..b6b1cf328 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
@@ -125,11 +125,10 @@ function Table({
const [settings] = useSettings();
return (
<div class="table-container">
- {onLoadMoreBefore && (
+ {hasMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more transfers before the first one`}
- disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<i18n.Translate>load newer transfers</i18n.Translate>
@@ -198,11 +197,10 @@ function Table({
})}
</tbody>
</table>
- {onLoadMoreAfter && (
+ {hasMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more transfer after the last one`}
- disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<i18n.Translate>load older transfers</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
index 1bc1673ba..0fdbb9bc3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
@@ -21,7 +21,7 @@
import { ErrorType, HttpError } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading.js";
import { MerchantBackend } from "../../../../declaration.js";
import { useInstanceDetails } from "../../../../hooks/instance.js";
@@ -47,7 +47,6 @@ export default function ListTransfer({
onCreate,
onNotFound,
}: Props): VNode {
- const [form, setForm] = useState<Form>({ payto_uri: "" });
const setFilter = (s?: "yes" | "no") => setForm({ ...form, verified: s });
const [position, setPosition] = useState<string | undefined>(undefined);
@@ -56,6 +55,14 @@ export default function ListTransfer({
const accounts = !instance.ok
? []
: instance.data.accounts.map((a) => a.payto_uri);
+ const [form, setForm] = useState<Form>({ payto_uri: "" });
+
+ const shoulUseDefaultAccount = accounts.length === 1
+ useEffect(() => {
+ if (shoulUseDefaultAccount) {
+ setForm({...form, payto_uri: accounts[0]})
+ }
+ }, [shoulUseDefaultAccount])
const isVerifiedTransfers = form.verified === "yes";
const isNonVerifiedTransfers = form.verified === "no";
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
index cebc1ade6..4e813d777 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
@@ -118,6 +118,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
{state.otp_algorithm && state.otp_algorithm > 0 ? (
<Fragment>
<InputWithAddon<Entity>
+ expand
name="otp_key"
label={i18n.str`Device key`}
inputType={showKey ? "text" : "password"}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
index b639a6134..0c28027fe 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
@@ -126,11 +126,10 @@ function Table({
const { i18n } = useTranslationContext();
return (
<div class="table-container">
- {onLoadMoreBefore && (
+ {hasMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more devices before the first one`}
- disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<i18n.Translate>load newer devices</i18n.Translate>
@@ -180,11 +179,10 @@ function Table({
})}
</tbody>
</table>
- {onLoadMoreAfter && (
+ {hasMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more devices after the last one`}
- disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<i18n.Translate>load older devices</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
index 124ced1f1..42a179d2c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
@@ -126,11 +126,10 @@ function Table({
const { i18n } = useTranslationContext();
return (
<div class="table-container">
- {onLoadMoreBefore && (
+ {hasMoreBefore && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more webhooks before the first one`}
- disabled={!hasMoreBefore}
onClick={onLoadMoreBefore}
>
<i18n.Translate>load newer webhooks</i18n.Translate>
@@ -187,11 +186,10 @@ function Table({
})}
</tbody>
</table>
- {onLoadMoreAfter && (
+ {hasMoreAfter && (
<button
class="button is-fullwidth"
data-tooltip={i18n.str`load more webhooks after the last one`}
- disabled={!hasMoreAfter}
onClick={onLoadMoreAfter}
>
<i18n.Translate>load older webhooks</i18n.Translate>
diff --git a/packages/merchant-backoffice-ui/src/paths/login/index.tsx b/packages/merchant-backoffice-ui/src/paths/login/index.tsx
index a9e3c3a1b..e37ef4bef 100644
--- a/packages/merchant-backoffice-ui/src/paths/login/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/login/index.tsx
@@ -26,31 +26,15 @@ import { useBackendContext } from "../../context/backend.js";
import { useInstanceContext } from "../../context/instance.js";
import { AccessToken, LoginToken } from "../../declaration.js";
import { useCredentialsChecker } from "../../hooks/backend.js";
-import { useBackendURL } from "../../hooks/index.js";
interface Props {
onConfirm: (token: LoginToken | undefined) => void;
}
-function getTokenValuePart(t: string): string {
- if (!t) return t;
- const match = /secret-token:(.*)/.exec(t);
- if (!match || !match[1]) return "";
- return match[1];
-}
-
function normalizeToken(r: string): AccessToken {
return `secret-token:${r}` as AccessToken;
}
-function cleanUp(s: string): string {
- let result = s;
- if (result.indexOf("webui/") !== -1) {
- result = result.substring(0, result.indexOf("webui/"));
- }
- return result;
-}
-
export function LoginPage({ onConfirm }: Props): VNode {
const { url: backendURL, changeBackend, resetBackend } = useBackendContext();
const { admin, id } = useInstanceContext();
@@ -245,11 +229,14 @@ function AsyncButton({ onClick, disabled, type = "", children }: { type?: string
export function ConnectionPage({ onConfirm }: { onConfirm: (s: string) => void }): VNode {
const { url: backendURL } = useBackendContext()
- const [url, setURL] = useState(cleanUp(backendURL));
+ const [error, setError] = useState<string>();
+ const [url, setURL] = useState(backendURL ?? "");
const { i18n } = useTranslationContext();
async function doConnect() {
- onConfirm(url)
+ const withHttp = url.startsWith("http") ? url : "https://" + url
+
+ onConfirm(withHttp)
}
return (