diff options
author | Sebastian <sebasjm@gmail.com> | 2021-06-24 11:23:27 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-06-24 11:23:37 -0300 |
commit | 0399b8a12591a0529d7b399508b9b63d811bcb93 (patch) | |
tree | 5c4f49af92e433d1bb24e032ae4326c2961fdb49 /packages | |
parent | 997bba54049f163b5a94e99aa7bed1f526334dbb (diff) | |
download | merchant-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')
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 { |