From 24162c1086c017305253c78280a82bfa9a572b1e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 26 May 2022 15:55:14 -0300 Subject: transaction details template mayor change in the template of the transaction details for every transaction more work needs to be done in wallet core for tip and refund to show more information about the merchant like logo and website --- packages/taler-util/src/talerTypes.ts | 3 + packages/taler-util/src/transactionsTypes.ts | 12 + .../build-fast-with-linaria.mjs | 1 + .../src/components/Amount.tsx | 2 +- .../src/components/BalanceTable.tsx | 2 +- .../src/components/BankDetailsByPaytoType.tsx | 75 +- .../src/components/Part.tsx | 99 +- .../src/components/styled/index.tsx | 8 +- packages/taler-wallet-webextension/src/custom.d.ts | 6 +- packages/taler-wallet-webextension/src/stories.tsx | 8 +- .../taler-wallet-webextension/src/test-utils.ts | 13 +- .../src/wallet/Transaction.stories.tsx | 161 ++- .../src/wallet/Transaction.tsx | 1044 +++++++++++++------- .../static-dev/merchant-icon-11.jpeg | Bin 0 -> 60184 bytes 14 files changed, 975 insertions(+), 459 deletions(-) create mode 100644 packages/taler-wallet-webextension/static-dev/merchant-icon-11.jpeg (limited to 'packages') diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts index d9213ef5d..7fc3fcba0 100644 --- a/packages/taler-util/src/talerTypes.ts +++ b/packages/taler-util/src/talerTypes.ts @@ -362,6 +362,9 @@ export interface MerchantInfo { name: string; jurisdiction?: Location; address?: Location; + logo?: string; + website?: string; + email?: string; } export interface Tax { diff --git a/packages/taler-util/src/transactionsTypes.ts b/packages/taler-util/src/transactionsTypes.ts index 37c1c7ef1..dcaa56675 100644 --- a/packages/taler-util/src/transactionsTypes.ts +++ b/packages/taler-util/src/transactionsTypes.ts @@ -33,6 +33,7 @@ import { codecForInternationalizedString, codecForMerchantInfo, codecForProduct, + Location, } from "./talerTypes.js"; import { Codec, @@ -276,6 +277,17 @@ export interface OrderShortInfo { */ products: Product[] | undefined; + /** + * Time indicating when the order should be delivered. + * May be overwritten by individual products. + */ + delivery_date?: TalerProtocolTimestamp; + + /** + * Delivery location for (all!) products. + */ + delivery_location?: Location; + /** * URL of the fulfillment, given by the merchant */ diff --git a/packages/taler-wallet-webextension/build-fast-with-linaria.mjs b/packages/taler-wallet-webextension/build-fast-with-linaria.mjs index f6de67885..41747a745 100755 --- a/packages/taler-wallet-webextension/build-fast-with-linaria.mjs +++ b/packages/taler-wallet-webextension/build-fast-with-linaria.mjs @@ -54,6 +54,7 @@ export const buildConfig = { loader: { '.svg': 'text', '.png': 'dataurl', + '.jpeg': 'dataurl', }, target: [ 'es6' diff --git a/packages/taler-wallet-webextension/src/components/Amount.tsx b/packages/taler-wallet-webextension/src/components/Amount.tsx index c41f7faf6..b415a30cd 100644 --- a/packages/taler-wallet-webextension/src/components/Amount.tsx +++ b/packages/taler-wallet-webextension/src/components/Amount.tsx @@ -6,7 +6,7 @@ export function Amount({ value }: { value: AmountJson | AmountString }): VNode { const amount = Amounts.stringifyValue(aj, 2); return ( - {amount} {aj.currency} + {amount} {aj.currency} ); } diff --git a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx index e67fb6b4d..a2c91f4a1 100644 --- a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx +++ b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx @@ -44,7 +44,7 @@ export function BalanceTable({ width: "100%", }} > - {Amounts.stringifyValue(av)} + {Amounts.stringifyValue(av, 2)} ); diff --git a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx index 185021bc0..3a2a12c72 100644 --- a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx +++ b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx @@ -46,43 +46,47 @@ export function BankDetailsByPaytoType({ if (payto.isKnown && payto.targetType === "bitcoin") { const min = segwitMinAmount(amount.currency); return ( -
+
+

Bitcoin transfer details

- Bitcoin exchange need a transaction with 3 output, one output is the + The exchange need a transaction with 3 output, one output is the exchange account and the other two are segwit fake address for - metadata with an minimum amount. Reserve pub : {subject} + metadata with an minimum amount.

+ Reserve} + value={subject} + /> +

In bitcoincore wallet use 'Add Recipient' button to add two additional recipient and copy addresses and amounts -

    -
  • - {payto.targetPath} {Amounts.stringifyValue(amount)} BTC -
  • - {payto.segwitAddrs.map((addr, i) => ( -
  • - {addr} {Amounts.stringifyValue(min)} BTC -
  • - ))} -
- - In Electrum wallet paste the following three lines in 'Pay - to' field : - -
    -
  • - {payto.targetPath},{Amounts.stringifyValue(amount)} -
  • - {payto.segwitAddrs.map((addr, i) => ( -
  • - {addr} {Amounts.stringifyValue(min)} BTC -
  • - ))} -
+

+ + + + + + {payto.segwitAddrs.map((addr, i) => ( + + + + + ))} +
{payto.targetPath}{Amounts.stringifyValue(amount)} BTC
{addr}{Amounts.stringifyValue(min)} BTC
+

Make sure the amount show{" "} {Amounts.stringifyValue(Amounts.sum([amount, min, min]).amount)}{" "} @@ -93,7 +97,7 @@ export function BankDetailsByPaytoType({ ); } - const firstPart = !payto.isKnown ? ( + const accountPart = !payto.isKnown ? ( Account} value={payto.targetPath} @@ -113,10 +117,17 @@ export function BankDetailsByPaytoType({ IBAN} value={payto.iban} /> ) : undefined; return ( -

-

Bank transfer details

+
+

Bank transfer details

- {firstPart} + {accountPart} Exchange} value={exchangeBaseUrl} @@ -176,7 +187,7 @@ function Row({ )} - {literal ? ( diff --git a/packages/taler-wallet-webextension/src/components/Part.tsx b/packages/taler-wallet-webextension/src/components/Part.tsx index 21c0f65dc..58165a349 100644 --- a/packages/taler-wallet-webextension/src/components/Part.tsx +++ b/packages/taler-wallet-webextension/src/components/Part.tsx @@ -14,33 +14,122 @@ GNU Taler; see the file COPYING. If not, see */ import { PaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util"; +import { styled } from "@linaria/react"; import { Fragment, h, VNode } from "preact"; -import { ExtraLargeText, LargeText, SmallLightText } from "./styled/index.js"; +import { useState } from "preact/hooks"; +import { + ExtraLargeText, + LargeText, + SmallBoldText, + SmallLightText, +} from "./styled/index.js"; export type Kind = "positive" | "negative" | "neutral"; interface Props { - title: VNode; + title: VNode | string; text: VNode | string; - kind: Kind; + kind?: Kind; big?: boolean; + showSign?: boolean; } -export function Part({ text, title, kind, big }: Props): VNode { +export function Part({ + text, + title, + kind = "neutral", + big, + showSign, +}: Props): VNode { const Text = big ? ExtraLargeText : LargeText; return (
- {title} + {title} + {!showSign || kind === "neutral" + ? undefined + : kind === "positive" + ? "+" + : "-"} {text}
); } +const CollasibleBox = styled.div` + border: 1px solid black; + border-radius: 0.25em; + display: flex; + vertical-align: middle; + justify-content: space-between; + flex-direction: column; + /* margin: 0.5em; */ + padding: 0.5em; + /* margin: 1em; */ + /* width: 100%; */ + /* color: #721c24; */ + /* background: #f8d7da; */ + + & > div { + display: flex; + justify-content: space-between; + div { + margin-top: auto; + margin-bottom: auto; + } + & > button { + align-self: center; + font-size: 100%; + padding: 0; + height: 28px; + width: 28px; + } + } +`; +import arrowDown from "../svg/chevron-down.svg"; + +export function PartCollapsible({ text, title, big, showSign }: Props): VNode { + const Text = big ? ExtraLargeText : LargeText; + const [collapsed, setCollapsed] = useState(true); + + return ( + +
+ {title} + +
+ {/* + + */} + {!collapsed &&
{text}
} +
+ ); +} + interface PropsPayto { payto: PaytoUri; kind: Kind; diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 7517a1388..a531a15dc 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -87,7 +87,7 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` justify-content: space-between; align-items: center; & > * { - width: 500px; + width: 600px; } & > section { padding: ${({ noPadding }) => (noPadding ? "0px" : "8px")}; @@ -660,6 +660,12 @@ export const WarningText = styled.div` export const SmallText = styled.div` font-size: small; `; + +export const SmallBoldText = styled.div` + font-size: small; + font-weight: bold; +`; + export const LargeText = styled.div` font-size: large; `; diff --git a/packages/taler-wallet-webextension/src/custom.d.ts b/packages/taler-wallet-webextension/src/custom.d.ts index 521b824c7..711112ad8 100644 --- a/packages/taler-wallet-webextension/src/custom.d.ts +++ b/packages/taler-wallet-webextension/src/custom.d.ts @@ -13,7 +13,11 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -declare module "*.jpeg" { + declare module "*.jpeg" { + const content: any; + export default content; +} +declare module "*.jpg" { const content: any; export default content; } diff --git a/packages/taler-wallet-webextension/src/stories.tsx b/packages/taler-wallet-webextension/src/stories.tsx index 9c0f69ec4..fd5d3c590 100644 --- a/packages/taler-wallet-webextension/src/stories.tsx +++ b/packages/taler-wallet-webextension/src/stories.tsx @@ -330,9 +330,11 @@ function Application(): VNode { const hash = location.hash.substring(1); const found = document.getElementById(hash); if (found) { - found.scrollIntoView({ - block: "center", - }); + setTimeout(() => { + found.scrollIntoView({ + block: "center", + }); + }, 10); } } }, []); diff --git a/packages/taler-wallet-webextension/src/test-utils.ts b/packages/taler-wallet-webextension/src/test-utils.ts index eceda616f..9e219daa6 100644 --- a/packages/taler-wallet-webextension/src/test-utils.ts +++ b/packages/taler-wallet-webextension/src/test-utils.ts @@ -26,22 +26,27 @@ options.requestAnimationFrame = (fn: () => void) => { export function createExample( Component: FunctionalComponent, - props: Partial, + props: Partial | (() => Partial), ): ComponentChildren { + //FIXME: props are evaluated on build time + // in some cases we want to evaluated the props on render time so we can get some relative timestamp + // check how we can build evaluatedProps in render time + const evaluatedProps = typeof props === "function" ? props() : props const Render = (args: any): VNode => create(Component, args); - Render.args = props; + Render.args = evaluatedProps; return Render; } export function createExampleWithCustomContext( Component: FunctionalComponent, - props: Partial, + props: Partial | (() => Partial), ContextProvider: FunctionalComponent, contextProps: Partial, ): ComponentChildren { + const evaluatedProps = typeof props === "function" ? props() : props const Render = (args: any): VNode => create(Component, args); const WithContext = (args: any): VNode => create(ContextProvider, { ...contextProps, children: [Render(args)] } as any); - WithContext.args = props + WithContext.args = evaluatedProps return WithContext } diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx index f162543ae..493cdd1d7 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx @@ -30,6 +30,7 @@ import { TransactionTip, TransactionType, TransactionWithdrawal, + WithdrawalDetails, WithdrawalType, } from "@gnu-taler/taler-util"; import { DevContextProviderForTesting } from "../context/devContext.js"; @@ -57,6 +58,8 @@ const commonTransaction = { transactionId: "12", } as TransactionCommon; +import merchantIcon from "../../static-dev/merchant-icon-11.jpeg"; + const exampleData = { withdraw: { ...commonTransaction, @@ -65,27 +68,34 @@ const exampleData = { withdrawalDetails: { confirmed: false, reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", - exchangePaytoUris: ["payto://x-taler-bank/bank/account"], + exchangePaytoUris: ["payto://x-taler-bank/bank.demo.taler.net/Exchange"], type: WithdrawalType.ManualTransfer, }, } as TransactionWithdrawal, payment: { ...commonTransaction, - amountEffective: "KUDOS:11", + amountEffective: "KUDOS:12", type: TransactionType.Payment, info: { contractTermsHash: "ASDZXCASD", merchant: { name: "the merchant", + logo: merchantIcon, + website: "https://www.themerchant.taler", + email: "contact@merchant.taler", }, orderId: "2021.167-03NPY6MCYMVGT", products: [], summary: "Essay: Why the Devil's Advocate Doesn't Help Reach the Truth", fulfillmentMessage: "", + // delivery_date: { t_s: 1 }, + // delivery_location: { + // address_lines: [""], + // }, }, refundPending: undefined, - totalRefundEffective: "USD:0", - totalRefundRaw: "USD:0", + totalRefundEffective: "KUDOS:0", + totalRefundRaw: "KUDOS:0", proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", status: PaymentStatus.Accepted, } as TransactionPayment, @@ -93,7 +103,7 @@ const exampleData = { ...commonTransaction, type: TransactionType.Deposit, depositGroupId: "#groupId", - targetPaytoUri: "payto://x-taler-bank/bank/account", + targetPaytoUri: "payto://x-taler-bank/bank.demo.taler.net/Exchange", } as TransactionDeposit, refresh: { ...commonTransaction, @@ -117,7 +127,7 @@ const exampleData = { }, orderId: "2021.167-03NPY6MCYMVGT", products: [], - summary: "the summary", + summary: "Essay: Why the Devil's Advocate Doesn't Help Reach the Truth", fulfillmentMessage: "", }, refundPending: undefined, @@ -143,20 +153,27 @@ export const Withdraw = createExample(TestedComponent, { transaction: exampleData.withdraw, }); -export const WithdrawOneMinuteAgo = createExample(TestedComponent, { +export const WithdrawFiveMinutesAgo = createExample(TestedComponent, () => ({ transaction: { ...exampleData.withdraw, - timestamp: TalerProtocolTimestamp.fromSeconds(new Date().getTime() - 60), + timestamp: TalerProtocolTimestamp.fromSeconds( + new Date().getTime() / 1000 - 60 * 5, + ), }, -}); +})); -export const WithdrawOneMinuteAgoAndPending = createExample(TestedComponent, { - transaction: { - ...exampleData.withdraw, - timestamp: TalerProtocolTimestamp.fromSeconds(new Date().getTime() - 60), - pending: true, - }, -}); +export const WithdrawFiveMinutesAgoAndPending = createExample( + TestedComponent, + () => ({ + transaction: { + ...exampleData.withdraw, + timestamp: TalerProtocolTimestamp.fromSeconds( + new Date().getTime() / 1000 - 60 * 5, + ), + pending: true, + }, + }), +); export const WithdrawError = createExample(TestedComponent, { transaction: { @@ -177,17 +194,17 @@ export const WithdrawErrorInDevMode = createExampleInCustomContext( { value: true }, ); -export const WithdrawPendingManual = createExample(TestedComponent, { +export const WithdrawPendingManual = createExample(TestedComponent, () => ({ transaction: { ...exampleData.withdraw, withdrawalDetails: { type: WithdrawalType.ManualTransfer, exchangePaytoUris: ["payto://iban/asdasdasd"], reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", - }, + } as WithdrawalDetails, pending: true, }, -}); +})); export const WithdrawPendingTalerBankUnconfirmed = createExample( TestedComponent, @@ -231,10 +248,95 @@ export const PaymentError = createExample(TestedComponent, { }, }); -export const PaymentWithoutFee = createExample(TestedComponent, { +export const PaymentWithRefund = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:12", + totalRefundEffective: "KUDOS:1", + totalRefundRaw: "KUDOS:1", + }, +}); + +export const PaymentWithDeliveryDate = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:12", + info: { + ...exampleData.payment.info, + delivery_date: { + t_s: new Date().getTime() / 1000, + }, + }, + }, +}); + +export const PaymentWithDeliveryAddr = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:12", + info: { + ...exampleData.payment.info, + delivery_location: { + country: "Argentina", + street: "Elm Street", + district: "CABA", + post_code: "1101", + }, + }, + }, +}); + +export const PaymentWithDeliveryFull = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:12", + info: { + ...exampleData.payment.info, + delivery_date: { + t_s: new Date().getTime() / 1000, + }, + delivery_location: { + country: "Argentina", + street: "Elm Street", + district: "CABA", + post_code: "1101", + }, + }, + }, +}); + +export const PaymentWithRefundPending = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:12", + refundPending: "KUDOS:3", + totalRefundEffective: "KUDOS:1", + totalRefundRaw: "KUDOS:1", + }, +}); + +export const PaymentWithFeeAndRefund = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:11", + totalRefundEffective: "KUDOS:1", + totalRefundRaw: "KUDOS:1", + }, +}); + +export const PaymentWithFeeAndRefundFee = createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:11", + totalRefundEffective: "KUDOS:1", + totalRefundRaw: "KUDOS:2", + }, +}); + +export const PaymentWithoutFee = createExample(TestedComponent, { + transaction: { + ...exampleData.payment, + amountRaw: "KUDOS:12", }, }); @@ -249,7 +351,7 @@ export const PaymentWithProducts = createExample(TestedComponent, { ...exampleData.payment, info: { ...exampleData.payment.info, - summary: "this order has 5 products", + summary: "summary of 5 products", products: [ { description: "t-shirt", @@ -360,20 +462,3 @@ export const RefundError = createExample(TestedComponent, { 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/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 3377f98c7..9ccb353a9 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -16,14 +16,24 @@ import { AbsoluteTime, + AmountJson, Amounts, + Location, NotificationType, parsePaytoUri, parsePayUri, + TalerProtocolTimestamp, Transaction, + TransactionDeposit, + TransactionPayment, + TransactionRefresh, + TransactionRefund, + TransactionTip, TransactionType, + TransactionWithdrawal, WithdrawalType, } from "@gnu-taler/taler-util"; +import { styled } from "@linaria/react"; import { differenceInSeconds } from "date-fns"; import { ComponentChildren, Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; @@ -33,15 +43,17 @@ import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js" import { ErrorTalerOperation } from "../components/ErrorTalerOperation.js"; import { Loading } from "../components/Loading.js"; import { LoadingError } from "../components/LoadingError.js"; -import { Part, PartPayto } from "../components/Part.js"; +import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js"; import { Button, + ButtonBox, ButtonDestructive, ButtonPrimary, CenteredDialog, InfoBox, ListOfProducts, Overlay, + Row, RowBorderGray, SmallLightText, SubTitle, @@ -119,6 +131,14 @@ export interface WalletTransactionProps { onBack: () => void; } +const PurchaseDetailsTable = styled.table` + width: 100%; + + & > tr > td:nth-child(2n) { + text-align: right; + } +`; + export function TransactionView({ transaction, onDelete, @@ -168,9 +188,7 @@ export function TransactionView({ )} -
-
{children}
-
+
{children}
@@ -189,10 +207,8 @@ export function TransactionView({ } if (transaction.type === TransactionType.Withdrawal) { - const fee = Amounts.sub( - Amounts.parseOrThrow(transaction.amountRaw), - Amounts.parseOrThrow(transaction.amountEffective), - ).amount; + const total = Amounts.parseOrThrow(transaction.amountEffective); + const chosen = Amounts.parseOrThrow(transaction.amountRaw); return ( {confirmBeforeForget ? ( @@ -219,205 +235,125 @@ export function TransactionView({ ) : undefined} - - Withdrawal - - ); } - const showLargePic = (): void => { - return; - }; - if (transaction.type === TransactionType.Payment) { - const fee = Amounts.sub( - Amounts.parseOrThrow(transaction.amountEffective), - Amounts.parseOrThrow(transaction.amountRaw), - ).amount; - - const refundFee = Amounts.sub( - Amounts.parseOrThrow(transaction.totalRefundRaw), - Amounts.parseOrThrow(transaction.totalRefundEffective), - ).amount; - const refunded = Amounts.isNonZero( - Amounts.parseOrThrow(transaction.totalRefundRaw), - ); const pendingRefund = transaction.refundPending === undefined ? undefined : Amounts.parseOrThrow(transaction.refundPending); + + const total = Amounts.sub( + Amounts.parseOrThrow(transaction.amountEffective), + Amounts.parseOrThrow(transaction.totalRefundEffective), + ).amount; + return ( - - Payment - -
+ + + + )} + {location.address_lines && ( + + + + + )} + {location.building_number && ( + + + + + )} + {location.building_name && ( + + + + + )} + {location.street && ( + + + + + )} + {location.post_code && ( + + + + + )} + {location.town_location && ( + + + + + )} + {location.town && ( + + + + + )} + {location.district && ( + + + + + )} + {location.country_subdivision && ( + + + + + )} + + )} + + {!location || !date ? undefined : ( + + + + )} + {date && ( + + + + + + + )} + + ); +} + +function PurchaseDetails({ + transaction, +}: { + transaction: TransactionPayment; +}): VNode { + const { i18n } = useTranslationContext(); + + const partialFee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountEffective), + Amounts.parseOrThrow(transaction.amountRaw), + ).amount; + + const refundRaw = Amounts.parseOrThrow(transaction.totalRefundRaw); + + const refundFee = Amounts.sub( + refundRaw, + Amounts.parseOrThrow(transaction.totalRefundEffective), + ).amount; + + const fee = Amounts.sum([partialFee, refundFee]).amount; + + const hasProducts = + transaction.info.products && transaction.info.products.length > 0; + + const hasShipping = + transaction.info.delivery_date !== undefined || + transaction.info.delivery_location !== undefined; + + const showLargePic = (): void => { + return; + }; + + const total = Amounts.sub( + Amounts.parseOrThrow(transaction.amountEffective), + Amounts.parseOrThrow(transaction.totalRefundEffective), + ).amount; + + return ( + + + + + + + {Amounts.isNonZero(refundRaw) && ( + + + + + )} + {Amounts.isNonZero(fee) && ( + + + + + )} + + + + + + + + {hasProducts && ( + + + + )} + {hasShipping && ( + + + + )} + + ); +} + +function RefundDetails({ + transaction, +}: { + transaction: TransactionRefund; +}): VNode { + const { i18n } = useTranslationContext(); + + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount; + + return ( + + + + + + + {Amounts.isNonZero(fee) && ( + + + + + )} + + + + + + + + + ); +} + +function DepositDetails({ + transaction, +}: { + transaction: TransactionDeposit; +}): VNode { + const { i18n } = useTranslationContext(); + + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount; + + return ( + + + + + + + {Amounts.isNonZero(fee) && ( + + + + + )} + + + + + + + + + ); +} +function RefreshDetails({ + transaction, +}: { + transaction: TransactionRefresh; +}): VNode { + const { i18n } = useTranslationContext(); + + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount; + + return ( + + + + + + + + + + + + + + ); +} + +function TipDetails({ transaction }: { transaction: TransactionTip }): VNode { + const { i18n } = useTranslationContext(); + + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount; + + return ( + + + + + + + {Amounts.isNonZero(fee) && ( + + + + + )} + + + + + + + + + ); +} + +function WithdrawDetails({ + transaction, +}: { + transaction: TransactionWithdrawal; +}): VNode { + const { i18n } = useTranslationContext(); + + const fee = Amounts.sub( + Amounts.parseOrThrow(transaction.amountRaw), + Amounts.parseOrThrow(transaction.amountEffective), + ).amount; + + return ( + + + + + + + {Amounts.isNonZero(fee) && ( + + + + + )} + + + + + + + + + ); +} + +function Header({ + timestamp, + total, + children, + kind, + type, +}: { + timestamp: TalerProtocolTimestamp; + total: AmountJson; + children: ComponentChildren; + kind: Kind; + type: string; +}): VNode { + return ( +
+
+ {children} +
+
+ + } + kind={kind} + showSign + /> + +
+
+ ); +} diff --git a/packages/taler-wallet-webextension/static-dev/merchant-icon-11.jpeg b/packages/taler-wallet-webextension/static-dev/merchant-icon-11.jpeg new file mode 100644 index 000000000..1777936c8 Binary files /dev/null and b/packages/taler-wallet-webextension/static-dev/merchant-icon-11.jpeg differ -- cgit v1.2.3
+ {name}
+ Country + {location.country}
+ Address lines + {location.address_lines}
+ Building number + {location.building_number}
+ Building name + {location.building_name}
+ Street + {location.street}
+ Post code + {location.post_code}
+ Town location + {location.town_location}
+ Town + {location.town}
+ District + {location.district}
+ Country subdivision + {location.country_subdivision}
+
+
Date +
Price + +
Refunded + +
Transaction fees + +
+
+
Total + +
+ Products} + text={ + + {transaction.info.products?.map((p, k) => ( + + + + +
+ {p.quantity && p.quantity > 0 && ( + + x {p.quantity} {p.unit} + + )} +
{p.description}
+
+
+ ))} +
+ } + /> +
+ Delivery} + text={ + + } + /> +
Amount + +
Transaction fees + +
+
+
Total + +
Amount + +
Transaction fees + +
+
+
Total transfer + +
Amount + +
+
+
Transaction fees + +
Amount + +
Transaction fees + +
+
+
Total + +
Withdraw + +
Transaction fees + +
+
+
Total + +