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
---
.../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 +++++++++++++-------
10 files changed, 959 insertions(+), 459 deletions(-)
(limited to 'packages/taler-wallet-webextension/src')
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.targetPath}
+ {Amounts.stringifyValue(amount)} BTC
+
+ {payto.segwitAddrs.map((addr, i) => (
+
+ {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({
)}
-
+
{name}
{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}
+
{
+ setCollapsed((v) => !v);
+ }}
+ >
+
+
+
+ {/*
+
+ */}
+ {!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({
)}
-
+
@@ -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
-
-
- {transaction.pending ? (
- transaction.withdrawalDetails.type ===
- WithdrawalType.ManualTransfer ? (
-
-
-
-
-
- Make sure to use the correct subject, otherwise the money
- will not arrive in this wallet.
-
-
-
- Total withdrawn}
- text={ }
- kind="positive"
- />
- Exchange fee}
- text={ }
- kind="negative"
- />
-
- ) : (
-
- {!transaction.withdrawalDetails.confirmed &&
- transaction.withdrawalDetails.bankConfirmationUrl ? (
-
+
+ {transaction.exchangeBaseUrl}
+
+
+ {!transaction.pending ? undefined : transaction.withdrawalDetails
+ .type === WithdrawalType.ManualTransfer ? (
+
+
+
+
+ Make sure to use the correct subject, otherwise the money will
+ not arrive in this wallet.
+
+
+
+ ) : (
+
+ {!transaction.withdrawalDetails.confirmed &&
+ transaction.withdrawalDetails.bankConfirmationUrl ? (
+
+
- The bank is waiting for confirmation. Go to the
+ The bank did not yet confirmed the wire transfer. Go to the
+ {` `}
bank site
-
-
-
- ) : undefined}
- {transaction.withdrawalDetails.confirmed && (
-
-
- Waiting for the coins to arrive
+ {" "}
+ and check there is no pending step.
-
- )}
-
Total withdrawn}
- text={ }
- kind="positive"
- />
- Chosen amount}
- text={ }
- kind="neutral"
- />
- Exchange fee}
- text={ }
- kind="negative"
- />
-
- )
- ) : (
-
- Total withdrawn}
- text={ }
- kind="positive"
- />
- Chosen amount}
- text={ }
- kind="neutral"
- />
- Exchange fee}
- text={ }
- kind="negative"
- />
+
+
+ ) : undefined}
+ {transaction.withdrawalDetails.confirmed && (
+
+
+ Bank has confirmed the wire transfer. Waiting for the exchange
+ to send the coins
+
+
+ )}
)}
Exchange}
- text={new URL(transaction.exchangeBaseUrl).hostname}
- kind="neutral"
+ title={Details }
+ text={ }
/>
);
}
- 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
-
-
-
- Total paid}
- text={ }
+
- {Amounts.isNonZero(fee) && (
-
- Purchase amount}
- text={ }
- kind="neutral"
- />
- Purchase Fee}
- text={ }
- kind="negative"
- />
-
- )}
- {refunded && (
-
+ >
+ {transaction.info.fulfillmentUrl ? (
+
+ {transaction.info.summary}
+
+ ) : (
+ transaction.info.summary
+ )}
+
+
+ {pendingRefund !== undefined && Amounts.isNonZero(pendingRefund) && (
+
+
+ Merchant created a refund for this order but was not automatically
+ picked up.
+
Total refunded}
- text={ }
+ title={Offer }
+ text={ }
kind="positive"
/>
- {Amounts.isNonZero(refundFee) && (
-
- Refund amount}
- text={ }
- kind="neutral"
- />
- Refund fee}
- text={ }
- kind="negative"
- />
-
- )}
-
- )}
- {pendingRefund !== undefined && Amounts.isNonZero(pendingRefund) && (
- Refund pending}
- text={ }
- kind="positive"
- />
+
+
)}
Merchant}
@@ -425,268 +361,630 @@ export function TransactionView({
kind="neutral"
/>
Purchase}
- text={
- transaction.info.fulfillmentUrl ? (
-
- {transaction.info.summary}
-
- ) : (
- transaction.info.summary
- )
- }
+ title={Invoice ID }
+ text={transaction.info.orderId}
kind="neutral"
/>
Receipt}
- text={`#${transaction.info.orderId}`}
+ title={Details }
+ text={ }
kind="neutral"
/>
-
-
- {transaction.info.products && transaction.info.products.length > 0 && (
-
- {transaction.info.products.map((p, k) => (
-
-
-
-
-
- {p.quantity && p.quantity > 0 && (
-
- x {p.quantity} {p.unit}
-
- )}
-
{p.description}
-
-
- ))}
-
- )}
-
);
}
if (transaction.type === TransactionType.Deposit) {
- const fee = Amounts.sub(
- Amounts.parseOrThrow(transaction.amountEffective),
- Amounts.parseOrThrow(transaction.amountRaw),
- ).amount;
+ const total = Amounts.parseOrThrow(transaction.amountRaw);
const payto = parsePaytoUri(transaction.targetPaytoUri);
return (
-
- Deposit
-
-
-
+
+ {transaction.targetPaytoUri}
+
+ {payto && }
Total send}
- text={ }
+ title={Details }
+ text={ }
kind="neutral"
/>
- {Amounts.isNonZero(fee) && (
-
- Deposit amount}
- text={ }
- kind="positive"
- />
- Fee}
- text={ }
- kind="negative"
- />
-
- )}
- {payto && }
);
}
if (transaction.type === TransactionType.Refresh) {
- const fee = Amounts.sub(
+ const total = Amounts.sub(
Amounts.parseOrThrow(transaction.amountRaw),
Amounts.parseOrThrow(transaction.amountEffective),
).amount;
+
return (
-
- Refresh
-
-
-
- Total refresh}
- text={ }
+
+ {transaction.exchangeBaseUrl}
+
+ Details}
+ text={ }
/>
- {Amounts.isNonZero(fee) && (
-
- Refresh amount}
- text={ }
- kind="neutral"
- />
- Fee}
- text={ }
- kind="negative"
- />
-
- )}
);
}
if (transaction.type === TransactionType.Tip) {
- const fee = Amounts.sub(
- Amounts.parseOrThrow(transaction.amountRaw),
- Amounts.parseOrThrow(transaction.amountEffective),
- ).amount;
+ const total = Amounts.parseOrThrow(transaction.amountEffective);
+
return (
-
- Tip
-
-
+ {transaction.merchantBaseUrl}
+
+ {/* Merchant}
+ text={transaction.info.merchant.name}
+ kind="neutral"
/>
-
Total tip}
- text={ }
- kind="positive"
+ title={Invoice ID }
+ text={transaction.info.orderId}
+ kind="neutral"
+ /> */}
+ Details}
+ text={ }
/>
- {Amounts.isNonZero(fee) && (
-
- Received amount}
- text={ }
- kind="neutral"
- />
- Fee}
- text={ }
- kind="negative"
- />
-
- )}
);
}
if (transaction.type === TransactionType.Refund) {
- const fee = Amounts.sub(
- Amounts.parseOrThrow(transaction.amountRaw),
- Amounts.parseOrThrow(transaction.amountEffective),
- ).amount;
+ const total = Amounts.parseOrThrow(transaction.amountEffective);
return (
-
- Refund
-
-
-
- Total refund}
- text={ }
+
- {Amounts.isNonZero(fee) && (
-
- Refund amount}
- text={ }
- kind="neutral"
- />
- Fee}
- text={ }
- kind="negative"
- />
-
- )}
+ >
+ {transaction.info.summary}
+
+
Merchant}
text={transaction.info.merchant.name}
kind="neutral"
/>
-
Purchase}
+ title={Original order ID }
text={
- {transaction.info.summary}
+ {transaction.info.orderId}
}
kind="neutral"
/>
Receipt}
- text={`#${transaction.info.orderId}`}
+ title={Purchase summary }
+ text={transaction.info.summary}
kind="neutral"
/>
-
-
- {transaction.info.products && transaction.info.products.length > 0 && (
-
- {transaction.info.products.map((p, k) => (
-
-
-
-
-
- {p.quantity && p.quantity > 0 && (
-
- x {p.quantity} {p.unit}
-
- )}
-
{p.description}
-
-
- ))}
-
- )}
-
+ Details}
+ text={ }
+ />
);
}
return
;
}
+
+function DeliveryDetails({
+ date,
+ location,
+}: {
+ date: TalerProtocolTimestamp | undefined;
+ location: Location | undefined;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+
+ {location && (
+
+ {location.country && (
+
+
+ Country
+
+ {location.country}
+
+ )}
+ {location.address_lines && (
+
+
+ Address lines
+
+ {location.address_lines}
+
+ )}
+ {location.building_number && (
+
+
+ Building number
+
+ {location.building_number}
+
+ )}
+ {location.building_name && (
+
+
+ Building name
+
+ {location.building_name}
+
+ )}
+ {location.street && (
+
+
+ Street
+
+ {location.street}
+
+ )}
+ {location.post_code && (
+
+
+ Post code
+
+ {location.post_code}
+
+ )}
+ {location.town_location && (
+
+
+ Town location
+
+ {location.town_location}
+
+ )}
+ {location.town && (
+
+
+ Town
+
+ {location.town}
+
+ )}
+ {location.district && (
+
+
+ District
+
+ {location.district}
+
+ )}
+ {location.country_subdivision && (
+
+
+ Country subdivision
+
+ {location.country_subdivision}
+
+ )}
+
+ )}
+
+ {!location || !date ? undefined : (
+
+
+
+
+
+ )}
+ {date && (
+
+
+ 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 (
+
+
+ Price
+
+
+
+
+
+ {Amounts.isNonZero(refundRaw) && (
+
+ Refunded
+
+
+
+
+ )}
+ {Amounts.isNonZero(fee) && (
+
+ Transaction fees
+
+
+
+
+ )}
+
+
+
+
+
+
+ Total
+
+
+
+
+ {hasProducts && (
+
+
+ Products}
+ text={
+
+ {transaction.info.products?.map((p, k) => (
+
+
+
+
+
+ {p.quantity && p.quantity > 0 && (
+
+ x {p.quantity} {p.unit}
+
+ )}
+
{p.description}
+
+
+ ))}
+
+ }
+ />
+
+
+ )}
+ {hasShipping && (
+
+
+ Delivery}
+ text={
+
+ }
+ />
+
+
+ )}
+
+ );
+}
+
+function RefundDetails({
+ transaction,
+}: {
+ transaction: TransactionRefund;
+}): VNode {
+ const { i18n } = useTranslationContext();
+
+ const fee = Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount;
+
+ return (
+
+
+ Amount
+
+
+
+
+
+ {Amounts.isNonZero(fee) && (
+
+ Transaction fees
+
+
+
+
+ )}
+
+
+
+
+
+
+ Total
+
+
+
+
+
+ );
+}
+
+function DepositDetails({
+ transaction,
+}: {
+ transaction: TransactionDeposit;
+}): VNode {
+ const { i18n } = useTranslationContext();
+
+ const fee = Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount;
+
+ return (
+
+
+ Amount
+
+
+
+
+
+ {Amounts.isNonZero(fee) && (
+
+ Transaction fees
+
+
+
+
+ )}
+
+
+
+
+
+
+ Total transfer
+
+
+
+
+
+ );
+}
+function RefreshDetails({
+ transaction,
+}: {
+ transaction: TransactionRefresh;
+}): VNode {
+ const { i18n } = useTranslationContext();
+
+ const fee = Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount;
+
+ return (
+
+
+ Amount
+
+
+
+
+
+
+
+
+
+
+ Transaction fees
+
+
+
+
+
+ );
+}
+
+function TipDetails({ transaction }: { transaction: TransactionTip }): VNode {
+ const { i18n } = useTranslationContext();
+
+ const fee = Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount;
+
+ return (
+
+
+ Amount
+
+
+
+
+
+ {Amounts.isNonZero(fee) && (
+
+ Transaction fees
+
+
+
+
+ )}
+
+
+
+
+
+
+ Total
+
+
+
+
+
+ );
+}
+
+function WithdrawDetails({
+ transaction,
+}: {
+ transaction: TransactionWithdrawal;
+}): VNode {
+ const { i18n } = useTranslationContext();
+
+ const fee = Amounts.sub(
+ Amounts.parseOrThrow(transaction.amountRaw),
+ Amounts.parseOrThrow(transaction.amountEffective),
+ ).amount;
+
+ return (
+
+
+ Withdraw
+
+
+
+
+
+ {Amounts.isNonZero(fee) && (
+
+ Transaction fees
+
+
+
+
+ )}
+
+
+
+
+
+
+ Total
+
+
+
+
+
+ );
+}
+
+function Header({
+ timestamp,
+ total,
+ children,
+ kind,
+ type,
+}: {
+ timestamp: TalerProtocolTimestamp;
+ total: AmountJson;
+ children: ComponentChildren;
+ kind: Kind;
+ type: string;
+}): VNode {
+ return (
+
+
+ {children}
+
+
+
+
+ }
+ kind={kind}
+ showSign
+ />
+
+
+
+ );
+}
--
cgit v1.2.3