aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/components/Transactions
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-12-14 15:35:28 -0300
committerSebastian <sebasjm@gmail.com>2022-12-14 15:35:28 -0300
commit8d8d71807df6b775e5b0335eb1b2526a56d42ac6 (patch)
tree6b4a15698e8119d66995ead2e7e69e2fb8938450 /packages/demobank-ui/src/components/Transactions
parentd0dd7a155fcb63dc4d4859f51a681fc084ec4d7a (diff)
downloadwallet-core-8d8d71807df6b775e5b0335eb1b2526a56d42ac6.tar.xz
refactoring transaction component to standard component with test and examples
Diffstat (limited to 'packages/demobank-ui/src/components/Transactions')
-rw-r--r--packages/demobank-ui/src/components/Transactions/index.ts71
-rw-r--r--packages/demobank-ui/src/components/Transactions/state.ts133
-rw-r--r--packages/demobank-ui/src/components/Transactions/stories.tsx45
-rw-r--r--packages/demobank-ui/src/components/Transactions/test.ts174
-rw-r--r--packages/demobank-ui/src/components/Transactions/views.tsx68
5 files changed, 491 insertions, 0 deletions
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts b/packages/demobank-ui/src/components/Transactions/index.ts
new file mode 100644
index 000000000..618fcfb71
--- /dev/null
+++ b/packages/demobank-ui/src/components/Transactions/index.ts
@@ -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 <http://www.gnu.org/licenses/>
+ */
+
+import { Loading } from "../Loading.js";
+import { HookError, utils } from "@gnu-taler/web-util/lib/index.browser";
+// import { compose, StateViewMap } from "../../utils/index.js";
+// import { wxApi } from "../../wxApi.js";
+import { useComponentState } from "./state.js";
+import { LoadingUriView, ReadyView } from "./views.js";
+import { AbsoluteTime, AmountJson } from "@gnu-taler/taler-util";
+
+export interface Props {
+ pageNumber: number;
+ accountLabel: string;
+ balanceValue?: 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;
+ transactions: Transaction[];
+ }
+}
+
+export interface Transaction {
+ negative: boolean;
+ counterpart: string;
+ when: AbsoluteTime;
+ amount: AmountJson;
+ subject: string;
+}
+
+const viewMapping: utils.StateViewMap<State> = {
+ loading: Loading,
+ "loading-error": LoadingUriView,
+ ready: ReadyView,
+};
+
+export const Transactions = utils.compose(
+ (p: Props) => useComponentState(p),
+ viewMapping,
+);
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
new file mode 100644
index 000000000..ac76e31e2
--- /dev/null
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -0,0 +1,133 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+import { AbsoluteTime, Amounts } from "@gnu-taler/taler-util";
+import { parse } from "date-fns";
+import { useEffect } from "preact/hooks";
+import useSWR from "swr";
+import { Props, State } from "./index.js";
+
+export function useComponentState({ accountLabel, pageNumber, balanceValue }: Props): State {
+ const { data, error, mutate } = useSWR(
+ `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`,
+ );
+
+ useEffect(() => {
+ if (balanceValue) {
+ mutate();
+ }
+ }, [balanceValue ?? ""]);
+
+ if (error) {
+ switch (error.status) {
+ case 404:
+ return {
+ status: "loading-error",
+ error: {
+ hasError: true,
+ operational: false,
+ message: `Transactions page ${pageNumber} was not found.`
+ }
+ }
+ case 401:
+ return {
+ status: "loading-error",
+ error: {
+ hasError: true,
+ operational: false,
+ message: "Wrong credentials given."
+ }
+ }
+ default:
+ return {
+ status: "loading-error",
+ error: {
+ hasError: true,
+ operational: false,
+ message: `Transaction page ${pageNumber} could not be retrieved.`
+ } as any
+ }
+ }
+ }
+
+ if (!data) {
+ return {
+ status: "loading",
+ error: undefined
+ }
+ }
+
+
+ const transactions = data.transactions.map((item: unknown) => {
+ if (!item || typeof item !== "object" ||
+ !("direction" in item) ||
+ !("creditorIban" in item) ||
+ !("debtorIban" in item) ||
+ !("date" in item) ||
+ !("subject" in item) ||
+ !("currency" in item) ||
+ !("amount" in item)
+ ) {
+ //not valid
+ return;
+ }
+ const anyItem = item as any;
+ if (
+ !(typeof anyItem.creditorIban === 'string') ||
+ !(typeof anyItem.debtorIban === 'string') ||
+ !(typeof anyItem.date === 'string') ||
+ !(typeof anyItem.subject === 'string') ||
+ !(typeof anyItem.currency === 'string') ||
+ !(typeof anyItem.amount === 'string')
+ ) {
+ return;
+ }
+
+ const negative = anyItem.direction === "DBIT";
+ const counterpart = negative ? anyItem.creditorIban : anyItem.debtorIban;
+ // Pattern:
+ //
+ // DD/MM YYYY subject -5 EUR
+ // DD/MM YYYY subject 5 EUR
+ const dateRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{1,2})/;
+ const dateParse = dateRegex.exec(anyItem.date);
+ const dateStr =
+ dateParse !== null
+ ? `${dateParse[3]}/${dateParse[2]} ${dateParse[1]}`
+ : undefined;
+
+ const date = parse(dateStr ?? "", "dd/MM yyyy", new Date())
+
+ const when: AbsoluteTime = {
+ t_ms: date.getTime()
+ }
+ const amount = Amounts.parseOrThrow(`${anyItem.currency}:${anyItem.amount}`);
+ const subject = anyItem.subject;
+ return {
+ negative,
+ counterpart,
+ when,
+ amount,
+ subject,
+ }
+ });
+
+ return {
+ status: "ready",
+ error: undefined,
+ transactions,
+ };
+}
diff --git a/packages/demobank-ui/src/components/Transactions/stories.tsx b/packages/demobank-ui/src/components/Transactions/stories.tsx
new file mode 100644
index 000000000..77fdde092
--- /dev/null
+++ b/packages/demobank-ui/src/components/Transactions/stories.tsx
@@ -0,0 +1,45 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { ReadyView } from "./views.js";
+
+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: {
+ t_ms: new Date().getTime(),
+ },
+ },
+ ],
+});
diff --git a/packages/demobank-ui/src/components/Transactions/test.ts b/packages/demobank-ui/src/components/Transactions/test.ts
new file mode 100644
index 000000000..b746f6bb7
--- /dev/null
+++ b/packages/demobank-ui/src/components/Transactions/test.ts
@@ -0,0 +1,174 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { tests } from "@gnu-taler/web-util/lib/index.browser";
+import { SwrMockEnvironment } from "@gnu-taler/web-util/lib/tests/swr";
+import { expect } from "chai";
+import { TRANSACTION_API_EXAMPLE } from "../../endpoints.js";
+import { Props } from "./index.js";
+import { useComponentState } from "./state.js";
+
+
+describe("Transaction states", () => {
+
+ it("should query backend and render transactions", async () => {
+
+ const env = new SwrMockEnvironment();
+
+ const props: Props = {
+ accountLabel: "myAccount",
+ pageNumber: 0
+ }
+
+ env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_FIRST_PAGE, {
+ response: {
+ "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 = {
+ accountLabel: "myAccount",
+ pageNumber: 0
+ }
+
+ env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_NOT_FOUND, {});
+
+ const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
+ ({ status, error }) => {
+ expect(status).equals("loading");
+ expect(error).undefined;
+ },
+ ({ status, error }) => {
+ expect(status).equals("loading-error");
+ expect(error).deep.eq({
+ hasError: true,
+ operational: false,
+ message: "Transactions page 0 was not found."
+ });
+ },
+ ], env.buildTestingContext())
+
+ expect(hookBehavior).deep.eq({ result: "ok" })
+ expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" })
+ });
+
+ it("should show error message on server error", async () => {
+
+ const env = new SwrMockEnvironment(false);
+
+ const props: Props = {
+ accountLabel: "myAccount",
+ pageNumber: 0
+ }
+
+ env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_ERROR, {});
+
+ const hookBehavior = await tests.hookBehaveLikeThis(useComponentState, props, [
+ ({ status, error }) => {
+ expect(status).equals("loading");
+ expect(error).undefined;
+ },
+ ({ status, error }) => {
+ expect(status).equals("loading-error");
+ expect(error).deep.equal({
+ hasError: true,
+ operational: false,
+ message: "Transaction page 0 could not be retrieved."
+ });
+ },
+ ], env.buildTestingContext())
+
+ expect(hookBehavior).deep.eq({ result: "ok" })
+ expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" })
+ });
+});
+
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
new file mode 100644
index 000000000..b3683b743
--- /dev/null
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -0,0 +1,68 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+import { h, VNode } from "preact";
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { State } from "./index.js";
+import { format } from "date-fns";
+import { Amounts } from "@gnu-taler/taler-util";
+
+export function LoadingUriView({ error }: State.LoadingUriError): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <div>
+ <i18n.Translate>Could not load</i18n.Translate>
+ </div>
+ );
+}
+
+export function ReadyView({ transactions }: State.Ready): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="results">
+ <table class="pure-table pure-table-striped">
+ <thead>
+ <tr>
+ <th>{i18n.str`Date`}</th>
+ <th>{i18n.str`Amount`}</th>
+ <th>{i18n.str`Counterpart`}</th>
+ <th>{i18n.str`Subject`}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {transactions.map((item, idx) => {
+ return (
+ <tr key={idx}>
+ <td>
+ {item.when.t_ms === "never"
+ ? "never"
+ : format(item.when.t_ms, "dd/MM/yyyy")}
+ </td>
+ <td>
+ {item.negative ? "-" : ""}
+ {Amounts.stringifyValue(item.amount)} {item.amount.currency}
+ </td>
+ <td>{item.counterpart}</td>
+ <td>{item.subject}</td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ );
+}