summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-06-24 11:23:27 -0300
committerSebastian <sebasjm@gmail.com>2021-06-24 11:23:37 -0300
commit0399b8a12591a0529d7b399508b9b63d811bcb93 (patch)
tree5c4f49af92e433d1bb24e032ae4326c2961fdb49 /packages
parent997bba54049f163b5a94e99aa7bed1f526334dbb (diff)
downloadmerchant-backoffice-0399b8a12591a0529d7b399508b9b63d811bcb93.tar.gz
merchant-backoffice-0399b8a12591a0529d7b399508b9b63d811bcb93.tar.bz2
merchant-backoffice-0399b8a12591a0529d7b399508b9b63d811bcb93.zip
refactored order list page to split logic / view
Diffstat (limited to 'packages')
-rw-r--r--packages/frontend/src/paths/instance/orders/list/List.stories.tsx14
-rw-r--r--packages/frontend/src/paths/instance/orders/list/ListPage.tsx146
-rw-r--r--packages/frontend/src/paths/instance/orders/list/Table.tsx1
-rw-r--r--packages/frontend/src/paths/instance/orders/list/index.tsx151
4 files changed, 201 insertions, 111 deletions
diff --git a/packages/frontend/src/paths/instance/orders/list/List.stories.tsx b/packages/frontend/src/paths/instance/orders/list/List.stories.tsx
index e791f20..996c0aa 100644
--- a/packages/frontend/src/paths/instance/orders/list/List.stories.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/List.stories.tsx
@@ -20,15 +20,25 @@
*/
import { h, VNode, FunctionalComponent } from 'preact';
-import { CardTable as TestedComponent } from './Table';
+import { ListPage as TestedComponent } from './ListPage';
export default {
title: 'Pages/Order/List',
component: TestedComponent,
argTypes: {
+ onShowAll: { action: 'onShowAll' },
+ onShowPaid: { action: 'onShowPaid' },
+ onShowRefunded: { action: 'onShowRefunded' },
+ onShowNotWired: { action: 'onShowNotWired' },
+ onCopyURL: { action: 'onCopyURL' },
+ onSelectDate: { action: 'onSelectDate' },
+ onLoadMoreBefore: { action: 'onLoadMoreBefore' },
+ onLoadMoreAfter: { action: 'onLoadMoreAfter' },
+ onSelectOrder: { action: 'onSelectOrder' },
+ onRefundOrder: { action: 'onRefundOrder' },
+ onSearchOrderById: { action: 'onSearchOrderById' },
onCreate: { action: 'onCreate' },
- goBack: { action: 'goBack' },
},
};
diff --git a/packages/frontend/src/paths/instance/orders/list/ListPage.tsx b/packages/frontend/src/paths/instance/orders/list/ListPage.tsx
new file mode 100644
index 0000000..032801b
--- /dev/null
+++ b/packages/frontend/src/paths/instance/orders/list/ListPage.tsx
@@ -0,0 +1,146 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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 { format } from 'date-fns';
+import { h, VNode } from 'preact';
+import { useState } from 'preact/hooks';
+import { DatePicker } from '../../../../components/picker/DatePicker';
+import { MerchantBackend, WithId } from '../../../../declaration';
+import { Translate, useTranslator } from '../../../../i18n';
+import { CardTable } from './Table';
+
+export interface ListPageProps {
+ errorOrderId: string | undefined,
+
+ onShowAll: () => void,
+ onShowPaid: () => void,
+ onShowRefunded: () => void,
+ onShowNotWired: () => void,
+ onCopyURL: (id: string) => void;
+ isAllActive: string,
+ isPaidActive: string,
+ isRefundedActive: string,
+ isNotWiredActive: string,
+
+ jumpToDate?: Date,
+ onSelectDate: (date?: Date) => void,
+
+ orders: (MerchantBackend.Orders.OrderHistoryEntry & WithId)[];
+ onLoadMoreBefore?: () => void;
+ hasMoreBefore?: boolean;
+ hasMoreAfter?: boolean;
+ onLoadMoreAfter?: () => void;
+
+ onSelectOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void;
+ onRefundOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void;
+ onSearchOrderById: (id: string) => void;
+ onCreate: () => void;
+}
+
+export function ListPage({ orders, errorOrderId, isAllActive, onSelectOrder, onRefundOrder, onSearchOrderById, jumpToDate, onCopyURL, onShowAll, onShowPaid, onShowRefunded, onShowNotWired, onSelectDate, isPaidActive, isRefundedActive, isNotWiredActive, onCreate }: ListPageProps): VNode {
+ const i18n = useTranslator();
+ const dateTooltip = i18n`select date to show nearby orders`;
+ const [pickDate, setPickDate] = useState(false);
+ const [orderId, setOrderId] = useState<string>('');
+
+ return <section class="section is-main-section">
+
+ <div class="level">
+ <div class="level-left">
+ <div class="level-item">
+ <div class="field has-addons">
+ <div class="control">
+ <input class={errorOrderId ? "input is-danger" : "input"} type="text" value={orderId} onChange={e => setOrderId(e.currentTarget.value)} placeholder={i18n`order id`} />
+ {errorOrderId && <p class="help is-danger">{errorOrderId}</p>}
+ </div>
+ <span class="has-tooltip-bottom" data-tooltip={i18n`jump to order with the given order ID`}>
+ <button class="button" onClick={(e) => onSearchOrderById(orderId)}>
+ <span class="icon"><i class="mdi mdi-arrow-right" /></span>
+ </button>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="columns">
+ <div class="column is-two-thirds">
+ <div class="tabs" style={{overflow:'inherit'}}>
+ <ul>
+ <li class={isAllActive}>
+ <div class="has-tooltip-right" data-tooltip={i18n`remove all filters`}>
+ <a onClick={onShowAll}><Translate>All</Translate></a>
+ </div>
+ </li>
+ <li class={isPaidActive}>
+ <div class="has-tooltip-right" data-tooltip={i18n`only show paid orders`}>
+ <a onClick={onShowPaid}><Translate>Paid</Translate></a>
+ </div>
+ </li>
+ <li class={isRefundedActive}>
+ <div class="has-tooltip-right" data-tooltip={i18n`only show orders with refunds`}>
+ <a onClick={onShowRefunded}><Translate>Refunded</Translate></a>
+ </div>
+ </li>
+ <li class={isNotWiredActive}>
+ <div class="has-tooltip-left" data-tooltip={i18n`only show orders where customers paid, but wire payments from payment provider are still pending`}>
+ <a onClick={onShowNotWired}><Translate>Not wired</Translate></a>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <div class="column ">
+ <div class="buttons is-right">
+ <div class="field has-addons">
+ {jumpToDate && <div class="control">
+ <a class="button" onClick={() => onSelectDate(undefined)}>
+ <span class="icon" data-tooltip={i18n`clear date filter`}><i class="mdi mdi-close" /></span>
+ </a>
+ </div>}
+ <div class="control">
+ <span class="has-tooltip-top" data-tooltip={dateTooltip}>
+ <input class="input" type="text" readonly value={!jumpToDate ? '' : format(jumpToDate, 'yyyy/MM/dd')} placeholder={i18n`date (YYYY/MM/DD)`} onClick={() => { setPickDate(true); }} />
+ </span>
+ </div>
+ <div class="control">
+ <span class="has-tooltip-left" data-tooltip={dateTooltip}>
+ <a class="button" onClick={() => { setPickDate(true); }}>
+ <span class="icon"><i class="mdi mdi-calendar" /></span>
+ </a>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <DatePicker
+ opened={pickDate}
+ closeFunction={() => setPickDate(false)}
+ dateReceiver={onSelectDate} />
+
+ <CardTable orders={orders}
+ onCreate={onCreate}
+ onCopyURL={onCopyURL}
+ onSelect={onSelectOrder}
+ onRefund={onRefundOrder} />
+ </section>;
+}
diff --git a/packages/frontend/src/paths/instance/orders/list/Table.tsx b/packages/frontend/src/paths/instance/orders/list/Table.tsx
index 64a5d22..50da549 100644
--- a/packages/frontend/src/paths/instance/orders/list/Table.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/Table.tsx
@@ -32,7 +32,6 @@ import { MerchantBackend, WithId } from "../../../../declaration";
import { Translate, useTranslator } from "../../../../i18n";
import { RefundSchema } from "../../../../schemas";
import { mergeRefunds } from "../../../../utils/amount";
-import { AMOUNT_ZERO_REGEX } from "../../../../utils/constants";
import { Amounts } from "@gnu-taler/taler-util";
import { useConfigContext } from "../../../../context/config";
diff --git a/packages/frontend/src/paths/instance/orders/list/index.tsx b/packages/frontend/src/paths/instance/orders/list/index.tsx
index 85ba58b..a4e8b15 100644
--- a/packages/frontend/src/paths/instance/orders/list/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/index.tsx
@@ -19,18 +19,17 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { format } from 'date-fns';
-import { h, VNode } from 'preact';
+import { h, VNode, Fragment } from 'preact';
import { useState } from 'preact/hooks';
import { Loading } from '../../../../components/exception/loading';
-import { DatePicker } from '../../../../components/form/DatePicker';
import { NotificationCard } from '../../../../components/menu';
-import { MerchantBackend } from '../../../../declaration';
+import { MerchantBackend, WithId } from '../../../../declaration';
import { HttpError } from '../../../../hooks/backend';
import { InstanceOrderFilter, useInstanceOrders, useOrderAPI, useOrderDetails } from '../../../../hooks/order';
-import { Translate, useTranslator } from '../../../../i18n';
+import { useTranslator } from '../../../../i18n';
import { Notification } from '../../../../utils/types';
-import { CardTable, RefundModal } from './Table';
+import { RefundModal } from './Table';
+import { ListPage } from './ListPage';
interface Props {
onUnauthorized: () => VNode;
@@ -40,22 +39,17 @@ interface Props {
onCreate: () => void;
}
-
export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode {
const [filter, setFilter] = useState<InstanceOrderFilter>({})
- const [pickDate, setPickDate] = useState(false)
const [orderToBeRefunded, setOrderToBeRefunded] = useState<MerchantBackend.Orders.OrderHistoryEntry | undefined>(undefined)
- const setNewDate = (date: Date) => setFilter(prev => ({ ...prev, date }))
+ const setNewDate = (date?: Date) => setFilter(prev => ({ ...prev, date }))
const result = useInstanceOrders(filter, setNewDate)
const { refundOrder, getPaymentURL } = useOrderAPI()
const [notif, setNotif] = useState<Notification | undefined>(undefined)
- const [orderId, setOrderId] = useState<string | undefined>(undefined)
- const [errorOrderId, setErrorOrderId] = useState<string | undefined>(undefined)
-
if (result.clientError && result.isUnauthorized) return onUnauthorized()
if (result.clientError && result.isNotfound) return onNotFound()
if (result.loading) return <Loading />
@@ -66,7 +60,10 @@ export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNo
const isNotWiredActive = filter.wired === 'no' ? "is-active" : ''
const isAllActive = filter.paid === undefined && filter.refunded === undefined && filter.wired === undefined ? 'is-active' : ''
- async function testIfOrderExistAndSelect() {
+ const i18n = useTranslator()
+ const [errorOrderId, setErrorOrderId] = useState<string | undefined>(undefined)
+
+ async function testIfOrderExistAndSelect(orderId: string) {
if (!orderId) {
setErrorOrderId(i18n`Enter an order id`)
return;
@@ -80,95 +77,35 @@ export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNo
}
}
- const i18n = useTranslator()
- const dateTooltip = i18n`select date to show nearby orders`
-
- return <section class="section is-main-section">
+ return <Fragment>
<NotificationCard notification={notif} />
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <div class="field has-addons">
- <div class="control">
- <input class={errorOrderId ? "input is-danger" : "input"} type="text" value={orderId} onChange={e => setOrderId(e.currentTarget.value)} placeholder={i18n`order id`} />
- {errorOrderId && <p class="help is-danger">{errorOrderId}</p>}
- </div>
- <span class="has-tooltip-bottom" data-tooltip={i18n`jump to order with the given order ID`}>
- <button class="button" onClick={testIfOrderExistAndSelect}>
- <span class="icon"><i class="mdi mdi-arrow-right" /></span>
- </button>
- </span>
- </div>
- </div>
- </div>
- </div>
- <div class="columns">
- <div class="column">
- <div class="tabs">
- <ul>
- <li class={isAllActive}>
- <div class="has-tooltip-right" data-tooltip={i18n`remove all filters`}>
- <a onClick={() => setFilter({})}><Translate>All</Translate></a>
- </div>
- </li>
- <li class={isPaidActive}>
- <div class="has-tooltip-right" data-tooltip={i18n`only show paid orders`}>
- <a onClick={() => setFilter({ paid: 'yes' })}><Translate>Paid</Translate></a>
- </div>
- </li>
- <li class={isRefundedActive}>
- <div class="has-tooltip-right" data-tooltip={i18n`only show orders with refunds`}>
- <a onClick={() => setFilter({ refunded: 'yes' })}><Translate>Refunded</Translate></a>
- </div>
- </li>
- <li class={isNotWiredActive}>
- <div class="has-tooltip-left" data-tooltip={i18n`only show orders where customers paid, but wire payments from payment provider are still pending`}>
- <a onClick={() => setFilter({ wired: 'no' })}><Translate>Not wired</Translate></a>
- </div>
- </li>
- </ul>
- </div>
- </div>
- <div class="column ">
- <div class="buttons is-right">
- <div class="field has-addons">
- {filter.date && <div class="control">
- <a class="button" onClick={() => { setFilter(prev => ({ ...prev, date: undefined })) }}>
- <span class="icon" data-tooltip={i18n`clear date filter`}><i class="mdi mdi-close" /></span>
- </a>
- </div>}
- <div class="control">
- <span class="has-tooltip-top" data-tooltip={dateTooltip}>
- <input class="input" type="text" readonly value={!filter.date ? '' : format(filter.date, 'yyyy/MM/dd')} placeholder={i18n`date (YYYY/MM/DD)`} onClick={() => { setPickDate(true) }} />
- </span>
- </div>
- <div class="control">
- <span class="has-tooltip-left" data-tooltip={dateTooltip}>
- <a class="button" onClick={() => { setPickDate(true) }}>
- <span class="icon"><i class="mdi mdi-calendar" /></span>
- </a>
- </span>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <DatePicker
- opened={pickDate}
- closeFunction={() => setPickDate(false)}
- dateReceiver={setNewDate}
- />
-
- <CardTable orders={result.data.orders.map(o => ({ ...o, id: o.order_id }))}
- onCreate={onCreate}
- onSelect={(order) => onSelect(order.id)}
- onCopyURL={(id) => getPaymentURL(id).then((resp) => copyToClipboard(resp.data))}
- onRefund={(value) => setOrderToBeRefunded(value)}
+ <ListPage
+ orders={result.data.orders.map(o => ({ ...o, id: o.order_id }))}
onLoadMoreBefore={result.loadMorePrev} hasMoreBefore={!result.isReachingStart}
onLoadMoreAfter={result.loadMore} hasMoreAfter={!result.isReachingEnd}
+
+ onSelectOrder={(order) => onSelect(order.id)}
+ onRefundOrder={(value) => setOrderToBeRefunded(value)}
+
+ errorOrderId={errorOrderId}
+ isAllActive={isAllActive}
+ isNotWiredActive={isNotWiredActive}
+ isPaidActive={isPaidActive}
+ isRefundedActive={isRefundedActive}
+ jumpToDate={filter.date}
+ onCopyURL={(id) => getPaymentURL(id).then((resp) => copyToClipboard(resp.data))}
+
+ onCreate={onCreate}
+ onSearchOrderById={testIfOrderExistAndSelect}
+ onSelectDate={setNewDate}
+ onShowAll={() => setFilter({})}
+ onShowNotWired={() => setFilter({ paid: 'yes' })}
+ onShowPaid={() => setFilter({ refunded: 'yes' })}
+ onShowRefunded={() => setFilter({ wired: 'no' })}
+
/>
+
{orderToBeRefunded && <RefundModalForTable
id={orderToBeRefunded.order_id}
onCancel={() => setOrderToBeRefunded(undefined)}
@@ -182,16 +119,15 @@ export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNo
type: "ERROR",
description: error.message
}))
- .then(() => setOrderToBeRefunded(undefined))
- }
+ .then(() => setOrderToBeRefunded(undefined))}
onLoadError={(error) => {
setNotif({
message: i18n`could not create the refund`,
type: "ERROR",
description: error.message
- })
- setOrderToBeRefunded(undefined)
- return <div />
+ });
+ setOrderToBeRefunded(undefined);
+ return <div />;
}}
onUnauthorized={onUnauthorized}
onNotFound={() => {
@@ -199,12 +135,11 @@ export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNo
message: i18n`could not get the order to refund`,
type: "ERROR",
// description: error.message
- })
- setOrderToBeRefunded(undefined)
- return <div />
- }}
- />}
- </section>
+ });
+ setOrderToBeRefunded(undefined);
+ return <div />;
+ }} />}
+ </Fragment>
}
interface RefundProps {