diff options
author | Sebastian <sebasjm@gmail.com> | 2021-06-20 20:37:35 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-06-20 20:52:38 -0300 |
commit | 956fc35a20466ac5ae0889c08b05176207c0a54d (patch) | |
tree | 75aee44193669b25b6ad3ab36a0b944d3a4d9664 /packages/taler-wallet-webextension/src | |
parent | 7f3ce304cb4e699fff61703ad5700770c7cf507e (diff) |
wallet transaction detail view
Diffstat (limited to 'packages/taler-wallet-webextension/src')
6 files changed, 475 insertions, 228 deletions
diff --git a/packages/taler-wallet-webextension/src/custom.d.ts b/packages/taler-wallet-webextension/src/custom.d.ts new file mode 100644 index 000000000..1981067d4 --- /dev/null +++ b/packages/taler-wallet-webextension/src/custom.d.ts @@ -0,0 +1,27 @@ +/* + 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/> + */ +declare module "*.jpeg" { + const content: any; + export default content; +} +declare module "*.png" { + const content: any; + export default content; +} +declare module '*.svg' { + const content: any; + export default content; +} diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx b/packages/taler-wallet-webextension/src/popup/History.stories.tsx new file mode 100644 index 000000000..a73b7ea3f --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/History.stories.tsx @@ -0,0 +1,256 @@ +/* + 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, + TransactionCommon, TransactionDeposit, TransactionPayment, + TransactionRefresh, TransactionRefund, TransactionTip, TransactionType, + TransactionWithdrawal, + WithdrawalType +} from '@gnu-taler/taler-util'; +import { FunctionalComponent } from 'preact'; +import { HistoryView as TestedComponent } from './History'; + +export default { + title: 'popup/transaction/list', + component: TestedComponent, + 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, display: 'flex', padding: '0.5em', height: 'calc(20rem - 34px)', border: 'black solid 1px' }}> + <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, + amountEffective: 'USD:11', + type: TransactionType.Payment, + info: { + contractTermsHash: 'ASDZXCASD', + merchant: { + name: 'the merchant', + }, + orderId: '2021.167-03NPY6MCYMVGT', + products: [], + summary: 'the summary', + fulfillmentMessage: '', + }, + proposalId: '1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0', + 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: 'payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0', + info: { + contractTermsHash: 'ASDZXCASD', + merchant: { + name: 'the merchant', + }, + orderId: '2021.167-03NPY6MCYMVGT', + products: [], + summary: 'the summary', + fulfillmentMessage: '', + }, + } as TransactionRefund, +} + +function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) { + const r = (args: any) => <Component {...args} /> + r.args = props + return r +} + +export const Empty = createExample(TestedComponent, { + list: [] +}); + + +export const One = createExample(TestedComponent, { + list: [exampleData.withdraw] +}); + +export const Several = createExample(TestedComponent, { + list: [ + exampleData.withdraw, + exampleData.payment, + exampleData.withdraw, + exampleData.payment, + exampleData.refresh, + exampleData.refund, + exampleData.tip, + exampleData.deposit, + ] +}); + +// export const WithdrawPending = createExample(TestedComponent, { +// transaction: { ...exampleData.withdraw, pending: true }, +// }); + + +// export const Payment = createExample(TestedComponent, { +// transaction: exampleData.payment +// }); + +// export const PaymentWithoutFee = createExample(TestedComponent, { +// transaction: { +// ...exampleData.payment, +// amountRaw: 'USD:11', + +// } +// }); + +// export const PaymentPending = createExample(TestedComponent, { +// transaction: { ...exampleData.payment, pending: true }, +// }); + +// export const PaymentWithProducts = createExample(TestedComponent, { +// transaction: { +// ...exampleData.payment, +// info: { +// ...exampleData.payment.info, +// summary: 'this order has 5 products', +// products: [{ +// description: 't-shirt', +// unit: 'shirts', +// quantity: 1, +// }, { +// description: 't-shirt', +// unit: 'shirts', +// quantity: 1, +// }, { +// description: 'e-book', +// }, { +// description: 'beer', +// unit: 'pint', +// quantity: 15, +// }, { +// description: 'beer', +// unit: 'pint', +// quantity: 15, +// }] +// } +// } as TransactionPayment, +// }); + +// export const PaymentWithLongSummary = createExample(TestedComponent, { +// transaction: { +// ...exampleData.payment, +// info: { +// ...exampleData.payment.info, +// summary: 'this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, ', +// products: [{ +// description: 'an xl sized t-shirt with some drawings on it, color pink', +// unit: 'shirts', +// quantity: 1, +// }, { +// description: 'beer', +// unit: 'pint', +// quantity: 15, +// }] +// } +// } as TransactionPayment, +// }); + + +// export const Deposit = createExample(TestedComponent, { +// transaction: exampleData.deposit +// }); + +// export const DepositPending = createExample(TestedComponent, { +// transaction: { ...exampleData.deposit, pending: true } +// }); + +// export const Refresh = createExample(TestedComponent, { +// transaction: exampleData.refresh +// }); + +// export const Tip = createExample(TestedComponent, { +// transaction: exampleData.tip +// }); + +// export const TipPending = createExample(TestedComponent, { +// transaction: { ...exampleData.tip, pending: true } +// }); + +// export const Refund = createExample(TestedComponent, { +// transaction: exampleData.refund +// }); + +// export const RefundPending = createExample(TestedComponent, { +// transaction: { ...exampleData.refund, pending: true } +// }); + +// export const RefundWithProducts = createExample(TestedComponent, { +// transaction: { +// ...exampleData.refund, +// info: { +// ...exampleData.refund.info, +// products: [{ +// description: 't-shirt', +// }, { +// description: 'beer', +// }] +// } +// } as TransactionRefund, +// }); diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index ffcec5e41..1a70f00ff 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -38,17 +38,23 @@ export function HistoryPage(props: any): JSX.Element { return <div>Loading ...</div>; } - const txs = [...transactions.transactions].reverse(); + return <HistoryView list={[...transactions.transactions].reverse()} />; +} - return ( - <div> - {txs.map((tx, i) => ( - <TransactionItem key={i} tx={tx} /> - ))} - </div> - ); +export function HistoryView({ list }: { list: Transaction[] }) { + return <div style={{ height: 'calc(20rem - 34px )', overflow: 'auto', width: '100%' }}> + {list.map((tx, i) => ( + <TransactionItem key={i} tx={tx} /> + ))} + </div> } +import imageBank from '../../static/img/ri-bank-line.svg'; +import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg'; +import imageRefund from '../../static/img/ri-refund-2-line.svg'; +import imageHandHeart from '../../static/img/ri-hand-heart-line.svg'; +import imageRefresh from '../../static/img/ri-refresh-line.svg'; + function TransactionItem(props: { tx: Transaction }): JSX.Element { const tx = props.tx; switch (tx.type) { @@ -61,7 +67,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element { title="Withdrawal" subtitle={`via ${tx.exchangeBaseUrl}`} timestamp={tx.timestamp} - iconPath="/static/img/ri-bank-line.svg" + iconPath={imageBank} pending={tx.pending} ></TransactionLayout> ); @@ -74,7 +80,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element { title="Payment" subtitle={tx.info.summary} timestamp={tx.timestamp} - iconPath="/static/img/ri-shopping-cart-line.svg" + iconPath={imageShoppingCart} pending={tx.pending} ></TransactionLayout> ); @@ -87,7 +93,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element { title="Refund" subtitle={tx.info.summary} timestamp={tx.timestamp} - iconPath="/static/img/ri-refund-2-line.svg" + iconPath={imageRefund} pending={tx.pending} ></TransactionLayout> ); @@ -100,7 +106,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element { title="Tip" subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`} timestamp={tx.timestamp} - iconPath="/static/img/ri-hand-heart-line.svg" + iconPath={imageHandHeart} pending={tx.pending} ></TransactionLayout> ); @@ -113,7 +119,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element { title="Refresh" subtitle={`via exchange ${tx.exchangeBaseUrl}`} timestamp={tx.timestamp} - iconPath="/static/img/ri-refresh-line.svg" + iconPath={imageRefresh} pending={tx.pending} ></TransactionLayout> ); @@ -126,7 +132,7 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element { title="Refresh" subtitle={`to ${tx.targetPaytoUri}`} timestamp={tx.timestamp} - iconPath="/static/img/ri-refresh-line.svg" + iconPath={imageRefresh} pending={tx.pending} ></TransactionLayout> ); diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx index 3df2687fd..bf090cad7 100644 --- a/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx @@ -37,7 +37,7 @@ export default { <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 }}> + <div style={{ margin: "1em", width: 400, display: 'flex', padding: '0.5em', height: 'calc(20rem - 34px)', border: 'black solid 1px' }}> <Story /> </div> </div> @@ -74,7 +74,7 @@ const exampleData = { merchant: { name: 'the merchant', }, - orderId: '#12345', + orderId: '2021.167-03NPY6MCYMVGT', products: [], summary: 'the summary', fulfillmentMessage: '', @@ -107,7 +107,7 @@ const exampleData = { merchant: { name: 'the merchant', }, - orderId: '#12345', + orderId: '2021.167-03NPY6MCYMVGT', products: [], summary: 'the summary', fulfillmentMessage: '', @@ -121,69 +121,111 @@ function createExample<Props>(Component: FunctionalComponent<Props>, props: Part return r } -export const NotYetLoaded = createExample(TestedComponent,{}); +export const NotYetLoaded = createExample(TestedComponent, {}); -export const Withdraw = createExample(TestedComponent,{ +export const Withdraw = createExample(TestedComponent, { transaction: exampleData.withdraw }); -export const WithdrawPending = createExample(TestedComponent,{ +export const WithdrawPending = createExample(TestedComponent, { transaction: { ...exampleData.withdraw, pending: true }, }); -export const Payment = createExample(TestedComponent,{ +export const Payment = createExample(TestedComponent, { transaction: exampleData.payment }); -export const PaymentPending = createExample(TestedComponent,{ +export const PaymentWithoutFee = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: 'USD:11', + + } +}); + +export const PaymentPending = createExample(TestedComponent, { transaction: { ...exampleData.payment, pending: true }, }); -export const PaymentWithProducts = createExample(TestedComponent,{ +export const PaymentWithProducts = createExample(TestedComponent, { transaction: { ...exampleData.payment, info: { ...exampleData.payment.info, + summary: 'this order has 5 products', products: [{ description: 't-shirt', + unit: 'shirts', + quantity: 1, + }, { + description: 't-shirt', + unit: 'shirts', + quantity: 1, + }, { + description: 'e-book', + }, { + description: 'beer', + unit: 'pint', + quantity: 15, + }, { + description: 'beer', + unit: 'pint', + quantity: 15, + }] + } + } as TransactionPayment, +}); + +export const PaymentWithLongSummary = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + info: { + ...exampleData.payment.info, + summary: 'this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, ', + products: [{ + description: 'an xl sized t-shirt with some drawings on it, color pink', + unit: 'shirts', + quantity: 1, }, { description: 'beer', + unit: 'pint', + quantity: 15, }] } } as TransactionPayment, }); -export const Deposit = createExample(TestedComponent,{ +export const Deposit = createExample(TestedComponent, { transaction: exampleData.deposit }); -export const DepositPending = createExample(TestedComponent,{ +export const DepositPending = createExample(TestedComponent, { transaction: { ...exampleData.deposit, pending: true } }); -export const Refresh = createExample(TestedComponent,{ +export const Refresh = createExample(TestedComponent, { transaction: exampleData.refresh }); -export const Tip = createExample(TestedComponent,{ +export const Tip = createExample(TestedComponent, { transaction: exampleData.tip }); -export const TipPending = createExample(TestedComponent,{ +export const TipPending = createExample(TestedComponent, { transaction: { ...exampleData.tip, pending: true } }); -export const Refund = createExample(TestedComponent,{ +export const Refund = createExample(TestedComponent, { transaction: exampleData.refund }); -export const RefundPending = createExample(TestedComponent,{ +export const RefundPending = createExample(TestedComponent, { transaction: { ...exampleData.refund, pending: true } }); -export const RefundWithProducts = createExample(TestedComponent,{ +export const RefundWithProducts = createExample(TestedComponent, { transaction: { ...exampleData.refund, info: { diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.tsx index b1179228e..6939a08c3 100644 --- a/packages/taler-wallet-webextension/src/popup/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/popup/Transaction.tsx @@ -14,14 +14,14 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { Amounts, i18n, Transaction, TransactionType } from "@gnu-taler/taler-util"; +import { AmountJson, Amounts, i18n, Transaction, TransactionType } from "@gnu-taler/taler-util"; import { format } from "date-fns"; -import { JSX } from "preact"; +import { Fragment, JSX } from "preact"; import { route } from 'preact-router'; import { useEffect, useState } from "preact/hooks"; import * as wxApi from "../wxApi"; import { Pages } from "./popup"; - +import emptyImg from "../../static/img/empty.png" export function TransactionPage({ tid }: { tid: string; }): JSX.Element { const [transaction, setTransaction] = useState< @@ -59,11 +59,10 @@ export function TransactionView({ transaction, onDelete, onBack }: WalletTransac } function Footer() { - return <footer style={{ marginTop: 'auto', display: 'flex' }}> + return <footer style={{ marginTop: 'auto', display: 'flex', flexShrink: 0 }}> <button onClick={onBack}><i18n.Translate>back</i18n.Translate></button> <div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}> - <button onClick={onDelete}><i18n.Translate>remove</i18n.Translate></button> - + <button class="pure-button button-destructive" onClick={onDelete}><i18n.Translate>forget</i18n.Translate></button> </div> </footer> @@ -74,91 +73,66 @@ export function TransactionView({ transaction, onDelete, onBack }: WalletTransac return <span style={{ fontWeight: 'normal', fontSize: 16, color: 'gray' }}>(pending...)</span> } + const Fee = ({ value }: { value: AmountJson }) => Amounts.isNonZero(value) ? + <span style="font-size: 16px;font-weight: normal;color: gray;">(fee {Amounts.stringify(value)})</span> : null + if (transaction.type === TransactionType.Withdrawal) { + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} > - <section> - <h1>Withdrawal <Pending /></h1> - <p> + <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} > + <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}> + <span style="flat: left; font-size:small; color:gray">{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span> + <span style="float: right; font-size:small; color:gray"> From <b>{transaction.exchangeBaseUrl}</b> - </p> - <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}> - <tr> - <td>Amount subtracted</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> - </table> + </span> + <h3>Withdraw <Pending /></h3> + <h1>{transaction.amountEffective} <Fee value={fee} /></h1> </section> <Footer /> </div> ); } + const showLargePic = () => { + + } + if (transaction.type === TransactionType.Payment) { + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountEffective), + Amounts.parseOrThrow(transaction.amountRaw), + ).amount + return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} > - <section> - <h1>Payment ({transaction.proposalId.substring(0, 10)}...) <Pending /></h1> - <p> + <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} > + <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}> + <span style="flat: left; font-size:small; color:gray">{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span> + <span style="float: right; font-size:small; color:gray"> To <b>{transaction.info.merchant.name}</b> + </span> + <h3>Payment <Pending /></h3> + <h1>{transaction.amountEffective} <Fee value={fee} /></h1> + <span style="font-size:small; color:gray">#{transaction.info.orderId}</span> + <p> + {transaction.info.summary} </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> + <div> + {transaction.info.products && transaction.info.products.length > 0 && <div> + {transaction.info.products.map(p => <div style="display: flex; flex-direction: row; border: 1px solid gray; border-radius: 0.5em; margin: 0.5em 0px; justify-content: left; padding: 0.5em;"> + <a href="#" onClick={showLargePic}> + <img class="pure-img" style="display:inline-block" src={p.image ? p.image : emptyImg} width="32" height="32" /> + </a> + <div style="display: block; margin-left: 1em;"> + {p.quantity && p.quantity > 0 && <div style="font-size: small; color: gray;">x {p.quantity} {p.unit}</div>} + <div style={{ textOverflow: 'ellipsis', overflow: 'hidden', width: 'calc(20rem - 32px - 32px - 8px - 1em)', whiteSpace: 'nowrap' }}>{p.description}</div> + </div> + </div>)} + </div> } - <tr> - <td>Order amount</td> - <td>{transaction.amountRaw}</td> - </tr> - <tr> - <td>Order amount and fees</td> - <td>{transaction.amountEffective}</td> - </tr> - <tr> - <td>Exchange fee</td> - <td>{Amounts.stringify( - Amounts.sub( - Amounts.parseOrThrow(transaction.amountEffective), - Amounts.parseOrThrow(transaction.amountRaw), - ).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> - </table> + </div> </section> <Footer /> </div> @@ -166,36 +140,19 @@ export function TransactionView({ transaction, onDelete, onBack }: WalletTransac } if (transaction.type === TransactionType.Deposit) { + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} > - <section> - <h1>Deposit ({transaction.depositGroupId}) <Pending /></h1> - <p> + <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} > + <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}> + <span style="flat: left; font-size:small; color:gray">{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span> + <span style="float: right; font-size:small; color:gray"> To <b>{transaction.targetPaytoUri}</b> - </p> - <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}> - <tr> - <td>Amount deposit</td> - <td>{transaction.amountRaw}</td> - </tr> - <tr> - <td>Amount deposit and fees</td> - <td>{transaction.amountEffective}</td> - </tr> - <tr> - <td>Exchange fee</td> - <td>{Amounts.stringify( - Amounts.sub( - Amounts.parseOrThrow(transaction.amountEffective), - Amounts.parseOrThrow(transaction.amountRaw), - ).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> - </table> + </span> + <h3>Deposit <Pending /></h3> + <h1>{transaction.amountEffective} <Fee value={fee} /></h1> </section> <Footer /> </div> @@ -203,27 +160,19 @@ export function TransactionView({ transaction, onDelete, onBack }: WalletTransac } if (transaction.type === TransactionType.Refresh) { + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} > - <section> - <h1>Refresh <Pending /></h1> - <p> + <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} > + <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}> + <span style="flat: left; font-size:small; color:gray">{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span> + <span style="float: right; font-size:small; color:gray"> From <b>{transaction.exchangeBaseUrl}</b> - </p> - <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}> - <tr> - <td>Amount refreshed</td> - <td>{transaction.amountRaw}</td> - </tr> - <tr> - <td>Fees</td> - <td>{transaction.amountEffective}</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> - </table> + </span> + <h3>Refresh <Pending /></h3> + <h1>{transaction.amountEffective} <Fee value={fee} /></h1> </section> <Footer /> </div> @@ -231,91 +180,58 @@ export function TransactionView({ transaction, onDelete, onBack }: WalletTransac } if (transaction.type === TransactionType.Tip) { + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} > - <section> - <h1>Tip <Pending /></h1> - <p> + <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} > + <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}> + <span style="flat: left; font-size:small; color:gray">{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span> + <span style="float: right; font-size:small; color:gray"> From <b>{transaction.merchantBaseUrl}</b> - </p> - <table class={transaction.pending ? "detailsTable pending" : "detailsTable"}> - <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> - </table> + </span> + <h3>Tip <Pending /></h3> + <h1>{transaction.amountEffective} <Fee value={fee} /></h1> </section> <Footer /> </div> ); } - const TRANSACTION_FROM_REFUND = /[a-z]*:([\w]{10}).*/ if (transaction.type === TransactionType.Refund) { + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount return ( - <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: '20rem' }} > - <section> - <h1>Refund ({TRANSACTION_FROM_REFUND.exec(transaction.refundedTransactionId)![1]}...) <Pending /></h1> - <p> + <div style={{ display: 'flex', flexDirection: 'column', width: '100%' }} > + <section style={{ color: transaction.pending ? 'gray' : '', flex: '1 0 auto', height: 'calc(20rem - 34px - 45px)', overflow: 'auto' }}> + <span style="flat: left; font-size:small; color:gray">{transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')}</span> + <span style="float: right; font-size:small; color:gray"> From <b>{transaction.info.merchant.name}</b> + </span> + <h3>Refund <Pending /></h3> + <h1>{transaction.amountEffective} <Fee value={fee} /></h1> + <span style="font-size:small; color:gray">#{transaction.info.orderId}</span> + <p> + {transaction.info.summary} </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> + <div> + {transaction.info.products && transaction.info.products.length > 0 && <div> + {transaction.info.products.map(p => <div style="display: flex; flex-direction: row; border: 1px solid gray; border-radius: 0.5em; margin: 0.5em 0px; justify-content: left; padding: 0.5em;"> + <a href="#" onClick={showLargePic}> + <img class="pure-img" style="display:inline-block" src={p.image ? p.image : emptyImg} width="32" height="32" /> + </a> + <div style="display: block; margin-left: 1em;"> + {p.quantity && p.quantity > 0 && <div style="font-size: small; color: gray;">x {p.quantity} {p.unit}</div>} + <div style={{ textOverflow: 'ellipsis', overflow: 'hidden', width: 'calc(20rem - 32px - 32px - 8px - 1em)', whiteSpace: 'nowrap' }}>{p.description}</div> + </div> + </div>)} + </div> } - <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> - </table> + </div> + </section> <Footer /> </div> diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 6cc781aa7..543b68727 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -88,7 +88,7 @@ function Application() { return ( <div> <Match>{({ path }: any) => <WalletNavBar current={path} />}</Match > - <div style={{ margin: "1em", width: 400 }}> + <div style={{ margin: "1em", width: 400, height: 'calc(20rem - 34px)' }}> <Router> <Route path={Pages.balance} component={BalancePage} /> <Route path={Pages.settings} component={SettingsPage} /> |