summaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/admin/AdminHome.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/pages/admin/AdminHome.tsx')
-rw-r--r--packages/demobank-ui/src/pages/admin/AdminHome.tsx246
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>
-}