aboutsummaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/components/menu
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-10-24 10:46:14 +0200
committerFlorian Dold <florian@dold.me>2022-10-24 10:46:14 +0200
commit3e060b80428943c6562250a6ff77eff10a0259b7 (patch)
treed08472bc5ca28621c62ac45b229207d8215a9ea7 /packages/merchant-backoffice-ui/src/components/menu
parentfb52ced35ac872349b0e1062532313662552ff6c (diff)
downloadwallet-core-3e060b80428943c6562250a6ff77eff10a0259b7.tar.xz
repo: integrate packages from former merchant-backoffice.git
Diffstat (limited to 'packages/merchant-backoffice-ui/src/components/menu')
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx73
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx58
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx227
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/index.tsx210
4 files changed, 568 insertions, 0 deletions
diff --git a/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx
new file mode 100644
index 000000000..41d08a58b
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/menu/LangSelector.tsx
@@ -0,0 +1,73 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { useState } from "preact/hooks";
+import langIcon from '../../assets/icons/languageicon.svg';
+import { useTranslationContext } from "../../context/translation";
+import { strings as messages } from '../../i18n/strings'
+
+type LangsNames = {
+ [P in keyof typeof messages]: string
+}
+
+const names: LangsNames = {
+ es: 'Español [es]',
+ en: 'English [en]',
+ fr: 'Français [fr]',
+ de: 'Deutsch [de]',
+ sv: 'Svenska [sv]',
+ it: 'Italiano [it]',
+}
+
+function getLangName(s: keyof LangsNames | string) {
+ if (names[s]) return names[s]
+ return s
+}
+
+export function LangSelector(): VNode {
+ const [updatingLang, setUpdatingLang] = useState(false)
+ const { lang, changeLanguage } = useTranslationContext()
+
+ return <div class="dropdown is-active ">
+ <div class="dropdown-trigger">
+ <button class="button has-tooltip-left"
+ data-tooltip="change language selection"
+ aria-haspopup="true"
+ aria-controls="dropdown-menu" onClick={() => setUpdatingLang(!updatingLang)}>
+ <div class="icon is-small is-left">
+ <img src={langIcon} />
+ </div>
+ <span>{getLangName(lang)}</span>
+ <div class="icon is-right">
+ <i class="mdi mdi-chevron-down" />
+ </div>
+ </button>
+ </div>
+ {updatingLang && <div class="dropdown-menu" id="dropdown-menu" role="menu">
+ <div class="dropdown-content">
+ {Object.keys(messages)
+ .filter((l) => l !== lang)
+ .map(l => <a key={l} class="dropdown-item" value={l} onClick={() => { changeLanguage(l); setUpdatingLang(false) }}>{getLangName(l)}</a>)}
+ </div>
+ </div>}
+ </div>
+} \ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
new file mode 100644
index 000000000..e1bb4c7c0
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/menu/NavigationBar.tsx
@@ -0,0 +1,58 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 logo from '../../assets/logo.jpeg';
+import { LangSelector } from './LangSelector';
+
+interface Props {
+ onMobileMenu: () => void;
+ title: string;
+}
+
+export function NavigationBar({ onMobileMenu, title }: Props): VNode {
+ return (<nav class="navbar is-fixed-top" role="navigation" aria-label="main navigation">
+ <div class="navbar-brand">
+ <span class="navbar-item" style={{ fontSize: 24, fontWeight: 900 }}>{title}</span>
+
+ <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" onClick={(e) => {
+ onMobileMenu()
+ e.stopPropagation()
+ }}>
+ <span aria-hidden="true" />
+ <span aria-hidden="true" />
+ <span aria-hidden="true" />
+ </a>
+ </div>
+
+ <div class="navbar-menu ">
+ <a class="navbar-start is-justify-content-center is-flex-grow-1" href="https://taler.net">
+ <img src={logo} style={{ height: 50, maxHeight: 50 }} />
+ </a>
+ <div class="navbar-end">
+ <div class="navbar-item" style={{ paddingTop: 4, paddingBottom: 4 }}>
+ <LangSelector />
+ </div>
+ </div>
+ </div>
+ </nav>
+ );
+} \ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
new file mode 100644
index 000000000..e9c5ef8ae
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -0,0 +1,227 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { Fragment, h, VNode } from "preact";
+import { useCallback } from "preact/hooks";
+import { useBackendContext } from "../../context/backend";
+import { useConfigContext } from "../../context/config";
+import { useInstanceContext } from "../../context/instance";
+import { useInstanceKYCDetails } from "../../hooks/instance";
+import { Translate } from "../../i18n";
+import { LangSelector } from "./LangSelector";
+
+interface Props {
+ onLogout: () => void;
+ mobile?: boolean;
+ instance: string;
+ admin?: boolean;
+ mimic?: boolean;
+}
+
+export function Sidebar({
+ mobile,
+ instance,
+ onLogout,
+ admin,
+ mimic,
+}: Props): VNode {
+ const config = useConfigContext();
+ const backend = useBackendContext();
+
+ const kycStatus = useInstanceKYCDetails();
+ const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
+ // const withInstanceIdIfNeeded = useCallback(function (path: string) {
+ // if (mimic) {
+ // return path + '?instance=' + instance
+ // }
+ // return path
+ // },[instance])
+
+ return (
+ <aside class="aside is-placed-left is-expanded">
+ {mobile && (
+ <div
+ class="footer"
+ onClick={(e) => {
+ return e.stopImmediatePropagation();
+ }}
+ >
+ <LangSelector />
+ </div>
+ )}
+ <div class="aside-tools">
+ <div class="aside-tools-label">
+ <div>
+ <b>Taler</b> Backoffice
+ </div>
+ <div
+ class="is-size-7 has-text-right"
+ style={{ lineHeight: 0, marginTop: -10 }}
+ >
+ {process.env.__VERSION__} ({config.version})
+ </div>
+ </div>
+ </div>
+ <div class="menu is-menu-main">
+ {instance ? (
+ <Fragment>
+ <p class="menu-label">
+ <Translate>Instance</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">
+ <Translate>Settings</Translate>
+ </span>
+ </a>
+ </li>
+ <li>
+ <a href={"/orders"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-cash-register" />
+ </span>
+ <span class="menu-item-label">
+ <Translate>Orders</Translate>
+ </span>
+ </a>
+ </li>
+ <li>
+ <a href={"/products"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-shopping" />
+ </span>
+ <span class="menu-item-label">
+ <Translate>Products</Translate>
+ </span>
+ </a>
+ </li>
+ <li>
+ <a href={"/transfers"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-bank" />
+ </span>
+ <span class="menu-item-label">
+ <Translate>Transfers</Translate>
+ </span>
+ </a>
+ </li>
+ <li>
+ <a href={"/reserves"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-cash" />
+ </span>
+ <span class="menu-item-label">Reserves</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}
+ <p class="menu-label">
+ <Translate>Connection</Translate>
+ </p>
+ <ul class="menu-list">
+ <li>
+ <div>
+ <span style={{ width: "3rem" }} class="icon">
+ <i class="mdi mdi-currency-eur" />
+ </span>
+ <span class="menu-item-label">{config.currency}</span>
+ </div>
+ </li>
+ <li>
+ <div>
+ <span style={{ width: "3rem" }} class="icon">
+ <i class="mdi mdi-web" />
+ </span>
+ <span class="menu-item-label">
+ {new URL(backend.url).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">
+ <Translate>Instances</Translate>
+ </p>
+ <li>
+ <a href={"/instance/new"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-plus" />
+ </span>
+ <span class="menu-item-label">
+ <Translate>New</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">
+ <Translate>List</Translate>
+ </span>
+ </a>
+ </li>
+ </Fragment>
+ )}
+ <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">
+ <Translate>Log out</Translate>
+ </span>
+ </a>
+ </li>
+ </ul>
+ </div>
+ </aside>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
new file mode 100644
index 000000000..0a621af56
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -0,0 +1,210 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { ComponentChildren, Fragment, h, VNode } from "preact";
+import Match from "preact-router/match";
+import { useEffect, useState } from "preact/hooks";
+import { AdminPaths } from "../../AdminRoutes";
+import { InstancePaths } from "../../InstanceRoutes";
+import { Notification } from "../../utils/types";
+import { NavigationBar } from "./NavigationBar";
+import { Sidebar } from "./SideBar";
+
+function getInstanceTitle(path: string, id: string): string {
+ switch (path) {
+ case InstancePaths.update:
+ return `${id}: Settings`;
+ case InstancePaths.order_list:
+ return `${id}: Orders`;
+ case InstancePaths.order_new:
+ return `${id}: New order`;
+ case InstancePaths.product_list:
+ return `${id}: Products`;
+ case InstancePaths.product_new:
+ return `${id}: New product`;
+ case InstancePaths.product_update:
+ return `${id}: Update product`;
+ case InstancePaths.reserves_new:
+ return `${id}: New reserve`;
+ case InstancePaths.reserves_list:
+ return `${id}: Reserves`;
+ case InstancePaths.transfers_list:
+ return `${id}: Transfers`;
+ case InstancePaths.transfers_new:
+ return `${id}: New transfer`;
+ default:
+ return "";
+ }
+}
+
+function getAdminTitle(path: string, instance: string) {
+ if (path === AdminPaths.new_instance) return `New instance`;
+ if (path === AdminPaths.list_instances) return `Instances`;
+ return getInstanceTitle(path, instance);
+}
+
+interface MenuProps {
+ title?: string;
+ instance: string;
+ admin?: boolean;
+ onLogout?: () => void;
+ setInstanceName: (s: string) => void;
+}
+
+function WithTitle({
+ title,
+ children,
+}: {
+ title: string;
+ children: ComponentChildren;
+}): VNode {
+ useEffect(() => {
+ document.title = `Taler Backoffice: ${title}`;
+ }, [title]);
+ return <Fragment>{children}</Fragment>;
+}
+
+export function Menu({
+ onLogout,
+ title,
+ instance,
+ admin,
+ setInstanceName,
+}: MenuProps): VNode {
+ const [mobileOpen, setMobileOpen] = useState(false);
+
+ return (
+ <Match>
+ {({ path }: any) => {
+ const titleWithSubtitle = title
+ ? title
+ : !admin
+ ? getInstanceTitle(path, instance)
+ : getAdminTitle(path, instance);
+ const adminInstance = instance === "default";
+ const mimic = admin && !adminInstance;
+ return (
+ <WithTitle title={titleWithSubtitle}>
+ <div
+ class={mobileOpen ? "has-aside-mobile-expanded" : ""}
+ onClick={() => setMobileOpen(false)}
+ >
+ <NavigationBar
+ onMobileMenu={() => setMobileOpen(!mobileOpen)}
+ title={titleWithSubtitle}
+ />
+
+ {onLogout && (
+ <Sidebar
+ onLogout={onLogout}
+ admin={admin}
+ mimic={mimic}
+ instance={instance}
+ mobile={mobileOpen}
+ />
+ )}
+
+ {mimic && (
+ <nav class="level">
+ <div class="level-item has-text-centered has-background-warning">
+ <p class="is-size-5">
+ You are viewing the instance <b>"{instance}"</b>.{" "}
+ <a
+ href="#/instances"
+ onClick={(e) => {
+ setInstanceName("default");
+ }}
+ >
+ go back
+ </a>
+ </p>
+ </div>
+ </nav>
+ )}
+ </div>
+ </WithTitle>
+ );
+ }}
+ </Match>
+ );
+}
+
+interface NotYetReadyAppMenuProps {
+ title: string;
+ onLogout?: () => void;
+}
+
+interface NotifProps {
+ notification?: Notification;
+}
+export function NotificationCard({
+ notification: n,
+}: NotifProps): VNode | null {
+ if (!n) return null;
+ return (
+ <div class="notification">
+ <div class="columns is-vcentered">
+ <div class="column is-12">
+ <article
+ class={
+ n.type === "ERROR"
+ ? "message is-danger"
+ : n.type === "WARN"
+ ? "message is-warning"
+ : "message is-info"
+ }
+ >
+ <div class="message-header">
+ <p>{n.message}</p>
+ </div>
+ {n.description && (
+ <div class="message-body">
+ <div>{n.description}</div>
+ {n.details && <pre>{n.details}</pre>}
+ </div>
+ )}
+ </article>
+ </div>
+ </div>
+ </div>
+ );
+}
+
+export function NotYetReadyAppMenu({
+ onLogout,
+ title,
+}: NotYetReadyAppMenuProps): VNode {
+ const [mobileOpen, setMobileOpen] = useState(false);
+
+ useEffect(() => {
+ document.title = `Taler Backoffice: ${title}`;
+ }, [title]);
+
+ return (
+ <div
+ class={mobileOpen ? "has-aside-mobile-expanded" : ""}
+ onClick={() => setMobileOpen(false)}
+ >
+ <NavigationBar
+ onMobileMenu={() => setMobileOpen(!mobileOpen)}
+ title={title}
+ />
+ {onLogout && (
+ <Sidebar onLogout={onLogout} instance="" mobile={mobileOpen} />
+ )}
+ </div>
+ );
+}