From ddd32a690bd13b1eb1aef1356a1d59fd64e254bf Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 8 Mar 2024 14:09:31 -0300 Subject: demobank => bank --- packages/bank-ui/src/components/Cashouts/index.ts | 86 ++++++++ packages/bank-ui/src/components/Cashouts/state.ts | 51 +++++ .../bank-ui/src/components/Cashouts/stories.tsx | 29 +++ packages/bank-ui/src/components/Cashouts/test.ts | 68 ++++++ packages/bank-ui/src/components/Cashouts/views.tsx | 217 ++++++++++++++++++ .../src/components/EmptyComponentExample/index.ts | 56 +++++ .../src/components/EmptyComponentExample/state.ts | 25 +++ .../components/EmptyComponentExample/stories.tsx | 29 +++ .../src/components/EmptyComponentExample/test.ts | 28 +++ .../src/components/EmptyComponentExample/views.tsx | 25 +++ .../src/components/ErrorLoadingWithDebug.tsx | 24 ++ packages/bank-ui/src/components/QR.tsx | 51 +++++ packages/bank-ui/src/components/Time.tsx | 71 ++++++ .../bank-ui/src/components/Transactions/index.ts | 80 +++++++ .../bank-ui/src/components/Transactions/state.ts | 80 +++++++ .../src/components/Transactions/stories.tsx | 44 ++++ .../bank-ui/src/components/Transactions/test.ts | 202 +++++++++++++++++ .../bank-ui/src/components/Transactions/views.tsx | 243 +++++++++++++++++++++ packages/bank-ui/src/components/index.examples.ts | 17 ++ 19 files changed, 1426 insertions(+) create mode 100644 packages/bank-ui/src/components/Cashouts/index.ts create mode 100644 packages/bank-ui/src/components/Cashouts/state.ts create mode 100644 packages/bank-ui/src/components/Cashouts/stories.tsx create mode 100644 packages/bank-ui/src/components/Cashouts/test.ts create mode 100644 packages/bank-ui/src/components/Cashouts/views.tsx create mode 100644 packages/bank-ui/src/components/EmptyComponentExample/index.ts create mode 100644 packages/bank-ui/src/components/EmptyComponentExample/state.ts create mode 100644 packages/bank-ui/src/components/EmptyComponentExample/stories.tsx create mode 100644 packages/bank-ui/src/components/EmptyComponentExample/test.ts create mode 100644 packages/bank-ui/src/components/EmptyComponentExample/views.tsx create mode 100644 packages/bank-ui/src/components/ErrorLoadingWithDebug.tsx create mode 100644 packages/bank-ui/src/components/QR.tsx create mode 100644 packages/bank-ui/src/components/Time.tsx create mode 100644 packages/bank-ui/src/components/Transactions/index.ts create mode 100644 packages/bank-ui/src/components/Transactions/state.ts create mode 100644 packages/bank-ui/src/components/Transactions/stories.tsx create mode 100644 packages/bank-ui/src/components/Transactions/test.ts create mode 100644 packages/bank-ui/src/components/Transactions/views.tsx create mode 100644 packages/bank-ui/src/components/index.examples.ts (limited to 'packages/bank-ui/src/components') diff --git a/packages/bank-ui/src/components/Cashouts/index.ts b/packages/bank-ui/src/components/Cashouts/index.ts new file mode 100644 index 000000000..2c6bf681c --- /dev/null +++ b/packages/bank-ui/src/components/Cashouts/index.ts @@ -0,0 +1,86 @@ +/* + 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 + */ + +import { Loading, utils } from "@gnu-taler/web-util/browser"; +import { + AbsoluteTime, + AmountJson, + TalerCoreBankErrorsByMethod, + TalerCorebankApi, + TalerError, +} from "@gnu-taler/taler-util"; +import { ErrorLoadingWithDebug } from "../ErrorLoadingWithDebug.js"; +import { useComponentState } from "./state.js"; +import { FailedView, ReadyView } from "./views.js"; +import { RouteDefinition } from "../../route.js"; + +export interface Props { + account: string; + routeCashoutDetails: RouteDefinition<{ cid: string }>; +} + +export type State = + | State.Loading + | State.Failed + | State.LoadingUriError + | State.Ready; + +export namespace State { + export interface Loading { + status: "loading"; + error: undefined; + } + + export interface LoadingUriError { + status: "loading-error"; + error: TalerError; + } + + export interface Failed { + status: "failed"; + error: TalerCoreBankErrorsByMethod<"getAccountCashouts">; + } + + export interface BaseInfo { + error: undefined; + } + export interface Ready extends BaseInfo { + status: "ready"; + error: undefined; + cashouts: (TalerCorebankApi.CashoutStatusResponse & { id: number })[]; + routeCashoutDetails: RouteDefinition<{ cid: string }>; + } +} + +export interface Transaction { + negative: boolean; + counterpart: string; + when: AbsoluteTime; + amount: AmountJson | undefined; + subject: string; +} + +const viewMapping: utils.StateViewMap = { + loading: Loading, + "loading-error": ErrorLoadingWithDebug, + failed: FailedView, + ready: ReadyView, +}; + +export const Cashouts = utils.compose( + (p: Props) => useComponentState(p), + viewMapping, +); diff --git a/packages/bank-ui/src/components/Cashouts/state.ts b/packages/bank-ui/src/components/Cashouts/state.ts new file mode 100644 index 000000000..8616faa1b --- /dev/null +++ b/packages/bank-ui/src/components/Cashouts/state.ts @@ -0,0 +1,51 @@ +/* + 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 + */ + +import { TalerError } from "@gnu-taler/taler-util"; +import { useCashouts } from "../../hooks/regional.js"; +import { Props, State } from "./index.js"; + +export function useComponentState({ + account, + routeCashoutDetails, +}: Props): State { + const result = useCashouts(account); + if (!result) { + return { + status: "loading", + error: undefined, + }; + } + if (result instanceof TalerError) { + return { + status: "loading-error", + error: result, + }; + } + if (result.type === "fail") { + return { + status: "failed", + error: result, + }; + } + + return { + status: "ready", + error: undefined, + cashouts: result.body.cashouts, + routeCashoutDetails, + }; +} diff --git a/packages/bank-ui/src/components/Cashouts/stories.tsx b/packages/bank-ui/src/components/Cashouts/stories.tsx new file mode 100644 index 000000000..37ab64108 --- /dev/null +++ b/packages/bank-ui/src/components/Cashouts/stories.tsx @@ -0,0 +1,29 @@ +/* + 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import * as tests from "@gnu-taler/web-util/testing"; +import { ReadyView } from "./views.js"; + +export default { + title: "transaction list", +}; + +export const Ready = tests.createExample(ReadyView, {}); diff --git a/packages/bank-ui/src/components/Cashouts/test.ts b/packages/bank-ui/src/components/Cashouts/test.ts new file mode 100644 index 000000000..4bd6b5eac --- /dev/null +++ b/packages/bank-ui/src/components/Cashouts/test.ts @@ -0,0 +1,68 @@ +/* + 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import * as tests from "@gnu-taler/web-util/testing"; +import { SwrMockEnvironment } from "@gnu-taler/web-util/testing"; +import { expect } from "chai"; +import { Props } from "./index.js"; +import { useComponentState } from "./state.js"; +import { buildNullRoutDefinition } from "../../route.js"; + +describe("Cashout states", () => { + it.skip("should query backend and render transactions", async () => { + const env = new SwrMockEnvironment(); + + const props: Props = { + account: "123", + routeCashoutDetails: buildNullRoutDefinition(), + }; + + // env.addRequestExpectation(CASHOUT_API_EXAMPLE.LIST_FIRST_PAGE, { + // response: { + // cashouts: [], + // }, + // }); + + // env.addRequestExpectation(CASHOUT_API_EXAMPLE.MULTI_GET_EMPTY_FIRST_PAGE, { + // response: [], + // }); + + const hookBehavior = await tests.hookBehaveLikeThis( + useComponentState, + props, + [ + ({ status, error }) => { + expect(status).equals("loading"); + expect(error).undefined; + }, + ({ status, error }) => { + expect(status).equals("ready"); + expect(error).undefined; + }, + ], + env.buildTestingContext(), + ); + + expect(hookBehavior).deep.eq({ result: "ok" }); + + expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" }); + }); +}); 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..7f16d5840 --- /dev/null +++ b/packages/bank-ui/src/components/Cashouts/views.tsx @@ -0,0 +1,217 @@ +/* + 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 + */ + +import { + AbsoluteTime, + Amounts, + Duration, + 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 { State } from "./index.js"; +import { Time } from "../Time.js"; + +export function FailedView({ error }: State.Failed) { + const { i18n } = useTranslationContext(); + switch (error.case) { + case HttpStatusCode.NotImplemented: { + return ( + + Cashout should be enable by configuration and the conversion rate should be initialized with fee, ratio and rounding mode. + + ); + } + default: + assertUnreachable(error.case); + } +} + +export function ReadyView({ + cashouts, + routeCashoutDetails, +}: State.Ready): VNode { + const { i18n, dateLocale } = useTranslationContext(); + const resp = useConversionInfo(); + if (!resp) { + return ; + } + if (resp instanceof TalerError) { + return ; + } + if (resp.type === "fail") { + switch (resp.case) { + case HttpStatusCode.NotImplemented: { + return ( + + Cashout should be enable by configuration and the conversion rate should be initialized with fee, ratio and rounding mode. + + ); + } + default: + assertUnreachable(resp.case); + } + } + + if (!cashouts.length) return
; + 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, + ); + return ( +
+
+
+

+ Latest cashouts +

+
+
+
+ + + + + + + + + + + {Object.entries(txByDate).map(([date, txs], idx) => { + return ( + + + + + {txs.map((item) => { + return ( + + + + + + + + ); + })} + + ); + })} + +
{i18n.str`Created`}
+ {date} +
+
+
+ { + //FIXME: implement responsive view + } + {/*
+
Amount
+
+ {item.negative ? i18n.str`sent` : i18n.str`received`} {item.amount ? ( + + + + ) : ( + <{i18n.str`invalid value`}> + )}
+ +
Counterpart
+
+ {item.negative ? i18n.str`to` : i18n.str`from`} {item.counterpart} +
+
+
+                            {item.subject}
+                          
+
+
*/} +
+
+
+ ); +} diff --git a/packages/bank-ui/src/components/EmptyComponentExample/index.ts b/packages/bank-ui/src/components/EmptyComponentExample/index.ts new file mode 100644 index 000000000..da84f9921 --- /dev/null +++ b/packages/bank-ui/src/components/EmptyComponentExample/index.ts @@ -0,0 +1,56 @@ +/* + 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 + */ + +import { HookError, Loading, utils } from "@gnu-taler/web-util/browser"; +import { useComponentState } from "./state.js"; +import { LoadingUriView, ReadyView } from "./views.js"; + +export interface Props { + p: string; +} + +export type State = State.Loading | State.LoadingUriError | State.Ready; + +export namespace State { + export interface Loading { + status: "loading"; + error: undefined; + } + + export interface LoadingUriError { + status: "loading-error"; + error: HookError; + } + + export interface BaseInfo { + error: undefined; + } + export interface Ready extends BaseInfo { + status: "ready"; + error: undefined; + } +} + +const viewMapping: utils.StateViewMap = { + loading: Loading, + "loading-error": LoadingUriView, + ready: ReadyView, +}; + +export const ComponentName = utils.compose( + (p: Props) => useComponentState(p), + viewMapping, +); diff --git a/packages/bank-ui/src/components/EmptyComponentExample/state.ts b/packages/bank-ui/src/components/EmptyComponentExample/state.ts new file mode 100644 index 000000000..057664983 --- /dev/null +++ b/packages/bank-ui/src/components/EmptyComponentExample/state.ts @@ -0,0 +1,25 @@ +/* + 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 + */ + +// import { wxApi } from "../../wxApi.js"; +import { Props, State } from "./index.js"; + +export function useComponentState({ p: _p }: Props): State { + return { + status: "ready", + error: undefined, + }; +} diff --git a/packages/bank-ui/src/components/EmptyComponentExample/stories.tsx b/packages/bank-ui/src/components/EmptyComponentExample/stories.tsx new file mode 100644 index 000000000..160acdf79 --- /dev/null +++ b/packages/bank-ui/src/components/EmptyComponentExample/stories.tsx @@ -0,0 +1,29 @@ +/* + 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import * as tests from "@gnu-taler/web-util/testing"; +import { ReadyView } from "./views.js"; + +export default { + title: "example", +}; + +export const Ready = tests.createExample(ReadyView, {}); diff --git a/packages/bank-ui/src/components/EmptyComponentExample/test.ts b/packages/bank-ui/src/components/EmptyComponentExample/test.ts new file mode 100644 index 000000000..629948d91 --- /dev/null +++ b/packages/bank-ui/src/components/EmptyComponentExample/test.ts @@ -0,0 +1,28 @@ +/* + 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { expect } from "chai"; + +describe("test description", () => { + it("should assert", () => { + expect([]).deep.equals([]); + }); +}); diff --git a/packages/bank-ui/src/components/EmptyComponentExample/views.tsx b/packages/bank-ui/src/components/EmptyComponentExample/views.tsx new file mode 100644 index 000000000..457933a5f --- /dev/null +++ b/packages/bank-ui/src/components/EmptyComponentExample/views.tsx @@ -0,0 +1,25 @@ +/* + 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 + */ + +import { h, VNode } from "preact"; + +export function LoadingUriView(): VNode { + return
; +} + +export function ReadyView(): VNode { + return
; +} diff --git a/packages/bank-ui/src/components/ErrorLoadingWithDebug.tsx b/packages/bank-ui/src/components/ErrorLoadingWithDebug.tsx new file mode 100644 index 000000000..8679af050 --- /dev/null +++ b/packages/bank-ui/src/components/ErrorLoadingWithDebug.tsx @@ -0,0 +1,24 @@ +/* + 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 + */ +import { TalerError } from "@gnu-taler/taler-util"; +import { ErrorLoading } from "@gnu-taler/web-util/browser"; +import { VNode, h } from "preact"; +import { usePreferences } from "../hooks/preferences.js"; + +export function ErrorLoadingWithDebug({ error }: { error: TalerError }): VNode { + const [pref] = usePreferences(); + return ; +} diff --git a/packages/bank-ui/src/components/QR.tsx b/packages/bank-ui/src/components/QR.tsx new file mode 100644 index 000000000..b039bbd1e --- /dev/null +++ b/packages/bank-ui/src/components/QR.tsx @@ -0,0 +1,51 @@ +/* + 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 + */ + +import { h, VNode } from "preact"; +import { useEffect, useRef } from "preact/hooks"; +import qrcode from "qrcode-generator"; + +export function QR({ text }: { text: string }): VNode { + const divRef = useRef(null); + useEffect(() => { + const qr = qrcode(0, "L"); + qr.addData(text); + qr.make(); + if (divRef.current) + divRef.current.innerHTML = qr.createSvgTag({ + scalable: true, + }); + }); + + return ( +
+
+
+ ); +} diff --git a/packages/bank-ui/src/components/Time.tsx b/packages/bank-ui/src/components/Time.tsx new file mode 100644 index 000000000..39ce33f60 --- /dev/null +++ b/packages/bank-ui/src/components/Time.tsx @@ -0,0 +1,71 @@ +/* + This file is part of GNU Taler + (C) 2022 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 + */ + +import { AbsoluteTime, Duration } from "@gnu-taler/taler-util"; +import { useTranslationContext } from "@gnu-taler/web-util/browser"; +import { formatISO, format, formatDuration, intervalToDuration } from "date-fns"; +import { Fragment, h, VNode } from "preact"; + +/** + * + * @param timestamp time to be formatted + * @param relative duration threshold, if the difference is lower + * the timestamp will be formatted as relative time from "now" + * + * @returns + */ +export function Time({ + timestamp, + relative, + format: formatString, +}: { + timestamp: AbsoluteTime | undefined; + relative?: Duration, + format: string; +}): VNode { + const { i18n, dateLocale } = useTranslationContext() + if (!timestamp) return + + if (timestamp.t_ms === "never") { + return + } + + const now = AbsoluteTime.now(); + const diff = AbsoluteTime.difference(now, timestamp) + if (relative && now.t_ms !== "never" && Duration.cmp(diff, relative) === -1) { + const d = intervalToDuration({ + start: now.t_ms, + end: timestamp.t_ms + }) + d.seconds = 0 + const duration = formatDuration(d, { locale: dateLocale }) + const isFuture = AbsoluteTime.cmp(now, timestamp) < 0 + if (isFuture) { + return + } else { + return + } + } + return ( + + ); +} diff --git a/packages/bank-ui/src/components/Transactions/index.ts b/packages/bank-ui/src/components/Transactions/index.ts new file mode 100644 index 000000000..c8bb1e108 --- /dev/null +++ b/packages/bank-ui/src/components/Transactions/index.ts @@ -0,0 +1,80 @@ +/* + 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 + */ + +import { AbsoluteTime, AmountJson, TalerError } from "@gnu-taler/taler-util"; +import { Loading, utils } from "@gnu-taler/web-util/browser"; +import { ErrorLoadingWithDebug } from "../ErrorLoadingWithDebug.js"; +import { useComponentState } from "./state.js"; +import { ReadyView } from "./views.js"; +import { RouteDefinition } from "../../route.js"; + +export interface Props { + account: string; + routeCreateWireTransfer: RouteDefinition<{ + account?: string, + subject?: string, + amount?: string, + }> | undefined; +} + +export type State = State.Loading | State.LoadingUriError | State.Ready; + +export namespace State { + export interface Loading { + status: "loading"; + error: undefined; + } + + export interface LoadingUriError { + status: "loading-error"; + error: TalerError; + } + + export interface BaseInfo { + error: undefined; + } + export interface Ready extends BaseInfo { + status: "ready"; + error: undefined; + routeCreateWireTransfer: RouteDefinition<{ + account?: string, + subject?: string, + amount?: string, + }> | undefined; + transactions: Transaction[]; + onGoStart?: () => void; + onGoNext?: () => void; + } +} + +export interface Transaction { + negative: boolean; + counterpart: string; + when: AbsoluteTime; + amount: AmountJson | undefined; + subject: string; +} + +const viewMapping: utils.StateViewMap = { + loading: Loading, + "loading-error": ErrorLoadingWithDebug, + ready: ReadyView, +}; + +export const Transactions = utils.compose( + (p: Props) => useComponentState(p), + viewMapping, +); diff --git a/packages/bank-ui/src/components/Transactions/state.ts b/packages/bank-ui/src/components/Transactions/state.ts new file mode 100644 index 000000000..3e9103b59 --- /dev/null +++ b/packages/bank-ui/src/components/Transactions/state.ts @@ -0,0 +1,80 @@ +/* + 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 + */ + +import { + AbsoluteTime, + Amounts, + TalerError, + parsePaytoUri, +} from "@gnu-taler/taler-util"; +import { useTransactions } from "../../hooks/account.js"; +import { Props, State, Transaction } from "./index.js"; + +export function useComponentState({ account, routeCreateWireTransfer }: Props): State { + const txResult = useTransactions(account); + if (!txResult) { + return { + status: "loading", + error: undefined, + }; + } + if (txResult instanceof TalerError) { + return { + status: "loading-error", + error: txResult, + }; + } + + const transactions = + txResult.result + .map((tx) => { + const negative = tx.direction === "debit"; + const cp = parsePaytoUri( + negative ? tx.creditor_payto_uri : tx.debtor_payto_uri, + ); + const counterpart = + (cp === undefined || !cp.isKnown + ? undefined + : cp.targetType === "iban" + ? cp.iban + : cp.targetType === "x-taler-bank" + ? cp.account + : cp.targetType === "bitcoin" + ? `${cp.targetPath.substring(0, 6)}...` + : undefined) ?? "unknown"; + + const when = AbsoluteTime.fromProtocolTimestamp(tx.date); + const amount = Amounts.parse(tx.amount); + const subject = tx.subject; + return { + negative, + counterpart, + when, + amount, + subject, + }; + }) + .filter((x): x is Transaction => x !== undefined); + + return { + status: "ready", + error: undefined, + routeCreateWireTransfer, + transactions, + onGoNext: txResult.isLastPage ? undefined : txResult.loadNext, + onGoStart: txResult.isFirstPage ? undefined : txResult.loadFirst, + }; +} diff --git a/packages/bank-ui/src/components/Transactions/stories.tsx b/packages/bank-ui/src/components/Transactions/stories.tsx new file mode 100644 index 000000000..95014574b --- /dev/null +++ b/packages/bank-ui/src/components/Transactions/stories.tsx @@ -0,0 +1,44 @@ +/* + 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import * as tests from "@gnu-taler/web-util/testing"; +import { ReadyView } from "./views.js"; +import { AbsoluteTime } from "@gnu-taler/taler-util"; + +export default { + title: "transaction list", +}; + +export const Ready = tests.createExample(ReadyView, { + transactions: [ + { + amount: { + currency: "USD", + fraction: 0, + value: 1, + }, + counterpart: "ASD", + negative: false, + subject: "Some", + when: AbsoluteTime.now(), + }, + ], +}); diff --git a/packages/bank-ui/src/components/Transactions/test.ts b/packages/bank-ui/src/components/Transactions/test.ts new file mode 100644 index 000000000..d9442c742 --- /dev/null +++ b/packages/bank-ui/src/components/Transactions/test.ts @@ -0,0 +1,202 @@ +/* + 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 + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TalerErrorCode } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { SwrMockEnvironment } from "@gnu-taler/web-util/testing"; +import { expect } from "chai"; +import { Props } from "./index.js"; +import { useComponentState } from "./state.js"; + +describe("Transaction states", () => { + it.skip("should query backend and render transactions", async () => { + const env = new SwrMockEnvironment(); + + const props: Props = { + account: "myAccount", + routeCreateWireTransfer: undefined, + }; + + // env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_FIRST_PAGE, { + // response: { + // data: { + // transactions: [ + // { + // creditorIban: "DE159593", + // creditorBic: "SANDBOXX", + // creditorName: "exchange company", + // debtorIban: "DE118695", + // debtorBic: "SANDBOXX", + // debtorName: "Name unknown", + // amount: "1", + // currency: "KUDOS", + // subject: + // "Taler Withdrawal N588V8XE9TR49HKAXFQ20P0EQ0EYW2AC9NNANV8ZP5P59N6N0410", + // date: "2022-12-12Z", + // uid: "8PPFR9EM", + // direction: "DBIT", + // pmtInfId: null, + // msgId: null, + // }, + // { + // creditorIban: "DE159593", + // creditorBic: "SANDBOXX", + // creditorName: "exchange company", + // debtorIban: "DE118695", + // debtorBic: "SANDBOXX", + // debtorName: "Name unknown", + // amount: "5.00", + // currency: "KUDOS", + // subject: "HNEWWT679TQC5P1BVXJS48FX9NW18FWM6PTK2N80Z8GVT0ACGNK0", + // date: "2022-12-07Z", + // uid: "7FZJC3RJ", + // direction: "DBIT", + // pmtInfId: null, + // msgId: null, + // }, + // { + // creditorIban: "DE118695", + // creditorBic: "SANDBOXX", + // creditorName: "Name unknown", + // debtorIban: "DE579516", + // debtorBic: "SANDBOXX", + // debtorName: "The Bank", + // amount: "100", + // currency: "KUDOS", + // subject: "Sign-up bonus", + // date: "2022-12-07Z", + // uid: "I31A06J8", + // direction: "CRDT", + // pmtInfId: null, + // msgId: null, + // }, + // ], + // }, + // }, + // }); + + const hookBehavior = await tests.hookBehaveLikeThis( + useComponentState, + props, + [ + ({ status, error }) => { + expect(status).equals("loading"); + expect(error).undefined; + }, + ({ status, error }) => { + expect(status).equals("ready"); + expect(error).undefined; + }, + ], + env.buildTestingContext(), + ); + + expect(hookBehavior).deep.eq({ result: "ok" }); + + expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" }); + }); + + // it("should show error message on not found", async () => { + // const env = new SwrMockEnvironment(); + + // const props: Props = { + // account: "myAccount", + // }; + + // env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_NOT_FOUND, { + // response: { + // error: { + // description: "Transaction page 0 could not be retrieved.", + // }, + // }, + // }); + + // const hookBehavior = await tests.hookBehaveLikeThis( + // useComponentState, + // props, + // [ + // ({ status, error }) => { + // expect(status).equals("loading"); + // expect(error).undefined; + // }, + // ({ status, error }) => { + // expect(status).equals("loading-error"); + // if (error === undefined || error.type !== ErrorType.CLIENT) { + // throw Error("not the expected error"); + // } + // expect(error.payload).deep.equal({ + // error: { + // description: "Transaction page 0 could not be retrieved.", + // }, + // }); + // }, + // ], + // env.buildTestingContext(), + // ); + + // expect(hookBehavior).deep.eq({ result: "ok" }); + // expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" }); + // }); + + it.skip("should show error message on server error", async () => { + const env = new SwrMockEnvironment(); + + const props: Props = { + account: "myAccount", + routeCreateWireTransfer: undefined, + }; + + // env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_ERROR, { + // response: { + // error: { + // code: TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED, + // }, + // }, + // }); + + const hookBehavior = await tests.hookBehaveLikeThis( + useComponentState, + props, + [ + ({ status, error }) => { + expect(status).equals("loading"); + expect(error).undefined; + }, + ({ status, error }) => { + expect(status).equals("loading-error"); + if ( + error === undefined || + !error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED) + ) { + throw Error("not the expected error"); + } + expect(error.errorDetail.code).deep.equal( + TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED, + ); + }, + ], + env.buildTestingContext(), + ); + + expect(hookBehavior).deep.eq({ result: "ok" }); + expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" }); + }); +}); 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 + */ + +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
+
+
+

+ Transactions history +

+
+
+ + + + You can start sending a wire transfer or withdrawing to your wallet. + + +
; + } + + 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, + ); + return ( +
+
+
+

+ Transactions history +

+
+
+
+ + + + + + + + + + + {Object.entries(txByDate).map(([date, txs], idx) => { + return ( + + + + + {txs.map((item) => { + return ( + + + + + + + ); + })} + + ); + })} + +
{i18n.str`Date`}
+ {date} +
+
+
+
+
+ Amount +
+
+ {item.negative + ? i18n.str`sent` + : i18n.str`received`}{" "} + {item.amount ? ( + + + + ) : ( + + <{i18n.str`Invalid value`}> + + )} +
+ +
+ Counterpart +
+
+ {item.negative ? i18n.str`to` : i18n.str`from`}{" "} + {!routeCreateWireTransfer ? item.counterpart : + + {item.counterpart} + + } +
+
+
+                                {item.subject}
+                              
+
+
+
+ + +
+
+ ); +} diff --git a/packages/bank-ui/src/components/index.examples.ts b/packages/bank-ui/src/components/index.examples.ts new file mode 100644 index 000000000..20e013070 --- /dev/null +++ b/packages/bank-ui/src/components/index.examples.ts @@ -0,0 +1,17 @@ +/* + 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 + */ + +export * as tx from "./Transactions/stories.js"; -- cgit v1.2.3