From 1b2b5d62de5888eae895db69cf6ae51dbfddb32b Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Fri, 27 Jan 2023 12:35:10 -0300
Subject: [PATCH] add use-template button

---
 .../src/InstanceRoutes.tsx                    |  18 +++
 .../src/components/form/Input.tsx             |   1 +
 .../src/components/form/InputWithAddon.tsx    |   1 +
 .../src/declaration.d.ts                      |   5 +-
 .../src/hooks/templates.ts                    |  15 +--
 .../src/paths/instance/details/DetailPage.tsx |   4 +-
 .../src/paths/instance/details/stories.tsx    |   4 +-
 .../instance/orders/create/Create.stories.tsx |   2 +-
 .../instance/orders/create/CreatePage.tsx     |   4 +-
 .../instance/templates/create/CreatePage.tsx  |   2 +-
 .../instance/templates/list/ListPage.tsx      |   3 +
 .../paths/instance/templates/list/Table.tsx   |  30 +++--
 .../paths/instance/templates/list/index.tsx   |   5 +
 .../instance/templates/update/UpdatePage.tsx  |   2 +-
 .../instance/templates/use/Use.stories.tsx    |  28 ++++
 .../paths/instance/templates/use/UsePage.tsx  | 126 ++++++++++++++++++
 .../paths/instance/templates/use/index.tsx    |  86 ++++++++++++
 .../paths/instance/update/Update.stories.tsx  |   4 +-
 18 files changed, 312 insertions(+), 28 deletions(-)
 create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
 create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
 create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx

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",
   },