aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/components')
-rw-r--r--packages/demobank-ui/src/components/Cashouts/index.ts69
-rw-r--r--packages/demobank-ui/src/components/Cashouts/state.ts44
-rw-r--r--packages/demobank-ui/src/components/Cashouts/stories.tsx45
-rw-r--r--packages/demobank-ui/src/components/Cashouts/test.ts179
-rw-r--r--packages/demobank-ui/src/components/Cashouts/views.tsx66
-rw-r--r--packages/demobank-ui/src/components/Loading.tsx24
-rw-r--r--packages/demobank-ui/src/components/Transactions/index.ts10
-rw-r--r--packages/demobank-ui/src/components/Transactions/state.ts105
-rw-r--r--packages/demobank-ui/src/components/Transactions/test.ts9
-rw-r--r--packages/demobank-ui/src/components/app.tsx23
10 files changed, 507 insertions, 67 deletions
diff --git a/packages/demobank-ui/src/components/Cashouts/index.ts b/packages/demobank-ui/src/components/Cashouts/index.ts
new file mode 100644
index 000000000..db39ba7e4
--- /dev/null
+++ b/packages/demobank-ui/src/components/Cashouts/index.ts
@@ -0,0 +1,69 @@
+/*
+ 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 { HttpError, utils } from "@gnu-taler/web-util/lib/index.browser";
+import { Loading } from "../Loading.js";
+// import { compose, StateViewMap } from "../../utils/index.js";
+// import { wxApi } from "../../wxApi.js";
+import { AbsoluteTime, AmountJson } from "@gnu-taler/taler-util";
+import { useComponentState } from "./state.js";
+import { LoadingUriView, ReadyView } from "./views.js";
+
+export interface Props {
+ account: 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: HttpError<SandboxBackend.SandboxError>;
+ }
+
+ export interface BaseInfo {
+ error: undefined;
+ }
+ export interface Ready extends BaseInfo {
+ status: "ready";
+ error: undefined;
+ cashouts: SandboxBackend.Circuit.CashoutStatusResponse[];
+ }
+}
+
+export interface Transaction {
+ negative: boolean;
+ counterpart: string;
+ when: AbsoluteTime;
+ amount: AmountJson | undefined;
+ subject: string;
+}
+
+const viewMapping: utils.StateViewMap<State> = {
+ loading: Loading,
+ "loading-error": LoadingUriView,
+ ready: ReadyView,
+};
+
+export const Cashouts = utils.compose(
+ (p: Props) => useComponentState(p),
+ viewMapping,
+);
diff --git a/packages/demobank-ui/src/components/Cashouts/state.ts b/packages/demobank-ui/src/components/Cashouts/state.ts
new file mode 100644
index 000000000..7e420940f
--- /dev/null
+++ b/packages/demobank-ui/src/components/Cashouts/state.ts
@@ -0,0 +1,44 @@
+/*
+ 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 { useCashouts } from "../../hooks/circuit.js";
+import { Props, State, Transaction } from "./index.js";
+
+export function useComponentState({
+ account,
+}: Props): State {
+ const result = useCashouts()
+ if (result.loading) {
+ return {
+ status: "loading",
+ error: undefined
+ }
+ }
+ if (!result.ok) {
+ return {
+ status: "loading-error",
+ error: result
+ }
+ }
+
+
+ return {
+ status: "ready",
+ error: undefined,
+ cashout: result.data,
+ };
+}
diff --git a/packages/demobank-ui/src/components/Cashouts/stories.tsx b/packages/demobank-ui/src/components/Cashouts/stories.tsx
new file mode 100644
index 000000000..77fdde092
--- /dev/null
+++ b/packages/demobank-ui/src/components/Cashouts/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/Cashouts/test.ts b/packages/demobank-ui/src/components/Cashouts/test.ts
new file mode 100644
index 000000000..3f2d5fb68
--- /dev/null
+++ b/packages/demobank-ui/src/components/Cashouts/test.ts
@@ -0,0 +1,179 @@
+/*
+ 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 = {
+ account: "myAccount",
+ };
+
+ 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 = {
+ account: "myAccount",
+ };
+
+ 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 = {
+ account: "myAccount",
+ };
+
+ 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/Cashouts/views.tsx b/packages/demobank-ui/src/components/Cashouts/views.tsx
new file mode 100644
index 000000000..30803d4d1
--- /dev/null
+++ b/packages/demobank-ui/src/components/Cashouts/views.tsx
@@ -0,0 +1,66 @@
+/*
+ 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({ cashouts }: State.Ready): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="results">
+ <table class="pure-table pure-table-striped">
+ <thead>
+ <tr>
+ <th>{i18n.str`Created`}</th>
+ <th>{i18n.str`Confirmed`}</th>
+ <th>{i18n.str`Counterpart`}</th>
+ <th>{i18n.str`Subject`}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {cashouts.map((item, idx) => {
+ return (
+ <tr key={idx}>
+ <td>{format(item.creation_time, "dd/MM/yyyy HH:mm:ss")}</td>
+ <td>
+ {item.confirmation_time
+ ? format(item.confirmation_time, "dd/MM/yyyy HH:mm:ss")
+ : "-"}
+ </td>
+ <td>{Amounts.stringifyValue(item.amount_credit)}</td>
+ <td>{item.counterpart}</td>
+ <td>{item.subject}</td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ );
+}
diff --git a/packages/demobank-ui/src/components/Loading.tsx b/packages/demobank-ui/src/components/Loading.tsx
index 8fd01858b..7cbdad681 100644
--- a/packages/demobank-ui/src/components/Loading.tsx
+++ b/packages/demobank-ui/src/components/Loading.tsx
@@ -17,5 +17,27 @@
import { h, VNode } from "preact";
export function Loading(): VNode {
- return <div>loading...</div>;
+ return (
+ <div
+ class="columns is-centered is-vcentered"
+ style={{
+ height: "calc(100% - 3rem)",
+ position: "absolute",
+ width: "100%",
+ }}
+ >
+ <Spinner />
+ </div>
+ );
+}
+
+export function Spinner(): VNode {
+ return (
+ <div class="lds-ring">
+ <div />
+ <div />
+ <div />
+ <div />
+ </div>
+ );
}
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts b/packages/demobank-ui/src/components/Transactions/index.ts
index 0c9084946..e43b9401c 100644
--- a/packages/demobank-ui/src/components/Transactions/index.ts
+++ b/packages/demobank-ui/src/components/Transactions/index.ts
@@ -14,18 +14,16 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { HttpError, utils } from "@gnu-taler/web-util/lib/index.browser";
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 { AbsoluteTime, AmountJson } from "@gnu-taler/taler-util";
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;
+ account: string;
}
export type State = State.Loading | State.LoadingUriError | State.Ready;
@@ -38,7 +36,7 @@ export namespace State {
export interface LoadingUriError {
status: "loading-error";
- error: HookError;
+ error: HttpError<SandboxBackend.SandboxError>;
}
export interface BaseInfo {
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts b/packages/demobank-ui/src/components/Transactions/state.ts
index a5087ef32..9e1bce39b 100644
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -15,66 +15,65 @@
*/
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";
+import { useTransactions } from "../../hooks/access.js";
+import { Props, State, Transaction } from "./index.js";
export function useComponentState({
- accountLabel,
- pageNumber,
- balanceValue,
+ account,
}: 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,
- };
+ const result = useTransactions(account)
+ if (result.loading) {
+ return {
+ status: "loading",
+ error: undefined
}
}
-
- if (!data) {
+ if (!result.ok) {
return {
- status: "loading",
- error: undefined,
- };
+ status: "loading-error",
+ error: result
+ }
}
+ // 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) => {
+ const transactions = result.data.transactions.map((item: unknown) => {
if (
!item ||
typeof item !== "object" ||
@@ -120,7 +119,7 @@ export function useComponentState({
amount,
subject,
};
- });
+ }).filter((x): x is Transaction => x !== undefined);
return {
status: "ready",
diff --git a/packages/demobank-ui/src/components/Transactions/test.ts b/packages/demobank-ui/src/components/Transactions/test.ts
index 21a0eefbb..3f2d5fb68 100644
--- a/packages/demobank-ui/src/components/Transactions/test.ts
+++ b/packages/demobank-ui/src/components/Transactions/test.ts
@@ -31,8 +31,7 @@ describe("Transaction states", () => {
const env = new SwrMockEnvironment();
const props: Props = {
- accountLabel: "myAccount",
- pageNumber: 0,
+ account: "myAccount",
};
env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_FIRST_PAGE, {
@@ -116,8 +115,7 @@ describe("Transaction states", () => {
const env = new SwrMockEnvironment();
const props: Props = {
- accountLabel: "myAccount",
- pageNumber: 0,
+ account: "myAccount",
};
env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_NOT_FOUND, {});
@@ -150,8 +148,7 @@ describe("Transaction states", () => {
const env = new SwrMockEnvironment(false);
const props: Props = {
- accountLabel: "myAccount",
- pageNumber: 0,
+ account: "myAccount",
};
env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_ERROR, {});
diff --git a/packages/demobank-ui/src/components/app.tsx b/packages/demobank-ui/src/components/app.tsx
index 8679b05dd..e024be41b 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -24,6 +24,9 @@ import { PageStateProvider } from "../context/pageState.js";
import { Routing } from "../pages/Routing.js";
import { strings } from "../i18n/strings.js";
import { TranslationProvider } from "@gnu-taler/web-util/lib/index.browser";
+import { SWRConfig } from "swr";
+
+const WITH_LOCAL_STORAGE_CACHE = false;
/**
* FIXME:
@@ -47,7 +50,15 @@ const App: FunctionalComponent = () => {
<TranslationProvider source={strings}>
<PageStateProvider>
<BackendStateProvider>
- <Routing />
+ <SWRConfig
+ value={{
+ provider: WITH_LOCAL_STORAGE_CACHE
+ ? localStorageProvider
+ : undefined,
+ }}
+ >
+ <Routing />
+ </SWRConfig>
</BackendStateProvider>
</PageStateProvider>
</TranslationProvider>
@@ -58,4 +69,14 @@ const App: FunctionalComponent = () => {
return globalLogLevel;
};
+function localStorageProvider(): Map<unknown, unknown> {
+ const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]"));
+
+ window.addEventListener("beforeunload", () => {
+ const appCache = JSON.stringify(Array.from(map.entries()));
+ localStorage.setItem("app-cache", appCache);
+ });
+ return map;
+}
+
export default App;