aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/components
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2023-08-15 13:47:29 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2023-08-15 13:47:29 +0200
commit819949d7f2cfcce8d334cbfdb701255daac64951 (patch)
tree592043554f59ec209f9afc1c31c20c4fb585c3ca /packages/merchant-backoffice-ui/src/components
parent77ea209ddb6d457db0ce113f91247207cc29fbd8 (diff)
parentadb0e70f151e63d103f27852312319520f4d0a84 (diff)
Merge branch 'master' into age-withdraw
Diffstat (limited to 'packages/merchant-backoffice-ui/src/components')
-rw-r--r--packages/merchant-backoffice-ui/src/components/exception/login.tsx97
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx91
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx64
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/index.tsx14
5 files changed, 234 insertions, 34 deletions
diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
index 42c5e89d0..f2f94a7c5 100644
--- a/packages/merchant-backoffice-ui/src/components/exception/login.tsx
+++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
@@ -20,7 +20,7 @@
*/
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
+import { ComponentChildren, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useBackendContext } from "../../context/backend.js";
import { useInstanceContext } from "../../context/instance.js";
@@ -40,7 +40,7 @@ function getTokenValuePart(t: string): string {
}
function normalizeToken(r: string): string {
- return `secret-token:${encodeURIComponent(r)}`;
+ return `secret-token:${r}`;
}
function cleanUp(s: string): string {
@@ -53,7 +53,7 @@ function cleanUp(s: string): string {
export function LoginModal({ onConfirm, withMessage }: Props): VNode {
const { url: backendUrl, token: baseToken } = useBackendContext();
- const { admin, token: instanceToken } = useInstanceContext();
+ const { admin, token: instanceToken, id } = useInstanceContext();
const testLogin = useCredentialsChecker();
const currentToken = getTokenValuePart(
(!admin ? baseToken : instanceToken) ?? "",
@@ -63,6 +63,78 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
const [url, setURL] = useState(cleanUp(backendUrl));
const { i18n } = useTranslationContext();
+ 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={"set new access token"}
+ name="token"
+ onKeyPress={(e) =>
+ e.keyCode === 13
+ ? onConfirm(url, normalizeToken(token))
+ : null
+ }
+ value={token}
+ onInput={(e): void => setToken(e?.currentTarget.value)}
+ />
+ </p>
+ </div>
+ </div>
+ </div>
+ </section>
+ <footer
+ class="modal-card-foot "
+ style={{
+ justifyContent: "flex-end",
+ border: "1px solid",
+ borderTop: 0,
+ }}
+ >
+ <AsyncButton
+ onClick={async () => {
+ const secretToken = normalizeToken(token);
+ const { valid, cause } = await testLogin(`${url}/instances/${id}`, secretToken);
+ if (valid) {
+ onConfirm(url, secretToken);
+ } else {
+ onConfirm(url);
+ }
+ }}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </footer>
+ </div>
+ </div>
+ </div>)
+ }
+
return (
<div class="columns is-centered" style={{ margin: "auto" }}>
<div class="column is-two-thirds ">
@@ -137,8 +209,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
borderTop: 0,
}}
>
- <button
- class="button is-info"
+ <AsyncButton
onClick={async () => {
const secretToken = normalizeToken(token);
const { valid, cause } = await testLogin(url, secretToken);
@@ -150,10 +221,24 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
}}
>
<i18n.Translate>Confirm</i18n.Translate>
- </button>
+ </AsyncButton>
</footer>
</div>
</div>
</div>
);
}
+
+function AsyncButton({ onClick, children }: { onClick: () => Promise<void>, children: ComponentChildren }): VNode {
+ const [running, setRunning] = useState(false)
+ return <button class="button is-info" disabled={running} onClick={() => {
+ setRunning(true)
+ onClick().then(() => {
+ setRunning(false)
+ }).catch(() => {
+ setRunning(false)
+ })
+ }}>
+ {children}
+ </button>
+}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
new file mode 100644
index 000000000..61ddf3c84
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
@@ -0,0 +1,91 @@
+/*
+ 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 { h, VNode } from "preact";
+import { InputProps, useField } from "./useField.js";
+
+interface Props<T> extends InputProps<T> {
+ name: T;
+ readonly?: boolean;
+ expand?: boolean;
+ threeState?: boolean;
+ toBoolean?: (v?: any) => boolean | undefined;
+ fromBoolean?: (s: boolean | undefined) => any;
+}
+
+const defaultToBoolean = (f?: any): boolean | undefined => f || "";
+const defaultFromBoolean = (v: boolean | undefined): any => v as any;
+
+export function InputToggle<T>({
+ name,
+ readonly,
+ placeholder,
+ tooltip,
+ label,
+ help,
+ threeState,
+ expand,
+ fromBoolean = defaultFromBoolean,
+ toBoolean = defaultToBoolean,
+}: Props<keyof T>): VNode {
+ const { error, value, onChange } = useField<T>(name);
+
+ const onCheckboxClick = (): void => {
+ const c = toBoolean(value);
+ if (c === false && threeState) return onChange(undefined as any);
+ return onChange(fromBoolean(!c));
+ };
+
+ return (
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label" style={{ width: 200 }}>
+ {label}
+ {tooltip && (
+ <span class="icon has-tooltip-right" data-tooltip={tooltip}>
+ <i class="mdi mdi-information" />
+ </span>
+ )}
+ </label>
+ </div>
+ <div class="field-body is-flex-grow-1">
+ <div class="field">
+ <p class={expand ? "control is-expanded" : "control"}>
+ <label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
+ <input
+ type="checkbox"
+ class={toBoolean(value) === undefined ? "is-indeterminate" : "toggle-checkbox"}
+ checked={toBoolean(value)}
+ placeholder={placeholder}
+ readonly={readonly}
+ name={String(name)}
+ disabled={readonly}
+ onChange={onCheckboxClick}
+ />
+ <div class="toggle-switch"></div>
+ </label>
+ {help}
+ </p>
+ {error && <p class="help is-danger">{error}</p>}
+ </div>
+ </div>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
index 9624a2c38..9f1b33893 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
@@ -20,7 +20,6 @@
*/
import { h, VNode } from "preact";
-import { LangSelector } from "./LangSelector.js";
import logo from "../../assets/logo-2021.svg";
interface Props {
@@ -65,7 +64,6 @@ export function NavigationBar({ onMobileMenu, title }: Props): VNode {
</a>
<div class="navbar-end">
<div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
- <LangSelector />
</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 6fee600eb..f3cf80b92 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -31,6 +31,7 @@ const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
interface Props {
onLogout: () => void;
+ onShowSettings: () => void;
mobile?: boolean;
instance: string;
admin?: boolean;
@@ -40,6 +41,7 @@ interface Props {
export function Sidebar({
mobile,
instance,
+ onShowSettings,
onLogout,
admin,
mimic,
@@ -78,21 +80,8 @@ export function Sidebar({
<div class="menu is-menu-main">
{instance ? (
<Fragment>
- <p class="menu-label">
- <i18n.Translate>Instance</i18n.Translate>
- </p>
<ul class="menu-list">
- <li>
- <a href={"/update"} class="has-icon">
- <span class="icon">
- <i class="mdi mdi-square-edit-outline" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Settings</i18n.Translate>
- </span>
- </a>
- </li>
- <li>
+ <li>
<a href={"/orders"} class="has-icon">
<span class="icon">
<i class="mdi mdi-cash-register" />
@@ -132,6 +121,31 @@ export function Sidebar({
</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={"/update"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-square-edit-outline" />
+ </span>
+ <span class="menu-item-label">
+ <i18n.Translate>Account</i18n.Translate>
+ </span>
+ </a>
+ </li>
<li>
<a href={"/reserves"} class="has-icon">
<span class="icon">
@@ -150,16 +164,6 @@ export function Sidebar({
</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>
</Fragment>
) : undefined}
@@ -168,6 +172,18 @@ export function Sidebar({
</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>Settings</i18n.Translate>
+ </span>
+ </a>
+ </li>
+ <li>
<div>
<span style={{ width: "3rem" }} class="icon">
<i class="mdi mdi-currency-eur" />
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
index 56573b8ca..cdbae4ae0 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -75,6 +75,7 @@ interface MenuProps {
instance: string;
admin?: boolean;
onLogout?: () => void;
+ onShowSettings: () => void;
setInstanceName: (s: string) => void;
}
@@ -93,6 +94,7 @@ function WithTitle({
export function Menu({
onLogout,
+ onShowSettings,
title,
instance,
path,
@@ -121,6 +123,7 @@ export function Menu({
{onLogout && (
<Sidebar
+ onShowSettings={onShowSettings}
onLogout={onLogout}
admin={admin}
mimic={mimic}
@@ -130,7 +133,12 @@ export function Menu({
)}
{mimic && (
- <nav class="level">
+ <nav class="level" style={{
+ zIndex: 100,
+ position:"fixed",
+ width:"50%",
+ marginLeft: "20%"
+ }}>
<div class="level-item has-text-centered has-background-warning">
<p class="is-size-5">
You are viewing the instance <b>&quot;{instance}&quot;</b>.{" "}
@@ -154,6 +162,7 @@ export function Menu({
interface NotYetReadyAppMenuProps {
title: string;
onLogout?: () => void;
+ onShowSettings: () => void;
}
interface NotifProps {
@@ -194,6 +203,7 @@ export function NotificationCard({
export function NotYetReadyAppMenu({
onLogout,
+ onShowSettings,
title,
}: NotYetReadyAppMenuProps): VNode {
const [mobileOpen, setMobileOpen] = useState(false);
@@ -212,7 +222,7 @@ export function NotYetReadyAppMenu({
title={title}
/>
{onLogout && (
- <Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} />
+ <Sidebar onShowSettings={onShowSettings} onLogout={onLogout} instance="" mobile={mobileOpen} />
)}
</div>
);