diff options
author | Sebastian <sebasjm@gmail.com> | 2023-08-07 06:51:10 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-08-07 08:14:44 -0300 |
commit | 7d1621767cec57e9e7217f620844655279ab1af5 (patch) | |
tree | f171293fc160dd9447e7f7f8a6bfb6917ac98e19 | |
parent | 8eb0183c784865225569da1fa1f91a8038693405 (diff) |
ui settings view
10 files changed, 118 insertions, 59 deletions
diff --git a/packages/merchant-backoffice-ui/src/Application.tsx b/packages/merchant-backoffice-ui/src/Application.tsx index 23510c456..f6a81ff8d 100644 --- a/packages/merchant-backoffice-ui/src/Application.tsx +++ b/packages/merchant-backoffice-ui/src/Application.tsx @@ -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`, diff --git a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx index 7731dac88..277c2b176 100644 --- a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx +++ b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx @@ -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.`, diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx index cb4abdd40..1547442ea 100644 --- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx +++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx @@ -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 */} diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx b/packages/merchant-backoffice-ui/src/components/exception/login.tsx index 984b6fe06..f2f94a7c5 100644 --- a/packages/merchant-backoffice-ui/src/components/exception/login.tsx +++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx @@ -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) 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 2beaf6956..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} @@ -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> ); 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 56d9dda74..37770d273 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 @@ -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); }} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx index 4dde202c4..e20b9bc27 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx @@ -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.`} /> diff --git a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx new file mode 100644 index 000000000..1d0b4128a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx @@ -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> +}
\ No newline at end of file |