wallet transaction detail
This commit is contained in:
parent
9f09f5a1a5
commit
aa0edbdd68
@ -145,7 +145,7 @@ interface WithdrawalDetailsForTalerBankIntegrationApi {
|
|||||||
|
|
||||||
// This should only be used for actual withdrawals
|
// This should only be used for actual withdrawals
|
||||||
// and not for tips that have their own transactions type.
|
// and not for tips that have their own transactions type.
|
||||||
interface TransactionWithdrawal extends TransactionCommon {
|
export interface TransactionWithdrawal extends TransactionCommon {
|
||||||
type: TransactionType.Withdrawal;
|
type: TransactionType.Withdrawal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -266,7 +266,7 @@ export interface OrderShortInfo {
|
|||||||
fulfillmentMessage_i18n?: InternationalizedString;
|
fulfillmentMessage_i18n?: InternationalizedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransactionRefund extends TransactionCommon {
|
export interface TransactionRefund extends TransactionCommon {
|
||||||
type: TransactionType.Refund;
|
type: TransactionType.Refund;
|
||||||
|
|
||||||
// ID for the transaction that is refunded
|
// ID for the transaction that is refunded
|
||||||
@ -282,7 +282,7 @@ interface TransactionRefund extends TransactionCommon {
|
|||||||
amountEffective: AmountString;
|
amountEffective: AmountString;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransactionTip extends TransactionCommon {
|
export interface TransactionTip extends TransactionCommon {
|
||||||
type: TransactionType.Tip;
|
type: TransactionType.Tip;
|
||||||
|
|
||||||
// Raw amount of the tip, without extra fees that apply
|
// Raw amount of the tip, without extra fees that apply
|
||||||
@ -297,7 +297,7 @@ interface TransactionTip extends TransactionCommon {
|
|||||||
// A transaction shown for refreshes that are not associated to other transactions
|
// A transaction shown for refreshes that are not associated to other transactions
|
||||||
// such as a refresh necessary before coin expiration.
|
// such as a refresh necessary before coin expiration.
|
||||||
// It should only be returned by the API if the effective amount is different from zero.
|
// It should only be returned by the API if the effective amount is different from zero.
|
||||||
interface TransactionRefresh extends TransactionCommon {
|
export interface TransactionRefresh extends TransactionCommon {
|
||||||
type: TransactionType.Refresh;
|
type: TransactionType.Refresh;
|
||||||
|
|
||||||
// Exchange that the coins are refreshed with
|
// Exchange that the coins are refreshed with
|
||||||
@ -314,7 +314,7 @@ interface TransactionRefresh extends TransactionCommon {
|
|||||||
* Deposit transaction, which effectively sends
|
* Deposit transaction, which effectively sends
|
||||||
* money from this wallet somewhere else.
|
* money from this wallet somewhere else.
|
||||||
*/
|
*/
|
||||||
interface TransactionDeposit extends TransactionCommon {
|
export interface TransactionDeposit extends TransactionCommon {
|
||||||
type: TransactionType.Deposit;
|
type: TransactionType.Deposit;
|
||||||
|
|
||||||
depositGroupId: string;
|
depositGroupId: string;
|
||||||
|
@ -12,12 +12,13 @@
|
|||||||
"test": "jest ./tests",
|
"test": "jest ./tests",
|
||||||
"compile": "tsc && rollup -c",
|
"compile": "tsc && rollup -c",
|
||||||
"build-storybook": "build-storybook",
|
"build-storybook": "build-storybook",
|
||||||
"storybook": "start-storybook -p 6006",
|
"storybook": "start-storybook -s static -p 6006",
|
||||||
"watch": "tsc --watch & rollup -w -c"
|
"watch": "tsc --watch & rollup -w -c"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gnu-taler/taler-util": "workspace:*",
|
"@gnu-taler/taler-util": "workspace:*",
|
||||||
"@gnu-taler/taler-wallet-core": "workspace:*",
|
"@gnu-taler/taler-wallet-core": "workspace:*",
|
||||||
|
"date-fns": "^2.22.1",
|
||||||
"preact": "^10.5.13",
|
"preact": "^10.5.13",
|
||||||
"preact-router": "^3.2.1",
|
"preact-router": "^3.2.1",
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
|
@ -19,7 +19,7 @@ export enum Pages {
|
|||||||
return_coins = '/return-coins',
|
return_coins = '/return-coins',
|
||||||
tips = '/tips',
|
tips = '/tips',
|
||||||
withdraw = '/withdraw',
|
withdraw = '/withdraw',
|
||||||
popup = '/popup/:rest',
|
popup = '/popup/:rest*',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Application() {
|
export function Application() {
|
||||||
|
191
packages/taler-wallet-webextension/src/pages/popup.stories.tsx
Normal file
191
packages/taler-wallet-webextension/src/pages/popup.stories.tsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2021 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 { PaymentStatus, TransactionPayment, TransactionType, TransactionWithdrawal, TransactionDeposit, TransactionRefresh, TransactionTip, TransactionRefund, WithdrawalType, TransactionCommon } from '@gnu-taler/taler-util';
|
||||||
|
import { Fragment, h } from 'preact';
|
||||||
|
import { WalletTransactionView as Component } from './popup';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'popup/transaction details',
|
||||||
|
component: Component,
|
||||||
|
decorators: [
|
||||||
|
(Story: any) => <div>
|
||||||
|
<link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||||
|
<link key="2" rel="stylesheet" type="text/css" href="/style/popup.css" />
|
||||||
|
<link key="3" rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
||||||
|
<div style={{ margin: "1em", width: 400 }}>
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const commonTransaction = {
|
||||||
|
amountRaw: 'USD:10',
|
||||||
|
amountEffective: 'USD:9',
|
||||||
|
pending: false,
|
||||||
|
timestamp: {
|
||||||
|
t_ms: new Date().getTime()
|
||||||
|
},
|
||||||
|
transactionId: '12',
|
||||||
|
} as TransactionCommon
|
||||||
|
|
||||||
|
const exampleData = {
|
||||||
|
withdraw: {
|
||||||
|
...commonTransaction,
|
||||||
|
type: TransactionType.Withdrawal,
|
||||||
|
exchangeBaseUrl: 'http://exchange.taler',
|
||||||
|
withdrawalDetails: {
|
||||||
|
confirmed: false,
|
||||||
|
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
|
||||||
|
type: WithdrawalType.ManualTransfer,
|
||||||
|
}
|
||||||
|
} as TransactionWithdrawal,
|
||||||
|
payment: {
|
||||||
|
...commonTransaction,
|
||||||
|
type: TransactionType.Payment,
|
||||||
|
info: {
|
||||||
|
contractTermsHash: 'ASDZXCASD',
|
||||||
|
merchant: {
|
||||||
|
name: 'the merchant',
|
||||||
|
},
|
||||||
|
orderId: '#12345',
|
||||||
|
products: [],
|
||||||
|
summary: 'the summary',
|
||||||
|
fulfillmentMessage: '',
|
||||||
|
},
|
||||||
|
proposalId: '#proposalId',
|
||||||
|
status: PaymentStatus.Accepted,
|
||||||
|
} as TransactionPayment,
|
||||||
|
deposit: {
|
||||||
|
...commonTransaction,
|
||||||
|
type: TransactionType.Deposit,
|
||||||
|
depositGroupId: '#groupId',
|
||||||
|
targetPaytoUri: 'payto://x-taler-bank/bank/account',
|
||||||
|
} as TransactionDeposit,
|
||||||
|
refresh: {
|
||||||
|
...commonTransaction,
|
||||||
|
type: TransactionType.Refresh,
|
||||||
|
exchangeBaseUrl: 'http://exchange.taler',
|
||||||
|
} as TransactionRefresh,
|
||||||
|
tip: {
|
||||||
|
...commonTransaction,
|
||||||
|
type: TransactionType.Tip,
|
||||||
|
merchantBaseUrl: 'http://merchant.taler',
|
||||||
|
} as TransactionTip,
|
||||||
|
refund: {
|
||||||
|
...commonTransaction,
|
||||||
|
type: TransactionType.Refund,
|
||||||
|
refundedTransactionId: '#refundId',
|
||||||
|
info: {
|
||||||
|
contractTermsHash: 'ASDZXCASD',
|
||||||
|
merchant: {
|
||||||
|
name: 'the merchant',
|
||||||
|
},
|
||||||
|
orderId: '#12345',
|
||||||
|
products: [],
|
||||||
|
summary: 'the summary',
|
||||||
|
fulfillmentMessage: '',
|
||||||
|
},
|
||||||
|
} as TransactionRefund,
|
||||||
|
}
|
||||||
|
|
||||||
|
function dynamic<T>(props: any) {
|
||||||
|
const r = (args: any) => <Component {...args} />
|
||||||
|
r.args = props
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NotYetLoaded = dynamic({});
|
||||||
|
|
||||||
|
export const Withdraw = dynamic({
|
||||||
|
transaction: exampleData.withdraw
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WithdrawPending = dynamic({
|
||||||
|
transaction: { ...exampleData.withdraw, pending: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const Payment = dynamic({
|
||||||
|
transaction: exampleData.payment
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PaymentPending = dynamic({
|
||||||
|
transaction: { ...exampleData.payment, pending: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PaymentWithProducts = dynamic({
|
||||||
|
transaction: {
|
||||||
|
...exampleData.payment,
|
||||||
|
info: {
|
||||||
|
...exampleData.payment.info,
|
||||||
|
products: [{
|
||||||
|
description: 't-shirt',
|
||||||
|
}, {
|
||||||
|
description: 'beer',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
} as TransactionPayment,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const Deposit = dynamic({
|
||||||
|
transaction: exampleData.deposit
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DepositPending = dynamic({
|
||||||
|
transaction: { ...exampleData.deposit, pending: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Refresh = dynamic({
|
||||||
|
transaction: exampleData.refresh
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Tip = dynamic({
|
||||||
|
transaction: exampleData.tip
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TipPending = dynamic({
|
||||||
|
transaction: { ...exampleData.tip, pending: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Refund = dynamic({
|
||||||
|
transaction: exampleData.refund
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RefundPending = dynamic({
|
||||||
|
transaction: { ...exampleData.refund , pending: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const RefundWithProducts = dynamic({
|
||||||
|
transaction: {
|
||||||
|
...exampleData.refund,
|
||||||
|
info: {
|
||||||
|
...exampleData.refund.info,
|
||||||
|
products: [{
|
||||||
|
description: 't-shirt',
|
||||||
|
}, {
|
||||||
|
description: 'beer',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
} as TransactionRefund,
|
||||||
|
});
|
@ -38,7 +38,8 @@ import {
|
|||||||
Timestamp,
|
Timestamp,
|
||||||
amountFractionalBase,
|
amountFractionalBase,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { Component, ComponentChildren, JSX } from "preact";
|
import { format } from "date-fns";
|
||||||
|
import { Component, ComponentChildren, Fragment, JSX } from "preact";
|
||||||
import { route, Route, Router } from 'preact-router';
|
import { route, Route, Router } from 'preact-router';
|
||||||
import { Match } from 'preact-router/match';
|
import { Match } from 'preact-router/match';
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
@ -268,6 +269,7 @@ interface TransactionLayoutProps {
|
|||||||
amount: AmountString | "unknown";
|
amount: AmountString | "unknown";
|
||||||
timestamp: Timestamp;
|
timestamp: Timestamp;
|
||||||
title: string;
|
title: string;
|
||||||
|
id: string;
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
iconPath: string;
|
iconPath: string;
|
||||||
pending: boolean;
|
pending: boolean;
|
||||||
@ -297,7 +299,7 @@ function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
|
|||||||
>
|
>
|
||||||
<div style={{ fontSize: "small", color: "gray" }}>{dateStr}</div>
|
<div style={{ fontSize: "small", color: "gray" }}>{dateStr}</div>
|
||||||
<div style={{ fontVariant: "small-caps", fontSize: "x-large" }}>
|
<div style={{ fontVariant: "small-caps", fontSize: "x-large" }}>
|
||||||
<span>{props.title}</span>
|
<a href={Pages.transaction.replace(':tid', props.id)}><span>{props.title}</span></a>
|
||||||
{props.pending ? (
|
{props.pending ? (
|
||||||
<span style={{ color: "darkblue" }}> (Pending)</span>
|
<span style={{ color: "darkblue" }}> (Pending)</span>
|
||||||
) : null}
|
) : null}
|
||||||
@ -320,6 +322,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|||||||
case TransactionType.Withdrawal:
|
case TransactionType.Withdrawal:
|
||||||
return (
|
return (
|
||||||
<TransactionLayout
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title="Withdrawal"
|
title="Withdrawal"
|
||||||
@ -332,6 +335,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|||||||
case TransactionType.Payment:
|
case TransactionType.Payment:
|
||||||
return (
|
return (
|
||||||
<TransactionLayout
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"debit"}
|
debitCreditIndicator={"debit"}
|
||||||
title="Payment"
|
title="Payment"
|
||||||
@ -344,6 +348,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|||||||
case TransactionType.Refund:
|
case TransactionType.Refund:
|
||||||
return (
|
return (
|
||||||
<TransactionLayout
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title="Refund"
|
title="Refund"
|
||||||
@ -356,6 +361,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|||||||
case TransactionType.Tip:
|
case TransactionType.Tip:
|
||||||
return (
|
return (
|
||||||
<TransactionLayout
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title="Tip"
|
title="Tip"
|
||||||
@ -368,6 +374,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|||||||
case TransactionType.Refresh:
|
case TransactionType.Refresh:
|
||||||
return (
|
return (
|
||||||
<TransactionLayout
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"credit"}
|
debitCreditIndicator={"credit"}
|
||||||
title="Refresh"
|
title="Refresh"
|
||||||
@ -380,6 +387,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|||||||
case TransactionType.Deposit:
|
case TransactionType.Deposit:
|
||||||
return (
|
return (
|
||||||
<TransactionLayout
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
amount={tx.amountEffective}
|
amount={tx.amountEffective}
|
||||||
debitCreditIndicator={"debit"}
|
debitCreditIndicator={"debit"}
|
||||||
title="Refresh"
|
title="Refresh"
|
||||||
@ -420,6 +428,223 @@ function WalletHistory(props: any): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WalletTransactionProps {
|
||||||
|
transaction?: Transaction,
|
||||||
|
onDelete: () => void,
|
||||||
|
onBack: () => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WalletTransactionView({ transaction, onDelete, onBack }: WalletTransactionProps) {
|
||||||
|
if (!transaction) {
|
||||||
|
return <div>Loading ...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Footer() {
|
||||||
|
return <footer style={{ marginTop: 'auto', display: 'flex' }}>
|
||||||
|
<button onClick={onBack}>back</button>
|
||||||
|
<div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}>
|
||||||
|
<button onClick={onDelete}>remove</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Pending() {
|
||||||
|
if (!transaction?.pending) return null
|
||||||
|
return <span style={{fontWeight:'normal', fontSize:16, color: 'gray'}}>(pending...)</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommonFields() {
|
||||||
|
if (!transaction) return null;
|
||||||
|
return <Fragment>
|
||||||
|
<tr>
|
||||||
|
<td>Amount deduce</td>
|
||||||
|
<td>{transaction.amountRaw}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Amount received</td>
|
||||||
|
<td>{transaction.amountEffective}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Exchange fee</td>
|
||||||
|
<td>{Amounts.stringify(
|
||||||
|
Amounts.sub(
|
||||||
|
Amounts.parseOrThrow(transaction.amountRaw),
|
||||||
|
Amounts.parseOrThrow(transaction.amountEffective),
|
||||||
|
).amount
|
||||||
|
)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>When</td>
|
||||||
|
<td>{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</td>
|
||||||
|
</tr>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.type === TransactionType.Withdrawal) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
|
||||||
|
<section>
|
||||||
|
<h1>Withdrawal <Pending /></h1>
|
||||||
|
<p>
|
||||||
|
From <b>{transaction.exchangeBaseUrl}</b>
|
||||||
|
</p>
|
||||||
|
<table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
|
||||||
|
<CommonFields />
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.type === TransactionType.Payment) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
|
||||||
|
<section>
|
||||||
|
<h1>Payment ({transaction.proposalId}) <Pending /></h1>
|
||||||
|
<p>
|
||||||
|
To <b>{transaction.info.merchant.name}</b>
|
||||||
|
</p>
|
||||||
|
<table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
|
||||||
|
<tr>
|
||||||
|
<td>Order id</td>
|
||||||
|
<td>{transaction.info.orderId}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Summary</td>
|
||||||
|
<td>{transaction.info.summary}</td>
|
||||||
|
</tr>
|
||||||
|
{transaction.info.products && transaction.info.products.length > 0 &&
|
||||||
|
<tr>
|
||||||
|
<td>Products</td>
|
||||||
|
<td><ol style={{margin:0, textAlign:'left'}}>
|
||||||
|
{transaction.info.products.map(p =>
|
||||||
|
<li>{p.description}</li>
|
||||||
|
)}</ol></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<CommonFields />
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.type === TransactionType.Deposit) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
|
||||||
|
<section>
|
||||||
|
<h1>Deposit ({transaction.depositGroupId}) <Pending /></h1>
|
||||||
|
<p>
|
||||||
|
To <b>{transaction.targetPaytoUri}</b>
|
||||||
|
</p>
|
||||||
|
<table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
|
||||||
|
<CommonFields />
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.type === TransactionType.Refresh) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
|
||||||
|
<section>
|
||||||
|
<h1>Refresh <Pending /></h1>
|
||||||
|
<p>
|
||||||
|
From <b>{transaction.exchangeBaseUrl}</b>
|
||||||
|
</p>
|
||||||
|
<table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
|
||||||
|
<CommonFields />
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.type === TransactionType.Tip) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
|
||||||
|
<section>
|
||||||
|
<h1>Tip <Pending /></h1>
|
||||||
|
<p>
|
||||||
|
From <b>{transaction.merchantBaseUrl}</b>
|
||||||
|
</p>
|
||||||
|
<table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
|
||||||
|
<CommonFields />
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.type === TransactionType.Refund) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} >
|
||||||
|
<section>
|
||||||
|
<h1>Refund ({transaction.refundedTransactionId}) <Pending /></h1>
|
||||||
|
<p>
|
||||||
|
From <b>{transaction.info.merchant.name}</b>
|
||||||
|
</p>
|
||||||
|
<table class={transaction.pending ? "detailsTable pending" : "detailsTable"}>
|
||||||
|
<tr>
|
||||||
|
<td>Order id</td>
|
||||||
|
<td>{transaction.info.orderId}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Summary</td>
|
||||||
|
<td>{transaction.info.summary}</td>
|
||||||
|
</tr>
|
||||||
|
{transaction.info.products && transaction.info.products.length > 0 &&
|
||||||
|
<tr>
|
||||||
|
<td>Products</td>
|
||||||
|
<td><ol>
|
||||||
|
{transaction.info.products.map(p =>
|
||||||
|
<li>{p.description}</li>
|
||||||
|
)}</ol></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<CommonFields />
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return <div></div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function WalletTransaction({ tid }: { tid: string }): JSX.Element {
|
||||||
|
const [transaction, setTransaction] = useState<
|
||||||
|
Transaction | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async (): Promise<void> => {
|
||||||
|
const res = await wxApi.getTransactions();
|
||||||
|
const ts = res.transactions.filter(t => t.transactionId === tid)
|
||||||
|
if (ts.length === 1) {
|
||||||
|
setTransaction(ts[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <WalletTransactionView
|
||||||
|
transaction={transaction}
|
||||||
|
onDelete={() => wxApi.deleteTransaction(tid)}
|
||||||
|
onBack={() => { history.go(-1) }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
class WalletSettings extends Component<any, any> {
|
class WalletSettings extends Component<any, any> {
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
@ -597,6 +822,7 @@ export function WalletPopup(): JSX.Element {
|
|||||||
<Route path={Pages.settings} component={WalletSettings} />
|
<Route path={Pages.settings} component={WalletSettings} />
|
||||||
<Route path={Pages.debug} component={WalletDebug} />
|
<Route path={Pages.debug} component={WalletDebug} />
|
||||||
<Route path={Pages.history} component={WalletHistory} />
|
<Route path={Pages.history} component={WalletHistory} />
|
||||||
|
<Route path={Pages.transaction} component={WalletTransaction} />
|
||||||
</Router>
|
</Router>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -605,6 +831,7 @@ export function WalletPopup(): JSX.Element {
|
|||||||
|
|
||||||
enum Pages {
|
enum Pages {
|
||||||
balance = '/popup/balance',
|
balance = '/popup/balance',
|
||||||
|
transaction = '/popup/transaction/:tid',
|
||||||
settings = '/popup/settings',
|
settings = '/popup/settings',
|
||||||
debug = '/popup/debug',
|
debug = '/popup/debug',
|
||||||
history = '/popup/history',
|
history = '/popup/history',
|
||||||
|
@ -35,6 +35,7 @@ import {
|
|||||||
PrepareTipRequest,
|
PrepareTipRequest,
|
||||||
PrepareTipResult,
|
PrepareTipResult,
|
||||||
AcceptTipRequest,
|
AcceptTipRequest,
|
||||||
|
DeleteTransactionRequest,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
|
import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
|
||||||
|
|
||||||
@ -130,6 +131,15 @@ export function getTransactions(): Promise<TransactionsResponse> {
|
|||||||
return callBackend("getTransactions", {});
|
return callBackend("getTransactions", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get balances for all currencies/exchanges.
|
||||||
|
*/
|
||||||
|
export function deleteTransaction(transactionId: string): Promise<void> {
|
||||||
|
return callBackend("deleteTransaction", {
|
||||||
|
transactionId
|
||||||
|
} as DeleteTransactionRequest);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download a refund and accept it.
|
* Download a refund and accept it.
|
||||||
*/
|
*/
|
||||||
|
@ -238,3 +238,25 @@ button.accept:disabled {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background: #00fa9a;
|
background: #00fa9a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.detailsTable td {
|
||||||
|
text-align: right;
|
||||||
|
border: 0px;
|
||||||
|
border-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.detailsTable td {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.detailsTable tr:last-child td {
|
||||||
|
border-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.detailsTable {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.detailsTable.pending {
|
||||||
|
color: gray;
|
||||||
|
}
|
@ -224,6 +224,7 @@ importers:
|
|||||||
'@types/node': ^14.14.22
|
'@types/node': ^14.14.22
|
||||||
ava: 3.15.0
|
ava: 3.15.0
|
||||||
babel-plugin-transform-react-jsx: ^6.24.1
|
babel-plugin-transform-react-jsx: ^6.24.1
|
||||||
|
date-fns: ^2.22.1
|
||||||
enzyme: ^3.11.0
|
enzyme: ^3.11.0
|
||||||
enzyme-adapter-preact-pure: ^3.1.0
|
enzyme-adapter-preact-pure: ^3.1.0
|
||||||
history: 4.10.1
|
history: 4.10.1
|
||||||
@ -243,6 +244,7 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@gnu-taler/taler-util': link:../taler-util
|
'@gnu-taler/taler-util': link:../taler-util
|
||||||
'@gnu-taler/taler-wallet-core': link:../taler-wallet-core
|
'@gnu-taler/taler-wallet-core': link:../taler-wallet-core
|
||||||
|
date-fns: 2.22.1
|
||||||
preact: 10.5.13
|
preact: 10.5.13
|
||||||
preact-router: 3.2.1_preact@10.5.13
|
preact-router: 3.2.1_preact@10.5.13
|
||||||
tslib: 2.1.0
|
tslib: 2.1.0
|
||||||
@ -8087,6 +8089,11 @@ packages:
|
|||||||
whatwg-url: 8.5.0
|
whatwg-url: 8.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/date-fns/2.22.1:
|
||||||
|
resolution: {integrity: sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==}
|
||||||
|
engines: {node: '>=0.11'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/date-time/3.1.0:
|
/date-time/3.1.0:
|
||||||
resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
|
resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
Loading…
Reference in New Issue
Block a user