diff options
Diffstat (limited to 'packages/frontend/src/paths/instance/orders/details')
-rw-r--r-- | packages/frontend/src/paths/instance/orders/details/DetailPage.tsx | 171 | ||||
-rw-r--r-- | packages/frontend/src/paths/instance/orders/details/Timeline.tsx | 5 |
2 files changed, 96 insertions, 80 deletions
diff --git a/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx b/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx index be05b43..9f148b6 100644 --- a/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx +++ b/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx @@ -19,6 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ +import { AmountJson, Amounts } from "@gnu-taler/taler-util"; import { format } from "date-fns"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -29,7 +30,7 @@ import { InputDate } from "../../../../components/form/InputDate"; import { InputDuration } from "../../../../components/form/InputDuration"; import { InputGroup } from "../../../../components/form/InputGroup"; import { InputLocation } from "../../../../components/form/InputLocation"; -import { NotificationCard } from "../../../../components/menu"; +import { TextField } from "../../../../components/form/TextField"; import { ProductList } from "../../../../components/product/ProductList"; import { MerchantBackend } from "../../../../declaration"; import { Translate, useTranslator } from "../../../../i18n"; @@ -52,6 +53,35 @@ type Unpaid = MerchantBackend.Orders.CheckPaymentUnpaidResponse type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse +function ContractTerms({ value }: { value: CT }) { + const i18n = useTranslator() + + return <InputGroup name="contract_terms" label={i18n`Contract Terms`}> + <FormProvider<CT> object={value} valueHandler={null} > + <Input<CT> readonly name="summary" label={i18n`Summary`} tooltip={i18n`human-readable description of the whole purchase`} /> + <InputCurrency<CT> readonly name="amount" label={i18n`Amount`} tooltip={i18n`total price for the transaction`} /> + {value.fulfillment_url && + <Input<CT> readonly name="fulfillment_url" label={i18n`Fulfillment URL`} tooltip={i18n`URL for this purchase`} /> + } + <Input<CT> readonly name="max_fee" label={i18n`Max fee`} tooltip={i18n`maximum total deposit fee accepted by the merchant for this contract`} /> + <Input<CT> readonly name="max_wire_fee" label={i18n`Max wire fee`} tooltip={i18n`maximum wire fee accepted by the merchant`} /> + <Input<CT> readonly name="wire_fee_amortization" label={i18n`Wire fee amortization`} tooltip={i18n`over how many customer transactions does the merchant expect to amortize wire fees on average`} /> + <InputDate<CT> readonly name="timestamp" label={i18n`Created at`} tooltip={i18n`time when this contract was generated`} /> + <InputDate<CT> readonly name="refund_deadline" label={i18n`Refund deadline`} tooltip={i18n`after this deadline has passed no refunds will be accepted`} /> + <InputDate<CT> readonly name="pay_deadline" label={i18n`Payment deadline`} tooltip={i18n`after this deadline, the merchant won't accept payments for the contract`} /> + <InputDate<CT> readonly name="wire_transfer_deadline" label={i18n`Wire transfer deadline`} tooltip={i18n`transfer deadline for the exchange`} /> + <InputDate<CT> readonly name="delivery_date" label={i18n`Delivery date`} tooltip={i18n`time indicating when the order should be delivered`} /> + {value.delivery_date && + <InputGroup name="delivery_location" label={i18n`Location`} tooltip={i18n`where the order will be delivered`} > + <InputLocation name="payments.delivery_location" /> + </InputGroup> + } + <InputDuration<CT> readonly name="auto_refund" label={i18n`Auto-refund delay`} tooltip={i18n`how long the wallet should try to get an automatic refund for the purchase`} /> + <Input<CT> readonly name="extra" label={i18n`Extra info`} tooltip={i18n`extra data that is only interpreted by the merchant frontend`} /> + </FormProvider> + </InputGroup> +} + function ClaimedPage({ id, order }: { id: string; order: MerchantBackend.Orders.CheckPaymentClaimedResponse }) { const events: Event[] = [] events.push({ @@ -126,11 +156,8 @@ function ClaimedPage({ id, order }: { id: string; order: MerchantBackend.Orders. whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', - // maxWidth: '100%', }}> - {/* <a href={order.order_status_url} rel="nofollow" target="new">{order.order_status_url}</a> */} - <p><Translate>pay at</Translate>: <b>missing value, there is no order_status_url</b></p> - <p><Translate>created at</Translate>: {format(new Date(order.contract_terms.timestamp.t_ms), 'yyyy-MM-dd HH:mm:ss')}</p> + <p><b><Translate>claimed at</Translate>:</b> {format(new Date(order.contract_terms.timestamp.t_ms), 'yyyy-MM-dd HH:mm:ss')}</p> </div> </div> </div> @@ -155,18 +182,12 @@ function ClaimedPage({ id, order }: { id: string; order: MerchantBackend.Orders. </div> </section> - {order.contract_terms.products.length > 0 && - <section class="section"> - <div class="columns"> - <div class="column is-12" > - <div class="title"><Translate>Product list</Translate></div> - <ProductList list={order.contract_terms.products} /> - </div> - <div class="column" /> - </div> - </section> - } + {order.contract_terms.products.length ? <Fragment> + <div class="title"><Translate>Product list</Translate></div> + <ProductList list={order.contract_terms.products} /> + </Fragment> : undefined} + {value.contract_terms && <ContractTerms value={value.contract_terms} />} </div> <div class="column" /> </div> @@ -207,19 +228,43 @@ function PaidPage({ id, order, onRefund }: { id: string; order: MerchantBackend. type: 'refund', }) }) - order.wire_details.forEach(e => { - events.push({ - when: new Date(e.execution_time.t_ms), - description: `wired`, - type: 'wired', - }) - }) - if (order.contract_terms.wire_transfer_deadline.t_ms !== 'never' && - order.contract_terms.wire_transfer_deadline.t_ms < new Date().getTime()) events.push({ - when: new Date(order.contract_terms.wire_transfer_deadline.t_ms - 1000 * 10), - description: `wired (faked)`, - type: 'wired', - }) + if (order.wire_details && order.wire_details.length) { + if (order.wire_details.length > 1) { + let last: MerchantBackend.Orders.TransactionWireTransfer | null = null + let first: MerchantBackend.Orders.TransactionWireTransfer | null = null + let total: AmountJson | null = null + + order.wire_details.forEach(w => { + if (last === null || last.execution_time.t_ms < w.execution_time.t_ms) { + last = w + } + if (first === null || first.execution_time.t_ms > w.execution_time.t_ms) { + first = w + } + total = total === null ? Amounts.parseOrThrow(w.amount) : Amounts.add(total, Amounts.parseOrThrow(w.amount)).amount + }) + events.push({ + when: new Date(last!.execution_time.t_ms), + description: `wired ${Amounts.stringify(total!)}`, + type: 'wired-range', + }) + events.push({ + when: new Date(first!.execution_time.t_ms), + description: `wire transfer started...`, + type: 'wired-range', + }) + } else { + order.wire_details.forEach(e => { + events.push({ + when: new Date(e.execution_time.t_ms), + description: `wired ${e.amount}`, + type: 'wired', + }) + }) + + } + + } const [value, valueHandler] = useState<Partial<Paid>>(order) @@ -261,10 +306,9 @@ function PaidPage({ id, order, onRefund }: { id: string; order: MerchantBackend. <div class="level-item"> <h1 class="title"> <div class="buttons"> - {refundable && <button class="button is-danger" onClick={() => onRefund(id)}><Translate>refund</Translate></button>} - <button class="button is-info" onClick={() => { - if (order.contract_terms.fulfillment_url) copyToClipboard(order.contract_terms.fulfillment_url) - }}><Translate>copy url</Translate></button> + <span class="has-tooltip-left" data-tooltip={refundable ? i18n`refund order`: i18n`not refundable`}> + <button class="button is-danger" disabled={!refundable} onClick={() => onRefund(id)}><Translate>refund</Translate></button> + </span> </div> </h1> </div> @@ -301,53 +345,23 @@ function PaidPage({ id, order, onRefund }: { id: string; order: MerchantBackend. <InputCurrency<Paid> name="deposit_total" readonly label={i18n`Deposit total`} /> {order.refunded && <InputCurrency<Paid> name="refund_amount" readonly label={i18n`Refunded amount`} />} <Input<Paid> name="order_status" readonly label={i18n`Order status`} /> - {order.order_status_url && <Input<Paid> name="order_status_url" readonly label={i18n`Status URL`} />} + <TextField<Paid> name="order_status_url" label={i18n`Status URL`} > + <a target="_blank" href={order.order_status_url}> + {order.order_status_url} + </a> + </TextField> </FormProvider> </div> </div> </section> - {value.contract_terms && <section class="section"> - <div class="columns"> - <div class="column is-12" > - <div class="title"><Translate>Contract Terms</Translate></div> - <FormProvider<CT> object={value.contract_terms} valueHandler={null} > - <Input<CT> readonly name="summary" label={i18n`Summary`} tooltip={i18n`human-readable description of the whole purchase`} /> - <InputCurrency<CT> readonly name="amount" label={i18n`Amount`} tooltip={i18n`total price for the transaction`} /> - {value.contract_terms.fulfillment_url && - <Input<CT> readonly name="fulfillment_url" label={i18n`Fulfillment URL`} tooltip={i18n`URL for this purchase`} /> - } - <Input<CT> readonly name="max_fee" label={i18n`Max fee`} tooltip={i18n`maximum total deposit fee accepted by the merchant for this contract`} /> - <Input<CT> readonly name="max_wire_fee" label={i18n`Max wire fee`} tooltip={i18n`maximum wire fee accepted by the merchant`} /> - <Input<CT> readonly name="wire_fee_amortization" label={i18n`Wire fee amortization`} tooltip={i18n`over how many customer transactions does the merchant expect to amortize wire fees on average`} /> - <InputDate<CT> readonly name="timestamp" label={i18n`Created at`} tooltip={i18n`time when this contract was generated`} /> - <InputDate<CT> readonly name="refund_deadline" label={i18n`Refund deadline`} tooltip={i18n`after this deadline has passed no refunds will be accepted`} /> - <InputDate<CT> readonly name="pay_deadline" label={i18n`Payment deadline`} tooltip={i18n`after this deadline, the merchant won't accept payments for the contract`} /> - <InputDate<CT> readonly name="wire_transfer_deadline" label={i18n`Wire transfer deadline`} tooltip={i18n`transfer deadline for the exchange`} /> - <InputDate<CT> readonly name="delivery_date" label={i18n`Delivery date`} tooltip={i18n`time indicating when the order should be delivered`} /> - {value.contract_terms.delivery_date && - <InputGroup name="delivery_location" label={i18n`Location`} tooltip={i18n`where the order will be delivered`} > - <InputLocation name="payments.delivery_location" /> - </InputGroup> - } - <InputDuration<CT> readonly name="auto_refund" label={i18n`Auto-refund delay`} tooltip={i18n`how long the wallet should try to get an automatic refund for the purchase`} /> - <Input<CT> readonly name="extra" label={i18n`Extra info`} tooltip={i18n`extra data that is only interpreted by the merchant frontend`} /> - </FormProvider> - </div> - <div class="column" /> - </div> - </section>} - {order.contract_terms.products.length ? <section class="section"> - <div class="columns"> - <div class="column is-12" > - <div class="title"><Translate>Product list</Translate></div> - <ProductList list={order.contract_terms.products} /> - </div> - <div class="column" /> - </div> - </section> : undefined} + {order.contract_terms.products.length ? <Fragment> + <div class="title"><Translate>Product list</Translate></div> + <ProductList list={order.contract_terms.products} /> + </Fragment> : undefined} + {value.contract_terms && <ContractTerms value={value.contract_terms} />} </div> <div class="column" /> </div> @@ -380,10 +394,9 @@ function UnpaidPage({ id, order }: { id: string; order: MerchantBackend.Orders.C whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', - // maxWidth: '100%', }}> - <p><Translate>pay at</Translate>: <a href={order.order_status_url} rel="nofollow" target="new">{order.order_status_url}</a></p> - <p><Translate>created at</Translate>: <b>missing value, there is no contract term yet</b></p> + <p><b><Translate>pay at</Translate>:</b> <a href={order.order_status_url} rel="nofollow" target="new">{order.order_status_url}</a></p> + <p><b><Translate>created at</Translate>:</b> {format(new Date(order.creation_time.t_ms), 'yyyy-MM-dd HH:mm:ss')}</p> </div> </div> </div> @@ -394,8 +407,10 @@ function UnpaidPage({ id, order }: { id: string; order: MerchantBackend.Orders.C <section class="section is-main-section"> <div class="columns"> <div class="column" /> - <div class="column is-6"> + <div class="column is-four-fifths"> <FormProvider<Unpaid> object={value} valueHandler={valueHandler} > + <Input<Unpaid> readonly name="summary" label={i18n`Summary`} tooltip={i18n`human-readable description of the whole purchase`} /> + <InputCurrency<Unpaid> readonly name="total_amount" label={i18n`Amount`} tooltip={i18n`total price for the transaction`} /> <Input<Unpaid> name="order_status" readonly label={i18n`Order status`} /> <Input<Unpaid> name="order_status_url" readonly label={i18n`Order status URL`} /> <Input<Unpaid> name="taler_pay_uri" readonly label={i18n`Payment URI`} /> @@ -432,7 +447,7 @@ export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode { />} <div class="columns"> <div class="column" /> - <div class="column is-two-thirds"> + <div class="column is-four-fifths"> <div class="buttons is-right mt-5"> <button class="button" onClick={onBack}><Translate>Back</Translate></button> </div> diff --git a/packages/frontend/src/paths/instance/orders/details/Timeline.tsx b/packages/frontend/src/paths/instance/orders/details/Timeline.tsx index d4f17c4..16adbcb 100644 --- a/packages/frontend/src/paths/instance/orders/details/Timeline.tsx +++ b/packages/frontend/src/paths/instance/orders/details/Timeline.tsx @@ -47,7 +47,7 @@ export function Timeline({ events:e }: Props) { } }) return <div class="timeline"> - {events.map((e,i) => { + {state.map((e,i) => { return <div key={i} class="timeline-item"> {(() => { switch (e.type) { @@ -55,6 +55,7 @@ export function Timeline({ events:e }: Props) { case "delivery": return <div class="timeline-marker is-icon "><i class="mdi mdi-delivery" /></div> case "start": return <div class="timeline-marker is-icon is-success"><i class="mdi mdi-flag " /></div> case "wired": return <div class="timeline-marker is-icon is-success"><i class="mdi mdi-cash" /></div> + case "wired-range": return <div class="timeline-marker is-icon is-success"><i class="mdi mdi-cash" /></div> case "refund": return <div class="timeline-marker is-icon is-danger"><i class="mdi mdi-cash" /></div> case "now": return <div class="timeline-marker is-icon is-info"><i class="mdi mdi-clock" /></div> } @@ -71,5 +72,5 @@ export function Timeline({ events:e }: Props) { export interface Event { when: Date; description: string; - type: 'start' | 'refund' | 'wired' | 'deadline' | 'delivery' | 'now' + type: 'start' | 'refund' | 'wired' | 'wired-range' |'deadline' | 'delivery' | 'now' } |