diff --git a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
new file mode 100644
index 000000000..61ddf3c84
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
@@ -0,0 +1,91 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+import { h, VNode } from "preact";
+import { InputProps, useField } from "./useField.js";
+
+interface Props extends InputProps {
+ name: T;
+ readonly?: boolean;
+ expand?: boolean;
+ threeState?: boolean;
+ toBoolean?: (v?: any) => boolean | undefined;
+ fromBoolean?: (s: boolean | undefined) => any;
+}
+
+const defaultToBoolean = (f?: any): boolean | undefined => f || "";
+const defaultFromBoolean = (v: boolean | undefined): any => v as any;
+
+export function InputToggle({
+ name,
+ readonly,
+ placeholder,
+ tooltip,
+ label,
+ help,
+ threeState,
+ expand,
+ fromBoolean = defaultFromBoolean,
+ toBoolean = defaultToBoolean,
+}: Props): VNode {
+ const { error, value, onChange } = useField(name);
+
+ const onCheckboxClick = (): void => {
+ const c = toBoolean(value);
+ if (c === false && threeState) return onChange(undefined as any);
+ return onChange(fromBoolean(!c));
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {help}
+
+ {error &&
{error}
}
+
+
+
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/hooks/index.ts b/packages/merchant-backoffice-ui/src/hooks/index.ts
index 10e77716e..b77b9dea8 100644
--- a/packages/merchant-backoffice-ui/src/hooks/index.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/index.ts
@@ -53,15 +53,17 @@ export function useBackendURL(
export function useBackendDefaultToken(
initialValue?: string,
-): [string | undefined, ((d:string | undefined) => void)] {
- const {update, value} = useMemoryStorage(`backend-token`, initialValue)
+): [string | undefined, ((d: string | undefined) => void)] {
+ // uncomment for testing
+ initialValue = "secret-token:secret" as string | undefined
+ const { update, value } = useMemoryStorage(`backend-token`, initialValue)
return [value, update];
}
export function useBackendInstanceToken(
id: string,
-): [string | undefined, ((d:string | undefined) => void)] {
- const {update:setToken, value:token, reset} = useMemoryStorage(`backend-token-${id}`)
+): [string | undefined, ((d: string | undefined) => void)] {
+ const { update: setToken, value: token, reset } = useMemoryStorage(`backend-token-${id}`)
const [defaultToken, defaultSetToken] = useBackendDefaultToken();
// instance named 'default' use the default token
diff --git a/packages/merchant-backoffice-ui/src/hooks/useSettings.ts b/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
new file mode 100644
index 000000000..5c0932f27
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
@@ -0,0 +1,59 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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
+ */
+
+import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
+import {
+ Codec,
+ buildCodecForObject,
+ codecForBoolean,
+} from "@gnu-taler/taler-util";
+
+function parse_json_or_undefined(str: string | undefined): T | undefined {
+ if (str === undefined) return undefined;
+ try {
+ return JSON.parse(str);
+ } catch {
+ return undefined;
+ }
+}
+
+export interface Settings {
+ advanceOrderMode: boolean
+}
+
+const defaultSettings: Settings = {
+ advanceOrderMode: false,
+}
+
+export const codecForSettings = (): Codec =>
+ buildCodecForObject()
+ .property("advanceOrderMode", codecForBoolean())
+ .build("Settings");
+
+const SETTINGS_KEY = buildStorageKey("merchant-settings", codecForSettings());
+
+export function useSettings(): [
+ Readonly,
+ (key: T, value: Settings[T]) => void,
+] {
+ const { value, update } = useLocalStorage(SETTINGS_KEY);
+
+ const parsed: Settings = value ?? defaultSettings;
+ function updateField(k: T, v: Settings[T]) {
+ update({ ...parsed, [k]: v });
+ }
+ return [parsed, updateField];
+}
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 c8cc20ae0..fa9347c6e 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
@@ -43,6 +43,7 @@ import { Duration, MerchantBackend, WithId } from "../../../../declaration.js";
import { OrderCreateSchema as schema } from "../../../../schemas/index.js";
import { rate } from "../../../../utils/amount.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
+import { useSettings } from "../../../../hooks/useSettings.js";
interface Props {
onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
@@ -62,8 +63,8 @@ function with_defaults(config: InstanceConfig): Partial {
!config.default_pay_delay || config.default_pay_delay.d_us === "forever"
? undefined
: add(new Date(), {
- seconds: config.default_pay_delay.d_us / (1000 * 1000),
- });
+ seconds: config.default_pay_delay.d_us / (1000 * 1000),
+ });
return {
inventoryProducts: {},
@@ -138,7 +139,7 @@ export function CreatePage({
const [value, valueHandler] = useState(with_defaults(instanceConfig));
const config = useConfigContext();
const zero = Amounts.zeroOfCurrency(config.currency);
-
+ const [settings] = useSettings()
const inventoryList = Object.values(value.inventoryProducts || {});
const productList = Object.values(value.products || {});
@@ -154,10 +155,10 @@ export function CreatePage({
order_price: !value.pricing?.order_price
? i18n.str`required`
: !parsedPrice
- ? i18n.str`not valid`
- : Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
+ ? i18n.str`not valid`
+ : Amounts.isZero(parsedPrice)
+ ? i18n.str`must be greater than 0`
+ : undefined,
}),
extra:
value.extra && !stringIsValidJSON(value.extra)
@@ -167,47 +168,47 @@ export function CreatePage({
refund_deadline: !value.payments?.refund_deadline
? undefined
: !isFuture(value.payments.refund_deadline)
- ? i18n.str`should be in the future`
- : value.payments.pay_deadline &&
- isBefore(value.payments.refund_deadline, value.payments.pay_deadline)
- ? i18n.str`refund deadline cannot be before pay deadline`
- : value.payments.wire_transfer_deadline &&
- isBefore(
- value.payments.wire_transfer_deadline,
- value.payments.refund_deadline,
- )
- ? i18n.str`wire transfer deadline cannot be before refund deadline`
- : undefined,
+ ? i18n.str`should be in the future`
+ : value.payments.pay_deadline &&
+ isBefore(value.payments.refund_deadline, value.payments.pay_deadline)
+ ? i18n.str`refund deadline cannot be before pay deadline`
+ : value.payments.wire_transfer_deadline &&
+ isBefore(
+ value.payments.wire_transfer_deadline,
+ value.payments.refund_deadline,
+ )
+ ? i18n.str`wire transfer deadline cannot be before refund deadline`
+ : undefined,
pay_deadline: !value.payments?.pay_deadline
? undefined
: !isFuture(value.payments.pay_deadline)
- ? i18n.str`should be in the future`
- : value.payments.wire_transfer_deadline &&
- isBefore(
- value.payments.wire_transfer_deadline,
- value.payments.pay_deadline,
- )
- ? i18n.str`wire transfer deadline cannot be before pay deadline`
- : undefined,
+ ? i18n.str`should be in the future`
+ : value.payments.wire_transfer_deadline &&
+ isBefore(
+ value.payments.wire_transfer_deadline,
+ value.payments.pay_deadline,
+ )
+ ? i18n.str`wire transfer deadline cannot be before pay deadline`
+ : undefined,
auto_refund_deadline: !value.payments?.auto_refund_deadline
? undefined
: !isFuture(value.payments.auto_refund_deadline)
- ? i18n.str`should be in the future`
- : !value.payments?.refund_deadline
- ? i18n.str`should have a refund deadline`
- : !isAfter(
- value.payments.refund_deadline,
- value.payments.auto_refund_deadline,
- )
- ? i18n.str`auto refund cannot be after refund deadline`
- : undefined,
+ ? i18n.str`should be in the future`
+ : !value.payments?.refund_deadline
+ ? i18n.str`should have a refund deadline`
+ : !isAfter(
+ value.payments.refund_deadline,
+ value.payments.auto_refund_deadline,
+ )
+ ? i18n.str`auto refund cannot be after refund deadline`
+ : undefined,
}),
shipping: undefinedIfEmpty({
delivery_date: !value.shipping?.delivery_date
? undefined
: !isFuture(value.shipping.delivery_date)
- ? i18n.str`should be in the future`
- : undefined,
+ ? i18n.str`should be in the future`
+ : undefined,
}),
};
const hasErrors = Object.keys(errors).some(
@@ -227,27 +228,27 @@ export function CreatePage({
extra: value.extra,
pay_deadline: value.payments.pay_deadline
? {
- t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000),
- }
+ t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000),
+ }
: undefined,
wire_transfer_deadline: value.payments.wire_transfer_deadline
? {
- t_s: Math.floor(
- value.payments.wire_transfer_deadline.getTime() / 1000,
- ),
- }
+ t_s: Math.floor(
+ value.payments.wire_transfer_deadline.getTime() / 1000,
+ ),
+ }
: undefined,
refund_deadline: value.payments.refund_deadline
? {
- t_s: Math.floor(value.payments.refund_deadline.getTime() / 1000),
- }
+ t_s: Math.floor(value.payments.refund_deadline.getTime() / 1000),
+ }
: undefined,
auto_refund: value.payments.auto_refund_deadline
? {
- d_us: Math.floor(
- value.payments.auto_refund_deadline.getTime() * 1000,
- ),
- }
+ d_us: Math.floor(
+ value.payments.auto_refund_deadline.getTime() * 1000,
+ ),
+ }
: undefined,
wire_fee_amortization: value.payments.wire_fee_amortization as number,
max_fee: value.payments.max_fee as string,
@@ -374,13 +375,15 @@ export function CreatePage({
inventory={instanceInventory}
/>
- {
- setEditingProduct(undefined);
- return addNewProduct(p);
- }}
- />
+ {settings.advanceOrderMode &&
+ {
+ setEditingProduct(undefined);
+ return addNewProduct(p);
+ }}
+ />
+ }
{allProducts.length > 0 && (
0 &&
(discountOrRise < 1
? `discount of %${Math.round(
- (1 - discountOrRise) * 100,
- )}`
+ (1 - discountOrRise) * 100,
+ )}`
: `rise of %${Math.round((discountOrRise - 1) * 100)}`)
}
tooltip={i18n.str`Amount to be paid by the customer`}
@@ -445,102 +448,108 @@ export function CreatePage({
tooltip={i18n.str`Title of the order to be shown to the customer`}
/>
-
-
- {value.shipping?.delivery_date && (
-
-
-
- )}
-
-
+ {settings.advanceOrderMode &&
+
+
+ {value.shipping?.delivery_date && (
+
+
+
+ )}
+
+
+ }
-
-
-
-
-
+ {settings.advanceOrderMode &&
+
+
+
+
+
-
-
-
-
- 0
- ? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
- : undefined
- }
- />
-
+
+
+
+
+ 0
+ ? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
+ : undefined
+ }
+ />
+
+ }
-
-
-
+ label={i18n.str`Additional information`}
+ tooltip={i18n.str`Custom information to be included in the contract for this order.`}
+ >
+
+
+ }