refactoring transaction component to standard component with test and examples
This commit is contained in:
parent
d0dd7a155f
commit
8d8d71807d
@ -44,7 +44,29 @@ const preactCompatPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
const entryPoints = ["src/index.tsx", "src/stories.tsx"];
|
||||
function getFilesInDirectory(startPath, regex) {
|
||||
if (!fs.existsSync(startPath)) {
|
||||
return;
|
||||
}
|
||||
const files = fs.readdirSync(startPath);
|
||||
const result = files.flatMap(file => {
|
||||
const filename = path.join(startPath, file);
|
||||
|
||||
const stat = fs.lstatSync(filename);
|
||||
if (stat.isDirectory()) {
|
||||
return getFilesInDirectory(filename, regex);
|
||||
}
|
||||
else if (regex.test(filename)) {
|
||||
return filename
|
||||
}
|
||||
}).filter(x => !!x)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const allTestFiles = getFilesInDirectory(path.join(BASE, 'src'), /.test.ts$/)
|
||||
|
||||
const entryPoints = ["src/index.tsx", "src/stories.tsx", ...allTestFiles];
|
||||
|
||||
let GIT_ROOT = BASE;
|
||||
while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") {
|
||||
@ -128,6 +150,7 @@ export const buildConfig = {
|
||||
sourcemap: true,
|
||||
jsxFactory: "h",
|
||||
jsxFragment: "Fragment",
|
||||
external: ["async_hooks"],
|
||||
define: {
|
||||
__VERSION__: `"${_package.version}"`,
|
||||
__GIT_HASH__: `"${GIT_HASH}"`,
|
||||
|
@ -15,9 +15,9 @@
|
||||
*/
|
||||
|
||||
import { Loading } from "../../components/Loading.js";
|
||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import { wxApi } from "../../wxApi.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";
|
||||
|
||||
@ -47,14 +47,13 @@ export namespace State {
|
||||
}
|
||||
}
|
||||
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
const viewMapping: utils.StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-error": LoadingUriView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
export const ComponentName = compose(
|
||||
"ComponentName",
|
||||
(p: Props) => useComponentState(p, wxApi),
|
||||
export const ComponentName = utils.compose(
|
||||
(p: Props) => useComponentState(p),
|
||||
viewMapping,
|
||||
);
|
||||
|
@ -14,10 +14,10 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { wxApi } from "../../wxApi.js";
|
||||
// import { wxApi } from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState({ p }: Props, api: typeof wxApi): State {
|
||||
export function useComponentState({ p }: Props): State {
|
||||
return {
|
||||
status: "ready",
|
||||
error: undefined,
|
||||
|
@ -19,11 +19,11 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { createExample } from "../../test-utils.js";
|
||||
import { tests } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { ReadyView } from "./views.js";
|
||||
|
||||
export default {
|
||||
title: "example",
|
||||
};
|
||||
|
||||
export const Ready = createExample(ReadyView, {});
|
||||
export const Ready = tests.createExample(ReadyView, {});
|
||||
|
@ -15,18 +15,16 @@
|
||||
*/
|
||||
|
||||
import { h, VNode } from "preact";
|
||||
import { LoadingError } from "../../components/LoadingError.js";
|
||||
import { useTranslationContext } from "../../context/translation.js";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { State } from "./index.js";
|
||||
|
||||
export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
return (
|
||||
<LoadingError
|
||||
title={<i18n.Translate>Could not load</i18n.Translate>}
|
||||
error={error}
|
||||
/>
|
||||
<div>
|
||||
<i18n.Translate>Could not load</i18n.Translate>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
71
packages/demobank-ui/src/components/Transactions/index.ts
Normal file
71
packages/demobank-ui/src/components/Transactions/index.ts
Normal file
@ -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,
|
||||
);
|
133
packages/demobank-ui/src/components/Transactions/state.ts
Normal file
133
packages/demobank-ui/src/components/Transactions/state.ts
Normal file
@ -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,
|
||||
};
|
||||
}
|
45
packages/demobank-ui/src/components/Transactions/stories.tsx
Normal file
45
packages/demobank-ui/src/components/Transactions/stories.tsx
Normal file
@ -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(),
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
174
packages/demobank-ui/src/components/Transactions/test.ts
Normal file
174
packages/demobank-ui/src/components/Transactions/test.ts
Normal file
@ -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" })
|
||||
});
|
||||
});
|
||||
|
68
packages/demobank-ui/src/components/Transactions/views.tsx
Normal file
68
packages/demobank-ui/src/components/Transactions/views.tsx
Normal file
@ -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>
|
||||
);
|
||||
}
|
17
packages/demobank-ui/src/components/index.examples.ts
Normal file
17
packages/demobank-ui/src/components/index.examples.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
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/>
|
||||
*/
|
||||
|
||||
export * as tx from "./Transactions/stories.js";
|
37
packages/demobank-ui/src/endpoints.ts
Normal file
37
packages/demobank-ui/src/endpoints.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
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)
|
||||
*/
|
||||
|
||||
export const TRANSACTION_API_EXAMPLE = {
|
||||
LIST_FIRST_PAGE: {
|
||||
method: "get" as const,
|
||||
url: "access-api/accounts/myAccount/transactions?page=0",
|
||||
},
|
||||
LIST_ERROR: {
|
||||
method: "get" as const,
|
||||
url: "access-api/accounts/myAccount/transactions?page=0",
|
||||
code: 500
|
||||
},
|
||||
LIST_NOT_FOUND: {
|
||||
method: "get" as const,
|
||||
url: "access-api/accounts/myAccount/transactions?page=0",
|
||||
code: 404
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ import { getIbanFromPayto, prepareHeaders } from "../utils.js";
|
||||
import { BankFrame } from "./BankFrame.js";
|
||||
import { LoginForm } from "./LoginForm.js";
|
||||
import { PaymentOptions } from "./PaymentOptions.js";
|
||||
import { Transactions } from "./Transactions.js";
|
||||
import { Transactions } from "../components/Transactions/index.js";
|
||||
import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
|
||||
|
||||
export function AccountPage(): VNode {
|
||||
|
@ -24,7 +24,7 @@ import { PageStateType, usePageContext } from "../context/pageState.js";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
import { getBankBackendBaseUrl } from "../utils.js";
|
||||
import { BankFrame } from "./BankFrame.js";
|
||||
import { Transactions } from "./Transactions.js";
|
||||
import { Transactions } from "../components/Transactions/index.js";
|
||||
|
||||
const logger = new Logger("PublicHistoriesPage");
|
||||
|
||||
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
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 { Logger } from "@gnu-taler/taler-util";
|
||||
import { h, VNode } from "preact";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import useSWR from "swr";
|
||||
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
|
||||
|
||||
const logger = new Logger("Transactions");
|
||||
/**
|
||||
* Show one page of transactions.
|
||||
*/
|
||||
export function Transactions({
|
||||
pageNumber,
|
||||
accountLabel,
|
||||
balanceValue,
|
||||
}: {
|
||||
pageNumber: number;
|
||||
accountLabel: string;
|
||||
balanceValue?: string;
|
||||
}): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
const { data, error, mutate } = useSWR(
|
||||
`access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`,
|
||||
);
|
||||
useEffect(() => {
|
||||
if (balanceValue) {
|
||||
mutate();
|
||||
}
|
||||
}, [balanceValue ?? ""]);
|
||||
if (typeof error !== "undefined") {
|
||||
logger.error("transactions not found error", error);
|
||||
switch (error.status) {
|
||||
case 404: {
|
||||
return <p>Transactions page {pageNumber} was not found.</p>;
|
||||
}
|
||||
case 401: {
|
||||
return <p>Wrong credentials given.</p>;
|
||||
}
|
||||
default: {
|
||||
return <p>Transaction page {pageNumber} could not be retrieved.</p>;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!data) {
|
||||
logger.trace(`History data of ${accountLabel} not arrived`);
|
||||
return <p>Transactions page loading...</p>;
|
||||
}
|
||||
logger.trace(`History data of ${accountLabel}`, data);
|
||||
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>
|
||||
{data.transactions.map((item: any, idx: number) => {
|
||||
const sign = item.direction == "DBIT" ? "-" : "";
|
||||
const counterpart =
|
||||
item.direction == "DBIT" ? item.creditorIban : item.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(item.date);
|
||||
const date =
|
||||
dateParse !== null
|
||||
? `${dateParse[3]}/${dateParse[2]} ${dateParse[1]}`
|
||||
: "date not found";
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{date}</td>
|
||||
<td>
|
||||
{sign}
|
||||
{item.amount} {item.currency}
|
||||
</td>
|
||||
<td>{counterpart}</td>
|
||||
<td>{item.subject}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
import { strings } from "./i18n/strings.js";
|
||||
|
||||
import * as pages from "./pages/index.stories.js";
|
||||
import * as components from "./components/index.examples.js";
|
||||
|
||||
import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
|
||||
|
||||
@ -32,7 +33,7 @@ function SortStories(a: any, b: any): number {
|
||||
|
||||
function main(): void {
|
||||
renderStories(
|
||||
{ pages },
|
||||
{ pages, components },
|
||||
{
|
||||
strings,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user