summaryrefslogtreecommitdiff
path: root/packages/bank-ui/src/components/Cashouts/views.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bank-ui/src/components/Cashouts/views.tsx')
-rw-r--r--packages/bank-ui/src/components/Cashouts/views.tsx218
1 files changed, 218 insertions, 0 deletions
diff --git a/packages/bank-ui/src/components/Cashouts/views.tsx b/packages/bank-ui/src/components/Cashouts/views.tsx
new file mode 100644
index 000000000..22b8d8c1b
--- /dev/null
+++ b/packages/bank-ui/src/components/Cashouts/views.tsx
@@ -0,0 +1,218 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import {
+ AbsoluteTime,
+ Amounts,
+ HttpStatusCode,
+ TalerError,
+ assertUnreachable,
+} from "@gnu-taler/taler-util";
+import {
+ Attention,
+ Loading,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { format } from "date-fns";
+import { Fragment, VNode, h } from "preact";
+import { useConversionInfo } from "../../hooks/regional.js";
+import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
+import { ErrorLoadingWithDebug } from "../ErrorLoadingWithDebug.js";
+import { Time } from "../Time.js";
+import { State } from "./index.js";
+
+export function FailedView({ error }: State.Failed) {
+ const { i18n } = useTranslationContext();
+ switch (error.case) {
+ case HttpStatusCode.NotImplemented: {
+ return (
+ <Attention type="danger" title={i18n.str`Cashout are disabled`}>
+ <i18n.Translate>
+ Cashout should be enable by configuration and the conversion rate
+ should be initialized with fee, ratio and rounding mode.
+ </i18n.Translate>
+ </Attention>
+ );
+ }
+ default:
+ assertUnreachable(error.case);
+ }
+}
+
+export function ReadyView({
+ cashouts,
+ routeCashoutDetails,
+}: State.Ready): VNode {
+ const { i18n, dateLocale } = useTranslationContext();
+ const resp = useConversionInfo();
+ if (!resp) {
+ return <Loading />;
+ }
+ if (resp instanceof TalerError) {
+ return <ErrorLoadingWithDebug error={resp} />;
+ }
+ if (resp.type === "fail") {
+ switch (resp.case) {
+ case HttpStatusCode.NotImplemented: {
+ return (
+ <Attention type="danger" title={i18n.str`Cashout are disabled`}>
+ <i18n.Translate>
+ Cashout should be enable by configuration and the conversion rate
+ should be initialized with fee, ratio and rounding mode.
+ </i18n.Translate>
+ </Attention>
+ );
+ }
+ default:
+ assertUnreachable(resp.case);
+ }
+ }
+
+ if (!cashouts.length) return <div />;
+ const txByDate = cashouts.reduce(
+ (prev, cur) => {
+ const d =
+ cur.creation_time.t_s === "never"
+ ? ""
+ : format(cur.creation_time.t_s * 1000, "dd/MM/yyyy", {
+ locale: dateLocale,
+ });
+ if (!prev[d]) {
+ prev[d] = [];
+ }
+ prev[d].push(cur);
+ return prev;
+ },
+ {} as Record<string, typeof cashouts>,
+ );
+ return (
+ <div class="px-4 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>Latest cashouts</i18n.Translate>
+ </h1>
+ </div>
+ </div>
+ <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
+ <table class="min-w-full divide-y divide-gray-300">
+ <thead>
+ <tr>
+ <th
+ scope="col"
+ class=" pl-2 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >{i18n.str`Created`}</th>
+ <th
+ scope="col"
+ class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >{i18n.str`Total debit`}</th>
+ <th
+ scope="col"
+ class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >{i18n.str`Total credit`}</th>
+ <th
+ scope="col"
+ class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900"
+ >{i18n.str`Subject`}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {Object.entries(txByDate).map(([date, txs], idx) => {
+ return (
+ <Fragment key={idx}>
+ <tr class="border-t border-gray-200">
+ <th
+ colSpan={6}
+ scope="colgroup"
+ class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3"
+ >
+ {date}
+ </th>
+ </tr>
+ {txs.map((item) => {
+ return (
+ <a
+ name="cashout details"
+ key={idx}
+ class="table-row border-b border-gray-200 hover:bg-gray-200 last:border-none"
+ // class="table-row"
+ href={routeCashoutDetails.url({
+ cid: String(item.id),
+ })}
+ >
+ <td class="relative py-2 pl-2 pr-2 text-sm ">
+ <div class="font-medium text-gray-900">
+ <Time
+ format="HH:mm:ss"
+ timestamp={AbsoluteTime.fromProtocolTimestamp(
+ item.creation_time,
+ )}
+ // relative={Duration.fromSpec({ days: 1 })}
+ />
+ </div>
+ {
+ //FIXME: implement responsive view
+ }
+ {/* <dl class="font-normal sm:hidden">
+ <dt class="sr-only sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt>
+ <dd class="mt-1 truncate text-gray-700">
+ {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? (
+ <span data-negative={item.negative ? "true" : "false"} class="data-[negative=false]:text-green-600 data-[negative=true]:text-red-600">
+ <RenderAmount value={item.amount} />
+ </span>
+ ) : (
+ <span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
+ )}</dd>
+
+ <dt class="sr-only sm:hidden"><i18n.Translate>Counterpart</i18n.Translate></dt>
+ <dd class="mt-1 truncate text-gray-500 sm:hidden">
+ {item.negative ? i18n.str`to` : i18n.str`from`} {item.counterpart}
+ </dd>
+ <dd class="mt-1 text-gray-500 sm:hidden" >
+ <pre class="break-words w-56 whitespace-break-spaces p-2 rounded-md mx-auto my-2 bg-gray-100">
+ {item.subject}
+ </pre>
+ </dd>
+ </dl> */}
+ </td>
+ <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-red-600 cursor-pointer">
+ <RenderAmount
+ value={Amounts.parseOrThrow(item.amount_debit)}
+ spec={resp.body.regional_currency_specification}
+ />
+ </td>
+ <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-green-600 cursor-pointer">
+ <RenderAmount
+ value={Amounts.parseOrThrow(item.amount_credit)}
+ spec={resp.body.fiat_currency_specification}
+ />
+ </td>
+
+ <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">
+ {item.subject}
+ </td>
+ </a>
+ );
+ })}
+ </Fragment>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ );
+}