aboutsummaryrefslogtreecommitdiff
path: root/packages/aml-backoffice-ui/src/Dashboard.tsx
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-11-06 11:54:45 -0300
committerSebastian <sebasjm@gmail.com>2023-11-06 11:54:45 -0300
commit4ee903eb5e639fa0e122a689cc431e8f18ca1197 (patch)
treed2cf28bcddb03f650fe7913ff2db8e48145740c4 /packages/aml-backoffice-ui/src/Dashboard.tsx
parent3d1ab082d4a66df08fcb468d04198c055d00b8c5 (diff)
downloadwallet-core-4ee903eb5e639fa0e122a689cc431e8f18ca1197.tar.xz
aml ui
Diffstat (limited to 'packages/aml-backoffice-ui/src/Dashboard.tsx')
-rw-r--r--packages/aml-backoffice-ui/src/Dashboard.tsx564
1 files changed, 120 insertions, 444 deletions
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.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
+ <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>
- );
-}