fix ui transaction list
This commit is contained in:
parent
514395eec3
commit
bbcae18f6a
@ -0,0 +1,169 @@
|
|||||||
|
import { AmountString, Timestamp, Transaction, TransactionType } from '@gnu-taler/taler-util';
|
||||||
|
import { format, formatDistance } from 'date-fns';
|
||||||
|
import { h } from 'preact';
|
||||||
|
import imageBank from '../../static/img/ri-bank-line.svg';
|
||||||
|
import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
|
||||||
|
import imageRefresh from '../../static/img/ri-refresh-line.svg';
|
||||||
|
import imageRefund from '../../static/img/ri-refund-2-line.svg';
|
||||||
|
import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
|
||||||
|
import { Pages } from "../NavigationBar";
|
||||||
|
import { Column, ExtraLargeText, HistoryRow, SmallTextLight, LargeText } from './styled/index';
|
||||||
|
|
||||||
|
export function TransactionItem(props: { tx: Transaction, multiCurrency: boolean }): JSX.Element {
|
||||||
|
const tx = props.tx;
|
||||||
|
switch (tx.type) {
|
||||||
|
case TransactionType.Withdrawal:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageBank}
|
||||||
|
pending={tx.pending}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Payment:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"debit"}
|
||||||
|
title={tx.info.merchant.name}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageShoppingCart}
|
||||||
|
pending={tx.pending}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Refund:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title={tx.info.merchant.name}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageRefund}
|
||||||
|
pending={tx.pending}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Tip:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title={new URL(tx.merchantBaseUrl).hostname}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageHandHeart}
|
||||||
|
pending={tx.pending}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Refresh:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageRefresh}
|
||||||
|
pending={tx.pending}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Deposit:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"debit"}
|
||||||
|
title={tx.targetPaytoUri}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageRefresh}
|
||||||
|
pending={tx.pending}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
|
||||||
|
const date = new Date(props.timestamp.t_ms);
|
||||||
|
const dateStr = format(date, 'dd MMM, hh:mm')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HistoryRow href={Pages.transaction.replace(':tid', props.id)}>
|
||||||
|
<img src={props.iconPath} />
|
||||||
|
<Column>
|
||||||
|
<LargeText>
|
||||||
|
<span>{props.title}</span>
|
||||||
|
{props.pending ? (
|
||||||
|
<span style={{ color: "darkblue" }}> (Pending)</span>
|
||||||
|
) : null}
|
||||||
|
</LargeText>
|
||||||
|
<SmallTextLight>{dateStr}</SmallTextLight>
|
||||||
|
</Column>
|
||||||
|
<TransactionAmount
|
||||||
|
pending={props.pending}
|
||||||
|
amount={props.amount}
|
||||||
|
multiCurrency={props.multiCurrency}
|
||||||
|
debitCreditIndicator={props.debitCreditIndicator}
|
||||||
|
/>
|
||||||
|
</HistoryRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionLayoutProps {
|
||||||
|
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||||
|
amount: AmountString | "unknown";
|
||||||
|
timestamp: Timestamp;
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
iconPath: string;
|
||||||
|
pending: boolean;
|
||||||
|
multiCurrency: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionAmountProps {
|
||||||
|
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||||
|
amount: AmountString | "unknown";
|
||||||
|
pending: boolean;
|
||||||
|
multiCurrency: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransactionAmount(props: TransactionAmountProps): JSX.Element {
|
||||||
|
const [currency, amount] = props.amount.split(":");
|
||||||
|
let sign: string;
|
||||||
|
switch (props.debitCreditIndicator) {
|
||||||
|
case "credit":
|
||||||
|
sign = "+";
|
||||||
|
break;
|
||||||
|
case "debit":
|
||||||
|
sign = "-";
|
||||||
|
break;
|
||||||
|
case "unknown":
|
||||||
|
sign = "";
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Column style={{
|
||||||
|
color:
|
||||||
|
props.pending ? "gray" :
|
||||||
|
(sign === '+' ? 'darkgreen' :
|
||||||
|
(sign === '-' ? 'darkred' :
|
||||||
|
undefined))
|
||||||
|
}}>
|
||||||
|
<ExtraLargeText>
|
||||||
|
{sign}
|
||||||
|
{amount}
|
||||||
|
</ExtraLargeText>
|
||||||
|
{props.multiCurrency && <div>{currency}</div>}
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -269,6 +269,7 @@ export const RowLightBorderGray = styled(Row2)`
|
|||||||
|
|
||||||
export const HistoryRow = styled.a`
|
export const HistoryRow = styled.a`
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
color: #212121;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -312,6 +313,10 @@ export const LightText = styled.div`
|
|||||||
export const SmallText = styled.div`
|
export const SmallText = styled.div`
|
||||||
font-size: small;
|
font-size: small;
|
||||||
`
|
`
|
||||||
|
export const LargeText = styled.div`
|
||||||
|
font-size: large;
|
||||||
|
`
|
||||||
|
|
||||||
export const ExtraLargeText = styled.div`
|
export const ExtraLargeText = styled.div`
|
||||||
font-size: x-large;
|
font-size: x-large;
|
||||||
`
|
`
|
||||||
|
@ -48,7 +48,7 @@ const exampleData = {
|
|||||||
withdraw: {
|
withdraw: {
|
||||||
...commonTransaction,
|
...commonTransaction,
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
exchangeBaseUrl: 'http://exchange.taler',
|
exchangeBaseUrl: 'http://exchange.demo.taler.net',
|
||||||
withdrawalDetails: {
|
withdrawalDetails: {
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
|
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
|
||||||
|
@ -14,19 +14,13 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
|
import { AmountString, Balance, Transaction, TransactionsResponse } from "@gnu-taler/taler-util";
|
||||||
import { formatDistance } from "date-fns";
|
import { h, JSX } from "preact";
|
||||||
import { JSX, h } from "preact";
|
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import imageBank from '../../static/img/ri-bank-line.svg';
|
import { PopupBox } from "../components/styled";
|
||||||
import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
|
import { TransactionItem } from "../components/TransactionItem";
|
||||||
import imageRefresh from '../../static/img/ri-refresh-line.svg';
|
|
||||||
import imageRefund from '../../static/img/ri-refund-2-line.svg';
|
|
||||||
import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
|
|
||||||
import { Column, ExtraLargeText, HistoryRow, PopupBox, SmallTextLight } from "../components/styled";
|
|
||||||
import { useBalances } from "../hooks/useBalances";
|
import { useBalances } from "../hooks/useBalances";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
import { Pages } from "../NavigationBar";
|
|
||||||
|
|
||||||
|
|
||||||
export function HistoryPage(props: any): JSX.Element {
|
export function HistoryPage(props: any): JSX.Element {
|
||||||
@ -59,20 +53,20 @@ function amountToString(c: AmountString) {
|
|||||||
|
|
||||||
|
|
||||||
export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) {
|
export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) {
|
||||||
|
const multiCurrency = balances.length > 1
|
||||||
return <PopupBox noPadding>
|
return <PopupBox noPadding>
|
||||||
{balances.length > 0 && <header>
|
{balances.length > 0 && <header>
|
||||||
{balances.length === 1 && <div class="title">
|
{multiCurrency ? <div class="title">
|
||||||
Balance: <span>{amountToString(balances[0].available)}</span>
|
|
||||||
</div>}
|
|
||||||
{balances.length > 1 && <div class="title">
|
|
||||||
Balance: <ul style={{ margin: 0 }}>
|
Balance: <ul style={{ margin: 0 }}>
|
||||||
{balances.map(b => <li>{b.available}</li>)}
|
{balances.map(b => <li>{b.available}</li>)}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div> : <div class="title">
|
||||||
|
Balance: <span>{amountToString(balances[0].available)}</span>
|
||||||
</div>}
|
</div>}
|
||||||
</header>}
|
</header>}
|
||||||
<section>
|
<section>
|
||||||
{list.slice(0, 3).map((tx, i) => (
|
{list.slice(0, 3).map((tx, i) => (
|
||||||
<TransactionItem key={i} tx={tx} />
|
<TransactionItem key={i} tx={tx} multiCurrency={multiCurrency}/>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
<footer style={{ justifyContent: 'space-around' }}>
|
<footer style={{ justifyContent: 'space-around' }}>
|
||||||
@ -83,160 +77,3 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
|
|||||||
</footer>
|
</footer>
|
||||||
</PopupBox>
|
</PopupBox>
|
||||||
}
|
}
|
||||||
|
|
||||||
function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|
||||||
const tx = props.tx;
|
|
||||||
switch (tx.type) {
|
|
||||||
case TransactionType.Withdrawal:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Withdrawal"
|
|
||||||
subtitle={`via ${tx.exchangeBaseUrl}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageBank}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Payment:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"debit"}
|
|
||||||
title="Payment"
|
|
||||||
subtitle={tx.info.summary}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageShoppingCart}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Refund:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Refund"
|
|
||||||
subtitle={tx.info.summary}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageRefund}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Tip:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Tip"
|
|
||||||
subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageHandHeart}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Refresh:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Refresh"
|
|
||||||
subtitle={`via exchange ${tx.exchangeBaseUrl}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageRefresh}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Deposit:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"debit"}
|
|
||||||
title="Refresh"
|
|
||||||
subtitle={`to ${tx.targetPaytoUri}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageRefresh}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
|
|
||||||
const date = new Date(props.timestamp.t_ms);
|
|
||||||
const now = new Date();
|
|
||||||
const dateStr = formatDistance(date, now, { addSuffix: true })
|
|
||||||
return (
|
|
||||||
<HistoryRow href={Pages.transaction.replace(':tid', props.id)}>
|
|
||||||
<img src={props.iconPath} />
|
|
||||||
<Column>
|
|
||||||
<ExtraLargeText>
|
|
||||||
<span>{props.title}</span>
|
|
||||||
{props.pending ? (
|
|
||||||
<span style={{ color: "darkblue" }}> (Pending)</span>
|
|
||||||
) : null}
|
|
||||||
</ExtraLargeText>
|
|
||||||
<SmallTextLight>{dateStr}</SmallTextLight>
|
|
||||||
</Column>
|
|
||||||
<TransactionAmount
|
|
||||||
pending={props.pending}
|
|
||||||
amount={props.amount}
|
|
||||||
debitCreditIndicator={props.debitCreditIndicator}
|
|
||||||
/>
|
|
||||||
</HistoryRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TransactionLayoutProps {
|
|
||||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
|
||||||
amount: AmountString | "unknown";
|
|
||||||
timestamp: Timestamp;
|
|
||||||
title: string;
|
|
||||||
id: string;
|
|
||||||
subtitle: string;
|
|
||||||
iconPath: string;
|
|
||||||
pending: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TransactionAmountProps {
|
|
||||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
|
||||||
amount: AmountString | "unknown";
|
|
||||||
pending: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransactionAmount(props: TransactionAmountProps): JSX.Element {
|
|
||||||
const [currency, amount] = props.amount.split(":");
|
|
||||||
let sign: string;
|
|
||||||
switch (props.debitCreditIndicator) {
|
|
||||||
case "credit":
|
|
||||||
sign = "+";
|
|
||||||
break;
|
|
||||||
case "debit":
|
|
||||||
sign = "-";
|
|
||||||
break;
|
|
||||||
case "unknown":
|
|
||||||
sign = "";
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Column style={{
|
|
||||||
color:
|
|
||||||
props.pending ? "gray" :
|
|
||||||
(sign === '+' ? 'darkgreen' :
|
|
||||||
(sign === '-' ? 'darkred' :
|
|
||||||
undefined))
|
|
||||||
}}>
|
|
||||||
<ExtraLargeText>
|
|
||||||
{sign}
|
|
||||||
{amount}
|
|
||||||
</ExtraLargeText>
|
|
||||||
<div>{currency}</div>
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ const exampleData = {
|
|||||||
withdraw: {
|
withdraw: {
|
||||||
...commonTransaction(),
|
...commonTransaction(),
|
||||||
type: TransactionType.Withdrawal,
|
type: TransactionType.Withdrawal,
|
||||||
exchangeBaseUrl: 'http://exchange.taler',
|
exchangeBaseUrl: 'http://exchange.demo.taler.net',
|
||||||
withdrawalDetails: {
|
withdrawalDetails: {
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
|
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
|
||||||
@ -64,7 +64,7 @@ const exampleData = {
|
|||||||
info: {
|
info: {
|
||||||
contractTermsHash: 'ASDZXCASD',
|
contractTermsHash: 'ASDZXCASD',
|
||||||
merchant: {
|
merchant: {
|
||||||
name: 'the merchant',
|
name: 'Blog',
|
||||||
},
|
},
|
||||||
orderId: '2021.167-03NPY6MCYMVGT',
|
orderId: '2021.167-03NPY6MCYMVGT',
|
||||||
products: [],
|
products: [],
|
||||||
@ -88,7 +88,7 @@ const exampleData = {
|
|||||||
tip: {
|
tip: {
|
||||||
...commonTransaction(),
|
...commonTransaction(),
|
||||||
type: TransactionType.Tip,
|
type: TransactionType.Tip,
|
||||||
merchantBaseUrl: 'http://merchant.taler',
|
merchantBaseUrl: 'http://ads.merchant.taler.net/',
|
||||||
} as TransactionTip,
|
} as TransactionTip,
|
||||||
refund: {
|
refund: {
|
||||||
...commonTransaction(),
|
...commonTransaction(),
|
||||||
|
@ -14,19 +14,14 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
|
import { AmountString, Balance, Transaction, TransactionsResponse } from "@gnu-taler/taler-util";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { Fragment, JSX, h } from "preact";
|
import { Fragment, h, JSX } from "preact";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import imageBank from '../../static/img/ri-bank-line.svg';
|
import { DateSeparator, WalletBox } from "../components/styled";
|
||||||
import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
|
import { TransactionItem } from "../components/TransactionItem";
|
||||||
import imageRefresh from '../../static/img/ri-refresh-line.svg';
|
|
||||||
import imageRefund from '../../static/img/ri-refund-2-line.svg';
|
|
||||||
import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
|
|
||||||
import { Column, ExtraLargeText, HistoryRow, WalletBox, DateSeparator, SmallTextLight } from "../components/styled";
|
|
||||||
import { useBalances } from "../hooks/useBalances";
|
import { useBalances } from "../hooks/useBalances";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
import { Pages } from "../NavigationBar";
|
|
||||||
|
|
||||||
|
|
||||||
export function HistoryPage(props: any): JSX.Element {
|
export function HistoryPage(props: any): JSX.Element {
|
||||||
@ -65,6 +60,8 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
|
|||||||
return rv;
|
return rv;
|
||||||
}, {} as { [x: string]: Transaction[] });
|
}, {} as { [x: string]: Transaction[] });
|
||||||
|
|
||||||
|
const multiCurrency = balances.length > 1
|
||||||
|
|
||||||
return <WalletBox noPadding>
|
return <WalletBox noPadding>
|
||||||
{balances.length > 0 && <header>
|
{balances.length > 0 && <header>
|
||||||
{balances.length === 1 && <div class="title">
|
{balances.length === 1 && <div class="title">
|
||||||
@ -81,168 +78,10 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
|
|||||||
return <Fragment>
|
return <Fragment>
|
||||||
<DateSeparator>{d}</DateSeparator>
|
<DateSeparator>{d}</DateSeparator>
|
||||||
{byDate[d].map((tx, i) => (
|
{byDate[d].map((tx, i) => (
|
||||||
<TransactionItem key={i} tx={tx} />
|
<TransactionItem key={i} tx={tx} multiCurrency={multiCurrency}/>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
})}
|
})}
|
||||||
</section>
|
</section>
|
||||||
</WalletBox>
|
</WalletBox>
|
||||||
}
|
}
|
||||||
|
|
||||||
function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
|
||||||
const tx = props.tx;
|
|
||||||
switch (tx.type) {
|
|
||||||
case TransactionType.Withdrawal:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Withdrawal"
|
|
||||||
subtitle={`via ${tx.exchangeBaseUrl}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageBank}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Payment:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"debit"}
|
|
||||||
title="Payment"
|
|
||||||
subtitle={tx.info.summary}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageShoppingCart}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Refund:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Refund"
|
|
||||||
subtitle={tx.info.summary}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageRefund}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Tip:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Tip"
|
|
||||||
subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageHandHeart}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Refresh:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"credit"}
|
|
||||||
title="Refresh"
|
|
||||||
subtitle={`via exchange ${tx.exchangeBaseUrl}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageRefresh}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
case TransactionType.Deposit:
|
|
||||||
return (
|
|
||||||
<TransactionLayout
|
|
||||||
id={tx.transactionId}
|
|
||||||
amount={tx.amountEffective}
|
|
||||||
debitCreditIndicator={"debit"}
|
|
||||||
title="Refresh"
|
|
||||||
subtitle={`to ${tx.targetPaytoUri}`}
|
|
||||||
timestamp={tx.timestamp}
|
|
||||||
iconPath={imageRefresh}
|
|
||||||
pending={tx.pending}
|
|
||||||
></TransactionLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
|
|
||||||
const date = new Date(props.timestamp.t_ms);
|
|
||||||
const dateStr = format(date, 'HH:mm:ss')
|
|
||||||
return (
|
|
||||||
// <a href={Pages.transaction.replace(':tid', props.id)}>
|
|
||||||
<HistoryRow href={Pages.transaction.replace(':tid', props.id)}>
|
|
||||||
<img src={props.iconPath} />
|
|
||||||
<Column>
|
|
||||||
<ExtraLargeText>
|
|
||||||
<span>{props.title}</span>
|
|
||||||
{props.pending ? (
|
|
||||||
<span style={{ color: "darkblue" }}> (Pending)</span>
|
|
||||||
) : null}
|
|
||||||
</ExtraLargeText>
|
|
||||||
<SmallTextLight>{dateStr}</SmallTextLight>
|
|
||||||
</Column>
|
|
||||||
<TransactionAmount
|
|
||||||
pending={props.pending}
|
|
||||||
amount={props.amount}
|
|
||||||
debitCreditIndicator={props.debitCreditIndicator}
|
|
||||||
/>
|
|
||||||
</HistoryRow>
|
|
||||||
// </a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TransactionLayoutProps {
|
|
||||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
|
||||||
amount: AmountString | "unknown";
|
|
||||||
timestamp: Timestamp;
|
|
||||||
title: string;
|
|
||||||
id: string;
|
|
||||||
subtitle: string;
|
|
||||||
iconPath: string;
|
|
||||||
pending: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TransactionAmountProps {
|
|
||||||
debitCreditIndicator: "debit" | "credit" | "unknown";
|
|
||||||
amount: AmountString | "unknown";
|
|
||||||
pending: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TransactionAmount(props: TransactionAmountProps): JSX.Element {
|
|
||||||
const [currency, amount] = props.amount.split(":");
|
|
||||||
let sign: string;
|
|
||||||
switch (props.debitCreditIndicator) {
|
|
||||||
case "credit":
|
|
||||||
sign = "+";
|
|
||||||
break;
|
|
||||||
case "debit":
|
|
||||||
sign = "-";
|
|
||||||
break;
|
|
||||||
case "unknown":
|
|
||||||
sign = "";
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Column style={{
|
|
||||||
color:
|
|
||||||
props.pending ? "gray" :
|
|
||||||
(sign === '+' ? 'darkgreen' :
|
|
||||||
(sign === '-' ? 'darkred' :
|
|
||||||
undefined))
|
|
||||||
}}>
|
|
||||||
<ExtraLargeText>
|
|
||||||
{sign}
|
|
||||||
{amount}
|
|
||||||
</ExtraLargeText>
|
|
||||||
<div>{currency}</div>
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user