taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit f51fd65ad2d0227ac84b1b5e28c774f79f9d7188
parent 843d70ed8cf4921decfd0339a4420daba33d0ab4
Author: Sebastian <sebasjm@gmail.com>
Date:   Wed, 16 Apr 2025 11:58:01 -0300

adding validation of account before jump into details

Diffstat:
Mpackages/aml-backoffice-ui/src/pages/Cases.tsx | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 69 insertions(+), 30 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx b/packages/aml-backoffice-ui/src/pages/Cases.tsx @@ -23,15 +23,17 @@ import { InputToggle, Loading, RouteDefinition, + useExchangeApiContext, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useCurrentDecisions } from "../hooks/decisions.js"; -import { useState } from "preact/hooks"; +import { useState, useEffect } from "preact/hooks"; import { privatePages } from "../Routing.js"; import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js"; import { Officer } from "./Officer.js"; +import { useOfficer } from "../hooks/officer.js"; type FormType = { // state: TalerExchangeApi.AmlState; @@ -338,6 +340,8 @@ export function Pagination({ ); } +let latestTimeout: undefined | ReturnType<typeof setTimeout> = undefined; + function JumpByIdForm({ caseByIdRoute, fitered, @@ -349,38 +353,73 @@ function JumpByIdForm({ }): VNode { const { i18n } = useTranslationContext(); const [account, setAccount] = useState<string>(""); + const officer = useOfficer(); + const session = officer.state === "ready" ? officer.account : undefined; + const { lib } = useExchangeApiContext(); + const [valid, setValid] = useState(false); + const [error, setError] = useState<string>(); + useEffect(() => { + if (!session || !account) return; + const activeSession = session; + if (latestTimeout) { + clearTimeout(latestTimeout); + } + setError(undefined) + setValid(false) + latestTimeout = setTimeout(async function checkAccouunt() { + let found = false + try { + const result = await lib.exchange.getAmlAttributesForAccount( + activeSession, + account, + { limit: 1 }, + ); + found = (result.type === "ok"); + } catch (e) { + console.log(e) + } + setValid(found) + if (!found) { + setError(i18n.str`account not found`); + } + }, 500); + }, [account, session]); return ( <form class="mt-5 grid grid-cols-1"> - <div class="flex flex-row"> - <div class="w-full sm:max-w-xs"> - <input - name="account" - onChange={(e) => { - setAccount(e.currentTarget.value); - }} - class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6" - placeholder={i18n.str`Search by ID`} - /> - </div> - <a - href={caseByIdRoute.url({ cid: account })} - class="mt-3 inline-flex w-full items-center justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold 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 sm:ml-3 sm:mt-0 sm:w-auto" - > - <svg - xmlns="http://www.w3.org/2000/svg" - fill="none" - viewBox="0 0 24 24" - stroke-width="1.5" - stroke="currentColor" - class="size-6 w-6 h-6" - > - <path - stroke-linecap="round" - stroke-linejoin="round" - d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" + <div> + <div class="flex flex-row"> + <div class="w-full sm:max-w-xs"> + <input + name="account" + onChange={(e) => { + setAccount(e.currentTarget.value); + }} + class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6" + placeholder={i18n.str`Search by ID`} /> - </svg> - </a> + </div> + <a + href={!valid ? undefined : caseByIdRoute.url({ cid: account })} + data-disabled={!valid} + class="data-[disabled=true]:bg-gray-400 mt-3 inline-flex w-full items-center justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold 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 sm:ml-3 sm:mt-0 sm:w-auto" + > + <svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="size-6 w-6 h-6" + > + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" + /> + </svg> + </a> + </div> + {!error ? undefined : <p class="mt-2 text-sm text-red-600">{error}</p>} </div> <div class="mt-2 cursor-default"> <InputToggle