diff options
Diffstat (limited to 'packages/demobank-ui/src/pages/admin/AdminHome.tsx')
-rw-r--r-- | packages/demobank-ui/src/pages/admin/AdminHome.tsx | 246 |
1 files changed, 0 insertions, 246 deletions
diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx b/packages/demobank-ui/src/pages/admin/AdminHome.tsx deleted file mode 100644 index 82a341dbe..000000000 --- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import { AmountString, Amounts, CurrencySpecification, TalerCorebankApi, TalerError, assertUnreachable } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { format, getDate, getHours, getMonth, getYear, setDate, setHours, setMonth, setYear, sub } from "date-fns"; -import { Fragment, VNode, h } from "preact"; -import { useState } from "preact/hooks"; -import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js"; -import { Transactions } from "../../components/Transactions/index.js"; -import { useBankCoreApiContext } from "../../context/config.js"; -import { useConversionInfo, useLastMonitorInfo } from "../../hooks/circuit.js"; -import { RenderAmount } from "../PaytoWireTransferForm.js"; -import { WireTransfer } from "../WireTransfer.js"; -import { AccountList } from "./AccountList.js"; - - -/** - * Query account information and show QR code if there is pending withdrawal - */ -interface Props { - onRegister: () => void; - - onCreateAccount: () => void; - onShowAccountDetails: (aid: string) => void; - onRemoveAccount: (aid: string) => void; - onUpdateAccountPassword: (aid: string) => void; - onShowCashoutForAccount: (aid: string) => void; -} -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} - onShowCashoutForAccount={onShowCashoutForAccount} - onShowAccountDetails={onShowAccountDetails} - onUpdateAccountPassword={onUpdateAccountPassword} - /> - - </Fragment> -} - -function getDateForTimeframe(which: number, timeframe: TalerCorebankApi.MonitorTimeframeParam): string { - const time = Date.now() - - switch (timeframe) { - case TalerCorebankApi.MonitorTimeframeParam.hour: return `${format(setHours(time, which), "HH")}hs`; - case TalerCorebankApi.MonitorTimeframeParam.day: return format(setDate(time, which), "EEEE"); - case TalerCorebankApi.MonitorTimeframeParam.month: return format(setMonth(time, which), "MMMM"); - case TalerCorebankApi.MonitorTimeframeParam.year: return format(setYear(time, which), "yyyy"); - case TalerCorebankApi.MonitorTimeframeParam.decade: return format(setYear(time, which), "yyyy"); - } - assertUnreachable(timeframe) -} - -export 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) - } -} - - -function Metrics(): VNode { - const { i18n } = useTranslationContext() - const [metricType, setMetricType] = useState<TalerCorebankApi.MonitorTimeframeParam>(TalerCorebankApi.MonitorTimeframeParam.hour); - const { config } = useBankCoreApiContext(); - const respInfo = useConversionInfo() - const params = getTimeframesForDate(new Date(), metricType) - - const resp = useLastMonitorInfo(params.current, params.previous, metricType); - if (!resp) return <Fragment />; - if (resp instanceof TalerError) { - return <ErrorLoadingWithDebug error={resp} /> - } - if (resp.current.type !== "ok" || resp.previous.type !== "ok") { - return <Fragment /> - } - const fiatSpec = respInfo && (!(respInfo instanceof TalerError)) ? respInfo.body.fiat_currency_specification : undefined - 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"> - <i18n.Translate>Trading volume on {getDateForTimeframe(params.current, metricType)} compared to {getDateForTimeframe(params.previous, metricType)}</i18n.Translate> - </h1> - </div> - <dl class="mt-5 grid grid-cols-1 md:grid-cols-2 divide-y divide-gray-200 overflow-hidden rounded-lg bg-white shadow-lg md:divide-x md:divide-y-0"> - - {!fiatSpec || resp.current.body.type !== "with-conversions" || resp.previous.body.type !== "with-conversions" ? undefined : - <Fragment> - <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.cashinFiatVolume} - previous={resp.previous.body.cashinFiatVolume} - spec={fiatSpec} - /> - </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.cashoutFiatVolume} - previous={resp.previous.body.cashoutFiatVolume} - spec={fiatSpec} - /> - </div> - </Fragment> - } - <div class="px-4 py-5 sm:p-6"> - <dt class="text-base font-normal text-gray-900"> - <i18n.Translate>Payin</i18n.Translate> - </dt> - <MetricValue - current={resp.current.body.talerInVolume} - previous={resp.previous.body.talerInVolume} - spec={config.currency_specification} - /> - </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.talerOutVolume} - previous={resp.previous.body.talerOutVolume} - spec={config.currency_specification} - /> - </div> - </dl> - <div class="flex justify-end mt-2"> - <a href="#/download-stats" - class="disabled:opacity-50 disabled:cursor-default cursor-pointer 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" - ><i18n.Translate> - download stats as csv - </i18n.Translate></a> - </div> - </Fragment> - -} - - -function MetricValue({ current, previous, spec }: { spec: CurrencySpecification, current: AmountString | undefined, previous: AmountString | undefined }): VNode { - const { i18n } = useTranslationContext() - const cmp = current && previous ? Amounts.cmp(current, previous) : 0; - const cv = !current ? undefined : Amounts.stringifyValue(current) - const currAmount = !cv ? undefined : Number.parseFloat(cv) - 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 negative = cmp === 0 ? undefined : cmp === -1 - const rateStr = `${(Math.abs(rate) * 100).toFixed(2)}%` - return <Fragment> - <dd class="mt-1 block "> - <div class="flex justify-start text-2xl items-baseline font-semibold text-indigo-600"> - {!current ? "-" : <RenderAmount value={Amounts.parseOrThrow(current)} spec={spec} hideSmall />} - </div> - <div class="flex flex-col"> - - <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)} spec={spec} hideSmall />} - </small> - </div> - {!!rate && - <span data-negative={negative} class="flex items-center gap-x-1.5 w-fit rounded-md bg-green-100 text-green-800 data-[negative=true]:bg-red-100 px-2 py-1 text-xs font-medium data-[negative=true]:text-red-700 whitespace-pre"> - {negative ? - <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> - <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m0 0l6.75-6.75M12 19.5l-6.75-6.75" /> - </svg> - : - <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> - <path stroke-linecap="round" stroke-linejoin="round" d="M12 19.5v-15m0 0l-6.75 6.75M12 4.5l6.75 6.75" /> - </svg> - } - - {negative ? - <span class="sr-only"><i18n.Translate>Descreased by</i18n.Translate></span> : - <span class="sr-only"><i18n.Translate>Increased by</i18n.Translate></span> - } - {rateStr} - </span> - } - </div> - - </dd> - </Fragment> -} |