diff options
Diffstat (limited to 'packages/exchange-backoffice-ui/src/Dashboard.tsx')
-rw-r--r-- | packages/exchange-backoffice-ui/src/Dashboard.tsx | 599 |
1 files changed, 0 insertions, 599 deletions
diff --git a/packages/exchange-backoffice-ui/src/Dashboard.tsx b/packages/exchange-backoffice-ui/src/Dashboard.tsx deleted file mode 100644 index 6794ca1f8..000000000 --- a/packages/exchange-backoffice-ui/src/Dashboard.tsx +++ /dev/null @@ -1,599 +0,0 @@ -import { useNotifications } 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 logo from "./assets/logo-2021.svg"; -import { Pages } from "./pages.js"; -import { Router, useCurrentLocation } from "./route.js"; - -function classNames(...classes: string[]) { - return classes.filter(Boolean).join(" "); -} - -/** - * mapping route to view - * not found (error page) - * nested, index element, relative routes - * link interception - * form POST interception, call action - * fromData => Object.fromEntries - * segments in the URL - * navigationState: idle, submitting, loading - * form GET interception: does a navigateTo - * form GET Sync: - * 1.- back after submit: useEffect to sync URL to form - * 2.- refresh after submit: input default value - * useSubmit for form submission onChange, history replace - * - * post form without redirect - * - * - * @param param0 - * @returns - */ - -const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined; -const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined; - -const versionText = VERSION - ? GIT_HASH - ? `v${VERSION} (${GIT_HASH.substring(0, 8)})` - : VERSION - : ""; - -/** - * TO BE FIXED: - * - * 1.- when the form change to other form and both form share the same structure - * the same input component may be rendered in the same place, - * since input are uncontrolled the are not re-rendered and since they are - * uncontrolled it will keep the value of the previous form. - * One solutions could be to remove the form when unloading and when the new - * form load it will start without previous vdom, preventing the cache - * to create this behavior. - * Other solutions could be using IDs in the fields that are constructed - * with the ID of the form, so two fields of different form will need to re-render - * cleaning up the state of the previous form. - * - * 2.- currently the design prop and the behavior prop of the flexible form - * are two side of the same coin. From the design point of view, it is important - * to design the form in a list-of-field manner and there may be additional - * content that is not directly mapped to the form structure (object) - * So maybe we want to change the current shape so the computation of the state - * of the form is in a field level, but this computation required the field value and - * the whole form values and state (since one field may be disabled/hidden) because - * of the value of other field. - * - * 3.- given the previous requirement, maybe the name of the field of the form could be - * a function (P: F -> V) where F is the form (or parent object) and V is the type of the - * property. That will help with the typing of the forms props - * - * 4.- tooltip are not placed correctly: the arrow should point the question mark - * and the text area should be bigger - * - * 5.- date field should have the calendar icon clickable so the user can select date without - * 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" - /> - Cases - </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" - /> - Account - </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, -}: { - children?: ComponentChildren; -}): VNode { - const [sidebarOpen, setSidebarOpen] = useState(false); - - 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 /> - <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>; - }} - /> - </div> - </main> - </div> - </Fragment> - ); -} - -const pageList = Object.values(Pages); - -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> - - <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> - - <div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col"> - {children} - </div> - </Fragment> - ); -} - -function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) { - 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> - ); -} - -// 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> - -// {/* Separator */} -// <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"> -// <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> */} - -// {/* Separator */} -// <div -// class="hidden lg:block lg:h-6 lg:w-px lg:bg-gray-900/10" -// aria-hidden="true" -// /> - -// {/* {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> -// ); -// } - -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> - ); -} |