diff options
Diffstat (limited to 'packages/exchange-backoffice-ui/src/Dashboard.tsx')
-rw-r--r-- | packages/exchange-backoffice-ui/src/Dashboard.tsx | 217 |
1 files changed, 173 insertions, 44 deletions
diff --git a/packages/exchange-backoffice-ui/src/Dashboard.tsx b/packages/exchange-backoffice-ui/src/Dashboard.tsx index 9a0ba41d5..9be86c533 100644 --- a/packages/exchange-backoffice-ui/src/Dashboard.tsx +++ b/packages/exchange-backoffice-ui/src/Dashboard.tsx @@ -3,19 +3,26 @@ import { ChevronDownIcon, MagnifyingGlassIcon, UserIcon, + XCircleIcon, } from "@heroicons/react/20/solid"; import { Bars3Icon, BellIcon, + CheckCircleIcon, Cog6ToothIcon, 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 { useEffect, useRef, useState } from "preact/hooks"; import { Pages } from "./pages.js"; import { Router, useCurrentLocation } from "./route.js"; import { InformationCircleIcon } from "@heroicons/react/24/solid"; +import { + useLocalStorage, + useMemoryStorage, + useNotifications, +} from "@gnu-taler/web-util/browser"; /** * references between forms @@ -259,6 +266,7 @@ export function Dashboard({ setSidebarOpen(true); }} /> + <Notifications /> <main class="py-10 px-4 sm:px-6 lg:px-8"> <div class="mx-auto max-w-3xl"> <Router @@ -355,6 +363,9 @@ function NavigationBar({ } function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) { + const password = useMemoryStorage("password"); + const officer = useLocalStorage("officer"); + 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 @@ -402,60 +413,66 @@ function TopBar({ onOpenSidebar }: { onOpenSidebar: () => void }) { aria-hidden="true" /> - {/* Profile dropdown */} - <Menu - as="div" - /* @ts-ignore */ - 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" - > - Tom Cook - </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" + {officer.value === undefined ? ( + <div /> + ) : ( + <Menu + as="div" + /* @ts-ignore */ + class="relative" > - <Menu.Items class="absolute right-0 z-10 mt-2.5 w-32 origin-top-right rounded-md bg-white py-2 shadow-lg ring-1 ring-gray-900/5 focus:outline-none"> - {userNavigation.map((item) => ( - <Menu.Item key={item.name}> + <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" + > + {/* Tom Cook */} + {officer.value?.substring(0, 6)} + </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 - href={item.href} + // href={item.href} + onClick={() => { + officer.reset(); + password.reset(); + }} class={classNames( active ? "bg-gray-50" : "", "block px-3 py-1 text-sm leading-6 text-gray-900", )} > - {item.name} + Forget account </a> )} </Menu.Item> - ))} - </Menu.Items> - </Transition> - </Menu> + </Menu.Items> + </Transition> + </Menu> + )} </div> </div> </div> @@ -473,3 +490,115 @@ function Footer() { </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 --> */ + } + console.log("render", ns.length); + 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> + ); +} |