summaryrefslogtreecommitdiff
path: root/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx')
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx139
1 files changed, 67 insertions, 72 deletions
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 02d5b0bfc..5ece34409 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
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2021 Taler Systems S.A.
+ (C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -19,9 +19,12 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util";
+import {
+ useTranslationContext
+} from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
-import { h, VNode } from "preact";
+import { VNode, h } from "preact";
import { StateUpdater, useState } from "preact/hooks";
import {
FormErrors,
@@ -32,12 +35,14 @@ import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputGroup } from "../../../../components/form/InputGroup.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { ConfirmModal } from "../../../../components/modal/index.js";
-import { useConfigContext } from "../../../../context/config.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { Translate, useTranslator } from "../../../../i18n/index.js";
+import { useSessionContext } from "../../../../context/session.js";
+import {
+ datetimeFormatForSettings,
+ usePreference,
+} from "../../../../hooks/preference.js";
import { mergeRefunds } from "../../../../utils/amount.js";
-type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId;
+type Entity = TalerMerchantApi.OrderHistoryEntry & WithId;
interface Props {
orders: Entity[];
onRefund: (value: Entity) => void;
@@ -45,8 +50,6 @@ interface Props {
onCreate: () => void;
onSelect: (order: Entity) => void;
onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
onLoadMoreAfter?: () => void;
}
@@ -58,12 +61,10 @@ export function CardTable({
onSelect,
onLoadMoreAfter,
onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
}: Props): VNode {
const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
- const i18n = useTranslator();
+ const { i18n } = useTranslationContext();
return (
<div class="card has-table">
@@ -72,13 +73,13 @@ export function CardTable({
<span class="icon">
<i class="mdi mdi-cash-register" />
</span>
- <Translate>Orders</Translate>
+ <i18n.Translate>Orders</i18n.Translate>
</p>
<div class="card-header-icon" aria-label="more options" />
<div class="card-header-icon" aria-label="more options">
- <span class="has-tooltip-left" data-tooltip={i18n`create order`}>
+ <span class="has-tooltip-left" data-tooltip={i18n.str`create order`}>
<button class="button is-info" type="button" onClick={onCreate}>
<span class="icon is-small">
<i class="mdi mdi-plus mdi-36px" />
@@ -100,8 +101,6 @@ export function CardTable({
rowSelectionHandler={rowSelectionHandler}
onLoadMoreAfter={onLoadMoreAfter}
onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
/>
) : (
<EmptyTable />
@@ -120,8 +119,6 @@ interface TableProps {
onSelect: (id: Entity) => void;
rowSelectionHandler: StateUpdater<string[]>;
onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
onLoadMoreAfter?: () => void;
}
@@ -132,31 +129,27 @@ function Table({
onCopyURL,
onLoadMoreAfter,
onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
}: TableProps): VNode {
+ const { i18n } = useTranslationContext();
+ const [settings] = usePreference();
return (
<div class="table-container">
{onLoadMoreBefore && (
- <button
- class="button is-fullwidth"
- disabled={!hasMoreBefore}
- onClick={onLoadMoreBefore}
- >
- <Translate>load newer orders</Translate>
+ <button class="button is-fullwidth" onClick={onLoadMoreBefore}>
+ <i18n.Translate>load first page</i18n.Translate>
</button>
)}
<table class="table is-striped is-hoverable is-fullwidth">
<thead>
<tr>
<th style={{ minWidth: 100 }}>
- <Translate>Date</Translate>
+ <i18n.Translate>Date</i18n.Translate>
</th>
<th style={{ minWidth: 100 }}>
- <Translate>Amount</Translate>
+ <i18n.Translate>Amount</i18n.Translate>
</th>
<th style={{ minWidth: 400 }}>
- <Translate>Summary</Translate>
+ <i18n.Translate>Summary</i18n.Translate>
</th>
<th style={{ minWidth: 50 }} />
</tr>
@@ -173,7 +166,7 @@ function Table({
? "never"
: format(
new Date(i.timestamp.t_s * 1000),
- "yyyy/MM/dd HH:mm:ss"
+ datetimeFormatForSettings(settings),
)}
</td>
<td
@@ -196,7 +189,7 @@ function Table({
type="button"
onClick={(): void => onRefund(i)}
>
- <Translate>Refund</Translate>
+ <i18n.Translate>Refund</i18n.Translate>
</button>
)}
{!i.paid && (
@@ -205,7 +198,7 @@ function Table({
type="button"
onClick={(): void => onCopyURL(i)}
>
- <Translate>copy url</Translate>
+ <i18n.Translate>copy url</i18n.Translate>
</button>
)}
</div>
@@ -216,12 +209,10 @@ function Table({
</tbody>
</table>
{onLoadMoreAfter && (
- <button
- class="button is-fullwidth"
- disabled={!hasMoreAfter}
- onClick={onLoadMoreAfter}
- >
- <Translate>load older orders</Translate>
+ <button class="button is-fullwidth"
+ data-tooltip={i18n.str`load more orders after the last one`}
+ onClick={onLoadMoreAfter}>
+ <i18n.Translate>load next page</i18n.Translate>
</button>
)}
</div>
@@ -229,15 +220,18 @@ function Table({
}
function EmptyTable(): VNode {
+ const { i18n } = useTranslationContext();
return (
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
+ <i class="mdi mdi-magnify mdi-48px" />
</span>
</p>
<p>
- <Translate>No orders have been found matching your query!</Translate>
+ <i18n.Translate>
+ No orders have been found matching your query!
+ </i18n.Translate>
</p>
</div>
);
@@ -245,8 +239,8 @@ function EmptyTable(): VNode {
interface RefundModalProps {
onCancel: () => void;
- onConfirm: (value: MerchantBackend.Orders.RefundRequest) => void;
- order: MerchantBackend.Orders.MerchantOrderStatusResponse;
+ onConfirm: (value: TalerMerchantApi.RefundRequest) => void;
+ order: TalerMerchantApi.MerchantOrderStatusResponse;
}
export function RefundModal({
@@ -256,19 +250,20 @@ export function RefundModal({
}: RefundModalProps): VNode {
type State = { mainReason?: string; description?: string; refund?: string };
const [form, setValue] = useState<State>({});
- const i18n = useTranslator();
+ const [settings] = usePreference();
+ const { i18n } = useTranslationContext();
// const [errors, setErrors] = useState<FormErrors<State>>({});
const refunds = (
order.order_status === "paid" ? order.refund_details : []
).reduce(mergeRefunds, []);
- const config = useConfigContext();
+ const { config } = useSessionContext();
const totalRefunded = refunds
.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"
@@ -277,28 +272,28 @@ export function RefundModal({
const totalRefundable = !orderPrice
? Amounts.zeroOfCurrency(totalRefunded.currency)
: refunds.length
- ? Amounts.sub(orderPrice, totalRefunded).amount
- : orderPrice;
+ ? Amounts.sub(orderPrice, totalRefunded).amount
+ : orderPrice;
const isRefundable = Amounts.isNonZero(totalRefundable);
- const duplicatedText = i18n`duplicated`;
+ const duplicatedText = i18n.str`duplicated`;
const errors: FormErrors<State> = {
- mainReason: !form.mainReason ? i18n`required` : undefined,
+ mainReason: !form.mainReason ? i18n.str`required` : undefined,
description:
!form.description && form.mainReason !== duplicatedText
- ? i18n`required`
+ ? i18n.str`required`
: undefined,
refund: !form.refund
- ? i18n`required`
+ ? i18n.str`required`
: !Amounts.parse(form.refund)
- ? i18n`invalid format`
- : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
- ? i18n`this value exceed the refundable amount`
- : undefined,
+ ? i18n.str`invalid format`
+ : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
+ ? i18n.str`this value exceed the refundable amount`
+ : undefined,
};
const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined
+ (k) => (errors as Record<string, unknown>)[k] !== undefined,
);
const validateAndConfirm = () => {
@@ -306,7 +301,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
@@ -339,13 +334,13 @@ export function RefundModal({
<thead>
<tr>
<th>
- <Translate>date</Translate>
+ <i18n.Translate>date</i18n.Translate>
</th>
<th>
- <Translate>amount</Translate>
+ <i18n.Translate>amount</i18n.Translate>
</th>
<th>
- <Translate>reason</Translate>
+ <i18n.Translate>reason</i18n.Translate>
</th>
</tr>
</thead>
@@ -358,7 +353,7 @@ export function RefundModal({
? "never"
: format(
new Date(r.timestamp.t_s * 1000),
- "yyyy-MM-dd HH:mm:ss"
+ datetimeFormatForSettings(settings),
)}
</td>
<td>{r.amount}</td>
@@ -377,32 +372,32 @@ export function RefundModal({
<FormProvider<State>
errors={errors}
object={form}
- valueHandler={(d) => setValue(d as any)}
+ valueHandler={(d) => setValue(d)}
>
<InputCurrency<State>
name="refund"
- label={i18n`Refund`}
- tooltip={i18n`amount to be refunded`}
+ label={i18n.str`Refund`}
+ tooltip={i18n.str`amount to be refunded`}
>
- <Translate>Max refundable:</Translate>{" "}
+ <i18n.Translate>Max refundable:</i18n.Translate>{" "}
{Amounts.stringify(totalRefundable)}
</InputCurrency>
<InputSelector
name="mainReason"
- label={i18n`Reason`}
+ label={i18n.str`Reason`}
values={[
- i18n`Choose one...`,
+ i18n.str`Choose one...`,
duplicatedText,
- i18n`requested by the customer`,
- i18n`other`,
+ i18n.str`requested by the customer`,
+ i18n.str`other`,
]}
- tooltip={i18n`why this order is being refunded`}
+ tooltip={i18n.str`why this order is being refunded`}
/>
{form.mainReason && form.mainReason !== duplicatedText ? (
<Input<State>
- label={i18n`Description`}
+ label={i18n.str`Description`}
name="description"
- tooltip={i18n`more information to give context`}
+ tooltip={i18n.str`more information to give context`}
/>
) : undefined}
</FormProvider>