gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 02/03: aml ui


From: gnunet
Subject: [taler-wallet-core] 02/03: aml ui
Date: Mon, 06 Nov 2023 15:55:26 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit 4ee903eb5e639fa0e122a689cc431e8f18ca1197
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Nov 6 11:54:45 2023 -0300

    aml ui
---
 packages/aml-backoffice-ui/src/App.tsx             |  15 +-
 packages/aml-backoffice-ui/src/Dashboard.tsx       | 564 +++++----------------
 packages/aml-backoffice-ui/src/assets/home.svg     |   3 +
 packages/aml-backoffice-ui/src/assets/people.svg   |   3 +
 packages/aml-backoffice-ui/src/hooks/useCases.ts   |  11 +-
 .../aml-backoffice-ui/src/hooks/useSettings.ts     |  70 +++
 packages/aml-backoffice-ui/src/pages.ts            |  52 +-
 packages/aml-backoffice-ui/src/pages/Cases.tsx     |  12 +-
 .../aml-backoffice-ui/src/pages/CreateAccount.tsx  |  36 +-
 .../src/pages/HandleAccountNotReady.tsx            |   3 +
 packages/aml-backoffice-ui/src/pages/Home.tsx      |   5 -
 packages/aml-backoffice-ui/src/pages/Officer.tsx   |  10 +-
 packages/aml-backoffice-ui/src/pages/Settings.tsx  |   5 -
 .../aml-backoffice-ui/src/pages/UnlockAccount.tsx  |  19 +-
 packages/aml-backoffice-ui/src/pages/Welcome.tsx   |   9 -
 packages/aml-backoffice-ui/src/route.ts            |  17 +-
 packages/aml-backoffice-ui/src/settings.ts         |   2 +-
 packages/aml-backoffice-ui/src/utils/Loading.tsx   |  43 --
 packages/aml-backoffice-ui/tailwind.config.js      |   8 +-
 19 files changed, 306 insertions(+), 581 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/App.tsx 
b/packages/aml-backoffice-ui/src/App.tsx
index 0e29279ff..52c86c273 100644
--- a/packages/aml-backoffice-ui/src/App.tsx
+++ b/packages/aml-backoffice-ui/src/App.tsx
@@ -1,9 +1,14 @@
 import { TranslationProvider } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
-import { ExchangeAmlFrame, Main } from "./Dashboard.js";
+import { ExchangeAmlFrame } from "./Dashboard.js";
 import "./scss/main.css";
 import { ExchangeApiProvider } from "./context/config.js";
 import { getInitialBackendBaseURL } from "./hooks/useBackend.js";
+import { Router } from "./route.js";
+import { Pages } from "./pages.js";
+
+const pageList = Object.values(Pages);
+
 
 export function App(): VNode {
   const baseUrl = getInitialBackendBaseURL();
@@ -11,7 +16,13 @@ export function App(): VNode {
     <TranslationProvider source={{}}>
       <ExchangeApiProvider baseUrl={baseUrl} frameOnError={ExchangeAmlFrame}>
         <ExchangeAmlFrame>
-          <Main />
+          <Router
+            pageList={pageList}
+            onNotFound={() => {
+              window.location.href = Pages.cases.url
+              return <div>not found</div>;
+            }}
+          />
         </ExchangeAmlFrame>
       </ExchangeApiProvider>
     </TranslationProvider>
diff --git a/packages/aml-backoffice-ui/src/Dashboard.tsx 
b/packages/aml-backoffice-ui/src/Dashboard.tsx
index bd8a48c45..5d86836d4 100644
--- a/packages/aml-backoffice-ui/src/Dashboard.tsx
+++ b/packages/aml-backoffice-ui/src/Dashboard.tsx
@@ -1,13 +1,17 @@
-import { useNotifications } from "@gnu-taler/web-util/browser";
+import { Footer, GlobalNotificationsBanner, Header, LangSelector, notifyError, 
notifyException, useNotifications, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Dialog, Transition } from "@headlessui/react";
 import { UserIcon, XCircleIcon } from "@heroicons/react/20/solid";
 import { CheckCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
 import { InformationCircleIcon } from "@heroicons/react/24/solid";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
+import { useEffect, useErrorBoundary, useState } from "preact/hooks";
 import logo from "./assets/logo-2021.svg";
 import { Pages } from "./pages.js";
-import { Router, useCurrentLocation } from "./route.js";
+import { PageEntry, Router, useCurrentLocation } from "./route.js";
+import { uiSettings } from "./settings.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
+import { useOfficer } from "./hooks/useOfficer.js";
+import { getAllBooleanSettings, getLabelForSetting, useSettings } from 
"./hooks/useSettings.js";
 
 function classNames(...classes: string[]) {
   return classes.filter(Boolean).join(" ");
@@ -78,6 +82,7 @@ const versionText = VERSION
  * writing text with the correct format
  */
 
+const pageList = Object.values(Pages);
 function LeftMenu() {
   const currentLocation = useCurrentLocation(pageList);
 
@@ -88,9 +93,9 @@ function LeftMenu() {
           <ul role="list" class="-mx-2 space-y-1">
             <li>
               <a
-                href={Pages.info.url}
+                href={Pages.cases.url}
                 class={classNames(
-                  Pages.info.url === currentLocation?.path
+                  Pages.cases.url === currentLocation?.path
                     ? "bg-indigo-700 text-white"
                     : "text-indigo-200 hover:text-white hover:bg-indigo-700",
                   "group flex gap-x-3 rounded-md p-2 text-sm leading-6 
font-semibold",
@@ -98,7 +103,7 @@ function LeftMenu() {
               >
                 <InformationCircleIcon
                   class={classNames(
-                    Pages.info.url === currentLocation?.path
+                    Pages.cases.url === currentLocation?.path
                       ? "text-white"
                       : "text-indigo-200 group-hover:text-white",
                     "h-6 w-6 shrink-0",
@@ -132,472 +137,143 @@ function LeftMenu() {
             </li>
           </ul>
         </li>
-        {/* <li>
-          <div class="text-xs font-semibold leading-6 text-indigo-200">
-            Info
-          </div>
-          <ul role="list" class="-mx-2 mt-2 space-y-1">
-            <li>
-              <a
-                href={Pages.info.url}
-                class={classNames(
-                  Pages.info.url === currentLocation?.path
-                    ? "bg-indigo-700 text-white"
-                    : "text-indigo-200 hover:text-white hover:bg-indigo-700",
-                  "group flex gap-x-3 rounded-md p-2 text-sm leading-6 
font-semibold",
-                )}
-              >
-                <span class="flex h-6 w-6 shrink-0 items-center justify-center 
rounded-lg border border-indigo-400 bg-indigo-500 text-[0.625rem] font-medium 
text-white">
-                  asd
-                </span>
-                <span class="truncate">qwe</span>
-              </a>
-            </li>
-          </ul>
-        </li> */}
-        {/* <li class="mt-auto">
-          <a
-            href={Pages.settings.url}
-            class={classNames(
-              Pages.settings.url === currentLocation?.path
-                ? "bg-indigo-700 text-white"
-                : "text-indigo-200 hover:text-white hover:bg-indigo-700",
-              "group -mx-2 flex gap-x-3 rounded-md p-2 text-sm leading-6 
font-semibold",
-            )}
-          >
-            <Cog6ToothIcon
-              class={classNames(
-                Pages.officer.url === currentLocation?.path
-                  ? "text-white"
-                  : "text-indigo-200 group-hover:text-white",
-                "h-6 w-6 shrink-0",
-              )}
-              aria-hidden="true"
-            />
-            Settings
-          </a>
-        </li> */}
       </ul>
     </nav>
   );
 }
 
+
 export function ExchangeAmlFrame({
   children,
 }: {
   children?: ComponentChildren;
 }): VNode {
-  const [sidebarOpen, setSidebarOpen] = useState(false);
+  const { i18n } = useTranslationContext();
 
-  return (
-    <Fragment>
-      <NavigationBar isOpen={sidebarOpen} setOpen={setSidebarOpen}>
-        <div class="flex grow flex-col gap-y-5 overflow-y-auto bg-indigo-600 
px-6 pb-4">
-          <div class="flex h-16 shrink-0 items-center">
-            <header class="flex items-center justify-between border-b 
border-white/5 ">
-              <h1 class="text-base font-semibold leading-7 text-white">
-                Exchange AML Backoffice
-              </h1>
-            </header>
-          </div>
-          <LeftMenu />
-          <Footer />
-        </div>
-      </NavigationBar>
-      <div class="lg:pl-72">
-        <TopBar
-          onOpenSidebar={() => {
-            setSidebarOpen(true);
-          }}
-        />
-        <Notifications />
-        {children}
-      </div>
-    </Fragment>
-  );
-}
+  const [error, resetError] = useErrorBoundary();
+
+  useEffect(() => {
+    if (error) {
+      if (error instanceof Error) {
+        notifyException(i18n.str`Internal error, please report.`, error)
+      } else {
+        notifyError(i18n.str`Internal error, please report.`, String(error) as 
TranslatedString)
+      }
+      resetError()
+    }
+  }, [error])
 
-export function Main(): VNode {
-  return <main class="py-10 px-4 sm:px-6 lg:px-8">
-    <div class="mx-auto max-w-3xl">
-      <Router
-        pageList={pageList}
-        onNotFound={() => {
-          return <div>not found</div>;
+  const officer = useOfficer();
+  const [settings, updateSettings] = useSettings();
+
+  return (<div class="min-h-full flex flex-col m-0 bg-slate-200" 
style="min-height: 100vh;">
+    <div class="bg-indigo-600 pb-32">
+      <Header
+        title="Exchange"
+        iconLinkURL={uiSettings.backendBaseURL ?? "#"}
+        onLogout={officer.state !== "ready" ? undefined : () => {
+          officer.lock()
         }}
-      />
+        sites={[]}
+        supportedLangs={["en", "es", "de"]}
+      >
+        <li>
+          <div class="text-xs font-semibold leading-6 text-gray-400">
+            <i18n.Translate>Preferences</i18n.Translate>
+          </div>
+          <ul role="list" class="space-y-1">
+            {getAllBooleanSettings().map(set => {
+              const isOn: boolean = !!settings[set]
+              return <li class="mt-2 pl-2">
+                <div class="flex items-center justify-between">
+                  <span class="flex flex-grow flex-col">
+                    <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
+                      {getLabelForSetting(set, i18n)}
+                    </span>
+                  </span>
+                  <button type="button" data-enabled={isOn} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
+
+                    onClick={() => { updateSettings(set, !isOn); }}>
+                    <span aria-hidden="true" data-enabled={isOn} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
+                  </button>
+                </div>
+              </li>
+            })}
+          </ul>
+        </li>
+      </Header>
     </div>
-  </main>
-}
 
-const pageList = Object.values(Pages);
+    <GlobalNotificationsBanner />
 
-function NavigationBar({
-  isOpen,
-  setOpen,
-  children,
-}: {
-  isOpen: boolean;
-  setOpen: (v: boolean) => void;
-  children: ComponentChildren;
-}) {
-  return (
-    <Fragment>
-      <Transition.Root show={isOpen} as={Fragment}>
-        <Dialog
-          as="div"
-          /* @ts-ignore */
-          class="relative z-50 lg:hidden"
-          onClose={setOpen}
-        >
-          <Transition.Child
-            as={Fragment}
-            enter="transition-opacity ease-linear duration-300"
-            enterFrom="opacity-0"
-            enterTo="opacity-100"
-            leave="transition-opacity ease-linear duration-300"
-            leaveFrom="opacity-100"
-            leaveTo="opacity-0"
-          >
-            <div class="fixed inset-0 bg-gray-900/80" />
-          </Transition.Child>
+    <main class="-mt-32 flex grow ">
+      {officer.state !== "ready" ? undefined :
+        <Navigation />
+      }
+      <div class="flex mx-auto my-4">
+        <div class="rounded-lg bg-white px-5 py-6 shadow sm:px-6">
+          {children}
+        </div>
+      </div>
 
-          <div class="fixed inset-0 flex">
-            <Transition.Child
-              as={Fragment}
-              enter="transition ease-in-out duration-300 transform"
-              enterFrom="-translate-x-full"
-              enterTo="translate-x-0"
-              leave="transition ease-in-out duration-300 transform"
-              leaveFrom="translate-x-0"
-              leaveTo="-translate-x-full"
-            >
-              <Dialog.Panel class="relative mr-16 flex w-full max-w-xs flex-1">
-                <Transition.Child
-                  as={Fragment}
-                  enter="ease-in-out duration-300"
-                  enterFrom="opacity-0"
-                  enterTo="opacity-100"
-                  leave="ease-in-out duration-300"
-                  leaveFrom="opacity-100"
-                  leaveTo="opacity-0"
-                >
-                  <div class="absolute left-full top-0 flex w-16 
justify-center pt-5">
-                    <button
-                      type="button"
-                      class="-m-2.5 p-2.5"
-                      onClick={() => setOpen(false)}
-                    >
-                      <span class="sr-only">Close sidebar</span>
-                      <XMarkIcon
-                        class="h-6 w-6 text-white"
-                        aria-hidden="true"
-                      />
-                    </button>
-                  </div>
-                </Transition.Child>
-                {children}
-              </Dialog.Panel>
-            </Transition.Child>
-          </div>
-        </Dialog>
-      </Transition.Root>
+    </main>
 
-      <div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 
lg:flex-col">
-        {children}
-      </div>
-    </Fragment>
+    <Footer
+      testingUrl={localStorage.getItem("exchange-base-url") ?? undefined}
+      GIT_HASH={GIT_HASH}
+      VERSION={VERSION}
+    />
+  </div>
   );
 }
 
-function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) {
+function Navigation(): VNode {
+  const { i18n } = useTranslationContext()
+  const pageList: Array<PageEntry> = [
+    Pages.officer,
+    Pages.cases
+  ]
   return (
-    <div class="relative flex h-16 justify-between">
-      <div class="relative z-10 flex p-2 lg:hidden">
-        <button
-          type="button"
-          onClick={() => {
-            onOpenSidebar();
-          }}
-          class="inline-flex items-center justify-center rounded-md p-2 
text-gray-400 hover:bg-gray-700 hover:text-gray-900 focus:outline-none 
focus:ring-2 focus:ring-inset focus:ring-gray-900"
-          aria-controls="mobile-menu"
-          aria-expanded="false"
-        >
-          <span class="sr-only">Open menu</span>
-          <svg
-            class="block h-6 w-6"
-            fill="none"
-            viewBox="0 0 24 24"
-            stroke-width="1.5"
-            stroke="currentColor"
-            aria-hidden="true"
-          >
-            <path
-              stroke-linecap="round"
-              stroke-linejoin="round"
-              d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
-            />
-          </svg>
-          <svg
-            class="hidden h-6 w-6"
-            fill="none"
-            viewBox="0 0 24 24"
-            stroke-width="1.5"
-            stroke="currentColor"
-            aria-hidden="true"
-          >
-            <path
-              stroke-linecap="round"
-              stroke-linejoin="round"
-              d="M6 18L18 6M6 6l12 12"
-            />
-          </svg>
-        </button>
-      </div>
-      <div class="relative z-0 flex flex-1 items-center justify-center px-2 
sm:absolute sm:inset-0">
-        <div class="w-full sm:max-w-xs flex flex-1 items-center 
justify-center">
-          <img
-            class="h-8 w-auto"
-            src={logo}
-            alt="Taler"
-            style={{ height: 35, margin: 10 }}
-          />
-        </div>
-      </div>
-      {/* <div class="relative z-10 flex items-center lg:hidden">dd</div> */}
-    </div>
-  );
-}
+    <div class="flex gap-y-5 w-48 bg-indigo-600 divide-y rounded-r-lg 
divide-cyan-800 overflow-y-auto overflow-x-clip">
 
-//   return (
-//     <div class="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 
border-b border-gray-200 bg-white px-4 shadow-sm sm:gap-x-6 sm:px-6 lg:px-8">
-//       <button
-//         type="button"
-//         class="-m-2.5 p-2.5 text-gray-700 lg:hidden"
-//         onClick={onOpenSidebar}
-//       >
-//         <span class="sr-only">Open sidebar</span>
-//         <Bars3Icon class="h-6 w-6" aria-hidden="true" />
-//       </button>
+      <nav class="flex flex-1 flex-col mx-4 mt-4 mb-2">
+        <ul role="list" class="flex flex-1 flex-col gap-y-7">
+          <li>
+            <ul role="list" class="-mx-2 space-y-1">
+              {pageList.map(p => {
+                return <li>
+                  {/* <!-- Current: "bg-indigo-700 text-white", Default: 
"text-indigo-200 hover:text-white hover:bg-indigo-700" --> */}
+                  <a href="#" class="bg-indigo-700 text-white group flex 
gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
+                    <img src={p.icon} />
+                    {p.name}
+                  </a>
+                </li>
 
-//       {/* Separator */}
-//       <div class="h-6 w-px bg-gray-900/10 lg:hidden" aria-hidden="true" />
+              })}
+              {/* <li>
+                <a href="#" class="text-indigo-200 hover:text-white 
hover:bg-indigo-700 group flex gap-x-3 rounded-md p-2 text-sm leading-6 
font-semibold">
 
-//       <div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
-//         <div class="relative flex flex-1" />
-//         {/* <form class="relative flex flex-1" action="#" method="GET">
-//           <label htmlFor="search-field" class="sr-only">
-//             Search
-//           </label>
-//           <MagnifyingGlassIcon
-//             class="pointer-events-none absolute inset-y-0 left-0 h-full w-5 
text-gray-400"
-//             aria-hidden="true"
-//           />
-//           <input
-//             id="search-field"
-//             class="block h-full w-full border-0 py-0 pl-8 pr-0 
text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm"
-//             placeholder="Search..."
-//             type="search"
-//             name="search"
-//           />
-//         </form> */}
-//         <div class="flex items-center gap-x-4 lg:gap-x-6">
-//           {/* <button
-//             type="button"
-//             class="-m-2.5 p-2.5 text-gray-400 hover:text-gray-500"
-//           >
-//             <span class="sr-only">View notifications</span>
-//             <BellIcon class="h-6 w-6" aria-hidden="true" />
-//           </button> */}
+                  <i18n.Translate>Officer</i18n.Translate>
+                </a>
+              </li> */}
+            </ul>
+          </li>
 
-//           {/* Separator */}
-//           <div
-//             class="hidden lg:block lg:h-6 lg:w-px lg:bg-gray-900/10"
-//             aria-hidden="true"
-//           />
+          {/* <li class="mt-auto ">
+            <a href="#" class="group -mx-2 flex gap-x-3 rounded-md p-2 text-sm 
font-semibold leading-6 text-indigo-200 hover:bg-indigo-700 hover:text-white">
+              <svg class="h-6 w-6 shrink-0 text-indigo-200 
group-hover:text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" 
stroke="currentColor" aria-hidden="true">
+                <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 
3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 
1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 
1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 
1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 
.255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 
2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.12 [...]
+                <path stroke-linecap="round" stroke-linejoin="round" d="M15 
12a3 3 0 11-6 0 3 3 0 016 0z" />
+              </svg>
+              Settings
+            </a>
+          </li> */}
 
-//           {/* {officerName === undefined ? (
-//             <div />
-//           ) : (
-//             <Menu
-//               as="div"
-//               class="relative"
-//             >
-//               <Menu.Button class="-m-1.5 flex items-center p-1.5">
-//                 <span class="sr-only">Open user menu</span>
-//                 <img
-//                   class="h-8 w-8 rounded-full bg-gray-50"
-//                   
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80";
-//                   alt=""
-//                 />
-//                 <span class="hidden lg:flex lg:items-center">
-//                   <span
-//                     class="ml-4 text-sm font-semibold leading-6 
text-gray-900"
-//                     aria-hidden="true"
-//                   >
-//                     {officerName}
-//                   </span>
-//                   <ChevronDownIcon
-//                     class="ml-2 h-5 w-5 text-gray-400"
-//                     aria-hidden="true"
-//                   />
-//                 </span>
-//               </Menu.Button>
-//               <Transition
-//                 as={Fragment}
-//                 enter="transition ease-out duration-100"
-//                 enterFrom="transform opacity-0 scale-95"
-//                 enterTo="transform opacity-100 scale-100"
-//                 leave="transition ease-in duration-75"
-//                 leaveFrom="transform opacity-100 scale-100"
-//                 leaveTo="transform opacity-0 scale-95"
-//               >
-//                 <Menu.Items class="absolute right-0 z-10 mt-2.5 w-48 
origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 
focus:outline-none">
-//                   <Menu.Item>
-//                     {({ active }: { active: boolean }) => (
-//                       <a
-//                         onClick={() => {
-//                           officer.reset();
-//                           password.reset();
-//                         }}
-//                         class={classNames(
-//                           active ? "bg-gray-50" : "",
-//                           "block px-3 py-1 text-sm leading-6 text-gray-900",
-//                         )}
-//                       >
-//                         Forget account
-//                       </a>
-//                     )}
-//                   </Menu.Item>
-//                 </Menu.Items>
-//               </Transition>
-//             </Menu>
-//           )} */}
-//         </div>
-//       </div>
-//     </div>
-//   );
-// }
+        </ul>
+      </nav>
+    </div>
+  )
 
-function Footer() {
-  return (
-    <footer class="absolute bottom-4">
-      <div class="mt-8 md:order-1 md:mt-0">
-        <p class="text-xs leading-5 text-gray-300">
-          Taler Systems SA. {versionText}
-        </p>
-      </div>
-    </footer>
-  );
 }
 
-function Notifications() {
-  const ns = useNotifications();
-
-  // useEffect(() => {
-  //   if (ns.length) {
-  //     // remove notifications after some timeout
-  //   }
-  // }, []);
-  {
-    /* <!-- Global notification live region, render this permanently at the 
end of the document --> */
-  }
-  return (
-    <div
-      aria-live="assertive"
-      class="pointer-events-none fixed inset-0 flex items-end px-4 py-6 
sm:items-start sm:p-6 z-50"
-    >
-      <div class="flex w-full flex-col items-center space-y-4 sm:items-end ">
-        {/* <!--
-  Notification panel, dynamically insert this into the live region when it 
needs to be displayed
 
-  Entering: "transform ease-out duration-300 transition"
-    From: "translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
-    To: "translate-y-0 opacity-100 sm:translate-x-0"
-  Leaving: "transition ease-in duration-100"
-    From: "opacity-100"
-    To: "opacity-0"
---> */}
-        {ns.map(({ message, remove }) => {
-          switch (message.type) {
-            case "error": {
-              return (
-                <div class="pointer-events-auto w-full max-w-sm 
overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 
">
-                  <div class="p-4 ">
-                    <div class="flex items-start ">
-                      <div class="flex-shrink-0">
-                        <XCircleIcon class="h-6 w-6 text-red-400" />
-                      </div>
-                      <div class="ml-3 w-0 flex-1 pt-0.5">
-                        <p class="text-sm font-medium text-gray-900">
-                          {message.title}
-                        </p>
-                        {message.description && (
-                          <p class="mt-1 text-sm text-gray-500">
-                            {message.description}
-                          </p>
-                        )}
-                      </div>
-                      <div class="ml-4 flex flex-shrink-0">
-                        <button
-                          type="button"
-                          onClick={remove}
-                          class="inline-flex rounded-md bg-white text-gray-400 
hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 
focus:ring-offset-2"
-                        >
-                          <span class="sr-only">Close</span>
-                          <svg
-                            class="h-5 w-5"
-                            viewBox="0 0 20 20"
-                            fill="currentColor"
-                            aria-hidden="true"
-                          >
-                            <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 
10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 
101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
-                          </svg>
-                        </button>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              );
-            }
-            case "info": {
-              return (
-                <div class="pointer-events-auto w-full max-w-sm 
overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 
">
-                  <div class="p-4 ">
-                    <div class="flex items-start ">
-                      <div class="flex-shrink-0">
-                        <CheckCircleIcon class="h-6 w-6 text-green-400" />
-                      </div>
-                      <div class="ml-3 w-0 flex-1 pt-0.5">
-                        <p class="text-sm font-medium text-gray-900">
-                          {message.title}
-                        </p>
-                      </div>
-                      <div class="ml-4 flex flex-shrink-0">
-                        <button
-                          type="button"
-                          onClick={remove}
-                          class="inline-flex rounded-md bg-white text-gray-400 
hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 
focus:ring-offset-2"
-                        >
-                          <span class="sr-only">Close</span>
-                          <svg
-                            class="h-5 w-5"
-                            viewBox="0 0 20 20"
-                            fill="currentColor"
-                            aria-hidden="true"
-                          >
-                            <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 
10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 
101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
-                          </svg>
-                        </button>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              );
-            }
-          }
-        })}
-      </div>
-    </div>
-  );
-}
diff --git a/packages/aml-backoffice-ui/src/assets/home.svg 
b/packages/aml-backoffice-ui/src/assets/home.svg
new file mode 100644
index 000000000..35f340162
--- /dev/null
+++ b/packages/aml-backoffice-ui/src/assets/home.svg
@@ -0,0 +1,3 @@
+<svg class="h-6 w-6 shrink-0 text-white" fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+  <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 
12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 
1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 
1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" 
/>
+</svg>
\ No newline at end of file
diff --git a/packages/aml-backoffice-ui/src/assets/people.svg 
b/packages/aml-backoffice-ui/src/assets/people.svg
new file mode 100644
index 000000000..1dc878b81
--- /dev/null
+++ b/packages/aml-backoffice-ui/src/assets/people.svg
@@ -0,0 +1,3 @@
+<svg class="h-6 w-6 shrink-0 text-indigo-200 group-hover:text-white" 
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" 
aria-hidden="true">
+  <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 
0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 
19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 
21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 
6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 
11-5.25 0 2.625 2.625 0 015.25 0z" />
+</svg>
\ No newline at end of file
diff --git a/packages/aml-backoffice-ui/src/hooks/useCases.ts 
b/packages/aml-backoffice-ui/src/hooks/useCases.ts
index 2a133f46d..c4edd9207 100644
--- a/packages/aml-backoffice-ui/src/hooks/useCases.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useCases.ts
@@ -5,7 +5,7 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { AmlExchangeBackend } from "../types.js";
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { AmountString, OfficerAccount, TalerExchangeApi, 
TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util";
+import { AmountString, OfficerAccount, OperationFail, TalerExchangeApi, 
TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook } from "swr";
 import { useExchangeApiContext } from "../context/config.js";
 import { useOfficer } from "./useOfficer.js";
@@ -70,6 +70,15 @@ export function useCases(state: AmlExchangeBackend.AmlState) 
{
   };
 
   // const public_accountslist = data?.type !== "ok" ? [] : 
data.body.public_accounts;
+  if (!session) {
+    return {
+      data: {
+        type: "fail",
+        case: "unauthorized",
+        detail: {}
+      } as OperationFail<never>
+    }
+  }
   if (data) {
     if (data.type === "fail") {
       return { data }
diff --git a/packages/aml-backoffice-ui/src/hooks/useSettings.ts 
b/packages/aml-backoffice-ui/src/hooks/useSettings.ts
new file mode 100644
index 000000000..52f6f1614
--- /dev/null
+++ b/packages/aml-backoffice-ui/src/hooks/useSettings.ts
@@ -0,0 +1,70 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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 {
+  Codec,
+  TranslatedString,
+  buildCodecForObject,
+  codecForBoolean,
+  codecForNumber,
+  codecForString,
+  codecOptional
+} from "@gnu-taler/taler-util";
+import { buildStorageKey, useLocalStorage, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+
+interface Settings {
+  allowInsecurePassword: boolean;
+}
+
+export function getAllBooleanSettings(): Array<keyof Settings> {
+  return ["allowInsecurePassword"]
+}
+
+export function getLabelForSetting(k: keyof Settings, i18n: ReturnType<typeof 
useTranslationContext>["i18n"]): TranslatedString {
+  switch (k) {
+    case "allowInsecurePassword": return i18n.str`Allow Insecure password`
+  }
+}
+
+export const codecForSettings = (): Codec<Settings> =>
+  buildCodecForObject<Settings>()
+    .property("allowInsecurePassword", (codecForBoolean()))
+    .build("Settings");
+
+const defaultSettings: Settings = {
+  allowInsecurePassword: false,
+};
+
+const EXCHANGE_SETTINGS_KEY = buildStorageKey(
+  "exchange-settings",
+  codecForSettings(),
+);
+
+export function useSettings(): [
+  Readonly<Settings>,
+  <T extends keyof Settings>(key: T, value: Settings[T]) => void,
+] {
+  const { value, update } = useLocalStorage(
+    EXCHANGE_SETTINGS_KEY,
+    defaultSettings,
+  );
+
+  function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
+    const newValue = { ...value, [k]: v };
+    update(newValue);
+  }
+  return [value, updateField];
+}
diff --git a/packages/aml-backoffice-ui/src/pages.ts 
b/packages/aml-backoffice-ui/src/pages.ts
index e4e16f05f..17ede651d 100644
--- a/packages/aml-backoffice-ui/src/pages.ts
+++ b/packages/aml-backoffice-ui/src/pages.ts
@@ -1,55 +1,51 @@
-import { Home } from "./pages/Home.js";
-import { Settings } from "./pages/Settings.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { AntiMoneyLaunderingForm } from "./pages/AntiMoneyLaunderingForm.js";
-import { Welcome } from "./pages/Welcome.js";
-import { PageEntry, pageDefinition } from "./route.js";
-import { Officer } from "./pages/Officer.js";
-import { Cases } from "./pages/Cases.js";
 import { CaseDetails } from "./pages/CaseDetails.js";
+import { Cases } from "./pages/Cases.js";
 import { NewFormEntry } from "./pages/NewFormEntry.js";
-
-const home: PageEntry = {
-  url: "#/",
-  view: Home,
-};
+import { Officer } from "./pages/Officer.js";
+import { PageEntry, pageDefinition } from "./route.js";
+import homeLogo from "./assets/home.svg";
+import peopleLogo from "./assets/people.svg";
 const cases: PageEntry = {
   url: "#/cases",
   view: Cases,
+  name: "Cases" as TranslatedString,
+  icon: homeLogo,
+};
+
+const officer: PageEntry = {
+  url: "#/officer",
+  view: Officer,
+  name: "Officer" as TranslatedString,
+  icon: peopleLogo,
 };
+
 const account: PageEntry<{ account: string }> = {
   url: pageDefinition("#/account/:account"),
   view: CaseDetails,
+  name: "Account" as TranslatedString,
+  // icon: () => undefined,
 };
 
 const newFormEntry: PageEntry<{ account?: string; type?: string }> = {
   url: pageDefinition("#/account/:account/new/:type?"),
   view: NewFormEntry,
+  name: "New Form" as TranslatedString,
+  // icon: () => undefined,
 };
 
-const settings: PageEntry = {
-  url: "#/settings",
-  view: Settings,
-};
-const officer: PageEntry = {
-  url: "#/officer",
-  view: Officer,
-};
-const welcome: PageEntry<{ asd?: string; name?: string }> = {
-  url: pageDefinition("#/welcome/:name?"),
-  view: Welcome,
-};
 const form: PageEntry<{ number?: string }> = {
   url: pageDefinition("#/form/:number?"),
   view: AntiMoneyLaunderingForm,
+  name: "Form" as TranslatedString,
+  // icon: () => undefined,
 };
 
 export const Pages = {
-  home,
-  info: cases,
+  cases,
   officer,
-  details: account,
-  settings,
-  welcome,
+  account,
   form,
   newFormEntry,
 };
diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx 
b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index 5f79db71e..624f2c985 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -4,17 +4,16 @@ import { VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { createNewForm } from "../handlers/forms.js";
 import { useCases } from "../hooks/useCases.js";
-import { useOfficer } from "../hooks/useOfficer.js";
 import { Pages } from "../pages.js";
 import { AmlExchangeBackend } from "../types.js";
 import { amlStateConverter } from "./CaseDetails.js";
+import { Officer } from "./Officer.js";
 
 export function Cases() {
   const { i18n } = useTranslationContext();
 
   const form = createNewForm<{ state: AmlExchangeBackend.AmlState }>();
 
-
   const initial = AmlExchangeBackend.AmlState.pending;
   const [stateFilter, setStateFilter] = useState(initial);
 
@@ -23,16 +22,15 @@ export function Cases() {
   if (!list) {
     return <Loading />
   }
-
   if (list instanceof TalerError) {
     return <ErrorLoading error={list} />
   }
 
   if (list.data.type === "fail") {
     switch (list.data.case) {
-      case "unauthorized":
-      case "officer-not-found":
-      case "officer-disabled": return <div />
+      case "unauthorized": return <Officer />
+      case "officer-not-found": return <Officer />
+      case "officer-disabled": return <Officer />
       default: assertUnreachable(list.data)
     }
   }
@@ -116,7 +114,7 @@ export function Cases() {
                           <td class="whitespace-nowrap px-3 py-5 text-sm 
text-gray-500 ">
                             <div class="text-gray-900">
                               <a
-                                href={Pages.details.url({ account: r.h_payto 
})}
+                                href={Pages.account.url({ account: r.h_payto 
})}
                                 class="text-indigo-600 hover:text-indigo-900"
                               >
                                 {r.h_payto}
diff --git a/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx 
b/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
index 5dcb8b21d..9b8c3c046 100644
--- a/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
@@ -5,6 +5,7 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { createNewForm } from "../handlers/forms.js";
+import { useSettings } from "../hooks/useSettings.js";
 
 export function CreateAccount({
   onNewAccount,
@@ -16,12 +17,13 @@ export function CreateAccount({
     password: string;
     repeat: string;
   }>();
+  const [settings] = useSettings()
 
   return (
     <div class="flex min-h-full flex-col ">
       <div class="sm:mx-auto sm:w-full sm:max-w-md">
         <h2 class="mt-6 text-center text-2xl font-bold leading-9 
tracking-tight text-gray-900">
-          Create account
+          <i18n.Translate>Create account</i18n.Translate>
         </h2>
       </div>
 
@@ -33,29 +35,29 @@ export function CreateAccount({
                 password: {
                   error: !v.password
                     ? i18n.str`required`
-                    : v.password.length < 8
-                    ? i18n.str`should have at least 8 characters`
-                    : !v.password.match(/[a-z]/) && v.password.match(/[A-Z]/)
-                    ? i18n.str`should have lowercase and uppercase characters`
-                    : !v.password.match(/\d/)
-                    ? i18n.str`should have numbers`
-                    : !v.password.match(/[^a-zA-Z\d]/)
-                    ? i18n.str`should have at least one character which is not 
a number or letter`
-                    : undefined,
+                    : settings.allowInsecurePassword
+                      ? undefined
+                      : v.password.length < 8
+                        ? i18n.str`should have at least 8 characters`
+                        : !v.password.match(/[a-z]/) && 
v.password.match(/[A-Z]/)
+                          ? i18n.str`should have lowercase and uppercase 
characters`
+                          : !v.password.match(/\d/)
+                            ? i18n.str`should have numbers`
+                            : !v.password.match(/[^a-zA-Z\d]/)
+                              ? i18n.str`should have at least one character 
which is not a number or letter`
+                              : undefined,
                 },
                 repeat: {
                   error: !v.repeat
                     ? i18n.str`required`
                     : v.repeat !== v.password
-                    ? i18n.str`doesn't match`
-                    : undefined,
+                      ? i18n.str`doesn't match`
+                      : undefined,
                 },
               };
             }}
             onSubmit={async (v, s) => {
-              console.log(v, s);
               const error = s?.password?.error ?? s?.repeat?.error;
-              console.log(error);
               if (error) {
                 notifyError(
                   "Can't create account" as TranslatedString,
@@ -72,7 +74,9 @@ export function CreateAccount({
                 name="password"
                 type="password"
                 help={
-                  "lower and upper case letters, number and special character" 
as TranslatedString
+                  settings.allowInsecurePassword
+                    ? i18n.str`short password are insecure, turn off insecure 
password in settings`
+                    : i18n.str`lower and upper case letters, number and 
special character`
                 }
                 required
               />
@@ -91,7 +95,7 @@ export function CreateAccount({
                 type="submit"
                 class="flex w-full justify-center rounded-md bg-indigo-600 
px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm 
hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
               >
-                Create
+                <i18n.Translate>Create</i18n.Translate>
               </button>
             </div>
           </Form.Provider>
diff --git a/packages/aml-backoffice-ui/src/pages/HandleAccountNotReady.tsx 
b/packages/aml-backoffice-ui/src/pages/HandleAccountNotReady.tsx
index 05fd0a019..b3d04d97e 100644
--- a/packages/aml-backoffice-ui/src/pages/HandleAccountNotReady.tsx
+++ b/packages/aml-backoffice-ui/src/pages/HandleAccountNotReady.tsx
@@ -30,5 +30,8 @@ export function HandleAccountNotReady({
       />
     );
   }
+  return <div>
+    some
+  </div>
   throw Error(`unexpected account state ${(officer as any).state}`);
 }
diff --git a/packages/aml-backoffice-ui/src/pages/Home.tsx 
b/packages/aml-backoffice-ui/src/pages/Home.tsx
deleted file mode 100644
index 838032d63..000000000
--- a/packages/aml-backoffice-ui/src/pages/Home.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { h } from "preact";
-
-export function Home() {
-  return <div>Home</div>;
-}
diff --git a/packages/aml-backoffice-ui/src/pages/Officer.tsx 
b/packages/aml-backoffice-ui/src/pages/Officer.tsx
index 4af34805a..abada3725 100644
--- a/packages/aml-backoffice-ui/src/pages/Officer.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Officer.tsx
@@ -1,9 +1,11 @@
 import { Fragment, h } from "preact";
 import { useOfficer } from "../hooks/useOfficer.js";
 import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
 
 export function Officer() {
   const officer = useOfficer();
+  const { i18n } = useTranslationContext()
   if (officer.state !== "ready") {
     return <HandleAccountNotReady officer={officer} />;
   }
@@ -11,7 +13,7 @@ export function Officer() {
   return (
     <div>
       <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 ">
-        Public key
+        <i18n.Translate>Public key</i18n.Translate>
       </h1>
       <div class="max-w-xl text-base leading-7 text-gray-700 lg:max-w-lg">
         <p class="mt-6 font-mono break-all">{officer.account.id}</p>
@@ -25,7 +27,7 @@ export function Officer() {
           rel="noreferrer"
           class="m-4 block rounded-md w-fit border-0 px-3 py-2 text-center 
text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
         >
-          Request account activation
+          <i18n.Translate>Request account activation</i18n.Translate>
         </a>
       </p>
       <p>
@@ -36,7 +38,7 @@ export function Officer() {
           }}
           class="m-4 block rounded-md border-0 bg-gray-200 px-3 py-2 
text-center text-sm text-black shadow-sm "
         >
-          Lock account
+          <i18n.Translate>Lock account</i18n.Translate>
         </button>
       </p>
       <p>
@@ -47,7 +49,7 @@ export function Officer() {
           }}
           class="m-4 block rounded-md bg-red-600 px-3 py-2 text-center text-sm 
 text-white shadow-sm hover:bg-red-500 "
         >
-          Remove account
+          <i18n.Translate>Forget account</i18n.Translate>
         </button>
       </p>
     </div>
diff --git a/packages/aml-backoffice-ui/src/pages/Settings.tsx 
b/packages/aml-backoffice-ui/src/pages/Settings.tsx
deleted file mode 100644
index ccff3b210..000000000
--- a/packages/aml-backoffice-ui/src/pages/Settings.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { h } from "preact";
-
-export function Settings() {
-  return <div>Settings</div>;
-}
diff --git a/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx 
b/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
index 83d8767fb..a6570ffcc 100644
--- a/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
+++ b/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
@@ -1,5 +1,5 @@
 import { TranslatedString, UnwrapKeyError } from "@gnu-taler/taler-util";
-import { notifyError, notifyInfo } from "@gnu-taler/web-util/browser";
+import { notifyError, notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { createNewForm } from "../handlers/forms.js";
 
@@ -10,6 +10,7 @@ export function UnlockAccount({
   onAccountUnlocked: (password: string) => void;
   onRemoveAccount: () => void;
 }): VNode {
+  const { i18n } = useTranslationContext()
   const Form = createNewForm<{
     password: string;
   }>();
@@ -17,12 +18,12 @@ export function UnlockAccount({
   return (
     <div class="flex min-h-full flex-col ">
       <div class="sm:mx-auto sm:w-full sm:max-w-md">
-        <h2 class="mt-6 text-center text-2xl font-bold leading-9 
tracking-tight text-gray-900">
-          Account locked
-        </h2>
+        <h1 class="mt-6 text-center text-2xl font-bold leading-9 
tracking-tight text-gray-900">
+          <i18n.Translate>Account locked</i18n.Translate>
+        </h1>
         <p class="mt-6 text-lg leading-8 text-gray-600">
-          Your account is normally locked anytime you reload. To unlock type
-          your password again.
+          <i18n.Translate>Your account is normally locked anytime you reload. 
To unlock type
+            your password again.</i18n.Translate>
         </p>
       </div>
 
@@ -30,7 +31,7 @@ export function UnlockAccount({
         <div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12">
           <Form.Provider
             initialValue={{
-              password: "welcometo.5146",
+              password: "qwe",
             }}
             onSubmit={async (v) => {
               try {
@@ -63,7 +64,7 @@ export function UnlockAccount({
                 type="submit"
                 class="flex w-full justify-center rounded-md bg-indigo-600 
px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm 
hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
               >
-                Unlock
+                <i18n.Translate>Unlock</i18n.Translate>
               </button>
             </div>
           </Form.Provider>
@@ -75,7 +76,7 @@ export function UnlockAccount({
           }}
           class="m-4 block rounded-md bg-red-600 px-3 py-2 text-center text-sm 
 text-white shadow-sm hover:bg-red-500 "
         >
-          Remove account
+          <i18n.Translate>Forget account</i18n.Translate>
         </button>
       </div>
     </div>
diff --git a/packages/aml-backoffice-ui/src/pages/Welcome.tsx 
b/packages/aml-backoffice-ui/src/pages/Welcome.tsx
deleted file mode 100644
index 433fbcf59..000000000
--- a/packages/aml-backoffice-ui/src/pages/Welcome.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { h } from "preact";
-
-export function Welcome({ name, asd }: { asd?: string; name?: string }) {
-  return (
-    <div>
-      {asd} Hello {name}
-    </div>
-  );
-}
diff --git a/packages/aml-backoffice-ui/src/route.ts 
b/packages/aml-backoffice-ui/src/route.ts
index d54f9be83..4c3331668 100644
--- a/packages/aml-backoffice-ui/src/route.ts
+++ b/packages/aml-backoffice-ui/src/route.ts
@@ -1,3 +1,4 @@
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { createHashHistory } from "history";
 import { h as create, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
@@ -45,14 +46,18 @@ export function pageDefinition<T extends Record<string, 
string>>(
 
 export type PageEntry<T = unknown> = T extends Record<string, string>
   ? {
-      url: PageDefinition<T>;
-      view: (props: T) => VNode;
-    }
+    url: PageDefinition<T>;
+    view: (props: T) => VNode;
+    name: TranslatedString,
+    icon?: string,
+  }
   : T extends unknown
   ? {
-      url: string;
-      view: (props: {}) => VNode;
-    }
+    url: string;
+    view: (props: {}) => VNode;
+    name: TranslatedString,
+    icon?: string,
+  }
   : never;
 
 export function Router({
diff --git a/packages/aml-backoffice-ui/src/settings.ts 
b/packages/aml-backoffice-ui/src/settings.ts
index 2897874a2..9c65837c3 100644
--- a/packages/aml-backoffice-ui/src/settings.ts
+++ b/packages/aml-backoffice-ui/src/settings.ts
@@ -24,7 +24,7 @@ export interface UiSettings {
  * Global settings for the UI.
  */
 const defaultSettings: UiSettings = {
-  backendBaseURL: "https://exchange.demo.taler.net/";,
+  backendBaseURL: "http://exchange.taler.test:1180/";,
   allowRegistrations: true,
   uiName: "Taler Bank",
 };
diff --git a/packages/aml-backoffice-ui/src/utils/Loading.tsx 
b/packages/aml-backoffice-ui/src/utils/Loading.tsx
deleted file mode 100644
index 7cbdad681..000000000
--- a/packages/aml-backoffice-ui/src/utils/Loading.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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 { h, VNode } from "preact";
-
-export function Loading(): VNode {
-  return (
-    <div
-      class="columns is-centered is-vcentered"
-      style={{
-        height: "calc(100% - 3rem)",
-        position: "absolute",
-        width: "100%",
-      }}
-    >
-      <Spinner />
-    </div>
-  );
-}
-
-export function Spinner(): VNode {
-  return (
-    <div class="lds-ring">
-      <div />
-      <div />
-      <div />
-      <div />
-    </div>
-  );
-}
diff --git a/packages/aml-backoffice-ui/tailwind.config.js 
b/packages/aml-backoffice-ui/tailwind.config.js
index 01f058b2e..ec51dfbb8 100644
--- a/packages/aml-backoffice-ui/tailwind.config.js
+++ b/packages/aml-backoffice-ui/tailwind.config.js
@@ -1,6 +1,12 @@
 /** @type {import('tailwindcss').Config} */
 export default {
-  content: ["./src/**/*.{html,tsx}"],
+  content: {
+    relative: true,
+    files: [
+      "./src/**/*.{html,tsx}",
+      "./node_modules/@gnu-taler/web-util/src/**/*.{html,tsx}"
+    ],
+  },
   theme: {
     extend: {},
   },

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]