fix ui transaction list

This commit is contained in:
Sebastian 2021-08-24 12:00:34 -03:00
parent 514395eec3
commit bbcae18f6a
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
6 changed files with 194 additions and 344 deletions

View File

@ -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>
);
}

View File

@ -269,6 +269,7 @@ export const RowLightBorderGray = styled(Row2)`
export const HistoryRow = styled.a`
text-decoration: none;
color: #212121;
display: flex;
justify-content: space-between;
@ -312,6 +313,10 @@ export const LightText = styled.div`
export const SmallText = styled.div`
font-size: small;
`
export const LargeText = styled.div`
font-size: large;
`
export const ExtraLargeText = styled.div`
font-size: x-large;
`

View File

@ -48,7 +48,7 @@ const exampleData = {
withdraw: {
...commonTransaction,
type: TransactionType.Withdrawal,
exchangeBaseUrl: 'http://exchange.taler',
exchangeBaseUrl: 'http://exchange.demo.taler.net',
withdrawalDetails: {
confirmed: false,
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],

View File

@ -14,19 +14,13 @@
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 { formatDistance } from "date-fns";
import { JSX, h } from "preact";
import { AmountString, Balance, Transaction, TransactionsResponse } from "@gnu-taler/taler-util";
import { h, JSX } from "preact";
import { useEffect, useState } from "preact/hooks";
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 { Column, ExtraLargeText, HistoryRow, PopupBox, SmallTextLight } from "../components/styled";
import { PopupBox } from "../components/styled";
import { TransactionItem } from "../components/TransactionItem";
import { useBalances } from "../hooks/useBalances";
import * as wxApi from "../wxApi";
import { Pages } from "../NavigationBar";
export function HistoryPage(props: any): JSX.Element {
@ -59,20 +53,20 @@ function amountToString(c: AmountString) {
export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) {
const multiCurrency = balances.length > 1
return <PopupBox noPadding>
{balances.length > 0 && <header>
{balances.length === 1 && <div class="title">
Balance: <span>{amountToString(balances[0].available)}</span>
</div>}
{balances.length > 1 && <div class="title">
{multiCurrency ? <div class="title">
Balance: <ul style={{ margin: 0 }}>
{balances.map(b => <li>{b.available}</li>)}
</ul>
</div> : <div class="title">
Balance: <span>{amountToString(balances[0].available)}</span>
</div>}
</header>}
<section>
{list.slice(0, 3).map((tx, i) => (
<TransactionItem key={i} tx={tx} />
<TransactionItem key={i} tx={tx} multiCurrency={multiCurrency}/>
))}
</section>
<footer style={{ justifyContent: 'space-around' }}>
@ -83,160 +77,3 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
</footer>
</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>
);
}

View File

@ -50,7 +50,7 @@ const exampleData = {
withdraw: {
...commonTransaction(),
type: TransactionType.Withdrawal,
exchangeBaseUrl: 'http://exchange.taler',
exchangeBaseUrl: 'http://exchange.demo.taler.net',
withdrawalDetails: {
confirmed: false,
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
@ -64,7 +64,7 @@ const exampleData = {
info: {
contractTermsHash: 'ASDZXCASD',
merchant: {
name: 'the merchant',
name: 'Blog',
},
orderId: '2021.167-03NPY6MCYMVGT',
products: [],
@ -88,7 +88,7 @@ const exampleData = {
tip: {
...commonTransaction(),
type: TransactionType.Tip,
merchantBaseUrl: 'http://merchant.taler',
merchantBaseUrl: 'http://ads.merchant.taler.net/',
} as TransactionTip,
refund: {
...commonTransaction(),

View File

@ -14,19 +14,14 @@
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 { Fragment, JSX, h } from "preact";
import { Fragment, h, JSX } from "preact";
import { useEffect, useState } from "preact/hooks";
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 { Column, ExtraLargeText, HistoryRow, WalletBox, DateSeparator, SmallTextLight } from "../components/styled";
import { DateSeparator, WalletBox } from "../components/styled";
import { TransactionItem } from "../components/TransactionItem";
import { useBalances } from "../hooks/useBalances";
import * as wxApi from "../wxApi";
import { Pages } from "../NavigationBar";
export function HistoryPage(props: any): JSX.Element {
@ -65,6 +60,8 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
return rv;
}, {} as { [x: string]: Transaction[] });
const multiCurrency = balances.length > 1
return <WalletBox noPadding>
{balances.length > 0 && <header>
{balances.length === 1 && <div class="title">
@ -81,168 +78,10 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
return <Fragment>
<DateSeparator>{d}</DateSeparator>
{byDate[d].map((tx, i) => (
<TransactionItem key={i} tx={tx} />
<TransactionItem key={i} tx={tx} multiCurrency={multiCurrency}/>
))}
</Fragment>
})}
</section>
</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>
);
}