diff options
Diffstat (limited to 'packages/bank-ui/src/components/Transactions/views.tsx')
-rw-r--r-- | packages/bank-ui/src/components/Transactions/views.tsx | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/packages/bank-ui/src/components/Transactions/views.tsx b/packages/bank-ui/src/components/Transactions/views.tsx new file mode 100644 index 000000000..7da9fc5a9 --- /dev/null +++ b/packages/bank-ui/src/components/Transactions/views.tsx @@ -0,0 +1,243 @@ +/* + 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 { Attention, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { format } from "date-fns"; +import { Fragment, VNode, h } from "preact"; +import { useBankCoreApiContext } from "../../context/config.js"; +import { RenderAmount } from "../../pages/PaytoWireTransferForm.js"; +import { State } from "./index.js"; +import { Duration } from "@gnu-taler/taler-util"; +import { Time } from "../Time.js"; + +export function ReadyView({ + transactions, + routeCreateWireTransfer, + onGoNext, + onGoStart, +}: State.Ready): VNode { + const { i18n, dateLocale } = useTranslationContext(); + const { config } = useBankCoreApiContext() + + if (!transactions.length) { + 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>Transactions history</i18n.Translate> + </h1> + </div> + </div> + + <Attention type="low" title={i18n.str`No transactions yet.`}> + <i18n.Translate> + You can start sending a wire transfer or withdrawing to your wallet. + </i18n.Translate> + </Attention> + </div>; + } + + const txByDate = transactions.reduce( + (prev, cur) => { + const d = + cur.when.t_ms === "never" + ? "" + : format(cur.when.t_ms, "dd/MM/yyyy", { locale: dateLocale }); + if (!prev[d]) { + prev[d] = []; + } + prev[d].push(cur); + return prev; + }, + {} as Record<string, typeof transactions>, + ); + 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>Transactions history</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`Date`}</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`Amount`}</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`Counterpart`}</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={4} + 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 ( + <tr + key={idx} + class="border-b border-gray-200 last:border-none" + > + <td class="relative py-2 pl-2 pr-2 text-sm "> + <div class="font-medium text-gray-900"> + <Time format="HH:mm:ss" + timestamp={item.when} + // relative={Duration.fromSpec({ days: 1 })} + /> + </div> + <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} + spec={config.currency_specification} + /> + </span> + ) : ( + <span style={{ color: "grey" }}> + <{i18n.str`Invalid value`}> + </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`}{" "} + {!routeCreateWireTransfer ? item.counterpart : + <a + name={`transfer to ${item.counterpart}`} + href={routeCreateWireTransfer.url({ + account: item.counterpart, + })} + class="text-indigo-600 hover:text-indigo-900" + > + {item.counterpart} + </a> + } + </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 + data-negative={item.negative ? "true" : "false"} + class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 " + > + {item.amount ? ( + <RenderAmount + value={item.amount} + negative={item.negative} + withColor + spec={config.currency_specification} + /> + ) : ( + <span style={{ color: "grey" }}> + <{i18n.str`Invalid value`}> + </span> + )} + </td> + <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500"> + {!routeCreateWireTransfer ? item.counterpart : + <a + name={`wire transfer to ${item.counterpart}`} + href={routeCreateWireTransfer.url({ + account: item.counterpart, + })} + class="text-indigo-600 hover:text-indigo-900" + > + {item.counterpart} + </a> + } + </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> + </tr> + ); + })} + </Fragment> + ); + })} + </tbody> + </table> + + <nav + class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6 rounded-lg" + aria-label="Pagination" + > + <div class="flex flex-1 justify-between sm:justify-end"> + <button + name="first page" + class="relative disabled:bg-gray-100 disabled:text-gray-500 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0" + disabled={!onGoStart} + onClick={onGoStart} + > + <i18n.Translate>First page</i18n.Translate> + </button> + <button + name="next page" + class="relative disabled:bg-gray-100 disabled:text-gray-500 ml-3 inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:outline-offset-0" + disabled={!onGoNext} + onClick={onGoNext} + > + <i18n.Translate>Next</i18n.Translate> + </button> + </div> + </nav> + </div> + </div> + ); +} |