diff options
author | Sebastian <sebasjm@gmail.com> | 2022-05-14 18:09:33 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2022-05-14 18:09:49 -0300 |
commit | e4ea2019430fb3c4b788f67427fbd743f604b7e5 (patch) | |
tree | e7426a82a2cc523c15d7f8b64e16c53722f7a87b /packages/taler-wallet-webextension/src | |
parent | c02dbc833bc384b72e5b18450a47ae2b212b0a8e (diff) |
feat: awaiting refund
Diffstat (limited to 'packages/taler-wallet-webextension/src')
6 files changed, 235 insertions, 112 deletions
diff --git a/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx index 6b7cf4621..bc2939896 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx @@ -33,6 +33,7 @@ export const Complete = createExample(TestedComponent, { state: { status: "completed", amount: Amounts.parseOrThrow("USD:1"), + granted: Amounts.parseOrThrow("USD:1"), hook: undefined, merchantName: "the merchant", products: undefined, @@ -44,9 +45,10 @@ export const InProgress = createExample(TestedComponent, { status: "in-progress", hook: undefined, amount: Amounts.parseOrThrow("USD:1"), + awaitingAmount: Amounts.parseOrThrow("USD:1"), + granted: Amounts.parseOrThrow("USD:0"), merchantName: "the merchant", products: undefined, - progress: 0.5, }, }); @@ -58,6 +60,8 @@ export const Ready = createExample(TestedComponent, { ignore: {}, amount: Amounts.parseOrThrow("USD:1"), + awaitingAmount: Amounts.parseOrThrow("USD:1"), + granted: Amounts.parseOrThrow("USD:0"), merchantName: "the merchant", products: [], orderId: "abcdef", @@ -73,6 +77,8 @@ export const WithAProductList = createExample(TestedComponent, { accept: {}, ignore: {}, amount: Amounts.parseOrThrow("USD:1"), + awaitingAmount: Amounts.parseOrThrow("USD:1"), + granted: Amounts.parseOrThrow("USD:0"), merchantName: "the merchant", products: [ { diff --git a/packages/taler-wallet-webextension/src/cta/Refund.test.ts b/packages/taler-wallet-webextension/src/cta/Refund.test.ts index e77f8e682..864b4f12c 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund.test.ts +++ b/packages/taler-wallet-webextension/src/cta/Refund.test.ts @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { Amounts, NotificationType, PrepareRefundResult } from "@gnu-taler/taler-util"; +import { AmountJson, Amounts, NotificationType, PrepareRefundResult } from "@gnu-taler/taler-util"; import { expect } from "chai"; import { mountHook } from "../test-utils.js"; import { SubsHandler } from "./Pay.test.js"; @@ -62,10 +62,12 @@ describe("Refund CTA states", () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState("taler://refund/asdasdas", { prepareRefund: async () => ({ - total: 0, - applied: 0, - failed: 0, - amountEffectivePaid: 'EUR:2', + effectivePaid: 'EUR:2', + awaiting: 'EUR:2', + gone: 'EUR:0', + granted: 'EUR:0', + pending: false, + proposalId: '1', info: { contractTermsHash: '123', merchant: { @@ -107,10 +109,12 @@ describe("Refund CTA states", () => { const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState("taler://refund/asdasdas", { prepareRefund: async () => ({ - total: 0, - applied: 0, - failed: 0, - amountEffectivePaid: 'EUR:2', + effectivePaid: 'EUR:2', + awaiting: 'EUR:2', + gone: 'EUR:0', + granted: 'EUR:0', + pending: false, + proposalId: '1', info: { contractTermsHash: '123', merchant: { @@ -161,21 +165,30 @@ describe("Refund CTA states", () => { }); it("should be in progress when doing refresh", async () => { - let numApplied = 1; + let granted = Amounts.getZero('EUR') + const unit: AmountJson = { currency: 'EUR', value: 1, fraction: 0 } + const refunded: AmountJson = { currency: 'EUR', value: 2, fraction: 0 } + let awaiting: AmountJson = refunded + let pending = true; + const subscriptions = new SubsHandler(); function notifyMelt(): void { - numApplied++; + granted = Amounts.add(granted, unit).amount; + pending = granted.value < refunded.value; + awaiting = Amounts.sub(refunded, granted).amount; subscriptions.notifyEvent(NotificationType.RefreshMelted) } const { getLastResultOrThrow, waitNextUpdate, assertNoPendingUpdate } = mountHook(() => useComponentState("taler://refund/asdasdas", { prepareRefund: async () => ({ - total: 3, - applied: numApplied, - failed: 0, - amountEffectivePaid: 'EUR:2', + awaiting: Amounts.stringify(awaiting), + effectivePaid: 'EUR:2', + gone: 'EUR:0', + granted: Amounts.stringify(granted), + pending, + proposalId: '1', info: { contractTermsHash: '123', merchant: { @@ -201,12 +214,12 @@ describe("Refund CTA states", () => { { const state = getLastResultOrThrow() - if (state.status !== 'in-progress') expect.fail(); + if (state.status !== 'in-progress') expect.fail('1'); if (state.hook) expect.fail(); expect(state.merchantName).eq('the merchant name'); expect(state.products).undefined; expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2")) - expect(state.progress).closeTo(1 / 3, 0.01) + // expect(state.progress).closeTo(1 / 3, 0.01) notifyMelt() } @@ -216,12 +229,12 @@ describe("Refund CTA states", () => { { const state = getLastResultOrThrow() - if (state.status !== 'in-progress') expect.fail(); + if (state.status !== 'in-progress') expect.fail('2'); if (state.hook) expect.fail(); expect(state.merchantName).eq('the merchant name'); expect(state.products).undefined; expect(state.amount).deep.eq(Amounts.parseOrThrow("EUR:2")) - expect(state.progress).closeTo(2 / 3, 0.01) + // expect(state.progress).closeTo(2 / 3, 0.01) notifyMelt() } @@ -231,7 +244,7 @@ describe("Refund CTA states", () => { { const state = getLastResultOrThrow() - if (state.status !== 'completed') expect.fail(); + if (state.status !== 'completed') expect.fail('3'); if (state.hook) expect.fail(); expect(state.merchantName).eq('the merchant name'); expect(state.products).undefined; diff --git a/packages/taler-wallet-webextension/src/cta/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx index f69fc4311..5387a1782 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund.tsx @@ -34,7 +34,6 @@ import { LoadingError } from "../components/LoadingError.js"; import { LogoHeader } from "../components/LogoHeader.js"; import { Part } from "../components/Part.js"; import { - Button, ButtonSuccess, SubTitle, WalletAction, @@ -99,6 +98,12 @@ export function View({ state }: ViewProps): VNode { <Part big title={<i18n.Translate>Total to refund</i18n.Translate>} + text={<Amount value={state.awaitingAmount} />} + kind="negative" + /> + <Part + big + title={<i18n.Translate>Refunded</i18n.Translate>} text={<Amount value={state.amount} />} kind="negative" /> @@ -108,9 +113,9 @@ export function View({ state }: ViewProps): VNode { <ProductList products={state.products} /> </section> ) : undefined} - <section> + {/* <section> <ProgressBar value={state.progress} /> - </section> + </section> */} </WalletAction> ); } @@ -128,6 +133,14 @@ export function View({ state }: ViewProps): VNode { <i18n.Translate>this refund is already accepted.</i18n.Translate> </p> </section> + <section> + <Part + big + title={<i18n.Translate>Total to refunded</i18n.Translate>} + text={<Amount value={state.granted} />} + kind="negative" + /> + </section> </WalletAction> ); } @@ -150,9 +163,23 @@ export function View({ state }: ViewProps): VNode { <section> <Part big - title={<i18n.Translate>Total to refund</i18n.Translate>} + title={<i18n.Translate>Order amount</i18n.Translate>} text={<Amount value={state.amount} />} - kind="negative" + kind="neutral" + /> + {Amounts.isNonZero(state.granted) && ( + <Part + big + title={<i18n.Translate>Already refunded</i18n.Translate>} + text={<Amount value={state.granted} />} + kind="neutral" + /> + )} + <Part + big + title={<i18n.Translate>Refund offered</i18n.Translate>} + text={<Amount value={state.awaitingAmount} />} + kind="positive" /> </section> {state.products && state.products.length ? ( @@ -164,9 +191,6 @@ export function View({ state }: ViewProps): VNode { <ButtonSuccess onClick={state.accept.onClick}> <i18n.Translate>Confirm refund</i18n.Translate> </ButtonSuccess> - <Button onClick={state.ignore.onClick}> - <i18n.Translate>Ignore</i18n.Translate> - </Button> </section> </WalletAction> ); @@ -184,6 +208,8 @@ interface Ready { merchantName: string; products: Product[] | undefined; amount: AmountJson; + awaitingAmount: AmountJson; + granted: AmountJson; accept: ButtonHandler; ignore: ButtonHandler; orderId: string; @@ -199,7 +225,8 @@ interface InProgress { merchantName: string; products: Product[] | undefined; amount: AmountJson; - progress: number; + awaitingAmount: AmountJson; + granted: AmountJson; } interface Completed { status: "completed"; @@ -207,6 +234,7 @@ interface Completed { merchantName: string; products: Product[] | undefined; amount: AmountJson; + granted: AmountJson; } export function useComponentState( @@ -253,25 +281,27 @@ export function useComponentState( }; } - const pending = refund.total > refund.applied + refund.failed; - const completed = refund.total > 0 && refund.applied === refund.total; + const awaitingAmount = Amounts.parseOrThrow(refund.awaiting); - if (pending) { + if (Amounts.isZero(awaitingAmount)) { return { - status: "in-progress", + status: "completed", hook: undefined, - amount: Amounts.parseOrThrow(info.response.refund.amountEffectivePaid), + amount: Amounts.parseOrThrow(info.response.refund.effectivePaid), + granted: Amounts.parseOrThrow(info.response.refund.granted), merchantName: info.response.refund.info.merchant.name, products: info.response.refund.info.products, - progress: (refund.applied + refund.failed) / refund.total, }; } - if (completed) { + if (refund.pending) { return { - status: "completed", + status: "in-progress", hook: undefined, - amount: Amounts.parseOrThrow(info.response.refund.amountEffectivePaid), + awaitingAmount, + amount: Amounts.parseOrThrow(info.response.refund.effectivePaid), + granted: Amounts.parseOrThrow(info.response.refund.granted), + merchantName: info.response.refund.info.merchant.name, products: info.response.refund.info.products, }; @@ -280,7 +310,9 @@ export function useComponentState( return { status: "ready", hook: undefined, - amount: Amounts.parseOrThrow(info.response.refund.amountEffectivePaid), + amount: Amounts.parseOrThrow(info.response.refund.effectivePaid), + granted: Amounts.parseOrThrow(info.response.refund.granted), + awaitingAmount, merchantName: info.response.refund.info.merchant.name, products: info.response.refund.info.products, orderId: info.response.refund.info.orderId, diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx index 92f1dea1b..3080a866e 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx @@ -78,6 +78,9 @@ const exampleData = { summary: "the summary", fulfillmentMessage: "", }, + refundPending: undefined, + totalRefundEffective: "USD:0", + totalRefundRaw: "USD:0", proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", status: PaymentStatus.Accepted, } as TransactionPayment, @@ -112,6 +115,7 @@ const exampleData = { summary: "the summary", fulfillmentMessage: "", }, + refundPending: undefined, } as TransactionRefund, }; diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx index b4dfb6ce0..f162543ae 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx @@ -83,6 +83,9 @@ const exampleData = { summary: "Essay: Why the Devil's Advocate Doesn't Help Reach the Truth", fulfillmentMessage: "", }, + refundPending: undefined, + totalRefundEffective: "USD:0", + totalRefundRaw: "USD:0", proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", status: PaymentStatus.Accepted, } as TransactionPayment, @@ -117,6 +120,7 @@ const exampleData = { summary: "the summary", fulfillmentMessage: "", }, + refundPending: undefined, } as TransactionRefund, }; diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index bcf6114a1..3377f98c7 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -40,7 +40,6 @@ import { ButtonPrimary, CenteredDialog, InfoBox, - LargeText, ListOfProducts, Overlay, RowBorderGray, @@ -51,6 +50,7 @@ import { import { Time } from "../components/Time.js"; import { useTranslationContext } from "../context/translation.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; +import { Pages } from "../NavigationBar.js"; import * as wxApi from "../wxApi.js"; interface Props { @@ -344,6 +344,17 @@ export function TransactionView({ 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); return ( <TransactionTemplate> <SubTitle> @@ -360,18 +371,54 @@ export function TransactionView({ text={<Amount value={transaction.amountEffective} />} kind="negative" /> - <Part - big - title={<i18n.Translate>Purchase amount</i18n.Translate>} - text={<Amount value={transaction.amountRaw} />} - kind="neutral" - /> - <Part - big - title={<i18n.Translate>Fee</i18n.Translate>} - text={<Amount value={fee} />} - kind="negative" - /> + {Amounts.isNonZero(fee) && ( + <Fragment> + <Part + big + title={<i18n.Translate>Purchase amount</i18n.Translate>} + text={<Amount value={transaction.amountRaw} />} + kind="neutral" + /> + <Part + title={<i18n.Translate>Purchase Fee</i18n.Translate>} + text={<Amount value={fee} />} + kind="negative" + /> + </Fragment> + )} + {refunded && ( + <Fragment> + <Part + big + title={<i18n.Translate>Total refunded</i18n.Translate>} + text={<Amount value={transaction.totalRefundEffective} />} + kind="positive" + /> + {Amounts.isNonZero(refundFee) && ( + <Fragment> + <Part + big + title={<i18n.Translate>Refund amount</i18n.Translate>} + text={<Amount value={transaction.totalRefundRaw} />} + kind="neutral" + /> + <Part + title={<i18n.Translate>Refund fee</i18n.Translate>} + text={<Amount value={refundFee} />} + kind="negative" + /> + </Fragment> + )} + </Fragment> + )} + {pendingRefund !== undefined && Amounts.isNonZero(pendingRefund) && ( + <Part + big + title={<i18n.Translate>Refund pending</i18n.Translate>} + text={<Amount value={pendingRefund} />} + kind="positive" + /> + )} <Part title={<i18n.Translate>Merchant</i18n.Translate>} text={transaction.info.merchant.name} @@ -447,18 +494,22 @@ export function TransactionView({ text={<Amount value={transaction.amountEffective} />} kind="neutral" /> - <Part - big - title={<i18n.Translate>Deposit amount</i18n.Translate>} - text={<Amount value={transaction.amountRaw} />} - kind="positive" - /> - <Part - big - title={<i18n.Translate>Fee</i18n.Translate>} - text={<Amount value={fee} />} - kind="negative" - /> + {Amounts.isNonZero(fee) && ( + <Fragment> + <Part + big + title={<i18n.Translate>Deposit amount</i18n.Translate>} + text={<Amount value={transaction.amountRaw} />} + kind="positive" + /> + <Part + big + title={<i18n.Translate>Fee</i18n.Translate>} + text={<Amount value={fee} />} + kind="negative" + /> + </Fragment> + )} {payto && <PartPayto big payto={payto} kind="neutral" />} </TransactionTemplate> ); @@ -485,18 +536,22 @@ export function TransactionView({ text={<Amount value={transaction.amountEffective} />} kind="negative" /> - <Part - big - title={<i18n.Translate>Refresh amount</i18n.Translate>} - text={<Amount value={transaction.amountRaw} />} - kind="neutral" - /> - <Part - big - title={<i18n.Translate>Fee</i18n.Translate>} - text={<Amount value={fee} />} - kind="negative" - /> + {Amounts.isNonZero(fee) && ( + <Fragment> + <Part + big + title={<i18n.Translate>Refresh amount</i18n.Translate>} + text={<Amount value={transaction.amountRaw} />} + kind="neutral" + /> + <Part + big + title={<i18n.Translate>Fee</i18n.Translate>} + text={<Amount value={fee} />} + kind="negative" + /> + </Fragment> + )} </TransactionTemplate> ); } @@ -522,18 +577,22 @@ export function TransactionView({ text={<Amount value={transaction.amountRaw} />} kind="positive" /> - <Part - big - title={<i18n.Translate>Received amount</i18n.Translate>} - text={<Amount value={transaction.amountEffective} />} - kind="neutral" - /> - <Part - big - title={<i18n.Translate>Fee</i18n.Translate>} - text={<Amount value={fee} />} - kind="negative" - /> + {Amounts.isNonZero(fee) && ( + <Fragment> + <Part + big + title={<i18n.Translate>Received amount</i18n.Translate>} + text={<Amount value={transaction.amountEffective} />} + kind="neutral" + /> + <Part + big + title={<i18n.Translate>Fee</i18n.Translate>} + text={<Amount value={fee} />} + kind="negative" + /> + </Fragment> + )} </TransactionTemplate> ); } @@ -559,37 +618,42 @@ export function TransactionView({ text={<Amount value={transaction.amountEffective} />} kind="positive" /> - <Part - big - title={<i18n.Translate>Refund amount</i18n.Translate>} - text={<Amount value={transaction.amountRaw} />} - kind="neutral" - /> - <Part - big - title={<i18n.Translate>Fee</i18n.Translate>} - text={<Amount value={fee} />} - kind="negative" - /> + {Amounts.isNonZero(fee) && ( + <Fragment> + <Part + big + title={<i18n.Translate>Refund amount</i18n.Translate>} + text={<Amount value={transaction.amountRaw} />} + kind="neutral" + /> + <Part + big + title={<i18n.Translate>Fee</i18n.Translate>} + text={<Amount value={fee} />} + kind="negative" + /> + </Fragment> + )} <Part title={<i18n.Translate>Merchant</i18n.Translate>} text={transaction.info.merchant.name} kind="neutral" /> + <Part title={<i18n.Translate>Purchase</i18n.Translate>} text={ - transaction.info.fulfillmentUrl ? ( - <a - href={transaction.info.fulfillmentUrl} - target="_bank" - rel="noreferrer" - > - {transaction.info.summary} - </a> - ) : ( - transaction.info.summary - ) + <a + href={Pages.balance_transaction.replace( + ":tid", + transaction.refundedTransactionId, + )} + // href={transaction.info.fulfillmentUrl} + // target="_bank" + // rel="noreferrer" + > + {transaction.info.summary} + </a> } kind="neutral" /> |