diff options
Diffstat (limited to 'packages/demobank-ui/src')
26 files changed, 406 insertions, 199 deletions
diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx index 1d587fe32..c94e74201 100644 --- a/packages/demobank-ui/src/components/Routing.tsx +++ b/packages/demobank-ui/src/components/Routing.tsx @@ -21,7 +21,7 @@ import { Route, Router, route } from "preact-router"; import { useEffect } from "preact/hooks"; import { useBackendState } from "../hooks/backend.js"; import { BankFrame } from "../pages/BankFrame.js"; -import { HomePage, WithdrawalOperationPage } from "../pages/HomePage.js"; +import { WithdrawalOperationPage } from "../pages/WithdrawalOperationPage.js"; import { LoginForm } from "../pages/LoginForm.js"; import { PublicHistoriesPage } from "../pages/PublicHistoriesPage.js"; import { RegistrationPage } from "../pages/RegistrationPage.js"; @@ -35,6 +35,7 @@ import { CreateNewAccount } from "../pages/admin/CreateNewAccount.js"; import { CashoutListForAccount } from "../pages/admin/CashoutListForAccount.js"; import { ShowCashoutDetails } from "../pages/business/ShowCashoutDetails.js"; import { WireTransfer } from "../pages/admin/Account.js"; +import { AccountPage } from "../pages/AccountPage/index.js"; export function Routing(): VNode { const history = createHashHistory(); @@ -301,17 +302,11 @@ export function Routing(): VNode { }} />; } else { - return <HomePage + return <AccountPage account={username} goToConfirmOperation={(wopid) => { route(`/operation/${wopid}`); }} - goToBusinessAccount={() => { - route("/business"); - }} - onRegister={() => { - route("/register"); - }} /> } }} diff --git a/packages/demobank-ui/src/hooks/circuit.ts b/packages/demobank-ui/src/hooks/circuit.ts index 06e068d6d..fc17c0184 100644 --- a/packages/demobank-ui/src/hooks/circuit.ts +++ b/packages/demobank-ui/src/hooks/circuit.ts @@ -18,10 +18,11 @@ import { useState } from "preact/hooks"; import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js"; import { useBackendState } from "./backend.js"; -import { AccessToken, AmountJson, Amounts, OperationOk, TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError } from "@gnu-taler/taler-util"; +import { AccessToken, AmountJson, AmountString, Amounts, OperationOk, TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError } from "@gnu-taler/taler-util"; import _useSWR, { SWRHook } from "swr"; import { useBankCoreApiContext } from "../context/config.js"; -import { assertUnreachable } from "../pages/HomePage.js"; +import { assertUnreachable } from "../pages/WithdrawalOperationPage.js"; +import { format, getDate, getDay, getHours, getMonth, getYear, set, sub } from "date-fns"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 const useSWR = _useSWR as unknown as SWRHook; @@ -121,11 +122,6 @@ export function useRatiosAndFeeConfig() { return undefined; } -interface PaginationFilter { - account?: string; - page?: number; -} - export function useBusinessAccounts() { const { state: credentials } = useBackendState(); const token = credentials.status !== "loggedIn" ? undefined : credentials.token @@ -250,3 +246,92 @@ export function useCashoutDetails(cashoutId: string) { if (error) return error; return undefined; } +export type MonitorMetrics = { + lastHour: TalerCoreBankResultByMethod<"getMonitor">, + lastDay: TalerCoreBankResultByMethod<"getMonitor">, + lastMonth: TalerCoreBankResultByMethod<"getMonitor">, +} + +function getTimeframesForDate(time: Date, timeframe: TalerCorebankApi.MonitorTimeframeParam): { current: number, previous: number } { + switch (timeframe) { + case TalerCorebankApi.MonitorTimeframeParam.hour: return { + current: getHours(sub(time, { hours: 1 })), + previous: getHours(sub(time, { hours: 2 })) + } + case TalerCorebankApi.MonitorTimeframeParam.day: return { + current: getDate(sub(time, { days: 1 })), + previous: getDate(sub(time, { days: 2 })) + } + case TalerCorebankApi.MonitorTimeframeParam.month: return { + current: getMonth(sub(time, { months: 1 })), + previous: getMonth(sub(time, { months: 2 })) + } + case TalerCorebankApi.MonitorTimeframeParam.year: return { + current: getYear(sub(time, { years: 1 })), + previous: getYear(sub(time, { years: 2 })) + } + case TalerCorebankApi.MonitorTimeframeParam.decade: return { + current: getYear(sub(time, { years: 10 })), + previous: getYear(sub(time, { years: 20 })) + } + default: assertUnreachable(timeframe) + } +} + +export type LastMonitor = { current: TalerCoreBankResultByMethod<"getMonitor">, previous: TalerCoreBankResultByMethod<"getMonitor"> } +export function useLastMonitorInfo(time: Date, timeframe: TalerCorebankApi.MonitorTimeframeParam) { + const { api, config } = useBankCoreApiContext(); + + async function fetcher() { + const params = getTimeframesForDate(time, timeframe) + // const resp = await Promise.all([ + // api.getMonitor({ timeframe, which: params.current }), + // api.getMonitor({ timeframe, which: params.previous }), + // ]) + const current: TalerCoreBankResultByMethod<"getMonitor"> = { + type: "ok" as const, + body: { + cashinCount: 1, + cashinExternalVolume: "LOCAL:1234" as AmountString, + cashoutCount: 2, + cashoutExternalVolume: "LOCAL:2345" as AmountString, + talerPayoutCount: 3, + talerPayoutInternalVolume: "LOCAL:3456" as AmountString, + } + } + + const previous = { + type: "ok" as const, + body: { + cashinCount: 1, + cashinExternalVolume: "LOCAL:2345" as AmountString, + cashoutCount: 2, + cashoutExternalVolume: "LOCAL:2345" as AmountString, + talerPayoutCount: 3, + talerPayoutInternalVolume: "LOCAL:3456" as AmountString, + } + + } + return { + current, + previous, + } + } + + const { data, error } = useSWR<LastMonitor, TalerHttpError>( + config.have_cashout || true ? ["useLastMonitorInfo"] : false, fetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + errorRetryCount: 0, + errorRetryInterval: 1, + shouldRetryOnError: false, + keepPreviousData: true, + }); + + if (data) return data; + if (error) return error; + return undefined; +} diff --git a/packages/demobank-ui/src/hooks/settings.ts b/packages/demobank-ui/src/hooks/settings.ts index cfc3b6a5b..ca2d131f2 100644 --- a/packages/demobank-ui/src/hooks/settings.ts +++ b/packages/demobank-ui/src/hooks/settings.ts @@ -15,18 +15,15 @@ */ import { - AmountString, Codec, TranslatedString, buildCodecForObject, - codecForAmountString, codecForBoolean, codecForNumber, codecForString, - codecOptional, + codecOptional } from "@gnu-taler/taler-util"; -import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; +import { buildStorageKey, useLocalStorage, useTranslationContext } from "@gnu-taler/web-util/browser"; interface Settings { currentWithdrawalOperationId: string | undefined; diff --git a/packages/demobank-ui/src/pages.ts b/packages/demobank-ui/src/pages.ts index c78240a02..cf003fde5 100644 --- a/packages/demobank-ui/src/pages.ts +++ b/packages/demobank-ui/src/pages.ts @@ -1,4 +1,4 @@ -import { WithdrawalOperationPage } from "./pages/HomePage.js"; +import { WithdrawalOperationPage } from "./pages/WithdrawalOperationPage.js"; import { PageEntry, pageDefinition } from "./route.js"; // const operationById: PageEntry<{ operationId: string }> = { diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts index ef6b4fede..87ed878b0 100644 --- a/packages/demobank-ui/src/pages/AccountPage/index.ts +++ b/packages/demobank-ui/src/pages/AccountPage/index.ts @@ -14,9 +14,8 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError, TalerErrorDetail } from "@gnu-taler/taler-util"; -import { HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser"; -import { VNode } from "preact"; +import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util"; +import { utils } from "@gnu-taler/web-util/browser"; import { ErrorLoading } from "../../components/ErrorLoading.js"; import { Loading } from "../../components/Loading.js"; import { LoginForm } from "../LoginForm.js"; @@ -25,7 +24,6 @@ import { InvalidIbanView, ReadyView } from "./views.js"; export interface Props { account: string; - goToBusinessAccount: () => void; goToConfirmOperation: (id: string) => void; } @@ -51,7 +49,6 @@ export namespace State { error: undefined; account: string, limit: AmountJson, - goToBusinessAccount: () => void; goToConfirmOperation: (id: string) => void; } diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts index 96d45b7bd..793593f0d 100644 --- a/packages/demobank-ui/src/pages/AccountPage/state.ts +++ b/packages/demobank-ui/src/pages/AccountPage/state.ts @@ -18,9 +18,9 @@ import { Amounts, HttpStatusCode, TalerError, TalerErrorCode, parsePaytoUri } fr import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser"; import { useAccountDetails } from "../../hooks/access.js"; import { Props, State } from "./index.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; -export function useComponentState({ account, goToBusinessAccount, goToConfirmOperation }: Props): State { +export function useComponentState({ account, goToConfirmOperation }: Props): State { const result = useAccountDetails(account); const { i18n } = useTranslationContext(); @@ -76,7 +76,6 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe return { status: "ready", - goToBusinessAccount, goToConfirmOperation, error: undefined, account, diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx index 00643ec3e..8fff37624 100644 --- a/packages/demobank-ui/src/pages/AccountPage/views.tsx +++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx @@ -53,7 +53,7 @@ function ShowDemoInfo(): VNode { </Attention> } -export function ReadyView({ account, limit, goToBusinessAccount, goToConfirmOperation }: State.Ready): VNode<{}> { +export function ReadyView({ account, limit, goToConfirmOperation }: State.Ready): VNode<{}> { return <Fragment> <ShowDemoInfo /> diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx index 6d1d35288..b18f29d86 100644 --- a/packages/demobank-ui/src/pages/LoginForm.tsx +++ b/packages/demobank-ui/src/pages/LoginForm.tsx @@ -23,7 +23,7 @@ import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { bankUiSettings } from "../settings.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { doAutoFocus } from "./PaytoWireTransferForm.js"; diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts index 9e34a846b..136a2b505 100644 --- a/packages/demobank-ui/src/pages/OperationState/state.ts +++ b/packages/demobank-ui/src/pages/OperationState/state.ts @@ -23,7 +23,7 @@ import { useBackendState } from "../../hooks/backend.js"; import { useSettings } from "../../hooks/settings.js"; import { buildRequestErrorMessage, withRuntimeErrorHandling } from "../../utils.js"; import { Props, State } from "./index.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { mutate } from "swr"; export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> { diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx index d859c10d7..63cb3e865 100644 --- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx +++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx @@ -43,7 +43,7 @@ import { } from "../utils.js"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { mutate } from "swr"; const logger = new Logger("PaytoWireTransferForm"); @@ -128,7 +128,7 @@ export function PaytoWireTransferForm({ if (rawPaytoInput) { const p = parsePaytoUri(rawPaytoInput) if (!p) return; - sendingAmount = p.params.amount + sendingAmount = p.params.amount as AmountString delete p.params.amount //if this payto is valid then it already have message payto_uri = stringifyPaytoUri(p) @@ -137,7 +137,7 @@ export function PaytoWireTransferForm({ const ibanPayto = buildPayto("iban", iban, undefined); ibanPayto.params.message = encodeURIComponent(subject); payto_uri = stringifyPaytoUri(ibanPayto); - sendingAmount = `${limit.currency}:${trimmedAmountStr}` + sendingAmount = `${limit.currency}:${trimmedAmountStr}` as AmountString } const puri = payto_uri; @@ -446,7 +446,7 @@ export function InputAmount( ); } -export function RenderAmount({ value, negative }: { value: AmountJson, negative?: boolean }): VNode { +export function RenderAmount({ value, negative, noCurrency }: { value: AmountJson, negative?: boolean, noCurrency?: boolean }): VNode { const { config } = useBankCoreApiContext() const str = Amounts.stringifyValue(value) const sep_pos = str.indexOf(FRAC_SEPARATOR) @@ -456,11 +456,11 @@ export function RenderAmount({ value, negative }: { value: AmountJson, negative? const small = str.substring(limit) return <span class="whitespace-nowrap"> {negative ? "-" : undefined} - {value.currency} {normal} <sup class="-ml-2">{small}</sup> + {noCurrency ? undefined : value.currency} {normal} <sup class="-ml-2">{small}</sup> </span> } return <span class="whitespace-nowrap"> {negative ? "-" : undefined} - {value.currency} {str} + {noCurrency ? undefined : value.currency} {str} </span> }
\ No newline at end of file diff --git a/packages/demobank-ui/src/pages/ProfileNavigation.tsx b/packages/demobank-ui/src/pages/ProfileNavigation.tsx index c061c9742..20a1ececd 100644 --- a/packages/demobank-ui/src/pages/ProfileNavigation.tsx +++ b/packages/demobank-ui/src/pages/ProfileNavigation.tsx @@ -1,7 +1,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useBankCoreApiContext } from "../context/config.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; export function ProfileNavigation({ current }: { current: "details" | "credentials" | "cashouts" }): VNode { const { i18n } = useTranslationContext() diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx b/packages/demobank-ui/src/pages/QrCodeSection.tsx index 64f9ec5ab..8948827aa 100644 --- a/packages/demobank-ui/src/pages/QrCodeSection.tsx +++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx @@ -32,7 +32,7 @@ import { useEffect } from "preact/hooks"; import { QR } from "../components/QR.js"; import { buildRequestErrorMessage, withRuntimeErrorHandling } from "../utils.js"; import { useBankCoreApiContext } from "../context/config.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; export function QrCodeSection({ withdrawUri, diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx index 21724474a..74346985a 100644 --- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx +++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx @@ -8,7 +8,7 @@ import { useBankCoreApiContext } from "../context/config.js"; import { useAccountDetails } from "../hooks/access.js"; import { useBackendState } from "../hooks/backend.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { LoginForm } from "./LoginForm.js"; import { AccountForm } from "./admin/AccountForm.js"; import { ProfileNavigation } from "./ProfileNavigation.js"; diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx index e3f0de8cc..ef3737e81 100644 --- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx +++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx @@ -5,7 +5,7 @@ import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { doAutoFocus } from "./PaytoWireTransferForm.js"; import { ProfileNavigation } from "./ProfileNavigation.js"; diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx index 28d5d7749..f1ff49068 100644 --- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx +++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx @@ -37,7 +37,7 @@ import { useBankCoreApiContext } from "../context/config.js"; import { useBackendState } from "../hooks/backend.js"; import { useSettings } from "../hooks/settings.js"; import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { OperationState } from "./OperationState/index.js"; import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js"; @@ -62,7 +62,7 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: { if (!!settings.currentWithdrawalOperationId) { return <Attention type="warning" title={i18n.str`There is an operation already`}> - <span ref={focus ? doAutoFocus : undefined}/> + <span ref={focus ? doAutoFocus : undefined} /> <i18n.Translate> To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a> </i18n.Translate> diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx index 87637f7ef..895094c28 100644 --- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx @@ -40,7 +40,7 @@ import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling } import { useSettings } from "../hooks/settings.js"; import { RenderAmount } from "./PaytoWireTransferForm.js"; import { useBankCoreApiContext } from "../context/config.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { mutate } from "swr"; const logger = new Logger("WithdrawalConfirmationQuestion"); diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx index bd85cea1e..7ef2a6c39 100644 --- a/packages/demobank-ui/src/pages/HomePage.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx @@ -15,62 +15,23 @@ */ import { - HttpStatusCode, Logger, TranslatedString, parseWithdrawUri, - stringifyWithdrawUri, + stringifyWithdrawUri } from "@gnu-taler/taler-util"; import { - ErrorType, - HttpResponse, - HttpResponsePaginated, - notify, notifyError, - useTranslationContext, + useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { Loading } from "../components/Loading.js"; import { useBankCoreApiContext } from "../context/config.js"; import { useSettings } from "../hooks/settings.js"; -import { AccountPage } from "./AccountPage/index.js"; -import { LoginForm } from "./LoginForm.js"; import { WithdrawalQRCode } from "./WithdrawalQRCode.js"; const logger = new Logger("AccountPage"); -/** - * show content based on state: - * - LoginForm if the user is not logged in - * - qr code if withdrawal in progress - * - else account information - * Use the handler to catch error cases - * - * @param param0 - * @returns - */ -export function HomePage({ - onRegister, - account, - goToConfirmOperation, - goToBusinessAccount, -}: { - account: string, - onRegister: () => void; - goToBusinessAccount: () => void; - goToConfirmOperation: (id: string) => void; -}): VNode { - const { i18n } = useTranslationContext(); - - return ( - <AccountPage - account={account} - goToConfirmOperation={goToConfirmOperation} - goToBusinessAccount={goToBusinessAccount} - /> - ); -} - export function WithdrawalOperationPage({ operationId, onContinue, diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx index 51edbc95f..5c300d0ab 100644 --- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx @@ -26,7 +26,7 @@ import { Fragment, VNode, h } from "preact"; import { ErrorLoading } from "../components/ErrorLoading.js"; import { Loading } from "../components/Loading.js"; import { useWithdrawalDetails } from "../hooks/access.js"; -import { assertUnreachable } from "./HomePage.js"; +import { assertUnreachable } from "./WithdrawalOperationPage.js"; import { QrCodeSection } from "./QrCodeSection.js"; import { WithdrawalConfirmationQuestion } from "./WithdrawalConfirmationQuestion.js"; import { Attention } from "../components/Attention.js"; diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx b/packages/demobank-ui/src/pages/admin/Account.tsx index 103747414..a1e80ccb9 100644 --- a/packages/demobank-ui/src/pages/admin/Account.tsx +++ b/packages/demobank-ui/src/pages/admin/Account.tsx @@ -4,7 +4,7 @@ import { Fragment, VNode, h } from "preact"; import { ErrorLoading } from "../../components/ErrorLoading.js"; import { Loading } from "../../components/Loading.js"; import { useAccountDetails } from "../../hooks/access.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { LoginForm } from "../LoginForm.js"; import { PaytoWireTransferForm } from "../PaytoWireTransferForm.js"; import { useBackendState } from "../../hooks/backend.js"; diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx b/packages/demobank-ui/src/pages/admin/AccountForm.tsx index bce089560..410683dcb 100644 --- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx +++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx @@ -6,7 +6,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { TalerCorebankApi, buildPayto, parsePaytoUri } from "@gnu-taler/taler-util"; import { doAutoFocus } from "../PaytoWireTransferForm.js"; import { CopyButton } from "../../components/CopyButton.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/; const EMAIL_REGEX = diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx b/packages/demobank-ui/src/pages/admin/AccountList.tsx index 39b43b9b1..7d3dd5595 100644 --- a/packages/demobank-ui/src/pages/admin/AccountList.tsx +++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx @@ -3,10 +3,10 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { ErrorLoading } from "../../components/ErrorLoading.js"; import { Loading } from "../../components/Loading.js"; +import { useBankCoreApiContext } from "../../context/config.js"; import { useBusinessAccounts } from "../../hooks/circuit.js"; -import { assertUnreachable } from "../HomePage.js"; import { RenderAmount } from "../PaytoWireTransferForm.js"; -import { useBankCoreApiContext } from "../../context/config.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; interface Props { onCreateAccount: () => void; @@ -36,119 +36,122 @@ export function AccountList({ onRemoveAccount, onShowAccountDetails, onUpdateAcc } const { accounts } = result.data.body; - return <div class="px-4 sm:px-6 lg:px-8"> - <div class="sm:flex sm:items-center"> - <div class="sm:flex-auto"> - <h1 class="text-base font-semibold leading-6 text-gray-900"> - <i18n.Translate>Accounts</i18n.Translate> - </h1> - <p class="mt-2 text-sm text-gray-700"> - <i18n.Translate>A list of all business account in the bank.</i18n.Translate> - </p> - </div> - <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none"> - <button type="button" class="block rounded-md bg-indigo-600 px-3 py-2 text-center 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" - onClick={(e) => { - e.preventDefault() - onCreateAccount() - }}> - <i18n.Translate>Create account</i18n.Translate> - </button> + return <Fragment> + <div class="px-4 sm:px-6 lg:px-8 mt-4"> + <div class="sm:flex sm:items-center"> + <div class="sm:flex-auto"> + <h1 class="text-base font-semibold leading-6 text-gray-900"> + <i18n.Translate>Accounts</i18n.Translate> + </h1> + <p class="mt-2 text-sm text-gray-700"> + <i18n.Translate>A list of all business account in the bank.</i18n.Translate> + </p> + </div> + <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none"> + <button type="button" class="block rounded-md bg-indigo-600 px-3 py-2 text-center 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" + onClick={(e) => { + e.preventDefault() + onCreateAccount() + }}> + <i18n.Translate>Create account</i18n.Translate> + </button> + </div> </div> - </div> - <div class="mt-8 flow-root"> - <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> - <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> - {!accounts.length ? ( - <div></div> - ) : ( - <table class="min-w-full divide-y divide-gray-300"> - <thead> - <tr> - <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">{i18n.str`Username`}</th> - <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Name`}</th> - <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Balance`}</th> - <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0"> - <span class="sr-only">{i18n.str`Actions`}</span> - </th> - </tr> - </thead> - <tbody class="divide-y divide-gray-200"> - {accounts.map((item, idx) => { - const balance = !item.balance - ? undefined - : Amounts.parse(item.balance.amount); - const noBalance = Amounts.isZero(item.balance.amount) - const balanceIsDebit = - item.balance && - item.balance.credit_debit_indicator == "debit"; + <div class="mt-8 flow-root"> + <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> + <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8"> + {!accounts.length ? ( + <div></div> + ) : ( + <table class="min-w-full divide-y divide-gray-300"> + <thead> + <tr> + <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">{i18n.str`Username`}</th> + <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Name`}</th> + <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">{i18n.str`Balance`}</th> + <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0"> + <span class="sr-only">{i18n.str`Actions`}</span> + </th> + </tr> + </thead> + <tbody class="divide-y divide-gray-200"> + {accounts.map((item, idx) => { + const balance = !item.balance + ? undefined + : Amounts.parse(item.balance.amount); + const noBalance = Amounts.isZero(item.balance.amount) + const balanceIsDebit = + item.balance && + item.balance.credit_debit_indicator == "debit"; - return <tr key={idx}> - <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0"> - <a href="#" class="text-indigo-600 hover:text-indigo-900" - onClick={(e) => { - e.preventDefault(); - onShowAccountDetails(item.username) - }} - > - {item.username} - </a> + return <tr key={idx}> + <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0"> + <a href="#" class="text-indigo-600 hover:text-indigo-900" + onClick={(e) => { + e.preventDefault(); + onShowAccountDetails(item.username) + }} + > + {item.username} + </a> - </td> - <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"> - {item.name} - </td> - <td data-negative={noBalance ? undefined : balanceIsDebit ? "true" : "false"} class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600 "> - {!balance ? ( - i18n.str`unknown` - ) : ( - <span class="amount"> - <RenderAmount value={balance} negative={balanceIsDebit} /> - </span> - )} - </td> - <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0"> - <a href="#" class="text-indigo-600 hover:text-indigo-900" - onClick={(e) => { - e.preventDefault(); - onUpdateAccountPassword(item.username) - }} - > - change password - </a> - <br /> - {config.have_cashout ? - <Fragment> + </td> + <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"> + {item.name} + </td> + <td data-negative={noBalance ? undefined : balanceIsDebit ? "true" : "false"} class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 data-[negative=false]:text-green-600 data-[negative=true]:text-red-600 "> + {!balance ? ( + i18n.str`unknown` + ) : ( + <span class="amount"> + <RenderAmount value={balance} negative={balanceIsDebit} /> + </span> + )} + </td> + <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-0"> + <a href="#" class="text-indigo-600 hover:text-indigo-900" + onClick={(e) => { + e.preventDefault(); + onUpdateAccountPassword(item.username) + }} + > + change password + </a> + <br /> + {config.have_cashout ? + <Fragment> + <a href="#" class="text-indigo-600 hover:text-indigo-900" onClick={(e) => { + e.preventDefault(); + onShowCashoutForAccount(item.username) + }} + > + cashouts + </a> + <br /> + </Fragment> + : undefined} + {noBalance ? <a href="#" class="text-indigo-600 hover:text-indigo-900" onClick={(e) => { e.preventDefault(); - onShowCashoutForAccount(item.username) + onRemoveAccount(item.username) }} > - cashouts + remove </a> - <br /> - </Fragment> - : undefined} - {noBalance ? - <a href="#" class="text-indigo-600 hover:text-indigo-900" onClick={(e) => { - e.preventDefault(); - onRemoveAccount(item.username) - }} - > - remove - </a> - : undefined} - </td> - </tr> - })} + : undefined} + </td> + </tr> + })} - </tbody> - </table> - )} + </tbody> + </table> + )} + </div> </div> </div> </div> - </div> + </Fragment> + }
\ No newline at end of file diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx b/packages/demobank-ui/src/pages/admin/AdminHome.tsx index 01f9f6dbd..78827d3a2 100644 --- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx +++ b/packages/demobank-ui/src/pages/admin/AdminHome.tsx @@ -1,7 +1,14 @@ +import { AmountJson, AmountString, Amounts, TalerCorebankApi, TalerError } from "@gnu-taler/taler-util"; +import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; +import { useState } from "preact/hooks"; import { Transactions } from "../../components/Transactions/index.js"; +import { MonitorMetrics, useLastMonitorInfo } from "../../hooks/circuit.js"; +import { RenderAmount } from "../PaytoWireTransferForm.js"; import { WireTransfer } from "./Account.js"; import { AccountList } from "./AccountList.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; +import { ErrorLoading } from "../../components/ErrorLoading.js"; /** * Query account information and show QR code if there is pending withdrawal @@ -17,6 +24,10 @@ interface Props { } export function AdminHome({ onCreateAccount, onRegister, onRemoveAccount, onShowAccountDetails, onShowCashoutForAccount, onUpdateAccountPassword }: Props): VNode { return <Fragment> + <Metrics /> + <WireTransfer onRegister={onRegister} /> + + <Transactions account="admin" /> <AccountList onCreateAccount={onCreateAccount} onRemoveAccount={onRemoveAccount} @@ -25,8 +36,167 @@ export function AdminHome({ onCreateAccount, onRegister, onRemoveAccount, onShow onUpdateAccountPassword={onUpdateAccountPassword} /> - <WireTransfer onRegister={onRegister} /> + </Fragment> +} - <Transactions account="admin" /> +function Metrics(): VNode { + const { i18n } = useTranslationContext() + const [metricType, setMetricType] = useState<TalerCorebankApi.MonitorTimeframeParam>(TalerCorebankApi.MonitorTimeframeParam.day); + + const resp = useLastMonitorInfo(new Date(), metricType); + console.log(resp) + if (!resp) return <Fragment />; + if (resp instanceof TalerError) { + return <ErrorLoading error={resp} /> + } + if (resp.current.type !== "ok" || resp.current.type !== "ok" || resp.current.type !== "ok") { + return <Fragment /> + } + if (resp.previous.type !== "ok" || resp.previous.type !== "ok" || resp.previous.type !== "ok") { + return <Fragment /> + } + + // const metric = getMetricInfo(metricType, current, previous); + + return <Fragment> + <div class="sm:hidden"> + <label for="tabs" class="sr-only"><i18n.Translate>Select a section</i18n.Translate></label> + <select id="tabs" name="tabs" class="block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500" onChange={(e) => { + // const op = e.currentTarget.value as typeof metricType + setMetricType(e.currentTarget.value as any) + }}> + <option value={TalerCorebankApi.MonitorTimeframeParam.hour} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour}><i18n.Translate>Last hour</i18n.Translate></option> + <option value={TalerCorebankApi.MonitorTimeframeParam.day} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day}><i18n.Translate>Last day</i18n.Translate></option> + <option value={TalerCorebankApi.MonitorTimeframeParam.month} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month}><i18n.Translate>Last month</i18n.Translate></option> + <option value={TalerCorebankApi.MonitorTimeframeParam.year} selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year}><i18n.Translate>Last year</i18n.Translate></option> + </select> + </div> + <div class="hidden sm:block"> + <nav class="isolate flex divide-x divide-gray-200 rounded-lg shadow" aria-label="Tabs"> + <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.hour) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour} class="rounded-l-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10" > + <span><i18n.Translate>Last hour</i18n.Translate></span> + <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.hour} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span> + </a> + <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.day) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day} aria-current="page" class=" text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10"> + <span><i18n.Translate>Last day</i18n.Translate></span> + <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.day} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span> + </a> + <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.month) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month} class="rounded-r-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10"> + <span><i18n.Translate>Last month</i18n.Translate></span> + <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.month} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span> + </a> + <a href="#" onClick={(e) => { e.preventDefault(); setMetricType(TalerCorebankApi.MonitorTimeframeParam.year) }} data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year} class="rounded-r-lg text-gray-500 hover:text-gray-700 data-[selected=true]:text-gray-900 group relative min-w-0 flex-1 overflow-hidden bg-white py-4 px-4 text-center text-sm font-medium hover:bg-gray-50 focus:z-10"> + <span><i18n.Translate>Last Year</i18n.Translate></span> + <span aria-hidden="true" data-selected={metricType == TalerCorebankApi.MonitorTimeframeParam.year} class="bg-transparent data-[selected=true]:bg-indigo-500 absolute inset-x-0 bottom-0 h-0.5"></span> + </a> + </nav> + </div> + + <div class="w-full flex justify-between"> + + <h1 class="text-base font-semibold leading-7 text-gray-900 mt-5"> + + Trading volume on Thursday + </h1> + <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"> + ASD + </span> + </span> */} + <button type="button" class="bg-gray-200 relative inline-flex h-6 w-48 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"> + <span class="sr-only">Use setting</span> + {/* <!-- Enabled: "translate-x-5", Not Enabled: "translate-x-0" --> */} + <span class="translate-x-0 pointer-events-none relative inline-block h-5 w-32 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"> + {/* <!-- Enabled: "opacity-0 duration-100 ease-out", Not Enabled: "opacity-100 duration-200 ease-in" --> */} + <span class="opacity-100 duration-200 ease-in absolute inset-0 flex h-full w-full items-center justify-center transition-opacity" aria-hidden="true"> + Transaction + </span> + {/* <!-- Enabled: "opacity-100 duration-200 ease-in", Not Enabled: "opacity-0 duration-100 ease-out" --> */} + <span class="opacity-0 duration-100 ease-out absolute inset-0 flex h-full w-full items-center justify-center transition-opacity" aria-hidden="true"> + <svg class="h-3 w-3 text-indigo-600" fill="currentColor" viewBox="0 0 12 12"> + <path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" /> + </svg> + </span> + </span> + </button> + </div> + + </div> + <dl class="mt-5 grid grid-cols-1 divide-y divide-gray-200 overflow-hidden rounded-lg bg-white shadow-lg md:grid-cols-3 md:divide-x md:divide-y-0"> + + <div class="px-4 py-5 sm:p-6"> + <dt class="text-base font-normal text-gray-900"> + <i18n.Translate>Cashin</i18n.Translate> + </dt> + <MetricValue + current={resp.current.body.cashinExternalVolume} + previous={resp.previous.body.cashinExternalVolume} + /> + </div> + + <div class="px-4 py-5 sm:p-6"> + <dt class="text-base font-normal text-gray-900"> + <i18n.Translate>Cashout</i18n.Translate> + </dt> + <MetricValue + current={resp.current.body.cashoutExternalVolume} + previous={resp.previous.body.cashoutExternalVolume} + /> + </div> + <div class="px-4 py-5 sm:p-6"> + <dt class="text-base font-normal text-gray-900"> + <i18n.Translate>Payout</i18n.Translate> + </dt> + <MetricValue + current={resp.current.body.talerPayoutInternalVolume} + previous={resp.previous.body.talerPayoutInternalVolume} + /> + </div> + </dl> </Fragment> -}
\ No newline at end of file + +} + + +function MetricValue({ current, previous }: { current: AmountString | undefined, previous: AmountString | undefined }): VNode { + const { i18n } = useTranslationContext() + const cmp = current && previous ? Amounts.cmp(current, previous) : 0; + const currAmount = !current ? undefined : Number.parseFloat(Amounts.stringifyValue(current)) + const prevAmount = !previous ? undefined : Number.parseFloat(Amounts.stringifyValue(previous)) + + const rate = !currAmount || Number.isNaN(currAmount) || !prevAmount || Number.isNaN(prevAmount) ? 0 : + cmp === -1 ? 1 - Math.round(currAmount) / Math.round(prevAmount) : + cmp === 1 ? (Math.round(currAmount) / Math.round(prevAmount)) - 1 : 0; + + const rateStr = `${(Math.abs(rate) * 100).toFixed(2)}%` + return <dd class="mt-1 flex justify-between md:block lg:flex"> + <div class="flex justify-start items-baseline text-2xl font-semibold text-indigo-600"> + {!current ? "-" : <RenderAmount value={Amounts.parseOrThrow(current)} />} + </div> + <div class="flex justify-end items-baseline text-2xl font-semibold text-indigo-600"> + <small class="ml-2 text-sm font-medium text-gray-500"> + <i18n.Translate>from</i18n.Translate> {!previous ? "-" : <RenderAmount value={Amounts.parseOrThrow(previous)} />} + </small> + </div> + + {cmp == 1 && + <div class="inline-flex items-baseline rounded-full px-2.5 py-0.5 text-sm font-medium bg-green-100 text-green-800 md:mt-2 lg:mt-0"> + <svg class="-ml-1 mr-0.5 h-5 w-5 flex-shrink-0 self-center text-green-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> + <path fill-rule="evenodd" d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z" clip-rule="evenodd" /> + </svg> + <span class="sr-only"><i18n.Translate>Increased by</i18n.Translate></span> + {rateStr} + </div> + } + {cmp == -1 && + <div class="inline-flex items-baseline rounded-full px-2.5 py-0.5 text-sm font-medium bg-red-100 text-red-800 md:mt-2 lg:mt-0"> + <svg class="-ml-1 mr-0.5 h-5 w-5 flex-shrink-0 self-center text-red-500" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> + <path fill-rule="evenodd" d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z" clip-rule="evenodd" /> + </svg> + <span class="sr-only"><i18n.Translate>Descreased by</i18n.Translate></span> + {rateStr} + </div> + } + </dd> +} diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx index 772ea6e84..ea40001c0 100644 --- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx @@ -7,7 +7,7 @@ import { getRandomPassword } from "../rnd.js"; import { AccountForm, AccountFormData } from "./AccountForm.js"; import { useBackendState } from "../../hooks/backend.js"; import { useBankCoreApiContext } from "../../context/config.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { mutate } from "swr"; import { Attention } from "../../components/Attention.js"; diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx index 88961c2cb..4aa17e302 100644 --- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx +++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx @@ -8,7 +8,7 @@ import { Loading } from "../../components/Loading.js"; import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; import { useAccountDetails } from "../../hooks/access.js"; import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling } from "../../utils.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { LoginForm } from "../LoginForm.js"; import { doAutoFocus } from "../PaytoWireTransferForm.js"; import { useBankCoreApiContext } from "../../context/config.js"; diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx b/packages/demobank-ui/src/pages/business/CreateCashout.tsx index 4696c899e..5595d3b51 100644 --- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx +++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx @@ -45,7 +45,7 @@ import { } from "../../utils.js"; import { LoginForm } from "../LoginForm.js"; import { InputAmount } from "../PaytoWireTransferForm.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; import { Attention } from "../../components/Attention.js"; interface Props { diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx index a8e34e4b9..5c09e2001 100644 --- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx +++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx @@ -37,7 +37,7 @@ import { undefinedIfEmpty, withRuntimeErrorHandling } from "../../utils.js"; -import { assertUnreachable } from "../HomePage.js"; +import { assertUnreachable } from "../WithdrawalOperationPage.js"; interface Props { id: string; |