diff options
author | Nic Eigel <nic@eigel.ch> | 2024-06-24 09:16:33 +0200 |
---|---|---|
committer | Nic Eigel <nic@eigel.ch> | 2024-06-24 09:16:33 +0200 |
commit | 75ff04672c011cf4c47b8f07d327adbf59323396 (patch) | |
tree | 24577e0429f641e3c19005b5a9180af8186232d0 /packages/auditor-backoffice-ui/src/paths/instance/orders/details | |
parent | 0b90a34e7c7c5d9bcca9a2ebe74df9fdfafc6577 (diff) | |
download | wallet-core-75ff04672c011cf4c47b8f07d327adbf59323396.tar.xz |
fixing merge-error
Diffstat (limited to 'packages/auditor-backoffice-ui/src/paths/instance/orders/details')
4 files changed, 0 insertions, 1129 deletions
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx deleted file mode 100644 index 22c6944b3..000000000 --- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-2024 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 { addDays } from "date-fns"; -import { h, VNode, FunctionalComponent } from "preact"; -import { MerchantBackend } from "../../../../declaration.js"; -import { DetailPage as TestedComponent } from "./DetailPage.js"; - -export default { - title: "Pages/Order/Detail", - component: TestedComponent, - argTypes: { - onRefund: { action: "onRefund" }, - onBack: { action: "onBack" }, - }, -}; - -function createExample<Props>( - Component: FunctionalComponent<Props>, - props: Partial<Props>, -) { - const r = (args: any) => <Component {...args} />; - r.args = props; - return r; -} - -const defaultContractTerm = { - amount: "TESTKUDOS:10", - timestamp: { - t_s: new Date().getTime() / 1000, - }, - auditors: [], - exchanges: [], - max_fee: "TESTKUDOS:1", - merchant: {} as any, - merchant_base_url: "http://merchant.url/", - order_id: "2021.165-03GDFC26Y1NNG", - products: [], - summary: "text summary", - wire_transfer_deadline: { - t_s: "never", - }, - refund_deadline: { t_s: "never" }, - merchant_pub: "ASDASDASDSd", - nonce: "QWEQWEQWE", - pay_deadline: { - t_s: "never", - }, - wire_method: "x-taler-bank", - h_wire: "asd", -} as MerchantBackend.ContractTerms; - -// contract_terms: defaultContracTerm, -export const Claimed = createExample(TestedComponent, { - id: "2021.165-03GDFC26Y1NNG", - selected: { - order_status: "claimed", - contract_terms: defaultContractTerm, - }, -}); - -export const PaidNotRefundable = createExample(TestedComponent, { - id: "2021.165-03GDFC26Y1NNG", - selected: { - order_status: "paid", - contract_terms: defaultContractTerm, - refunded: false, - deposit_total: "TESTKUDOS:10", - exchange_ec: 0, - order_status_url: "http://merchant.backend/status", - exchange_hc: 0, - refund_amount: "TESTKUDOS:0", - refund_details: [], - refund_pending: false, - wire_details: [], - wire_reports: [], - wired: false, - }, -}); - -export const PaidRefundable = createExample(TestedComponent, { - id: "2021.165-03GDFC26Y1NNG", - selected: { - order_status: "paid", - contract_terms: { - ...defaultContractTerm, - refund_deadline: { - t_s: addDays(new Date(), 2).getTime() / 1000, - }, - }, - refunded: false, - deposit_total: "TESTKUDOS:10", - exchange_ec: 0, - order_status_url: "http://merchant.backend/status", - exchange_hc: 0, - refund_amount: "TESTKUDOS:0", - refund_details: [], - refund_pending: false, - wire_details: [], - wire_reports: [], - wired: false, - }, -}); - -export const Unpaid = createExample(TestedComponent, { - id: "2021.165-03GDFC26Y1NNG", - selected: { - order_status: "unpaid", - order_status_url: "http://merchant.backend/status", - creation_time: { - t_s: new Date().getTime() / 1000, - }, - summary: "text summary", - taler_pay_uri: "pay uri", - total_amount: "TESTKUDOS:10", - }, -}); diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx deleted file mode 100644 index abcddb38a..000000000 --- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx +++ /dev/null @@ -1,770 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-2024 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 { AmountJson, Amounts, stringifyRefundUri } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { format, formatDistance } from "date-fns"; -import { Fragment, VNode, h } from "preact"; -import { useState } from "preact/hooks"; -import { FormProvider } from "../../../../components/form/FormProvider.js"; -import { Input } from "../../../../components/form/Input.js"; -import { InputCurrency } from "../../../../components/form/InputCurrency.js"; -import { InputDate } from "../../../../components/form/InputDate.js"; -import { InputDuration } from "../../../../components/form/InputDuration.js"; -import { InputGroup } from "../../../../components/form/InputGroup.js"; -import { InputLocation } from "../../../../components/form/InputLocation.js"; -import { TextField } from "../../../../components/form/TextField.js"; -import { ProductList } from "../../../../components/product/ProductList.js"; -import { useBackendContext } from "../../../../context/backend.js"; -import { MerchantBackend } from "../../../../declaration.js"; -import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js"; -import { mergeRefunds } from "../../../../utils/amount.js"; -import { RefundModal } from "../list/Table.js"; -import { Event, Timeline } from "./Timeline.js"; - -type Entity = MerchantBackend.Orders.MerchantOrderStatusResponse; -type CT = MerchantBackend.ContractTerms; - -interface Props { - onBack: () => void; - selected: Entity; - id: string; - onRefund: (id: string, value: MerchantBackend.Orders.RefundRequest) => void; -} - -type Paid = MerchantBackend.Orders.CheckPaymentPaidResponse & { - refund_taken: string; -}; -type Unpaid = MerchantBackend.Orders.CheckPaymentUnpaidResponse; -type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse; - -function ContractTerms({ value }: { value: CT }) { - const { i18n } = useTranslationContext(); - - return ( - <InputGroup name="contract_terms" label={i18n.str`Contract Terms`}> - <FormProvider<CT> object={value} valueHandler={null}> - <Input<CT> - readonly - name="summary" - label={i18n.str`Summary`} - tooltip={i18n.str`human-readable description of the whole purchase`} - /> - <InputCurrency<CT> - readonly - name="amount" - label={i18n.str`Amount`} - tooltip={i18n.str`total price for the transaction`} - /> - {value.fulfillment_url && ( - <Input<CT> - readonly - name="fulfillment_url" - label={i18n.str`Fulfillment URL`} - tooltip={i18n.str`URL for this purchase`} - /> - )} - <Input<CT> - readonly - name="max_fee" - label={i18n.str`Max fee`} - tooltip={i18n.str`maximum total deposit fee accepted by the merchant for this contract`} - /> - <InputDate<CT> - readonly - name="timestamp" - label={i18n.str`Created at`} - tooltip={i18n.str`time when this contract was generated`} - /> - <InputDate<CT> - readonly - name="refund_deadline" - label={i18n.str`Refund deadline`} - tooltip={i18n.str`after this deadline has passed no refunds will be accepted`} - /> - <InputDate<CT> - readonly - name="pay_deadline" - label={i18n.str`Payment deadline`} - tooltip={i18n.str`after this deadline, the merchant won't accept payments for the contract`} - /> - <InputDate<CT> - readonly - name="wire_transfer_deadline" - label={i18n.str`Wire transfer deadline`} - tooltip={i18n.str`transfer deadline for the exchange`} - /> - <InputDate<CT> - readonly - name="delivery_date" - label={i18n.str`Delivery date`} - tooltip={i18n.str`time indicating when the order should be delivered`} - /> - {value.delivery_date && ( - <InputGroup - name="delivery_location" - label={i18n.str`Location`} - tooltip={i18n.str`where the order will be delivered`} - > - <InputLocation name="payments.delivery_location" /> - </InputGroup> - )} - <InputDuration<CT> - readonly - name="auto_refund" - label={i18n.str`Auto-refund delay`} - tooltip={i18n.str`how long the wallet should try to get an automatic refund for the purchase`} - /> - <Input<CT> - readonly - name="extra" - label={i18n.str`Extra info`} - tooltip={i18n.str`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[] = []; - if (order.contract_terms.timestamp.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.timestamp.t_s * 1000), - description: "order created", - type: "start", - }); - } - if (order.contract_terms.pay_deadline.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.pay_deadline.t_s * 1000), - description: "pay deadline", - type: "deadline", - }); - } - if (order.contract_terms.refund_deadline.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.refund_deadline.t_s * 1000), - description: "refund deadline", - type: "deadline", - }); - } - if (order.contract_terms.wire_transfer_deadline.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000), - description: "wire deadline", - type: "deadline", - }); - } - if ( - order.contract_terms.delivery_date && - order.contract_terms.delivery_date.t_s !== "never" - ) { - events.push({ - when: new Date(order.contract_terms.delivery_date?.t_s * 1000), - description: "delivery", - type: "delivery", - }); - } - - const [value, valueHandler] = useState<Partial<Claimed>>(order); - const { i18n } = useTranslationContext(); - const [settings] = useSettings() - - return ( - <div> - <section class="section"> - <div class="columns"> - <div class="column" /> - <div class="column is-10"> - <section class="hero is-hero-bar"> - <div class="hero-body"> - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <i18n.Translate>Order</i18n.Translate> #{id} - <div class="tag is-info ml-4"> - <i18n.Translate>claimed</i18n.Translate> - </div> - </div> - </div> - </div> - - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <h1 class="title">{order.contract_terms.amount}</h1> - </div> - </div> - </div> - - <div class="level"> - <div class="level-left" style={{ maxWidth: "100%" }}> - <div class="level-item" style={{ maxWidth: "100%" }}> - <div - class="content" - style={{ - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - }} - > - <p> - <b> - <i18n.Translate>claimed at</i18n.Translate>: - </b>{" "} - {format( - new Date(order.contract_terms.timestamp.t_s * 1000), - datetimeFormatForSettings(settings) - )} - </p> - </div> - </div> - </div> - </div> - </div> - </section> - - <section class="section"> - <div class="columns"> - <div class="column is-4"> - <div class="title"> - <i18n.Translate>Timeline</i18n.Translate> - </div> - <Timeline events={events} /> - </div> - <div class="column is-8"> - <div class="title"> - <i18n.Translate>Payment details</i18n.Translate> - </div> - <FormProvider<Claimed> - object={value} - valueHandler={valueHandler} - > - <Input - name="contract_terms.summary" - readonly - inputType="multiline" - label={i18n.str`Summary`} - /> - <InputCurrency - name="contract_terms.amount" - readonly - label={i18n.str`Amount`} - /> - <Input<Claimed> - name="order_status" - readonly - label={i18n.str`Order status`} - /> - </FormProvider> - </div> - </div> - </section> - - {order.contract_terms.products.length ? ( - <Fragment> - <div class="title"> - <i18n.Translate>Product list</i18n.Translate> - </div> - <ProductList list={order.contract_terms.products} /> - </Fragment> - ) : undefined} - - {value.contract_terms && ( - <ContractTerms value={value.contract_terms} /> - )} - </div> - <div class="column" /> - </div> - </section> - </div> - ); -} -function PaidPage({ - id, - order, - onRefund, -}: { - id: string; - order: MerchantBackend.Orders.CheckPaymentPaidResponse; - onRefund: (id: string) => void; -}) { - const events: Event[] = []; - if (order.contract_terms.timestamp.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.timestamp.t_s * 1000), - description: "order created", - type: "start", - }); - } - if (order.contract_terms.pay_deadline.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.pay_deadline.t_s * 1000), - description: "pay deadline", - type: "deadline", - }); - } - if (order.contract_terms.refund_deadline.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.refund_deadline.t_s * 1000), - description: "refund deadline", - type: "deadline", - }); - } - if (order.contract_terms.wire_transfer_deadline.t_s !== "never") { - events.push({ - when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000), - description: "wire deadline", - type: "deadline", - }); - } - if ( - order.contract_terms.delivery_date && - order.contract_terms.delivery_date.t_s !== "never" - ) { - if (order.contract_terms.delivery_date) - events.push({ - when: new Date(order.contract_terms.delivery_date?.t_s * 1000), - description: "delivery", - type: "delivery", - }); - } - order.refund_details.reduce(mergeRefunds, []).forEach((e) => { - if (e.timestamp.t_s !== "never") { - events.push({ - when: new Date(e.timestamp.t_s * 1000), - description: `refund: ${e.amount}: ${e.reason}`, - type: e.pending ? "refund" : "refund-taken", - }); - } - }); - 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_s < w.execution_time.t_s) { - last = w; - } - if (first === null || first.execution_time.t_s > w.execution_time.t_s) { - first = w; - } - total = - total === null - ? Amounts.parseOrThrow(w.amount) - : Amounts.add(total, Amounts.parseOrThrow(w.amount)).amount; - }); - const last_time = last!.execution_time.t_s; - if (last_time !== "never") { - events.push({ - when: new Date(last_time * 1000), - description: `wired ${Amounts.stringify(total!)}`, - type: "wired-range", - }); - } - const first_time = first!.execution_time.t_s; - if (first_time !== "never") { - events.push({ - when: new Date(first_time * 1000), - description: `wire transfer started...`, - type: "wired-range", - }); - } - } else { - order.wire_details.forEach((e) => { - if (e.execution_time.t_s !== "never") { - events.push({ - when: new Date(e.execution_time.t_s * 1000), - description: `wired ${e.amount}`, - type: "wired", - }); - } - }); - } - } - - const now = new Date() - const nextEvent = events.find((e) => { - return e.when.getTime() > now.getTime() - }) - - const [value, valueHandler] = useState<Partial<Paid>>(order); - const { url: backendURL } = useBackendContext() - const refundurl = stringifyRefundUri({ - merchantBaseUrl: backendURL, - orderId: order.contract_terms.order_id - }) - const refundable = - new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000; - const { i18n } = useTranslationContext(); - - const amount = Amounts.parseOrThrow(order.contract_terms.amount); - const refund_taken = order.refund_details.reduce((prev, cur) => { - if (cur.pending) return prev; - return Amounts.add(prev, Amounts.parseOrThrow(cur.amount)).amount; - }, Amounts.zeroOfCurrency(amount.currency)); - value.refund_taken = Amounts.stringify(refund_taken); - - return ( - <div> - <section class="section"> - <div class="columns"> - <div class="column" /> - <div class="column is-10"> - <section class="hero is-hero-bar"> - <div class="hero-body"> - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <i18n.Translate>Order</i18n.Translate> #{id} - <div class="tag is-success ml-4"> - <i18n.Translate>paid</i18n.Translate> - </div> - {order.wired ? ( - <div class="tag is-success ml-4"> - <i18n.Translate>wired</i18n.Translate> - </div> - ) : null} - {order.refunded ? ( - <div class="tag is-danger ml-4"> - <i18n.Translate>refunded</i18n.Translate> - </div> - ) : null} - </div> - </div> - </div> - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <h1 class="title">{order.contract_terms.amount}</h1> - </div> - </div> - <div class="level-right"> - <div class="level-item"> - <h1 class="title"> - <div class="buttons"> - <span - class="has-tooltip-left" - data-tooltip={ - refundable - ? i18n.str`refund order` - : i18n.str`not refundable` - } - > - <button - class="button is-danger" - disabled={!refundable} - onClick={() => onRefund(id)} - > - <i18n.Translate>refund</i18n.Translate> - </button> - </span> - </div> - </h1> - </div> - </div> - </div> - - <div class="level"> - <div class="level-left" style={{ maxWidth: "100%" }}> - <div class="level-item" style={{ maxWidth: "100%" }}> - <div - class="content" - style={{ - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - }} - > - {nextEvent && - <p> - <i18n.Translate>Next event in </i18n.Translate> {formatDistance( - nextEvent.when, - new Date(), - // "yyyy/MM/dd HH:mm:ss", - )} - </p> - } - </div> - </div> - </div> - </div> - </div> - </section> - - <section class="section"> - <div class="columns"> - <div class="column is-4"> - <div class="title"> - <i18n.Translate>Timeline</i18n.Translate> - </div> - <Timeline events={events} /> - </div> - <div class="column is-8"> - <div class="title"> - <i18n.Translate>Payment details</i18n.Translate> - </div> - <FormProvider<Paid> - object={value} - valueHandler={valueHandler} - > - {/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n.str`Deposit total`} /> */} - {order.refunded && ( - <InputCurrency<Paid> - name="refund_amount" - readonly - label={i18n.str`Refunded amount`} - /> - )} - {order.refunded && ( - <InputCurrency<Paid> - name="refund_taken" - readonly - label={i18n.str`Refund taken`} - /> - )} - <Input<Paid> - name="order_status" - readonly - label={i18n.str`Order status`} - /> - <TextField<Paid> - name="order_status_url" - label={i18n.str`Status URL`} - > - <a - target="_blank" - rel="noreferrer" - href={order.order_status_url} - > - {order.order_status_url} - </a> - </TextField> - {order.refunded && ( - <TextField<Paid> - name="order_status_url" - label={i18n.str`Refund URI`} - > - <a target="_blank" rel="noreferrer" href={refundurl}> - {refundurl} - </a> - </TextField> - )} - </FormProvider> - </div> - </div> - </section> - - {order.contract_terms.products.length ? ( - <Fragment> - <div class="title"> - <i18n.Translate>Product list</i18n.Translate> - </div> - <ProductList list={order.contract_terms.products} /> - </Fragment> - ) : undefined} - - {value.contract_terms && ( - <ContractTerms value={value.contract_terms} /> - )} - </div> - <div class="column" /> - </div> - </section> - </div> - ); -} - -function UnpaidPage({ - id, - order, -}: { - id: string; - order: MerchantBackend.Orders.CheckPaymentUnpaidResponse; -}) { - const [value, valueHandler] = useState<Partial<Unpaid>>(order); - const { i18n } = useTranslationContext(); - const [settings] = useSettings() - return ( - <div> - <section class="hero is-hero-bar"> - <div class="hero-body"> - <div class="level"> - <div class="level-left"> - <div class="level-item"> - <h1 class="title"> - <i18n.Translate>Order</i18n.Translate> #{id} - </h1> - </div> - <div class="tag is-dark"> - <i18n.Translate>unpaid</i18n.Translate> - </div> - </div> - </div> - - <div class="level"> - <div class="level-left" style={{ maxWidth: "100%" }}> - <div class="level-item" style={{ maxWidth: "100%" }}> - <div - class="content" - style={{ - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - }} - > - <p> - <b> - <i18n.Translate>pay at</i18n.Translate>: - </b>{" "} - <a - href={order.order_status_url} - rel="nofollow" - target="new" - > - {order.order_status_url} - </a> - </p> - <p> - <b> - <i18n.Translate>created at</i18n.Translate>: - </b>{" "} - {order.creation_time.t_s === "never" - ? "never" - : format( - new Date(order.creation_time.t_s * 1000), - datetimeFormatForSettings(settings) - )} - </p> - </div> - </div> - </div> - </div> - </div> - </section> - - <section class="section is-main-section"> - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <FormProvider<Unpaid> object={value} valueHandler={valueHandler}> - <Input<Unpaid> - readonly - name="summary" - label={i18n.str`Summary`} - tooltip={i18n.str`human-readable description of the whole purchase`} - /> - <InputCurrency<Unpaid> - readonly - name="total_amount" - label={i18n.str`Amount`} - tooltip={i18n.str`total price for the transaction`} - /> - <Input<Unpaid> - name="order_status" - readonly - label={i18n.str`Order status`} - /> - <Input<Unpaid> - name="order_status_url" - readonly - label={i18n.str`Order status URL`} - /> - <TextField<Unpaid> - name="taler_pay_uri" - label={i18n.str`Payment URI`} - > - <a target="_blank" rel="noreferrer" href={value.taler_pay_uri}> - {value.taler_pay_uri} - </a> - </TextField> - </FormProvider> - </div> - <div class="column" /> - </div> - </section> - </div> - ); -} - -export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode { - const [showRefund, setShowRefund] = useState<string | undefined>(undefined); - const { i18n } = useTranslationContext(); - const DetailByStatus = function () { - switch (selected.order_status) { - case "claimed": - return <ClaimedPage id={id} order={selected} />; - case "paid": - return <PaidPage id={id} order={selected} onRefund={setShowRefund} />; - case "unpaid": - return <UnpaidPage id={id} order={selected} />; - default: - return ( - <div> - <i18n.Translate> - Unknown order status. This is an error, please contact the - administrator. - </i18n.Translate> - </div> - ); - } - }; - - return ( - <Fragment> - {DetailByStatus()} - {showRefund && ( - <RefundModal - order={selected} - onCancel={() => setShowRefund(undefined)} - onConfirm={(value) => { - onRefund(showRefund, value); - setShowRefund(undefined); - }} - /> - )} - <div class="columns"> - <div class="column" /> - <div class="column is-four-fifths"> - <div class="buttons is-right mt-5"> - <button class="button" onClick={onBack}> - <i18n.Translate>Back</i18n.Translate> - </button> - </div> - </div> - <div class="column" /> - </div> - </Fragment> - ); -} - -async function copyToClipboard(text: string) { - return navigator.clipboard.writeText(text); -} diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx deleted file mode 100644 index 1aae4da21..000000000 --- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-2024 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/> - */ -import { format } from "date-fns"; -import { h } from "preact"; -import { useEffect, useState } from "preact/hooks"; -import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js"; - -interface Props { - events: Event[]; -} - -export function Timeline({ events: e }: Props) { - const events = [...e]; - events.push({ - when: new Date(), - description: "now", - type: "now", - }); - - events.sort((a, b) => a.when.getTime() - b.when.getTime()); - const [settings] = useSettings(); - const [state, setState] = useState(events); - useEffect(() => { - const handle = setTimeout(() => { - const eventsWithoutNow = state.filter((e) => e.type !== "now"); - eventsWithoutNow.push({ - when: new Date(), - description: "now", - type: "now", - }); - setState(eventsWithoutNow); - }, 1000); - return () => { - clearTimeout(handle); - }; - }); - return ( - <div class="timeline"> - {events.map((e, i) => { - return ( - <div key={i} class="timeline-item"> - {(() => { - switch (e.type) { - case "deadline": - return ( - <div class="timeline-marker is-icon "> - <i class="mdi mdi-flag" /> - </div> - ); - case "delivery": - return ( - <div class="timeline-marker is-icon "> - <i class="mdi mdi-delivery" /> - </div> - ); - case "start": - return ( - <div class="timeline-marker is-icon"> - <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 "refund-taken": - return ( - <div class="timeline-marker is-icon is-success"> - <i class="mdi mdi-cash" /> - </div> - ); - case "now": - return ( - <div class="timeline-marker is-icon is-info"> - <i class="mdi mdi-clock" /> - </div> - ); - } - })()} - <div class="timeline-content"> - {e.description !== "now" && <p class="heading">{format(e.when, datetimeFormatForSettings(settings))}</p>} - <p>{e.description}</p> - </div> - </div> - ); - })} - </div> - ); -} -export interface Event { - when: Date; - description: string; - type: - | "start" - | "refund" - | "refund-taken" - | "wired" - | "wired-range" - | "deadline" - | "delivery" - | "now"; -} diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx deleted file mode 100644 index dfeaa4447..000000000 --- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021-2024 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/> - */ -import { - useTranslationContext, - HttpError, - ErrorType, -} from "@gnu-taler/web-util/browser"; -import { Fragment, h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { Loading } from "../../../../components/exception/loading.js"; -import { NotificationCard } from "../../../../components/menu/index.js"; -import { MerchantBackend } from "../../../../declaration.js"; -import { useOrderAPI, useOrderDetails } from "../../../../hooks/order.js"; -import { Notification } from "../../../../utils/types.js"; -import { DetailPage } from "./DetailPage.js"; -import { HttpStatusCode } from "@gnu-taler/taler-util"; - -export interface Props { - oid: string; - - onBack: () => void; - onUnauthorized: () => VNode; - onNotFound: () => VNode; - onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode; -} - -export default function Update({ - oid, - onBack, - onLoadError, - onNotFound, - onUnauthorized, -}: Props): VNode { - const { refundOrder } = useOrderAPI(); - const result = useOrderDetails(oid); - const [notif, setNotif] = useState<Notification | undefined>(undefined); - - const { i18n } = useTranslationContext(); - - if (result.loading) return <Loading />; - if (!result.ok) { - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.Unauthorized - ) - return onUnauthorized(); - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.NotFound - ) - return onNotFound(); - return onLoadError(result); - } - - return ( - <Fragment> - <NotificationCard notification={notif} /> - - <DetailPage - onBack={onBack} - id={oid} - onRefund={(id, value) => - refundOrder(id, value) - .then(() => - setNotif({ - message: i18n.str`refund created successfully`, - type: "SUCCESS", - }), - ) - .catch((error) => - setNotif({ - message: i18n.str`could not create the refund`, - type: "ERROR", - description: error.message, - }), - ) - } - selected={result.data} - /> - </Fragment> - ); -} |