aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/merchant-backoffice-ui/src/InstanceRoutes.tsx18
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/Input.tsx1
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx1
-rw-r--r--packages/merchant-backoffice-ui/src/declaration.d.ts5
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/templates.ts15
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx3
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx30
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx5
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx126
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx86
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx4
18 files changed, 312 insertions, 28 deletions
diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
index 1c55572bb..3be793ada 100644
--- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
@@ -48,6 +48,7 @@ import ReservesCreatePage from "./paths/instance/reserves/create/index.js";
import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
import ReservesListPage from "./paths/instance/reserves/list/index.js";
import TemplateCreatePage from "./paths/instance/templates/create/index.js";
+import TemplateUsePage from "./paths/instance/templates/use/index.js";
import TemplateListPage from "./paths/instance/templates/list/index.js";
import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
import TransferCreatePage from "./paths/instance/transfers/create/index.js";
@@ -85,6 +86,7 @@ export enum InstancePaths {
templates_list = "/templates",
templates_update = "/templates/:tid/update",
templates_new = "/templates/new",
+ templates_use = "/templates/:tid/use",
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -399,6 +401,9 @@ export function InstanceRoutes({
onCreate={() => {
route(InstancePaths.templates_new);
}}
+ onNewOrder={(id: string) => {
+ route(InstancePaths.templates_use.replace(":tid", id));
+ }}
onSelect={(id: string) => {
route(InstancePaths.templates_update.replace(":tid", id));
}}
@@ -426,6 +431,19 @@ export function InstanceRoutes({
route(InstancePaths.templates_list);
}}
/>
+ <Route
+ path={InstancePaths.templates_use}
+ component={TemplateUsePage}
+ onOrderCreated={(id: string) => {
+ route(InstancePaths.order_details.replace(":oid", id));
+ }}
+ onUnauthorized={LoginPageAccessDenied}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
+ onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+ onBack={() => {
+ route(InstancePaths.templates_list);
+ }}
+ />
{/**
* reserves pages
diff --git a/packages/merchant-backoffice-ui/src/components/form/Input.tsx b/packages/merchant-backoffice-ui/src/components/form/Input.tsx
index 54140ba4d..c1ddcb064 100644
--- a/packages/merchant-backoffice-ui/src/components/form/Input.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/Input.tsx
@@ -92,6 +92,7 @@ export function Input<T>({
inputType={inputType}
placeholder={placeholder}
readonly={readonly}
+ disabled={readonly}
name={String(name)}
value={toStr(value)}
onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void =>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
index 620922584..e5118c722 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputWithAddon.tsx
@@ -86,6 +86,7 @@ export function InputWithAddon<T>({
type={inputType}
placeholder={placeholder}
readonly={readonly}
+ disabled={readonly}
name={String(name)}
value={toStr(value)}
onChange={(e): void => onChange(fromStr(e.currentTarget.value))}
diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts b/packages/merchant-backoffice-ui/src/declaration.d.ts
index 25b66bdea..b0621c13c 100644
--- a/packages/merchant-backoffice-ui/src/declaration.d.ts
+++ b/packages/merchant-backoffice-ui/src/declaration.d.ts
@@ -1347,7 +1347,7 @@ export namespace MerchantBackend {
interface UsingTemplateDetails {
// Subject of the template
- subject?: string;
+ summary?: string;
// The amount entered by the customer.
amount?: Amount;
@@ -1355,7 +1355,8 @@ export namespace MerchantBackend {
interface UsingTemplateResponse {
// After enter the request. The user will be pay with a taler URL.
- taler_url: string;
+ order_id: string,
+ token: string,
}
}
diff --git a/packages/merchant-backoffice-ui/src/hooks/templates.ts b/packages/merchant-backoffice-ui/src/hooks/templates.ts
index 672eaf792..3a28b903d 100644
--- a/packages/merchant-backoffice-ui/src/hooks/templates.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/templates.ts
@@ -62,14 +62,14 @@ export function useTemplateAPI(): TemplateAPI {
return res;
};
- const createOrder = async (
+ const createOrderFromTemplate = async (
templateId: string,
data: MerchantBackend.Template.UsingTemplateDetails,
): Promise<
HttpResponseOk<MerchantBackend.Template.UsingTemplateResponse>
> => {
const res = await request<MerchantBackend.Template.UsingTemplateResponse>(
- `/private/templates/${templateId}`,
+ `/templates/${templateId}`,
{
method: "POST",
data,
@@ -79,7 +79,7 @@ export function useTemplateAPI(): TemplateAPI {
return res;
};
- return { createTemplate, updateTemplate, deleteTemplate, createOrder };
+ return { createTemplate, updateTemplate, deleteTemplate, createOrderFromTemplate };
}
export interface TemplateAPI {
@@ -91,7 +91,7 @@ export interface TemplateAPI {
data: MerchantBackend.Template.TemplatePatchDetails,
) => Promise<HttpResponseOk<void>>;
deleteTemplate: (id: string) => Promise<HttpResponseOk<void>>;
- createOrder: (
+ createOrderFromTemplate: (
id: string,
data: MerchantBackend.Template.UsingTemplateDetails,
) => Promise<HttpResponseOk<MerchantBackend.Template.UsingTemplateResponse>>;
@@ -174,10 +174,9 @@ export function useInstanceTemplates(
if (afterData.data.templates.length < MAX_RESULT_SIZE) {
setPageAfter(pageAfter + 1);
} else {
- const from = `${
- afterData.data.templates[afterData.data.templates.length - 1]
- .template_id
- }`;
+ const from = `${afterData.data.templates[afterData.data.templates.length - 1]
+ .template_id
+ }`;
if (from && updatePosition) updatePosition(from);
}
},
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
index 4ab415e85..9a35b0797 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
@@ -40,8 +40,8 @@ function convert(
const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri);
const defaults = {
default_wire_fee_amortization: 1,
- default_pay_delay: { d_us: 1000 * 60 * 60 }, //one hour
- default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 }, //two hours
+ default_pay_delay: { d_us: 1000 * 60 * 60 * 1000 }, //one hour
+ default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 * 1000 }, //two hours
};
return { ...defaults, ...rest, payto_uris };
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
index cdbe732a4..3a6e0fbfe 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
@@ -59,11 +59,11 @@ export const Example = createExample(TestedComponent, {
default_max_deposit_fee: "TESTKUDOS:2",
default_max_wire_fee: "TESTKUDOS:1",
default_pay_delay: {
- d_us: 1000000,
+ d_us: 1000 * 1000, //one second
},
default_wire_fee_amortization: 1,
default_wire_transfer_delay: {
- d_us: 100000,
+ d_us: 1000 * 1000, //one second
},
merchant_pub: "ASDWQEKASJDKSADJ",
},
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 5f8dbbdd9..fcf611c3c 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
@@ -45,7 +45,7 @@ export const Example = createExample(TestedComponent, {
default_max_deposit_fee: "",
default_max_wire_fee: "",
default_pay_delay: {
- d_us: 1000 * 60 * 60,
+ d_us: 1000 * 1000 * 60 * 60, //one hour
},
default_wire_fee_amortization: 1,
},
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 0c2e92418..f4a82f377 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
@@ -61,7 +61,9 @@ function with_defaults(config: InstanceConfig): Partial<Entity> {
const defaultPayDeadline =
!config.default_pay_delay || config.default_pay_delay.d_us === "forever"
? undefined
- : add(new Date(), { seconds: config.default_pay_delay.d_us / 1000 });
+ : add(new Date(), {
+ seconds: config.default_pay_delay.d_us / (1000 * 1000),
+ });
return {
inventoryProducts: {},
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index 2a47c22a0..2b8d718fb 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -72,7 +72,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
? i18n.str`can't be empty`
: state.template_contract.pay_duration.d_us === "forever"
? undefined
- : state.template_contract.pay_duration.d_us < 1000
+ : state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second
? i18n.str`to short`
: undefined,
}),
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
index 9d289e957..8482f7f52 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
@@ -31,6 +31,7 @@ export interface Props {
onCreate: () => void;
onDelete: (e: MerchantBackend.Template.TemplateEntry) => void;
onSelect: (e: MerchantBackend.Template.TemplateEntry) => void;
+ onNewOrder: (e: MerchantBackend.Template.TemplateEntry) => void;
}
export function ListPage({
@@ -38,6 +39,7 @@ export function ListPage({
onCreate,
onDelete,
onSelect,
+ onNewOrder,
onLoadMoreBefore,
onLoadMoreAfter,
}: Props): VNode {
@@ -54,6 +56,7 @@ export function ListPage({
onCreate={onCreate}
onDelete={onDelete}
onSelect={onSelect}
+ onNewOrder={onNewOrder}
onLoadMoreBefore={onLoadMoreBefore}
hasMoreBefore={!onLoadMoreBefore}
onLoadMoreAfter={onLoadMoreAfter}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
index fd6ea5f6f..6635d6c55 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx
@@ -30,6 +30,7 @@ interface Props {
templates: Entity[];
onDelete: (e: Entity) => void;
onSelect: (e: Entity) => void;
+ onNewOrder: (e: Entity) => void;
onCreate: () => void;
onLoadMoreBefore?: () => void;
hasMoreBefore?: boolean;
@@ -42,6 +43,7 @@ export function CardTable({
onCreate,
onDelete,
onSelect,
+ onNewOrder,
onLoadMoreAfter,
onLoadMoreBefore,
hasMoreAfter,
@@ -81,6 +83,7 @@ export function CardTable({
instances={templates}
onDelete={onDelete}
onSelect={onSelect}
+ onNewOrder={onNewOrder}
rowSelection={rowSelection}
rowSelectionHandler={rowSelectionHandler}
onLoadMoreAfter={onLoadMoreAfter}
@@ -101,6 +104,7 @@ interface TableProps {
rowSelection: string[];
instances: Entity[];
onDelete: (e: Entity) => void;
+ onNewOrder: (e: Entity) => void;
onSelect: (e: Entity) => void;
rowSelectionHandler: StateUpdater<string[]>;
onLoadMoreBefore?: () => void;
@@ -118,6 +122,7 @@ function Table({
instances,
onLoadMoreAfter,
onDelete,
+ onNewOrder,
onSelect,
onLoadMoreBefore,
hasMoreAfter,
@@ -164,14 +169,23 @@ function Table({
>
{i.template_description}
</td>
- <td>
- <button
- class="button is-danger is-small has-tooltip-left"
- data-tooltip={i18n.str`delete selected templates from the database`}
- onClick={() => onDelete(i)}
- >
- Delete
- </button>
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <button
+ class="button is-danger is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected templates from the database`}
+ onClick={() => onDelete(i)}
+ >
+ Delete
+ </button>
+ <button
+ class="button is-info is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected templates from the database`}
+ onClick={() => onNewOrder(i)}
+ >
+ New order
+ </button>
+ </div>
</td>
</tr>
);
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index e1a2d019e..5fce3a819 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -39,6 +39,7 @@ interface Props {
onNotFound: () => VNode;
onCreate: () => void;
onSelect: (id: string) => void;
+ onNewOrder: (id: string) => void;
}
export default function ListTemplates({
@@ -46,6 +47,7 @@ export default function ListTemplates({
onLoadError,
onCreate,
onSelect,
+ onNewOrder,
onNotFound,
}: Props): VNode {
const [position, setPosition] = useState<string | undefined>(undefined);
@@ -73,6 +75,9 @@ export default function ListTemplates({
onSelect={(e) => {
onSelect(e.template_id);
}}
+ onNewOrder={(e) => {
+ onNewOrder(e.template_id);
+ }}
onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
deleteTemplate(e.template_id)
.then(() =>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index a49e8000b..718a67ae0 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
@@ -65,7 +65,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
? i18n.str`can't be empty`
: state.template_contract.pay_duration.d_us === "forever"
? undefined
- : state.template_contract.pay_duration.d_us < 1000
+ : state.template_contract.pay_duration.d_us < 1000 * 1000 // less than one second
? i18n.str`to short`
: undefined,
}),
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
new file mode 100644
index 000000000..f073b4235
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode, FunctionalComponent } from "preact";
+import { CreatePage as TestedComponent } from "./UsePage.js";
+
+export default {
+ title: "Pages/Templates/Create",
+ component: TestedComponent,
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
new file mode 100644
index 000000000..a63469763
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
@@ -0,0 +1,126 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+ FormErrors,
+ FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { InputCurrency } from "../../../../components/form/InputCurrency.js";
+import { MerchantBackend } from "../../../../declaration.js";
+
+type Entity = MerchantBackend.Template.UsingTemplateDetails;
+
+interface Props {
+ template: MerchantBackend.Template.TemplateDetails;
+ onCreateOrder: (d: Entity) => Promise<void>;
+ onBack?: () => void;
+}
+
+export function UsePage({ template, onCreateOrder, onBack }: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ const [state, setState] = useState<Partial<Entity>>({
+ amount: template.template_contract.amount,
+ summary: template.template_contract.summary,
+ });
+
+ const errors: FormErrors<Entity> = {
+ amount:
+ !template.template_contract.amount && !state.amount
+ ? i18n.str`Amount is required`
+ : undefined,
+ summary:
+ !template.template_contract.summary && !state.summary
+ ? i18n.str`Order summary is required`
+ : undefined,
+ };
+
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
+
+ const submitForm = () => {
+ if (hasErrors) return Promise.reject();
+ if (template.template_contract.amount) {
+ delete state.amount;
+ }
+ if (template.template_contract.summary) {
+ delete state.summary;
+ }
+ return onCreateOrder(state as any);
+ };
+
+ return (
+ <div>
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ <FormProvider
+ object={state}
+ valueHandler={setState}
+ errors={errors}
+ >
+ <InputCurrency<Entity>
+ name="amount"
+ label={i18n.str`Amount`}
+ readonly={!!template.template_contract.amount}
+ tooltip={i18n.str`Amount of the order`}
+ />
+ <Input<Entity>
+ name="summary"
+ inputType="multiline"
+ label={i18n.str`Order summary`}
+ readonly={!!template.template_contract.summary}
+ tooltip={i18n.str`Title of the order to be shown to the customer`}
+ />
+ </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <AsyncButton
+ disabled={hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n.str`Need to complete marked fields`
+ : "confirm operation"
+ }
+ onClick={submitForm}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
new file mode 100644
index 000000000..fa9a98c6d
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
@@ -0,0 +1,86 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
+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";
+import {
+ useTemplateAPI,
+ useTemplateDetails,
+} from "../../../../hooks/templates.js";
+import { HttpError } from "../../../../utils/request.js";
+import { Notification } from "../../../../utils/types.js";
+import { UsePage } from "./UsePage.js";
+
+export type Entity = MerchantBackend.Transfers.TransferInformation;
+interface Props {
+ onBack?: () => void;
+ onOrderCreated: (id: string) => void;
+ onUnauthorized: () => VNode;
+ onNotFound: () => VNode;
+ onLoadError: (e: HttpError) => VNode;
+ tid: string;
+}
+
+export default function TemplateUsePage({
+ tid,
+ onOrderCreated,
+ onBack,
+ onLoadError,
+ onNotFound,
+ onUnauthorized,
+}: Props): VNode {
+ const { createOrderFromTemplate } = useTemplateAPI();
+ const result = useTemplateDetails(tid);
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+
+ 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 (
+ <>
+ <NotificationCard notification={notif} />
+ <UsePage
+ template={result.data}
+ onBack={onBack}
+ onCreateOrder={(
+ request: MerchantBackend.Template.UsingTemplateDetails,
+ ) => {
+ return createOrderFromTemplate(tid, request)
+ .then((res) => onOrderCreated(res.data.order_id))
+ .catch((error) => {
+ setNotif({
+ message: i18n.str`could not create order from template`,
+ type: "ERROR",
+ description: error.message,
+ });
+ });
+ }}
+ />
+ </>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
index 58b8d87ea..045c96c2c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
@@ -50,11 +50,11 @@ export const Example = createExample(TestedComponent, {
default_max_deposit_fee: "TESTKUDOS:2",
default_max_wire_fee: "TESTKUDOS:1",
default_pay_delay: {
- d_us: 1000000,
+ d_us: 1000 * 1000, //one second
},
default_wire_fee_amortization: 1,
default_wire_transfer_delay: {
- d_us: 100000,
+ d_us: 1000 * 1000, //one second
},
merchant_pub: "ASDWQEKASJDKSADJ",
},