diff options
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/orders')
11 files changed, 502 insertions, 335 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx index 4ce2eb43d..5f8dbbdd9 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx @@ -33,7 +33,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx index 379c5eab5..145df717d 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx @@ -166,7 +166,7 @@ export function CreatePage({ : value.payments.wire_transfer_deadline && isBefore( value.payments.wire_transfer_deadline, - value.payments.refund_deadline + value.payments.refund_deadline, ) ? i18n`wire transfer deadline cannot be before refund deadline` : undefined, @@ -177,7 +177,7 @@ export function CreatePage({ : value.payments.wire_transfer_deadline && isBefore( value.payments.wire_transfer_deadline, - value.payments.pay_deadline + value.payments.pay_deadline, ) ? i18n`wire transfer deadline cannot be before pay deadline` : undefined, @@ -189,7 +189,7 @@ export function CreatePage({ ? i18n`should have a refund deadline` : !isAfter( value.payments.refund_deadline, - value.payments.auto_refund_deadline + value.payments.auto_refund_deadline, ) ? i18n`auto refund cannot be after refund deadline` : undefined, @@ -203,7 +203,7 @@ export function CreatePage({ }), }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const submit = (): void => { @@ -225,7 +225,7 @@ export function CreatePage({ wire_transfer_deadline: value.payments.wire_transfer_deadline ? { t_s: Math.floor( - value.payments.wire_transfer_deadline.getTime() / 1000 + value.payments.wire_transfer_deadline.getTime() / 1000, ), } : undefined, @@ -237,7 +237,7 @@ export function CreatePage({ auto_refund: value.payments.auto_refund_deadline ? { d_us: Math.floor( - value.payments.auto_refund_deadline.getTime() * 1000 + value.payments.auto_refund_deadline.getTime() * 1000, ), } : undefined, @@ -264,7 +264,7 @@ export function CreatePage({ const addProductToTheInventoryList = ( product: MerchantBackend.Products.ProductDetail & WithId, - quantity: number + quantity: number, ) => { valueHandler((v) => { const inventoryProducts = { ...v.inventoryProducts }; @@ -332,13 +332,13 @@ export function CreatePage({ const discountOrRise = rate( value.pricing?.order_price || `${config.currency}:0`, - totalAsString + totalAsString, ); const minAgeByProducts = allProducts.reduce( (cur, prev) => !prev.minimum_age || cur > prev.minimum_age ? cur : prev.minimum_age, - 0 + 0, ); return ( <div> @@ -415,7 +415,7 @@ export function CreatePage({ discountOrRise > 0 && (discountOrRise < 1 ? `discount of %${Math.round( - (1 - discountOrRise) * 100 + (1 - discountOrRise) * 100, )}` : `rise of %${Math.round((discountOrRise - 1) * 100)}`) } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx index bd63ca371..6d3ac311a 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx @@ -26,64 +26,89 @@ interface Props { onCreateAnother?: () => void; } -export function OrderCreatedSuccessfully({ entity, onConfirm, onCreateAnother }: Props): VNode { - const { getPaymentURL } = useOrderAPI() - const [url, setURL] = useState<string | undefined>(undefined) +export function OrderCreatedSuccessfully({ + entity, + onConfirm, + onCreateAnother, +}: Props): VNode { + const { getPaymentURL } = useOrderAPI(); + const [url, setURL] = useState<string | undefined>(undefined); useEffect(() => { - getPaymentURL(entity.response.order_id).then(response => { - setURL(response.data) - }) - }, [getPaymentURL, entity.response.order_id]) + getPaymentURL(entity.response.order_id).then((response) => { + setURL(response.data); + }); + }, [getPaymentURL, entity.response.order_id]); - return <CreatedSuccessfully onConfirm={onConfirm} onCreateAnother={onCreateAnother}> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Amount</Translate></label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.request.order.amount} /> - </p> + return ( + <CreatedSuccessfully + onConfirm={onConfirm} + onCreateAnother={onCreateAnother} + > + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Amount</Translate> + </label> </div> - </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Summary</Translate></label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.request.order.summary} /> - </p> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={entity.request.order.amount} + /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Order ID</Translate></label> - </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={entity.response.order_id} /> - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Summary</Translate> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input + class="input" + readonly + value={entity.request.order.summary} + /> + </p> + </div> </div> </div> - </div> - <div class="field is-horizontal"> - <div class="field-label is-normal"> - <label class="label"><Translate>Payment URL</Translate></label> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Order ID</Translate> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={entity.response.order_id} /> + </p> + </div> + </div> </div> - <div class="field-body is-flex-grow-3"> - <div class="field"> - <p class="control"> - <input class="input" readonly value={url} /> - </p> + <div class="field is-horizontal"> + <div class="field-label is-normal"> + <label class="label"> + <Translate>Payment URL</Translate> + </label> + </div> + <div class="field-body is-flex-grow-3"> + <div class="field"> + <p class="control"> + <input class="input" readonly value={url} /> + </p> + </div> </div> </div> - </div> - </CreatedSuccessfully>; + </CreatedSuccessfully> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx index feb75aa25..95232da92 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx @@ -15,12 +15,12 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { Fragment, h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +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"; @@ -33,9 +33,9 @@ import { CreatePage } from "./CreatePage.js"; import { OrderCreatedSuccessfully } from "./OrderCreatedSuccessfully.js"; export type Entity = { - request: MerchantBackend.Orders.PostOrderRequest, - response: MerchantBackend.Orders.PostOrderResponse -} + request: MerchantBackend.Orders.PostOrderRequest; + response: MerchantBackend.Orders.PostOrderResponse; +}; interface Props { onBack?: () => void; onConfirm: () => void; @@ -43,40 +43,53 @@ interface Props { onNotFound: () => VNode; onLoadError: (error: HttpError) => VNode; } -export default function OrderCreate({ onConfirm, onBack, onLoadError, onNotFound, onUnauthorized }: Props): VNode { - const { createOrder } = useOrderAPI() - const [notif, setNotif] = useState<Notification | undefined>(undefined) +export default function OrderCreate({ + onConfirm, + onBack, + onLoadError, + onNotFound, + onUnauthorized, +}: Props): VNode { + const { createOrder } = useOrderAPI(); + const [notif, setNotif] = useState<Notification | undefined>(undefined); - const detailsResult = useInstanceDetails() - const inventoryResult = useInstanceProducts() + const detailsResult = useInstanceDetails(); + const inventoryResult = useInstanceProducts(); - if (detailsResult.clientError && detailsResult.isUnauthorized) return onUnauthorized() - if (detailsResult.clientError && detailsResult.isNotfound) return onNotFound() - if (detailsResult.loading) return <Loading /> - if (!detailsResult.ok) return onLoadError(detailsResult) + if (detailsResult.clientError && detailsResult.isUnauthorized) + return onUnauthorized(); + if (detailsResult.clientError && detailsResult.isNotfound) + return onNotFound(); + if (detailsResult.loading) return <Loading />; + if (!detailsResult.ok) return onLoadError(detailsResult); - if (inventoryResult.clientError && inventoryResult.isUnauthorized) return onUnauthorized() - if (inventoryResult.clientError && inventoryResult.isNotfound) return onNotFound() - if (inventoryResult.loading) return <Loading /> - if (!inventoryResult.ok) return onLoadError(inventoryResult) + if (inventoryResult.clientError && inventoryResult.isUnauthorized) + return onUnauthorized(); + if (inventoryResult.clientError && inventoryResult.isNotfound) + return onNotFound(); + if (inventoryResult.loading) return <Loading />; + if (!inventoryResult.ok) return onLoadError(inventoryResult); - return <Fragment> - - <NotificationCard notification={notif} /> + return ( + <Fragment> + <NotificationCard notification={notif} /> - <CreatePage - onBack={onBack} - onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => { - createOrder(request).then(onConfirm).catch((error) => { - setNotif({ - message: 'could not create order', - type: "ERROR", - description: error.message - }) - }) - }} - instanceConfig={detailsResult.data} - instanceInventory={inventoryResult.data} + <CreatePage + onBack={onBack} + onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => { + createOrder(request) + .then(onConfirm) + .catch((error) => { + setNotif({ + message: "could not create order", + type: "ERROR", + description: error.message, + }); + }); + }} + instanceConfig={detailsResult.data} + instanceInventory={inventoryResult.data} /> - </Fragment> + </Fragment> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx index 878ee7bde..e430ede56 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx @@ -35,7 +35,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx index 2074eeb32..e8927dd70 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx @@ -248,7 +248,7 @@ function ClaimedPage({ </b>{" "} {format( new Date(order.contract_terms.timestamp.t_s * 1000), - "yyyy-MM-dd HH:mm:ss" + "yyyy-MM-dd HH:mm:ss", )} </p> </div> @@ -519,7 +519,7 @@ function PaidPage({ <p> {format( new Date(order.contract_terms.timestamp.t_s * 1000), - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", )} </p> </div> @@ -669,7 +669,7 @@ function UnpaidPage({ ? "never" : format( new Date(order.creation_time.t_s * 1000), - "yyyy-MM-dd HH:mm:ss" + "yyyy-MM-dd HH:mm:ss", )} </p> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx index dbf1b685a..4633688ba 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx @@ -32,36 +32,49 @@ export interface Props { onLoadError: (error: HttpError) => VNode; } -export default function Update({ oid, onBack, onLoadError, onNotFound, onUnauthorized }: Props): 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 result = useOrderDetails(oid); + const [notif, setNotif] = useState<Notification | undefined>(undefined); - const i18n = useTranslator() + const i18n = useTranslator(); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <Fragment> + return ( + <Fragment> + <NotificationCard notification={notif} /> - <NotificationCard notification={notif} /> - - <DetailPage - onBack={onBack} - id={oid} - onRefund={(id, value) => refundOrder(id, value) - .then(() => setNotif({ - message: i18n`refund created successfully`, - type: "SUCCESS" - })).catch((error) => setNotif({ - message: i18n`could not create the refund`, - type: "ERROR", - description: error.message - })) - } - selected={result.data} - /> - </Fragment> -}
\ No newline at end of file + <DetailPage + onBack={onBack} + id={oid} + onRefund={(id, value) => + refundOrder(id, value) + .then(() => + setNotif({ + message: i18n`refund created successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message, + }), + ) + } + selected={result.data} + /> + </Fragment> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx index 8cddd7fd6..156c577f4 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx @@ -43,7 +43,7 @@ export default { function createExample<Props>( Component: FunctionalComponent<Props>, - props: Partial<Props> + props: Partial<Props>, ) { const r = (args: any) => <Component {...args} />; r.args = props; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx index 60be23c21..bca90e352 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx @@ -15,33 +15,33 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { format } from 'date-fns'; -import { h, VNode } from 'preact'; -import { useState } from 'preact/hooks'; +import { format } from "date-fns"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { DatePicker } from "../../../../components/picker/DatePicker.js"; import { MerchantBackend, WithId } from "../../../../declaration.js"; -import { Translate, useTranslator } from '../../../../i18n/index.js'; +import { Translate, useTranslator } from "../../../../i18n/index.js"; import { CardTable } from "./Table.js"; export interface ListPageProps { - errorOrderId: string | undefined, + errorOrderId: string | undefined; - onShowAll: () => void, - onShowPaid: () => void, - onShowRefunded: () => void, - onShowNotWired: () => void, + onShowAll: () => void; + onShowPaid: () => void; + onShowRefunded: () => void; + onShowNotWired: () => void; onCopyURL: (id: string) => void; - isAllActive: string, - isPaidActive: string, - isRefundedActive: string, - isNotWiredActive: string, + isAllActive: string; + isPaidActive: string; + isRefundedActive: string; + isNotWiredActive: string; - jumpToDate?: Date, - onSelectDate: (date?: Date) => void, + jumpToDate?: Date; + onSelectDate: (date?: Date) => void; orders: (MerchantBackend.Orders.OrderHistoryEntry & WithId)[]; onLoadMoreBefore?: () => void; @@ -55,92 +55,168 @@ export interface ListPageProps { onCreate: () => void; } -export function ListPage({ orders, errorOrderId, isAllActive, onSelectOrder, onRefundOrder, onSearchOrderById, jumpToDate, onCopyURL, onShowAll, onShowPaid, onShowRefunded, onShowNotWired, onSelectDate, isPaidActive, isRefundedActive, isNotWiredActive, onCreate }: ListPageProps): VNode { +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"> + const [orderId, setOrderId] = useState<string>(""); - <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>} + 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> - <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 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> - </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 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> - </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} /> + <DatePicker + opened={pickDate} + closeFunction={() => setPickDate(false)} + dateReceiver={onSelectDate} + /> - <CardTable orders={orders} - onCreate={onCreate} - onCopyURL={onCopyURL} - onSelect={onSelectOrder} - onRefund={onRefundOrder} /> - </section>; + <CardTable + orders={orders} + onCreate={onCreate} + onCopyURL={onCopyURL} + onSelect={onSelectOrder} + onRefund={onRefundOrder} + /> + </section> + ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx index 924d09682..a1ec8d291 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx @@ -173,7 +173,7 @@ function Table({ ? "never" : format( new Date(i.timestamp.t_s * 1000), - "yyyy/MM/dd HH:mm:ss" + "yyyy/MM/dd HH:mm:ss", )} </td> <td @@ -268,7 +268,7 @@ export function RefundModal({ .map((r) => r.amount) .reduce( (p, c) => Amounts.add(p, Amounts.parseOrThrow(c)).amount, - Amounts.zeroOfCurrency(config.currency) + Amounts.zeroOfCurrency(config.currency), ); const orderPrice = order.order_status === "paid" @@ -298,7 +298,7 @@ export function RefundModal({ : undefined, }; const hasErrors = Object.keys(errors).some( - (k) => (errors as any)[k] !== undefined + (k) => (errors as any)[k] !== undefined, ); const validateAndConfirm = () => { @@ -306,7 +306,7 @@ export function RefundModal({ if (!form.refund) return; onConfirm({ refund: Amounts.stringify( - Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount + Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount, ), reason: form.description === undefined @@ -358,7 +358,7 @@ export function RefundModal({ ? "never" : format( new Date(r.timestamp.t_s * 1000), - "yyyy-MM-dd HH:mm:ss" + "yyyy-MM-dd HH:mm:ss", )} </td> <td>{r.amount}</td> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx index a033e7b3a..b5fe7611c 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx @@ -15,18 +15,23 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { h, VNode, Fragment } from 'preact'; -import { useState } from 'preact/hooks'; +import { h, VNode, Fragment } from "preact"; +import { useState } from "preact/hooks"; import { Loading } from "../../../../components/exception/loading.js"; import { NotificationCard } from "../../../../components/menu/index.js"; import { MerchantBackend, WithId } from "../../../../declaration.js"; import { HttpError } from "../../../../hooks/backend.js"; -import { InstanceOrderFilter, useInstanceOrders, useOrderAPI, useOrderDetails } from "../../../../hooks/order.js"; -import { useTranslator } from '../../../../i18n/index.js'; +import { + InstanceOrderFilter, + useInstanceOrders, + useOrderAPI, + useOrderDetails, +} from "../../../../hooks/order.js"; +import { useTranslator } from "../../../../i18n/index.js"; import { Notification } from "../../../../utils/types.js"; import { RefundModal } from "./Table.js"; import { ListPage } from "./ListPage.js"; @@ -39,107 +44,133 @@ interface Props { onCreate: () => void; } -export default function ({ onUnauthorized, onLoadError, onCreate, onSelect, onNotFound }: Props): VNode { - const [filter, setFilter] = useState<InstanceOrderFilter>({}) - const [orderToBeRefunded, setOrderToBeRefunded] = useState<MerchantBackend.Orders.OrderHistoryEntry | undefined>(undefined) - - const setNewDate = (date?: Date) => setFilter(prev => ({ ...prev, date })) - - const result = useInstanceOrders(filter, setNewDate) - const { refundOrder, getPaymentURL } = useOrderAPI() - - const [notif, setNotif] = useState<Notification | undefined>(undefined) - - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) - - const isPaidActive = filter.paid === 'yes' ? "is-active" : '' - const isRefundedActive = filter.refunded === 'yes' ? "is-active" : '' - const isNotWiredActive = filter.wired === 'no' ? "is-active" : '' - const isAllActive = filter.paid === undefined && filter.refunded === undefined && filter.wired === undefined ? 'is-active' : '' - - const i18n = useTranslator() - const [errorOrderId, setErrorOrderId] = useState<string | undefined>(undefined) +export default function ({ + onUnauthorized, + onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { + const [filter, setFilter] = useState<InstanceOrderFilter>({}); + const [orderToBeRefunded, setOrderToBeRefunded] = useState< + MerchantBackend.Orders.OrderHistoryEntry | undefined + >(undefined); + + const setNewDate = (date?: Date) => setFilter((prev) => ({ ...prev, date })); + + const result = useInstanceOrders(filter, setNewDate); + const { refundOrder, getPaymentURL } = useOrderAPI(); + + const [notif, setNotif] = useState<Notification | undefined>(undefined); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); + + const isPaidActive = filter.paid === "yes" ? "is-active" : ""; + const isRefundedActive = filter.refunded === "yes" ? "is-active" : ""; + const isNotWiredActive = filter.wired === "no" ? "is-active" : ""; + const isAllActive = + filter.paid === undefined && + filter.refunded === undefined && + filter.wired === undefined + ? "is-active" + : ""; + + const i18n = useTranslator(); + const [errorOrderId, setErrorOrderId] = useState<string | undefined>( + undefined, + ); async function testIfOrderExistAndSelect(orderId: string) { if (!orderId) { - setErrorOrderId(i18n`Enter an order id`) + setErrorOrderId(i18n`Enter an order id`); return; } try { - await getPaymentURL(orderId) - onSelect(orderId) - setErrorOrderId(undefined) + await getPaymentURL(orderId); + onSelect(orderId); + setErrorOrderId(undefined); } catch { - setErrorOrderId(i18n`order not found`) + setErrorOrderId(i18n`order not found`); } } - return <Fragment> - <NotificationCard notification={notif} /> - - <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({})} - onShowPaid={() => setFilter({ paid: 'yes' })} - onShowRefunded={() => setFilter({ refunded: 'yes' })} - onShowNotWired={() => setFilter({ wired: 'no' })} - - /> - - {orderToBeRefunded && <RefundModalForTable - id={orderToBeRefunded.order_id} - onCancel={() => setOrderToBeRefunded(undefined)} - onConfirm={(value) => refundOrder(orderToBeRefunded.order_id, value) - .then(() => setNotif({ - message: i18n`refund created successfully`, - type: "SUCCESS" - })) - .catch((error) => setNotif({ - message: i18n`could not create the refund`, - type: "ERROR", - description: error.message - })) - .then(() => setOrderToBeRefunded(undefined))} - onLoadError={(error) => { - setNotif({ - message: i18n`could not create the refund`, - type: "ERROR", - description: error.message - }); - setOrderToBeRefunded(undefined); - return <div />; - }} - onUnauthorized={onUnauthorized} - onNotFound={() => { - setNotif({ - message: i18n`could not get the order to refund`, - type: "ERROR", - // description: error.message - }); - setOrderToBeRefunded(undefined); - return <div />; - }} />} - </Fragment> + return ( + <Fragment> + <NotificationCard notification={notif} /> + + <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({})} + onShowPaid={() => setFilter({ paid: "yes" })} + onShowRefunded={() => setFilter({ refunded: "yes" })} + onShowNotWired={() => setFilter({ wired: "no" })} + /> + + {orderToBeRefunded && ( + <RefundModalForTable + id={orderToBeRefunded.order_id} + onCancel={() => setOrderToBeRefunded(undefined)} + onConfirm={(value) => + refundOrder(orderToBeRefunded.order_id, value) + .then(() => + setNotif({ + message: i18n`refund created successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message, + }), + ) + .then(() => setOrderToBeRefunded(undefined)) + } + onLoadError={(error) => { + setNotif({ + message: i18n`could not create the refund`, + type: "ERROR", + description: error.message, + }); + setOrderToBeRefunded(undefined); + return <div />; + }} + onUnauthorized={onUnauthorized} + onNotFound={() => { + setNotif({ + message: i18n`could not get the order to refund`, + type: "ERROR", + // description: error.message + }); + setOrderToBeRefunded(undefined); + return <div />; + }} + /> + )} + </Fragment> + ); } interface RefundProps { @@ -151,21 +182,30 @@ interface RefundProps { onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void; } -function RefundModalForTable({ id, onUnauthorized, onLoadError, onNotFound, onConfirm, onCancel }: RefundProps) { +function RefundModalForTable({ + id, + onUnauthorized, + onLoadError, + onNotFound, + onConfirm, + onCancel, +}: RefundProps) { const result = useOrderDetails(id); - if (result.clientError && result.isUnauthorized) return onUnauthorized() - if (result.clientError && result.isNotfound) return onNotFound() - if (result.loading) return <Loading /> - if (!result.ok) return onLoadError(result) + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return <Loading />; + if (!result.ok) return onLoadError(result); - return <RefundModal - order={result.data} - onCancel={onCancel} - onConfirm={onConfirm} - /> + return ( + <RefundModal + order={result.data} + onCancel={onCancel} + onConfirm={onConfirm} + /> + ); } async function copyToClipboard(text: string) { - return navigator.clipboard.writeText(text) + return navigator.clipboard.writeText(text); } |