diff options
Diffstat (limited to 'packages/exchange-backoffice-ui/src')
6 files changed, 349 insertions, 267 deletions
| diff --git a/packages/exchange-backoffice-ui/src/Dashboard.tsx b/packages/exchange-backoffice-ui/src/Dashboard.tsx index 80f33954a..9a0ba41d5 100644 --- a/packages/exchange-backoffice-ui/src/Dashboard.tsx +++ b/packages/exchange-backoffice-ui/src/Dashboard.tsx @@ -2,27 +2,20 @@ import { Dialog, Menu, Transition } from "@headlessui/react";  import {    ChevronDownIcon,    MagnifyingGlassIcon, +  UserIcon,  } from "@heroicons/react/20/solid";  import {    Bars3Icon,    BellIcon,    Cog6ToothIcon, -  DocumentDuplicateIcon,    XMarkIcon,  } from "@heroicons/react/24/outline";  import { ComponentChildren, Fragment, VNode, h } from "preact";  import { ForwardedRef, forwardRef } from "preact/compat";  import { useRef, useState } from "preact/hooks"; -import { v1 as form_902_11e_v1 } from "./forms/902_11e.js"; -import { v1 as form_902_12e_v1 } from "./forms/902_12e.js"; -import { v1 as form_902_13e_v1 } from "./forms/902_13e.js"; -import { v1 as form_902_15e_v1 } from "./forms/902_15e.js"; -import { v1 as form_902_1e_v1 } from "./forms/902_1e.js"; -import { v1 as form_902_4e_v1 } from "./forms/902_4e.js"; -import { v1 as form_902_5e_v1 } from "./forms/902_5e.js"; -import { v1 as form_902_9e_v1 } from "./forms/902_9e.js";  import { Pages } from "./pages.js";  import { Router, useCurrentLocation } from "./route.js"; +import { InformationCircleIcon } from "@heroicons/react/24/solid";  /**   * references between forms @@ -52,53 +45,6 @@ import { Router, useCurrentLocation } from "./route.js";   * 902.4   */ -export const allForms = [ -  { -    name: "Identification form (902.1e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_1e_v1, -  }, -  { -    name: "Operational legal entity or partnership (902.11e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_11e_v1, -  }, -  { -    name: "Foundations (902.12e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_12e_v1, -  }, -  { -    name: "Declaration for trusts (902.13e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_13e_v1, -  }, -  { -    name: "Information on life insurance policies (902.15e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_15e_v1, -  }, -  { -    name: "Declaration of beneficial owner (902.9e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_9e_v1, -  }, -  { -    name: "Customer profile (902.5e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_5e_v1, -  }, -  { -    name: "Risk profile (902.4e)", -    icon: DocumentDuplicateIcon, -    impl: form_902_4e_v1, -  }, -]; -const teams = [ -  { id: 1, name: "Heroicons", href: "#", initial: "H", current: false }, -  { id: 2, name: "Tailwind Labs", href: "#", initial: "T", current: false }, -  { id: 3, name: "Workcation", href: "#", initial: "W", current: false }, -];  const userNavigation = [    { name: "Your profile", href: "#" },    { name: "Sign out", href: "#" }, @@ -135,7 +81,7 @@ const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;  const versionText = VERSION    ? GIT_HASH -    ? `Version ${VERSION} (${GIT_HASH.substring(0, 8)})` +    ? `v${VERSION} (${GIT_HASH.substring(0, 8)})`      : VERSION    : ""; @@ -173,6 +119,110 @@ const versionText = VERSION   * writing text with the correct format   */ +function LeftMenu() { +  const currentLocation = useCurrentLocation(pageList); + +  return ( +    <nav class="flex flex-1 flex-col"> +      <ul role="list" class="flex flex-1 flex-col gap-y-7"> +        <li> +          <ul role="list" class="-mx-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", +                )} +              > +                <InformationCircleIcon +                  class={classNames( +                    Pages.info.url === currentLocation?.path +                      ? "text-white" +                      : "text-indigo-200 group-hover:text-white", +                    "h-6 w-6 shrink-0", +                  )} +                  aria-hidden="true" +                /> +                Info +              </a> +            </li> +            <li> +              <a +                href={Pages.officer.url} +                class={classNames( +                  Pages.officer.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", +                )} +              > +                <UserIcon +                  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" +                /> +                Officer +              </a> +            </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 Dashboard({    children,  }: { @@ -185,11 +235,24 @@ export function Dashboard({      if (!logRef.current) return;      logRef.current.innerHTML = JSON.stringify(v, undefined, 1);    } - -  const Nav = forwardRef(NavigationBar);    return (      <Fragment> -      <Nav ref={logRef} isOpen={sidebarOpen} setOpen={setSidebarOpen} /> +      <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"> +            <img +              class="h-8 w-auto" +              src="https://tailwindui.com/img/logos/mark.svg?color=white" +              alt="Taler" +            /> +          </div> +          <LeftMenu /> +          <div class="text-white text-sm"> +            <pre ref={logRef}></pre> +          </div> +          <Footer /> +        </div> +      </NavigationBar>        <div class="lg:pl-72">          <TopBar            onOpenSidebar={() => { @@ -206,8 +269,6 @@ export function Dashboard({              />            </div>          </main> - -        <Footer />        </div>      </Fragment>    ); @@ -215,11 +276,15 @@ export function Dashboard({  const pageList = Object.values(Pages); -function NavigationBar( -  { isOpen, setOpen }: { isOpen: boolean; setOpen: (v: boolean) => void }, -  logRef: ForwardedRef<HTMLPreElement>, -) { -  const currentLocation = useCurrentLocation(pageList); +function NavigationBar({ +  isOpen, +  setOpen, +  children, +}: { +  isOpen: boolean; +  setOpen: (v: boolean) => void; +  children: ComponentChildren; +}) {    return (      <Fragment>        <Transition.Root show={isOpen} as={Fragment}> @@ -275,87 +340,7 @@ function NavigationBar(                      </button>                    </div>                  </Transition.Child> -                <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"> -                    <img -                      class="h-8 w-auto" -                      src="https://tailwindui.com/img/logos/mark.svg?color=white" -                      alt="Your Company" -                    /> -                  </div> -                  <nav class="flex flex-1 flex-col"> -                    <ul role="list" class="flex flex-1 flex-col gap-y-7"> -                      <li> -                        <ul role="list" class="-mx-2 space-y-1"> -                          {allForms.map((item, idx) => { -                            const url = Pages.form.url({ number: String(idx) }); -                            return ( -                              <li key={item.name}> -                                <a -                                  href={url} -                                  class={classNames( -                                    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", -                                  )} -                                > -                                  <item.icon -                                    class={classNames( -                                      url === currentLocation?.path -                                        ? "text-white" -                                        : "text-indigo-200 group-hover:text-white", -                                      "h-6 w-6 shrink-0", -                                    )} -                                    aria-hidden="true" -                                  /> -                                  {item.name} -                                </a> -                              </li> -                            ); -                          })} -                        </ul> -                      </li> -                      {/* <li> -                          <div class="text-xs font-semibold leading-6 text-indigo-200"> -                            Your teams -                          </div> -                          <ul role="list" class="-mx-2 mt-2 space-y-1"> -                            {teams.map((team) => ( -                              <li key={team.name}> -                                <a -                                  href={team.href} -                                  class={classNames( -                                    team.current -                                      ? "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"> -                                    {team.initial} -                                  </span> -                                  <span class="truncate">{team.name}</span> -                                </a> -                              </li> -                            ))} -                          </ul> -                        </li> */} -                      <li class="mt-auto"> -                        <a -                          href={Pages.settings.url} -                          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" -                        > -                          <Cog6ToothIcon -                            class="h-6 w-6 shrink-0 text-indigo-200 group-hover:text-white" -                            aria-hidden="true" -                          /> -                          Settings -                        </a> -                      </li> -                    </ul> -                  </nav> -                </div> +                {children}                </Dialog.Panel>              </Transition.Child>            </div> @@ -363,90 +348,7 @@ function NavigationBar(        </Transition.Root>        <div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col"> -        <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"> -            <img -              class="h-8 w-auto" -              src="https://tailwindui.com/img/logos/mark.svg?color=white" -              alt="Your Company" -            /> -          </div> -          <nav class="flex flex-1 flex-col"> -            <ul role="list" class="flex flex-1 flex-col gap-y-7"> -              <li> -                <ul role="list" class="-mx-2 space-y-1"> -                  {allForms.map((item, idx) => { -                    const url = Pages.form.url({ number: String(idx) }); -                    return ( -                      <li key={item.name}> -                        <a -                          href={url} -                          class={classNames( -                            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", -                          )} -                        > -                          <item.icon -                            class={classNames( -                              url === currentLocation?.path -                                ? "text-white" -                                : "text-indigo-200 group-hover:text-white", -                              "h-6 w-6 shrink-0", -                            )} -                            aria-hidden="true" -                          /> -                          {item.name} -                        </a> -                      </li> -                    ); -                  })} -                </ul> -              </li> -              {/* <li> -                  <div class="text-xs font-semibold leading-6 text-indigo-200"> -                    Your teams -                  </div> -                  <ul role="list" class="-mx-2 mt-2 space-y-1"> -                    {teams.map((team) => ( -                      <li key={team.name}> -                        <a -                          href={team.href} -                          class={classNames( -                            team.current -                              ? "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"> -                            {team.initial} -                          </span> -                          <span class="truncate">{team.name}</span> -                        </a> -                      </li> -                    ))} -                  </ul> -                </li> */} -              <li class="mt-auto"> -                <a -                  href={Pages.settings.url} -                  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" -                > -                  <Cog6ToothIcon -                    class="h-6 w-6 shrink-0 text-indigo-200 group-hover:text-white" -                    aria-hidden="true" -                  /> -                  Settings -                </a> -              </li> -            </ul> -          </nav> -          <div class="text-white text-sm"> -            <pre ref={logRef}></pre> -          </div> -        </div> +        {children}        </div>      </Fragment>    ); @@ -468,7 +370,8 @@ function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) {        <div class="h-6 w-px bg-gray-900/10 lg:hidden" aria-hidden="true" />        <div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6"> -        <form class="relative flex flex-1" action="#" method="GET"> +        <div class="relative flex flex-1" /> +        {/* <form class="relative flex flex-1" action="#" method="GET">            <label htmlFor="search-field" class="sr-only">              Search            </label> @@ -483,15 +386,15 @@ function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) {              type="search"              name="search"            /> -        </form> +        </form> */}          <div class="flex items-center gap-x-4 lg:gap-x-6"> -          <button +          {/* <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> +          </button> */}            {/* Separator */}            <div @@ -561,14 +464,11 @@ function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) {  function Footer() {    return ( -    <footer class="bg-white"> -      <div class="mx-auto px-4 py-2 md:flex md:items-center md:justify-between lg:px-8"> -        <div class="mt-8 md:order-1 md:mt-0"> -          <p class="text-center text-xs leading-5 text-gray-500"> -            Copyright © 2014—2023 Taler Systems SA. -            {versionText} -          </p> -        </div> +    <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>    ); diff --git a/packages/exchange-backoffice-ui/src/pages.ts b/packages/exchange-backoffice-ui/src/pages.ts index b46cf5d51..a78a137a0 100644 --- a/packages/exchange-backoffice-ui/src/pages.ts +++ b/packages/exchange-backoffice-ui/src/pages.ts @@ -1,25 +1,35 @@  import { Home } from "./pages/Home.js";  import { Settings } from "./pages/Settings.js"; -import { ShowForm } from "./pages/ShowForm.js"; +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 { Info } from "./pages/Info.js";  const home: PageEntry = {    url: "#/",    view: Home,  }; +const info: PageEntry = { +  url: "#/info", +  view: Info, +};  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: ShowForm, +  view: AntiMoneyLaunderingForm,  }; -export const Pages = { home, settings, welcome, form }; +export const Pages = { home, info, officer, settings, welcome, form }; diff --git a/packages/exchange-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx b/packages/exchange-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx new file mode 100644 index 000000000..ee9d1ce30 --- /dev/null +++ b/packages/exchange-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx @@ -0,0 +1,71 @@ +import { h } from "preact"; +import { NiceForm } from "../NiceForm.js"; +import { v1 as form_902_11e_v1 } from "../forms/902_11e.js"; +import { v1 as form_902_12e_v1 } from "../forms/902_12e.js"; +import { v1 as form_902_13e_v1 } from "../forms/902_13e.js"; +import { v1 as form_902_15e_v1 } from "../forms/902_15e.js"; +import { v1 as form_902_1e_v1 } from "../forms/902_1e.js"; +import { v1 as form_902_4e_v1 } from "../forms/902_4e.js"; +import { v1 as form_902_5e_v1 } from "../forms/902_5e.js"; +import { v1 as form_902_9e_v1 } from "../forms/902_9e.js"; +import { DocumentDuplicateIcon } from "@heroicons/react/24/solid"; + +export function AntiMoneyLaunderingForm({ number }: { number?: string }) { +  const selectedForm = Number.parseInt(number ?? "0", 10); +  if (Number.isNaN(selectedForm)) { +    return <div>WHAT! {number}</div>; +  } +  const showingFrom = allForms[selectedForm].impl; +  const storedValue = { +    fullName: "loggedIn_user_fullname", +    when: { +      t_ms: new Date().getTime(), +    }, +  }; +  return ( +    <NiceForm initial={storedValue} form={showingFrom} onUpdate={() => {}} /> +  ); +} + +export const allForms = [ +  { +    name: "Identification form (902.1e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_1e_v1, +  }, +  { +    name: "Operational legal entity or partnership (902.11e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_11e_v1, +  }, +  { +    name: "Foundations (902.12e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_12e_v1, +  }, +  { +    name: "Declaration for trusts (902.13e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_13e_v1, +  }, +  { +    name: "Information on life insurance policies (902.15e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_15e_v1, +  }, +  { +    name: "Declaration of beneficial owner (902.9e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_9e_v1, +  }, +  { +    name: "Customer profile (902.5e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_5e_v1, +  }, +  { +    name: "Risk profile (902.4e)", +    icon: DocumentDuplicateIcon, +    impl: form_902_4e_v1, +  }, +]; diff --git a/packages/exchange-backoffice-ui/src/pages/Info.tsx b/packages/exchange-backoffice-ui/src/pages/Info.tsx new file mode 100644 index 000000000..661ab02a7 --- /dev/null +++ b/packages/exchange-backoffice-ui/src/pages/Info.tsx @@ -0,0 +1,5 @@ +import { h } from "preact"; + +export function Info() { +  return <div>Show key and wire info</div>; +} diff --git a/packages/exchange-backoffice-ui/src/pages/Officer.tsx b/packages/exchange-backoffice-ui/src/pages/Officer.tsx new file mode 100644 index 000000000..c72ca0720 --- /dev/null +++ b/packages/exchange-backoffice-ui/src/pages/Officer.tsx @@ -0,0 +1,116 @@ +import { useLocalStorage } from "@gnu-taler/web-util/browser"; +import { h } from "preact"; +import { useEffect, useState } from "preact/hooks"; + +const oldKey = +  "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDPQVq8F0Ce6kTXKQ5Ea2fZRoap6poFYs0FOln8o8+ehGI8rDdMBzNU3pLIlOMKs/vKvhDNMG4m4xxb92wDbvefDxkxaEkbRSZnRiJd4MIbh8Lx8zvFbLp03rkXu9KPN8IprKOXxgN7xbxm0KKcu03rtqLiOvC1gMqja2LMIPCi32nyNneduszHZ57d+CqIKZdVnaqAcXOSMAQsVoEq2joBOeIaSAnIJHg+T8HQ+VcLV8Y722jhX/bH84IyEMup9e7mhgVFnHgINc77c6TONH8H+dHlXCQ+hMPGw9wM+wgpJgIDzrhIN+QSjn283EOXD6z6dpiWBdEYfJRLHwEWk8wNAgMBAAECggEAB/anZrMasQsoXP9qBG1Uvq+r4fXZODFtK5vBNGi+RAWAhCX2iU3SMPB3wbby0wj1DlESR91qBhrTjqG+/TzIzUxLuARyoVZysiTVkjeIzdJVcRgwU5bTbUUs5da6MaA/WNGWMZvoALFUMBEpMQ4uDCC8OSbG8/prDtoZSuWjHrxBhsqSyIoJ3Q0iPQxPT0ShC9d5T56QuhsRQeRIWhQVtFlytXl1lqEbqljhIEOzkvS5QOcXcS3OBo/Nvdit+vi9kkLuiP8z2p6WAiVZCgCXfffNH3EEbQG/BEpIOynkchiDy1L31mFRFk1oYJRs9xD8+oF/N75GhlmYO7IbxeHw0wKBgQDnYZWjGlRM2oHpeiPSII5m9rC7qohO0ImxqifYZPp47vdRMbBWrdbxX68SqdzGfSzXcDPLfBAObG4QR8Xol1LMNJUT9og9pERZHgob+yWkTd68lLSdxfCJEKRJaDmD8dHgSrBYe86ADUeAj+fC4dycYXH//fwed1gt/G8iXtdU9wKBgQDlTp9752+tEh9fMlUdINbZXmGbjHBrZMTnAYJI509iJLIvJvYroU5TvRMsp+rACDc2Zy2nbsaCM5Xzd5wUxRBvF+PiBCFoi7c/EBaLCtb9+vyXtHAIHtzHeYUP/1cq7MOdTwrWvZqzIoW6xm7L9HRX/5i+n+rVUSxnzYIxgTlaGwKBgQC0INgpXbn7CrDQXnG8h/PUXIBB2QS8tsQ7N8hFQndr5j1LTG+HS1ZmGqNk2DAzpgdewM7RvweQ8wDMU9PSutuOdfEI1YhC1LsQ1b3xApfPTX/1N59UpGAZlIcRTr5X5c4J2ptmhxu/vJbJkz5ODR997q6dJ9E6tpZDVp3+F+9zCQKBgQCrp+OzuVjcUoixltgoagDrz7951fQCMPlFhNenA6FlctsAeUYm+yXLgersrvcIsh3C2BJRGJf5t+w0ygFJewwGXff1pensfUq8Jqr5gy/WCSE135lOOuxDVzDI/Pif5YW6KQWQI3e/ScSaQRmIDINbrLcHXGdLMOzw9+LSdE4eqQKBgQDe86MfzwMLPoDH07WC09dCcoIUSYMThYrFwUK3qgEiYaJMZJvdAIwr12szVwVRYIX4wHBObFsQZLTaY5+O/REnze6Q1AQa2H6eH1TalC1r6jBS5/LhIrVWl/0VSdsUIe41tc8xPDWrm9hmLeJLZk+xb5/hAm3REsDM1Iif9O7zzg=="; +export function Officer() { +  const storage = useLocalStorage("officer"); +  const [keys, setKeys] = useState({ priv: "", pub: "" }); +  useEffect(() => { +    loadPreviousSession(oldKey).then((keys) => +      setKeys(keys ?? { priv: "", pub: "" }), +    ); +    // generateNewId().then((keys) => setKeys(keys)); +  }, []); + +  console.log(keys.pub); +  console.log(keys.priv); +  return ( +    <div> +      <div>Officer</div> +      <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 "> +        Public key +      </h1> +      <div> +        -----BEGIN PUBLIC KEY----- +        <p class="mt-6 leading-8 text-gray-700 break-all">{keys.pub}</p> +        -----END PUBLIC KEY----- +      </div> +      <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 "> +        Private key +      </h1> +      <div> +        -----BEGIN PRIVATE KEY----- +        <p class="mt-6 leading-8 text-gray-700 break-all">{keys.priv}</p> +        -----END PRIVATE KEY----- +      </div> +    </div> +  ); +} + +const rsaAlgorithm: RsaHashedKeyGenParams = { +  name: "RSA-OAEP", +  modulusLength: 2048, +  publicExponent: new Uint8Array([0x01, 0x00, 0x01]), +  hash: "SHA-256", +}; + +async function generateNewId() { +  const key = await crypto.subtle.generateKey(rsaAlgorithm, true, [ +    "encrypt", +    "decrypt", +  ]); + +  if (key instanceof CryptoKey) { +    throw Error("unexpected key without pair"); +  } +  const { privateKey, publicKey } = key; +  const privRaw = await crypto.subtle.exportKey("pkcs8", privateKey); + +  const pubRaw = await crypto.subtle.exportKey("spki", publicKey); + +  const priv = btoa(ab2str(privRaw)); + +  const pub = btoa(ab2str(pubRaw)); +  return { priv, pub }; +} + +async function loadPreviousSession(priv: string) { +  const key = str2ab(window.atob(priv)); +  const privateKey = await window.crypto.subtle +    .importKey("pkcs8", key, rsaAlgorithm, true, ["decrypt"]) +    .catch(throwErrorWithStack); + +  if (!privateKey) return undefined; + +  // export private key to JWK +  const jwk = await crypto.subtle +    .exportKey("jwk", privateKey) +    .catch(throwErrorWithStack); + +  // remove private data from JWK +  delete jwk.d; +  delete jwk.dp; +  delete jwk.dq; +  delete jwk.q; +  delete jwk.qi; +  jwk.key_ops = ["encrypt"]; + +  const publicKey = await crypto.subtle +    .importKey("jwk", jwk, rsaAlgorithm, true, ["encrypt"]) +    .catch(throwErrorWithStack); + +  const pubRaw = await crypto.subtle +    .exportKey("spki", publicKey) +    .catch(throwErrorWithStack); + +  const pub = btoa(ab2str(pubRaw)); + +  return { priv, pub }; +} + +function ab2str(buf: ArrayBuffer) { +  return String.fromCharCode.apply(null, Array.from(new Uint8Array(buf))); +} +function str2ab(str: string) { +  const buf = new ArrayBuffer(str.length); +  const bufView = new Uint8Array(buf); +  for (let i = 0, strLen = str.length; i < strLen; i++) { +    bufView[i] = str.charCodeAt(i); +  } +  return buf; +} +function throwErrorWithStack(e: Error): never { +  throw new Error(e.message); +} diff --git a/packages/exchange-backoffice-ui/src/pages/ShowForm.tsx b/packages/exchange-backoffice-ui/src/pages/ShowForm.tsx deleted file mode 100644 index 5440ab7e1..000000000 --- a/packages/exchange-backoffice-ui/src/pages/ShowForm.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { h } from "preact"; -import { allForms } from "../Dashboard.js"; -import { NiceForm } from "../NiceForm.js"; - -export function ShowForm({ number }: { number?: string }) { -  const selectedForm = Number.parseInt(number ?? "0", 10); -  if (Number.isNaN(selectedForm)) { -    return <div>WHAT! {number}</div>; -  } -  const showingFrom = allForms[selectedForm].impl; -  const storedValue = { -    fullName: "loggedIn_user_fullname", -    when: { -      t_ms: new Date().getTime(), -    }, -  }; -  return ( -    <NiceForm initial={storedValue} form={showingFrom} onUpdate={() => {}} /> -  ); -} | 
