ui settings view
This commit is contained in:
parent
8eb0183c78
commit
7d1621767c
@ -26,7 +26,7 @@ import {
|
||||
} from "@gnu-taler/web-util/browser";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { route } from "preact-router";
|
||||
import { useMemo } from "preact/hooks";
|
||||
import { useMemo, useState } from "preact/hooks";
|
||||
import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
|
||||
import { Loading } from "./components/exception/loading.js";
|
||||
import {
|
||||
@ -42,6 +42,7 @@ import { useBackendConfig } from "./hooks/backend.js";
|
||||
import { strings } from "./i18n/strings.js";
|
||||
import LoginPage from "./paths/login/index.js";
|
||||
import { HttpStatusCode } from "@gnu-taler/taler-util";
|
||||
import { Settings } from "./paths/settings/index.js";
|
||||
|
||||
export function Application(): VNode {
|
||||
return (
|
||||
@ -70,10 +71,19 @@ function ApplicationStatusRoutes(): VNode {
|
||||
: { currency: "unknown", version: "unknown" };
|
||||
const ctx = useMemo(() => ({ currency, version }), [currency, version]);
|
||||
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
|
||||
if (showSettings) {
|
||||
return <Fragment>
|
||||
<NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" />
|
||||
<Settings />
|
||||
</Fragment>
|
||||
}
|
||||
|
||||
if (!triedToLog) {
|
||||
return (
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Welcome!" />
|
||||
<NotYetReadyAppMenu title="Welcome!" onShowSettings={() => setShowSettings(true)} />
|
||||
<LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
|
||||
</Fragment>
|
||||
);
|
||||
@ -87,7 +97,7 @@ function ApplicationStatusRoutes(): VNode {
|
||||
) {
|
||||
return (
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Login" />
|
||||
<NotYetReadyAppMenu title="Login" onShowSettings={() => setShowSettings(true)} />
|
||||
<LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
|
||||
</Fragment>
|
||||
);
|
||||
@ -98,7 +108,7 @@ function ApplicationStatusRoutes(): VNode {
|
||||
) {
|
||||
return (
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Error" />
|
||||
<NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
|
||||
<NotificationCard
|
||||
notification={{
|
||||
message: i18n.str`Server not found`,
|
||||
@ -112,7 +122,7 @@ function ApplicationStatusRoutes(): VNode {
|
||||
}
|
||||
if (result.type === ErrorType.SERVER) {
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Error" />
|
||||
<NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
|
||||
<NotificationCard
|
||||
notification={{
|
||||
message: i18n.str`Server response with an error code`,
|
||||
@ -125,7 +135,7 @@ function ApplicationStatusRoutes(): VNode {
|
||||
}
|
||||
if (result.type === ErrorType.UNREADABLE) {
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Error" />
|
||||
<NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
|
||||
<NotificationCard
|
||||
notification={{
|
||||
message: i18n.str`Response from server is unreadable, http status: ${result.status}`,
|
||||
@ -138,7 +148,7 @@ function ApplicationStatusRoutes(): VNode {
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Error" />
|
||||
<NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
|
||||
<NotificationCard
|
||||
notification={{
|
||||
message: i18n.str`Unexpected Error`,
|
||||
|
@ -33,6 +33,7 @@ import { InstanceRoutes } from "./InstanceRoutes.js";
|
||||
import LoginPage from "./paths/login/index.js";
|
||||
import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
|
||||
import { HttpStatusCode } from "@gnu-taler/taler-util";
|
||||
import { Settings } from "./paths/settings/index.js";
|
||||
|
||||
export function ApplicationReadyRoutes(): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
@ -48,8 +49,15 @@ export function ApplicationReadyRoutes(): VNode {
|
||||
clearAllTokens();
|
||||
route("/");
|
||||
};
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
|
||||
if (result.loading) return <NotYetReadyAppMenu title="Loading..." />;
|
||||
if (showSettings) {
|
||||
return <Fragment>
|
||||
<NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" onLogout={clearTokenAndGoToRoot} />
|
||||
<Settings/>
|
||||
</Fragment>
|
||||
}
|
||||
if (result.loading) return <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Loading..." />;
|
||||
|
||||
let admin = true;
|
||||
let instanceNameByBackendURL;
|
||||
@ -61,7 +69,7 @@ export function ApplicationReadyRoutes(): VNode {
|
||||
) {
|
||||
return (
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Login" onLogout={clearTokenAndGoToRoot} />
|
||||
<NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Login" onLogout={clearTokenAndGoToRoot} />
|
||||
<NotificationCard
|
||||
notification={{
|
||||
message: i18n.str`Access denied`,
|
||||
@ -81,7 +89,7 @@ export function ApplicationReadyRoutes(): VNode {
|
||||
// does not match our pattern
|
||||
return (
|
||||
<Fragment>
|
||||
<NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} />
|
||||
<NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Error" onLogout={clearTokenAndGoToRoot} />
|
||||
<NotificationCard
|
||||
notification={{
|
||||
message: i18n.str`Couldn't access the server.`,
|
||||
|
@ -68,6 +68,7 @@ import LoginPage from "./paths/login/index.js";
|
||||
import NotFoundPage from "./paths/notfound/index.js";
|
||||
import { Notification } from "./utils/types.js";
|
||||
import { MerchantBackend } from "./declaration.js";
|
||||
import { Settings } from "./paths/settings/index.js";
|
||||
|
||||
export enum InstancePaths {
|
||||
// details = '/',
|
||||
@ -100,6 +101,8 @@ export enum InstancePaths {
|
||||
webhooks_list = "/webhooks",
|
||||
webhooks_update = "/webhooks/:tid/update",
|
||||
webhooks_new = "/webhooks/new",
|
||||
|
||||
settings = "/settings",
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
@ -240,6 +243,9 @@ export function InstanceRoutes({
|
||||
<Menu
|
||||
instance={id}
|
||||
admin={admin}
|
||||
onShowSettings={() => {
|
||||
route("/settings")
|
||||
}}
|
||||
path={path}
|
||||
onLogout={clearTokenAndGoToRoot}
|
||||
setInstanceName={setInstanceName}
|
||||
@ -558,6 +564,7 @@ export function InstanceRoutes({
|
||||
}}
|
||||
/>
|
||||
<Route path={InstancePaths.kyc} component={ListKYCPage} />
|
||||
<Route path={InstancePaths.settings} component={Settings} />
|
||||
{/**
|
||||
* Example pages
|
||||
*/}
|
||||
|
@ -229,7 +229,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
function AsyncButton({onClick, children}:{onClick: () => Promise<void>, children: ComponentChildren}):VNode {
|
||||
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)
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
@ -167,6 +171,18 @@ export function Sidebar({
|
||||
<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>Settings</i18n.Translate>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<span style={{ width: "3rem" }} class="icon">
|
||||
|
@ -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}
|
||||
@ -159,6 +162,7 @@ export function Menu({
|
||||
interface NotYetReadyAppMenuProps {
|
||||
title: string;
|
||||
onLogout?: () => void;
|
||||
onShowSettings: () => void;
|
||||
}
|
||||
|
||||
interface NotifProps {
|
||||
@ -199,6 +203,7 @@ export function NotificationCard({
|
||||
|
||||
export function NotYetReadyAppMenu({
|
||||
onLogout,
|
||||
onShowSettings,
|
||||
title,
|
||||
}: NotYetReadyAppMenuProps): VNode {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
@ -217,7 +222,7 @@ export function NotYetReadyAppMenu({
|
||||
title={title}
|
||||
/>
|
||||
{onLogout && (
|
||||
<Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} />
|
||||
<Sidebar onShowSettings={onShowSettings} onLogout={onLogout} instance="" mobile={mobileOpen} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -164,7 +164,7 @@ export function ListPage({
|
||||
<div class="field has-addons">
|
||||
{jumpToDate && (
|
||||
<div class="control">
|
||||
<a class="button" onClick={() => onSelectDate(undefined)}>
|
||||
<a class="button is-fullwidth" onClick={() => onSelectDate(undefined)}>
|
||||
<span
|
||||
class="icon"
|
||||
data-tooltip={i18n.str`clear date filter`}
|
||||
@ -191,7 +191,7 @@ export function ListPage({
|
||||
<div class="control">
|
||||
<span class="has-tooltip-left" data-tooltip={dateTooltip}>
|
||||
<a
|
||||
class="button"
|
||||
class="button is-fullwidth"
|
||||
onClick={() => {
|
||||
setPickDate(true);
|
||||
}}
|
||||
|
@ -85,34 +85,34 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
|
||||
template_contract: !state.template_contract
|
||||
? undefined
|
||||
: undefinedIfEmpty({
|
||||
amount: !state.template_contract?.amount
|
||||
? undefined
|
||||
: !parsedPrice
|
||||
amount: !state.template_contract?.amount
|
||||
? undefined
|
||||
: !parsedPrice
|
||||
? i18n.str`not valid`
|
||||
: Amounts.isZero(parsedPrice)
|
||||
? i18n.str`must be greater than 0`
|
||||
: undefined,
|
||||
minimum_age:
|
||||
state.template_contract.minimum_age < 0
|
||||
? i18n.str`should be greater that 0`
|
||||
? i18n.str`must be greater than 0`
|
||||
: undefined,
|
||||
pay_duration: !state.template_contract.pay_duration
|
||||
? i18n.str`can't be empty`
|
||||
: state.template_contract.pay_duration.d_us === "forever"
|
||||
minimum_age:
|
||||
state.template_contract.minimum_age < 0
|
||||
? i18n.str`should be greater that 0`
|
||||
: undefined,
|
||||
pay_duration: !state.template_contract.pay_duration
|
||||
? i18n.str`can't be empty`
|
||||
: state.template_contract.pay_duration.d_us === "forever"
|
||||
? undefined
|
||||
: state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second
|
||||
? i18n.str`to short`
|
||||
: undefined,
|
||||
} as Partial<MerchantTemplateContractDetails>),
|
||||
? i18n.str`to short`
|
||||
: undefined,
|
||||
} as Partial<MerchantTemplateContractDetails>),
|
||||
pos_key: !state.pos_key
|
||||
? !state.pos_algorithm
|
||||
? undefined
|
||||
: i18n.str`required`
|
||||
: !isBase32RFC3548Charset(state.pos_key)
|
||||
? i18n.str`just letters and numbers from 2 to 7`
|
||||
: state.pos_key.length !== 32
|
||||
? i18n.str`size of the key should be 32`
|
||||
: undefined,
|
||||
? i18n.str`just letters and numbers from 2 to 7`
|
||||
: state.pos_key.length !== 32
|
||||
? i18n.str`size of the key should be 32`
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const hasErrors = Object.keys(errors).some(
|
||||
@ -139,7 +139,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
|
||||
>
|
||||
<InputWithAddon<Entity>
|
||||
name="template_id"
|
||||
addonBefore={`${backend.url}/instances/templates/`}
|
||||
help={`${backend.url}/instances/templates/${state.template_id ?? ""}`}
|
||||
label={i18n.str`Identifier`}
|
||||
tooltip={i18n.str`Name of the template in URLs.`}
|
||||
/>
|
||||
|
15
packages/merchant-backoffice-ui/src/paths/settings/index.tsx
Normal file
15
packages/merchant-backoffice-ui/src/paths/settings/index.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { VNode, h } from "preact";
|
||||
|
||||
export function Settings(): VNode {
|
||||
return <div>
|
||||
<section class="section is-main-section">
|
||||
<div class="columns">
|
||||
<div class="column" />
|
||||
<div class="column is-four-fifths">
|
||||
settings view
|
||||
</div>
|
||||
<div class="column" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
}
|
Loading…
Reference in New Issue
Block a user