[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] branch master updated: order detail
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] branch master updated: order detail |
Date: |
Wed, 31 Mar 2021 15:16:34 +0200 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository merchant-backoffice.
The following commit(s) were added to refs/heads/master by this push:
new e9a4228 order detail
e9a4228 is described below
commit e9a4228ef2d7210e67aded9559eeed79dfa4624f
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Mar 31 10:16:17 2021 -0300
order detail
---
CHANGELOG.md | 8 +-
packages/frontend/package.json | 1 +
packages/frontend/src/InstanceRoutes.tsx | 33 --
packages/frontend/src/components/form/Input.tsx | 2 +-
.../src/components/form/InputWithAddon.tsx | 2 +-
packages/frontend/src/components/menu/SideBar.tsx | 4 +-
packages/frontend/src/messages/en.po | 47 ++-
.../paths/instance/orders/details/DetailPage.tsx | 368 +++++++++++++++++++--
.../src/paths/instance/orders/details/Timeline.tsx | 35 ++
.../src/paths/instance/orders/details/index.tsx | 18 +-
.../src/paths/instance/orders/list/Table.tsx | 7 +-
.../src/paths/instance/orders/list/index.tsx | 7 +-
packages/frontend/src/scss/main.scss | 25 +-
packages/frontend/src/utils/functions.ts | 5 +
pnpm-lock.yaml | 6 +
15 files changed, 452 insertions(+), 116 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 646d44e..082c849 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,14 +27,8 @@ and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0
- product detail: we could have some button that brings us to the detailed
screen for the product
- order id field to go
-created
-wired (if wired === true)
-refund
+frontend, too many redirects
-error details
-
-fatal error: exchange error
-warning: exchange repotred problem
- navigation to another instance should not do full refresh
- cleanup instance and token management, because code is a mess and can be
refactored
## [Unreleased]
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index e5ea502..34eff24 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -77,6 +77,7 @@
"bulma-radio": "^1.1.1",
"bulma-responsive-tables": "^1.2.3",
"bulma-switch-control": "^1.1.1",
+ "bulma-timeline": "^3.0.4",
"bulma-upload-control": "^1.2.0",
"dotenv": "^8.2.0",
"enzyme": "^3.11.0",
diff --git a/packages/frontend/src/InstanceRoutes.tsx
b/packages/frontend/src/InstanceRoutes.tsx
index ba0c8b2..2114c6c 100644
--- a/packages/frontend/src/InstanceRoutes.tsx
+++ b/packages/frontend/src/InstanceRoutes.tsx
@@ -204,37 +204,6 @@ export function InstanceRoutes({ id, admin }: Props):
VNode {
}
<Route path="/" component={Redirect} to={InstancePaths.order_list} />
- {/*
- component={DetailPage}
-
- onUnauthorized={() => {
- return <Fragment>
- <NotificationCard notification={{ message: i18n`Access denied`,
description: i18n`Check your token is valid`, type: 'ERROR', }} />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- }}
-
- onNotFound={() => {
- if (admin) {
- return <Fragment>
- <NotificationCard notification={{
- message: 'No default instance',
- description: 'in order to use merchant backoffice, you should
create the default instance',
- type: 'INFO'
- }} />
- <InstanceCreatePage onError={() => null} onConfirm={() => null}
/>
- </Fragment>
- }
- return <NotFoundPage />
- }}
-
- onLoadError={(error: SwrError) => {
- return <Fragment>
- <NotificationCard notification={{ message: i18n`Problem reaching
the server`, description: i18n`Got message: ${error.message} from:
${error.backend} (hasToken: ${error.hasToken})`, type: 'ERROR' }} />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- }}
- /> */}
<Route path={InstancePaths.update}
component={InstanceUpdatePage}
@@ -286,12 +255,10 @@ export function InstanceRoutes({ id, admin }: Props):
VNode {
}}
onConfirm={() => {
- // pushNotification({ message: i18n`create_success`, type: 'SUCCESS'
});
route(`/`);
}}
onUpdateError={(e: Error) => {
- // pushNotification({ message: i18n`update_error`, type: 'ERROR' });
}}
/>
diff --git a/packages/frontend/src/components/form/Input.tsx
b/packages/frontend/src/components/form/Input.tsx
index 565e17b..c3ffa25 100644
--- a/packages/frontend/src/components/form/Input.tsx
+++ b/packages/frontend/src/components/form/Input.tsx
@@ -59,7 +59,7 @@ export function Input<T>({ name, readonly, expand, inputType,
fromStr = defaultF
<TextInput error={error}
inputType={inputType}
placeholder={placeholder} readonly={readonly}
- name={String(name)} value={toStr(value)} disabled={readonly}
+ name={String(name)} value={toStr(value)}
onChange={(e:h.JSX.TargetedEvent<HTMLInputElement>): void =>
onChange(fromStr(e.currentTarget.value))} />
<Message id={`fields.instance.${name}.help`}> </Message>
</p>
diff --git a/packages/frontend/src/components/form/InputWithAddon.tsx
b/packages/frontend/src/components/form/InputWithAddon.tsx
index f583f08..6b194ab 100644
--- a/packages/frontend/src/components/form/InputWithAddon.tsx
+++ b/packages/frontend/src/components/form/InputWithAddon.tsx
@@ -59,7 +59,7 @@ export function InputWithAddon<T>({ name, readonly,
addonBefore, expand, inputTy
</div>}
<p class={ expand ? "control is-expanded" : "control" }>
<input class={error ? "input is-danger" : "input"} type={inputType}
- placeholder={placeholder} readonly={readonly} disabled={readonly}
+ placeholder={placeholder} readonly={readonly}
name={String(name)} value={toStr(value)}
onChange={(e): void => onChange(fromStr(e.currentTarget.value))}
/>
<Message id={`fields.instance.${name}.help`}> </Message>
diff --git a/packages/frontend/src/components/menu/SideBar.tsx
b/packages/frontend/src/components/menu/SideBar.tsx
index 92e0f46..a6fe766 100644
--- a/packages/frontend/src/components/menu/SideBar.tsx
+++ b/packages/frontend/src/components/menu/SideBar.tsx
@@ -74,12 +74,12 @@ export function Sidebar({ mobile, instance, onLogout, admin
}: Props): VNode {
<span class="menu-item-label">Transfers</span>
</a>
</li>
- <li>
+ {/* <li>
<a href="/tips" class="has-icon">
<span class="icon"><i class="mdi mdi-cash" /></span>
<span class="menu-item-label">Tips</span>
</a>
- </li>
+ </li> */}
</ul>
<p class="menu-label">Connection</p>
<ul class="menu-list">
diff --git a/packages/frontend/src/messages/en.po
b/packages/frontend/src/messages/en.po
index 4abba9a..c081637 100644
--- a/packages/frontend/src/messages/en.po
+++ b/packages/frontend/src/messages/en.po
@@ -311,50 +311,41 @@ msgstr "Description"
msgid "fields.instance.description.placeholder"
msgstr "add more information about the refund"
-# msgid "fields.instance.reason.tooltip"
-# msgstr ""
msgid "fields.instance.order_status.placeholder"
msgstr ""
-# msgid "fields.instance.order_status.tooltip"
-# msgstr ""
-
msgid "fields.instance.order_status.label"
msgstr "Order status"
-
-# msgid "fields.instance.order_status_url.placeholder"
-# msgstr ""
-
-# msgid "fields.instance.order_status_url.tooltip"
-# msgstr ""
-
msgid "fields.instance.order_status_url.label"
msgstr "Order status URL"
-# msgid "fields.instance.taler_pay_uri.placeholder"
-# msgstr ""
-
-# msgid "fields.instance.taler_pay_uri.tooltip"
-# msgstr ""
-
msgid "fields.instance.taler_pay_uri.label"
msgstr "Taler Pay URI"
-# msgid "fields.instance.amount.placeholder"
-# msgstr ""
-
-# msgid "fields.instance.amount.tooltip"
-# msgstr ""
msgid "fields.instance.amount.label"
msgstr "Amount"
-# msgid "fields.instance.summary.placeholder"
-# msgstr ""
+msgid "fields.instance.summary.label"
+msgstr "Summary"
-# msgid "fields.instance.summary.tooltip"
-# msgstr ""
-msgid "fields.instance.summary.label"
+msgid "fields.instance.contract_terms.amount.label"
+msgstr "Amount"
+
+msgid "fields.instance.contract_terms.summary.label"
msgstr "Summary"
+
+msgid "fields.instance.refund_amount.label"
+msgstr "Refund Amount"
+
+msgid "fields.instance.deposit_total.label"
+msgstr "Deposit Total"
+
+msgid "fields.instance.contract_terms.max_fee.label"
+msgstr "Max Fee"
+
+msgid "fields.instance.contract_terms.max_wire_fee.label"
+msgstr "Max Wire Fee"
+
diff --git a/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
b/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
index 4c81302..0359509 100644
--- a/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
+++ b/packages/frontend/src/paths/instance/orders/details/DetailPage.tsx
@@ -25,12 +25,19 @@ import { MerchantBackend } from "../../../../declaration";
import { Input } from "../../../../components/form/Input";
import { FormProvider } from "../../../../components/form/Field";
import { NotificationCard } from "../../../../components/menu";
+import { useConfigContext } from "../../../../context/backend";
+import { InputCurrency } from "../../../../components/form/InputCurrency";
+import { copyToClipboard } from "../../../../utils/functions";
+import { format } from "date-fns";
+import { Event, Timeline } from "./Timeline";
+import { RefundModal } from "../list/Table";
type Entity = MerchantBackend.Orders.MerchantOrderStatusResponse;
interface Props {
onBack: () => void;
selected: Entity;
-
+ id: string;
+ onRefund: (id:string, value: MerchantBackend.Orders.RefundRequest) => void;
}
interface KeyValue {
@@ -41,41 +48,316 @@ type Paid = MerchantBackend.Orders.CheckPaymentPaidResponse
type Unpaid = MerchantBackend.Orders.CheckPaymentUnpaidResponse
type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse
-export function DetailPage({ selected }: Props): VNode {
- const [value, valueHandler] = useState<Partial<Entity>>(selected)
+
+function ClaimedPage({ id, order }: { id: string; order:
MerchantBackend.Orders.CheckPaymentClaimedResponse }) {
+ const events: Event[] = []
+ events.push({
+ when: new Date(),
+ description: 'now',
+ type: 'now'
+ })
+ events.push({
+ when: new Date(order.contract_terms.timestamp.t_ms),
+ description: 'order created',
+ type: 'start'
+ })
+ events.push({
+ when: new Date(order.contract_terms.pay_deadline.t_ms),
+ description: 'pay deadline',
+ type: 'deadline'
+ })
+ events.push({
+ when: new Date(order.contract_terms.refund_deadline.t_ms),
+ description: 'refund deadline',
+ type: 'deadline'
+ })
+ events.push({
+ when: new Date(order.contract_terms.wire_transfer_deadline.t_ms),
+ description: 'wire deadline',
+ type: 'deadline'
+ })
+ if (order.contract_terms.delivery_date) events.push({
+ when: new Date(order.contract_terms.delivery_date?.t_ms),
+ description: 'delivery',
+ type: 'delivery'
+ })
+
+ events.sort((a, b) => a.when.getTime() - b.when.getTime())
+ const [value, valueHandler] = useState<Partial<Claimed>>(order)
const [errors, setErrors] = useState<KeyValue>({})
+ const config = useConfigContext()
+
+ 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">
+ Order #{id}
+ <div class="tag is-info ml-4">claimed</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 class="level-right">
+ <div class="level-item">
+ <h1 class="title">
+ <button class="button is-info" onClick={() => {
+ if (order.contract_terms.fulfillment_url)
copyToClipboard(order.contract_terms.fulfillment_url)
+ }}>copy url</button>
+ </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',
+ // maxWidth: '100%',
+ }}>
+ <p>pay at: <a
href={order.contract_terms.fulfillment_url} rel="nofollow"
target="new">{order.contract_terms.fulfillment_url}</a></p>
+ <p>{format(new
Date(order.contract_terms.timestamp.t_ms), '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">Timeline</div>
+ <Timeline events={events} />
+ </div>
+ <div class="column is-8" >
+ <div class="title">Payment details</div>
+ <FormProvider<Claimed> errors={errors} object={value}
valueHandler={valueHandler} >
+ <Input name="contract_terms.summary" readonly
inputType="multiline" />
+ <InputCurrency name="contract_terms.amount" readonly
currency={config.currency} />
+ <Input<Claimed> name="order_status" readonly />
+ </FormProvider>
+ </div>
+ </div>
+ </section>
+
+ <section class="section">
+ <div class="columns">
+ <div class="column is-2" />
+ <div class="column is-8" >
+ <div class="title">Payment details</div>
+ <FormProvider<Claimed> errors={errors} object={value}
valueHandler={valueHandler} >
+ <Input name="contract_terms.summary" readonly
inputType="multiline" />
+ <InputCurrency name="contract_terms.amount" readonly
currency={config.currency} />
+ <Input<Claimed> name="order_status" readonly />
+ </FormProvider>
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+
+ </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[] = []
+ events.push({
+ when: new Date(),
+ description: 'now',
+ type: 'now'
+ })
+ events.push({
+ when: new Date(order.contract_terms.timestamp.t_ms),
+ description: 'order created',
+ type: 'start'
+ })
+ events.push({
+ when: new Date(order.contract_terms.pay_deadline.t_ms),
+ description: 'pay deadline',
+ type: 'deadline'
+ })
+ events.push({
+ when: new Date(order.contract_terms.refund_deadline.t_ms),
+ description: 'refund deadline',
+ type: 'deadline'
+ })
+ events.push({
+ when: new Date(order.contract_terms.wire_transfer_deadline.t_ms),
+ description: 'wire deadline',
+ type: 'deadline'
+ })
+ if (order.contract_terms.delivery_date) events.push({
+ when: new Date(order.contract_terms.delivery_date?.t_ms),
+ description: 'delivery',
+ type: 'delivery'
+ })
+ order.refund_details.forEach(e => {
+ events.push({
+ when: new Date(e.timestamp.t_ms),
+ description: `refund: ${e.amount}`,
+ 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',
+ })
+
+ events.sort((a, b) => a.when.getTime() - b.when.getTime())
+ const [value, valueHandler] = useState<Partial<Paid>>({...order, fee:
'COL:0.1'} as any)
+ const [errors, setErrors] = useState<KeyValue>({})
+ const config = useConfigContext()
+
+ const refundable = !order.refunded &&
+ new Date().getTime() <order.contract_terms.refund_deadline.t_ms
return <div>
- <NotificationCard notification={{
- message: 'DEMO WARNING',
- type:'WARN',
- description: <Fragment>
- <p>UNDER CONSTRUCTION: for now we are showing some field of the order
depending on the state</p>
- <p><b>unpaid:</b> status_url and pay_uri</p>
- <p><b>claimed:</b> contractTerms.amount</p>
- <p><b>paid:</b> deposit_total</p>
- </Fragment>
- }} />
+ <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">
+ Order #{id}
+ <div class="tag is-success ml-4">paid</div>
+ {order.wired ?
+ <div class="tag is-success ml-4">wired</div> : null
+ }
+ {order.refunded ?
+ <div class="tag is-danger ml-4">refunded</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">
+ { refundable && <button class="button is-danger"
onClick={() => onRefund(id) }>refund</button> }
+ <button class="button is-info" onClick={() => {
+ if (order.contract_terms.fulfillment_url)
copyToClipboard(order.contract_terms.fulfillment_url)
+ }}>copy url</button>
+ </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',
+ // maxWidth: '100%',
+ }}>
+ <p><a href={order.contract_terms.fulfillment_url}
rel="nofollow" target="new">{order.contract_terms.fulfillment_url}</a></p>
+ <p>{format(new
Date(order.contract_terms.timestamp.t_ms), '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">Timeline</div>
+ <Timeline events={events} />
+ </div>
+ <div class="column is-8" >
+ <div class="title">Payment details</div>
+ <FormProvider<Paid> errors={errors} object={value}
valueHandler={valueHandler} >
+ <Input name="contract_terms.summary" readonly
inputType="multiline" />
+ <InputCurrency name="contract_terms.amount" readonly
currency={config.currency} />
+ <InputCurrency name="fee" readonly
currency={config.currency} />
+ <InputCurrency<Paid> name="refund_amount" readonly
currency={config.currency} />
+ <InputCurrency<Paid> name="deposit_total" readonly
currency={config.currency} />
+ <Input<Paid> name="order_status" readonly />
+ </FormProvider>
+ </div>
+ </div>
+ </section>
+
+ </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 [errors, setErrors] = useState<KeyValue>({})
+ 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">
+ Order #{id}
+ </h1>
+ </div>
+ <div class="tag is-dark">unpaid</div>
+ </div>
+ </div>
+ </div>
+ </section>
<section class="section is-main-section">
<div class="columns">
<div class="column" />
<div class="column is-6">
- <FormProvider<Entity> errors={errors} object={value}
valueHandler={valueHandler} >
- {selected.order_status === 'unpaid' && <Fragment>
- <Input<Unpaid> name="order_status" readonly />
- <Input<Unpaid> name="order_status_url" readonly />
- <Input<Unpaid> name="taler_pay_uri" readonly />
- </Fragment>}
- {selected.order_status === 'claimed' && <Fragment>
- <Input<Claimed> name="order_status" readonly />
- <Input name="contract_terms.amount" readonly />
- </Fragment>}
- {selected.order_status === 'paid' && <Fragment>
- <Input<Paid> name="order_status" readonly />
- <Input name="contract_terms.deposit_total" readonly />
- </Fragment>}
+ <FormProvider<Unpaid> errors={errors} object={value}
valueHandler={valueHandler} >
+ <Input<Unpaid> name="order_status" readonly />
+ <Input<Unpaid> name="order_status_url" readonly />
+ <Input<Unpaid> name="taler_pay_uri" readonly />
</FormProvider>
</div>
<div class="column" />
@@ -83,5 +365,37 @@ export function DetailPage({ selected }: Props): VNode {
</section>
</div>
+}
+
+export function DetailPage({ id, selected, onRefund }: Props): VNode {
+ const [showRefund, setShowRefund] = useState<string | undefined>(undefined)
+
+ const DetailByStatus = function (){
+ switch (selected.order_status) {
+ case 'claimed': return <ClaimedPage id={id} order={selected} />
+ case 'paid': return <PaidPage id={id} order={selected} onRefund={(order)
=> setShowRefund(id)} />
+ case 'unpaid': return <UnpaidPage id={id} order={selected} />
+ default: return <div>unknown order status</div>
+ }
+ }
+
+ return <Fragment>
+ <NotificationCard notification={{
+ message: 'DEMO WARNING',
+ type: 'WARN',
+ description: <ul>
+ <li>wired event is faked</li>
+ <li>fee value is fake, is not being calculated</li>
+ </ul>
+ }} />
+ {DetailByStatus()}
+ {showRefund && <RefundModal
+ onCancel={() => setShowRefund(undefined)}
+ onConfirm={(value) => {
+ onRefund(showRefund, value)
+ setShowRefund(undefined)
+ }}
+ />}
+ </Fragment>
}
\ No newline at end of file
diff --git a/packages/frontend/src/paths/instance/orders/details/Timeline.tsx
b/packages/frontend/src/paths/instance/orders/details/Timeline.tsx
new file mode 100644
index 0000000..c198cba
--- /dev/null
+++ b/packages/frontend/src/paths/instance/orders/details/Timeline.tsx
@@ -0,0 +1,35 @@
+import { format } from "date-fns";
+import { h } from "preact";
+
+interface Props {
+ events: Event[]
+}
+
+export function Timeline({ events }: Props) {
+ return <div class="timeline">
+ {events.map(e => {
+ return <div 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
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 "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>
+ }
+ })()}
+ <div class="timeline-content">
+ <p class="heading">{format(e.when, 'yyyy/MM/dd HH:mm:ss')}</p>
+ <p>{e.description}</p>
+ </div>
+ </div>
+ })}
+ </div >;
+
+}
+export interface Event {
+ when: Date;
+ description: string;
+ type: 'start' | 'refund' | 'wired' | 'deadline' | 'delivery' | 'now'
+}
diff --git a/packages/frontend/src/paths/instance/orders/details/index.tsx
b/packages/frontend/src/paths/instance/orders/details/index.tsx
index eae4f15..209d1ce 100644
--- a/packages/frontend/src/paths/instance/orders/details/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/details/index.tsx
@@ -14,9 +14,11 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading";
import { SwrError, useOrderDetails, useOrderMutateAPI } from
"../../../../hooks/backend";
- import { DetailPage } from "./DetailPage";
+import { Notification } from "../../../../utils/types";
+import { DetailPage } from "./DetailPage";
export interface Props {
oid: string;
@@ -30,6 +32,7 @@ export interface Props {
export default function Update({ oid, onBack, onLoadError, onNotFound,
onUnauthorized }: Props): VNode {
const { refundOrder } = useOrderMutateAPI();
const details = useOrderDetails(oid)
+ const [notif, setNotif] = useState<Notification | undefined>(undefined)
if (details.unauthorized) return onUnauthorized()
if (details.notfound) return onNotFound();
@@ -42,7 +45,18 @@ export default function Update({ oid, onBack, onLoadError,
onNotFound, onUnautho
return <Fragment>
<DetailPage
onBack={onBack}
+ id={oid}
+ onRefund={(id, value) => refundOrder(id, value)
+ .then(() => setNotif({
+ message: 'refund created successfully',
+ type: "SUCCESS"
+ })).catch((error) => setNotif({
+ message: 'could not create the refund',
+ type: "ERROR",
+ description: error.message
+ }))
+ }
selected={details.data}
- />
+ />
</Fragment>
}
\ No newline at end of file
diff --git a/packages/frontend/src/paths/instance/orders/list/Table.tsx
b/packages/frontend/src/paths/instance/orders/list/Table.tsx
index 82b3c83..a35281d 100644
--- a/packages/frontend/src/paths/instance/orders/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/Table.tsx
@@ -127,12 +127,12 @@ function Table({ instances, onSelect, onRefund,
onCopyURL, onLoadMoreAfter, onLo
<td onClick={(): void => onSelect(i)} style={{ cursor: 'pointer'
}} >{i.summary}</td>
<td class="is-actions-cell right-sticky">
<div class="buttons is-right">
- {(i.refundable || pos === 0) &&
+ {(i.refundable) &&
<button class="button is-small is-danger jb-modal"
type="button" onClick={(): void => onRefund(i)}>
Refund
</button>
}
- {(!i.paid || pos === 0) &&
+ {(!i.paid) &&
<button class="button is-small is-info jb-modal"
type="button" onClick={(): void => onCopyURL(i)}>
copy url
</button>
@@ -192,8 +192,5 @@ export function RefundModal({ onCancel, onConfirm }:
RefundModalProps): VNode {
<InputSelector name="mainReason" values={['duplicated', 'requested by
the customer', 'other']} />
{form.mainReason && <Input<State> name="description" />}
</FormProvider>
- <div class="block">
- You are going to refund the order
- </div>
</ConfirmModal>
}
diff --git a/packages/frontend/src/paths/instance/orders/list/index.tsx
b/packages/frontend/src/paths/instance/orders/list/index.tsx
index 47f3673..f31100b 100644
--- a/packages/frontend/src/paths/instance/orders/list/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/index.tsx
@@ -29,6 +29,7 @@ import { format } from 'date-fns';
import { DatePicker } from '../../../../components/form/DatePicker';
import { NotificationCard } from '../../../../components/menu';
import { Notification } from '../../../../utils/types';
+import { copyToClipboard } from '../../../../utils/functions';
interface Props {
onUnauthorized: () => VNode;
@@ -47,7 +48,7 @@ export default function ({ onUnauthorized, onLoadError,
onCreate, onSelect, onNo
const result = useInstanceOrders(filter, setNewDate)
const { createOrder, refundOrder, getPaymentURL } = useOrderMutateAPI()
- const { currency } = useConfigContext()
+ // const { currency } = useConfigContext()
let instances: (MerchantBackend.Orders.OrderHistoryEntry & { id: string })[];
@@ -119,9 +120,7 @@ export default function ({ onUnauthorized, onLoadError,
onCreate, onSelect, onNo
<CardTable instances={instances}
onCreate={onCreate}
onSelect={(order) => onSelect(order.id)}
- onCopyURL={(id) => getPaymentURL(id).then(url => {
- navigator.clipboard.writeText(url)
- })}
+ onCopyURL={(id) => getPaymentURL(id).then(copyToClipboard)}
onRefund={(id, value) => refundOrder(id, value)
.then(() => setNotif({
message: 'refund created successfully',
diff --git a/packages/frontend/src/scss/main.scss
b/packages/frontend/src/scss/main.scss
index ac57135..369571e 100644
--- a/packages/frontend/src/scss/main.scss
+++ b/packages/frontend/src/scss/main.scss
@@ -5,22 +5,22 @@
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)
*/
-
-/* Theme style (colors & sizes) */
-@import "theme-default";
+
+ /* Theme style (colors & sizes) */
+ @import "theme-default";
/* Core Libs & Lib configs */
@import "libs/all";
@@ -48,6 +48,19 @@
@import "icons/materialdesignicons-4.9.95.min.css";
@import
"../../node_modules/@creativebulma/bulma-tooltip/dist/bulma-tooltip.min.css";
+@import "../../node_modules/bulma-timeline/dist/css/bulma-timeline.min.css";
+
+.timeline .timeline-item .timeline-content {
+ padding-top: 0;
+}
+
+.timeline .timeline-item:last-child::before {
+ display: none;
+}
+
+.timeline .timeline-item .timeline-marker {
+ top: 0;
+}
.toast {
position: absolute;
diff --git a/packages/frontend/src/utils/functions.ts
b/packages/frontend/src/utils/functions.ts
index 7550e68..4fc2815 100644
--- a/packages/frontend/src/utils/functions.ts
+++ b/packages/frontend/src/utils/functions.ts
@@ -35,3 +35,8 @@ export function onTranslationError(error: MessageError) {
if (typeof window === "undefined") return;
window.MerchantBackoffice.missing_locales =
window.MerchantBackoffice.missing_locales.concat(error.path.join())
}
+
+export async function copyToClipboard(text:string) {
+ return navigator.clipboard.writeText(text)
+}
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1d791fe..259207a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -37,6 +37,7 @@ importers:
bulma-radio: 1.1.1
bulma-responsive-tables: 1.2.3
bulma-switch-control: 1.1.1
+ bulma-timeline: 3.0.4
bulma-upload-control: 1.2.0
dotenv: 8.2.0
enzyme: 3.11.0
@@ -86,6 +87,7 @@ importers:
bulma-radio: ^1.1.1
bulma-responsive-tables: ^1.2.3
bulma-switch-control: ^1.1.1
+ bulma-timeline: ^3.0.4
bulma-upload-control: ^1.2.0
date-fns: ^2.19.0
dotenv: ^8.2.0
@@ -4966,6 +4968,10 @@ packages:
dev: true
resolution:
integrity:
sha512-uvPhLeiip1P/JZf9nidbA+7cQmUYKzS5vVbzhEAUk0oz6H3hPhHDIef/rUwqig1veRUd7vXBZ1hOcsM9gLxv/A==
+ /bulma-timeline/3.0.4:
+ dev: true
+ resolution:
+ integrity:
sha512-gCUOcSUuzHoeVMkCpLF49j5Z5yl78XQ+KgJcT+1ju5WIGgBgVytRUob/dw5NHAxPLO2rmcvwYNbCJFp7w4WT4Q==
/bulma-upload-control/1.2.0:
dependencies:
bulma: 0.9.2
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-merchant-backoffice] branch master updated: order detail,
gnunet <=