add use-template button
This commit is contained in:
parent
7a3717125f
commit
1b2b5d62de
@ -48,6 +48,7 @@ import ReservesCreatePage from "./paths/instance/reserves/create/index.js";
|
|||||||
import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
|
import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
|
||||||
import ReservesListPage from "./paths/instance/reserves/list/index.js";
|
import ReservesListPage from "./paths/instance/reserves/list/index.js";
|
||||||
import TemplateCreatePage from "./paths/instance/templates/create/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 TemplateListPage from "./paths/instance/templates/list/index.js";
|
||||||
import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
|
import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
|
||||||
import TransferCreatePage from "./paths/instance/transfers/create/index.js";
|
import TransferCreatePage from "./paths/instance/transfers/create/index.js";
|
||||||
@ -85,6 +86,7 @@ export enum InstancePaths {
|
|||||||
templates_list = "/templates",
|
templates_list = "/templates",
|
||||||
templates_update = "/templates/:tid/update",
|
templates_update = "/templates/:tid/update",
|
||||||
templates_new = "/templates/new",
|
templates_new = "/templates/new",
|
||||||
|
templates_use = "/templates/:tid/use",
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
@ -399,6 +401,9 @@ export function InstanceRoutes({
|
|||||||
onCreate={() => {
|
onCreate={() => {
|
||||||
route(InstancePaths.templates_new);
|
route(InstancePaths.templates_new);
|
||||||
}}
|
}}
|
||||||
|
onNewOrder={(id: string) => {
|
||||||
|
route(InstancePaths.templates_use.replace(":tid", id));
|
||||||
|
}}
|
||||||
onSelect={(id: string) => {
|
onSelect={(id: string) => {
|
||||||
route(InstancePaths.templates_update.replace(":tid", id));
|
route(InstancePaths.templates_update.replace(":tid", id));
|
||||||
}}
|
}}
|
||||||
@ -426,6 +431,19 @@ export function InstanceRoutes({
|
|||||||
route(InstancePaths.templates_list);
|
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
|
* reserves pages
|
||||||
|
@ -92,6 +92,7 @@ export function Input<T>({
|
|||||||
inputType={inputType}
|
inputType={inputType}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
|
disabled={readonly}
|
||||||
name={String(name)}
|
name={String(name)}
|
||||||
value={toStr(value)}
|
value={toStr(value)}
|
||||||
onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void =>
|
onChange={(e: h.JSX.TargetedEvent<HTMLInputElement>): void =>
|
||||||
|
@ -86,6 +86,7 @@ export function InputWithAddon<T>({
|
|||||||
type={inputType}
|
type={inputType}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
|
disabled={readonly}
|
||||||
name={String(name)}
|
name={String(name)}
|
||||||
value={toStr(value)}
|
value={toStr(value)}
|
||||||
onChange={(e): void => onChange(fromStr(e.currentTarget.value))}
|
onChange={(e): void => onChange(fromStr(e.currentTarget.value))}
|
||||||
|
@ -1347,7 +1347,7 @@ export namespace MerchantBackend {
|
|||||||
|
|
||||||
interface UsingTemplateDetails {
|
interface UsingTemplateDetails {
|
||||||
// Subject of the template
|
// Subject of the template
|
||||||
subject?: string;
|
summary?: string;
|
||||||
|
|
||||||
// The amount entered by the customer.
|
// The amount entered by the customer.
|
||||||
amount?: Amount;
|
amount?: Amount;
|
||||||
@ -1355,7 +1355,8 @@ export namespace MerchantBackend {
|
|||||||
|
|
||||||
interface UsingTemplateResponse {
|
interface UsingTemplateResponse {
|
||||||
// After enter the request. The user will be pay with a taler URL.
|
// After enter the request. The user will be pay with a taler URL.
|
||||||
taler_url: string;
|
order_id: string,
|
||||||
|
token: string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,14 +62,14 @@ export function useTemplateAPI(): TemplateAPI {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createOrder = async (
|
const createOrderFromTemplate = async (
|
||||||
templateId: string,
|
templateId: string,
|
||||||
data: MerchantBackend.Template.UsingTemplateDetails,
|
data: MerchantBackend.Template.UsingTemplateDetails,
|
||||||
): Promise<
|
): Promise<
|
||||||
HttpResponseOk<MerchantBackend.Template.UsingTemplateResponse>
|
HttpResponseOk<MerchantBackend.Template.UsingTemplateResponse>
|
||||||
> => {
|
> => {
|
||||||
const res = await request<MerchantBackend.Template.UsingTemplateResponse>(
|
const res = await request<MerchantBackend.Template.UsingTemplateResponse>(
|
||||||
`/private/templates/${templateId}`,
|
`/templates/${templateId}`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data,
|
data,
|
||||||
@ -79,7 +79,7 @@ export function useTemplateAPI(): TemplateAPI {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { createTemplate, updateTemplate, deleteTemplate, createOrder };
|
return { createTemplate, updateTemplate, deleteTemplate, createOrderFromTemplate };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TemplateAPI {
|
export interface TemplateAPI {
|
||||||
@ -91,7 +91,7 @@ export interface TemplateAPI {
|
|||||||
data: MerchantBackend.Template.TemplatePatchDetails,
|
data: MerchantBackend.Template.TemplatePatchDetails,
|
||||||
) => Promise<HttpResponseOk<void>>;
|
) => Promise<HttpResponseOk<void>>;
|
||||||
deleteTemplate: (id: string) => Promise<HttpResponseOk<void>>;
|
deleteTemplate: (id: string) => Promise<HttpResponseOk<void>>;
|
||||||
createOrder: (
|
createOrderFromTemplate: (
|
||||||
id: string,
|
id: string,
|
||||||
data: MerchantBackend.Template.UsingTemplateDetails,
|
data: MerchantBackend.Template.UsingTemplateDetails,
|
||||||
) => Promise<HttpResponseOk<MerchantBackend.Template.UsingTemplateResponse>>;
|
) => Promise<HttpResponseOk<MerchantBackend.Template.UsingTemplateResponse>>;
|
||||||
@ -174,10 +174,9 @@ export function useInstanceTemplates(
|
|||||||
if (afterData.data.templates.length < MAX_RESULT_SIZE) {
|
if (afterData.data.templates.length < MAX_RESULT_SIZE) {
|
||||||
setPageAfter(pageAfter + 1);
|
setPageAfter(pageAfter + 1);
|
||||||
} else {
|
} else {
|
||||||
const from = `${
|
const from = `${afterData.data.templates[afterData.data.templates.length - 1]
|
||||||
afterData.data.templates[afterData.data.templates.length - 1]
|
.template_id
|
||||||
.template_id
|
}`;
|
||||||
}`;
|
|
||||||
if (from && updatePosition) updatePosition(from);
|
if (from && updatePosition) updatePosition(from);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -40,8 +40,8 @@ function convert(
|
|||||||
const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri);
|
const payto_uris = accounts.filter((a) => a.active).map((a) => a.payto_uri);
|
||||||
const defaults = {
|
const defaults = {
|
||||||
default_wire_fee_amortization: 1,
|
default_wire_fee_amortization: 1,
|
||||||
default_pay_delay: { d_us: 1000 * 60 * 60 }, //one hour
|
default_pay_delay: { d_us: 1000 * 60 * 60 * 1000 }, //one hour
|
||||||
default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 }, //two hours
|
default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 * 1000 }, //two hours
|
||||||
};
|
};
|
||||||
return { ...defaults, ...rest, payto_uris };
|
return { ...defaults, ...rest, payto_uris };
|
||||||
}
|
}
|
||||||
|
@ -59,11 +59,11 @@ export const Example = createExample(TestedComponent, {
|
|||||||
default_max_deposit_fee: "TESTKUDOS:2",
|
default_max_deposit_fee: "TESTKUDOS:2",
|
||||||
default_max_wire_fee: "TESTKUDOS:1",
|
default_max_wire_fee: "TESTKUDOS:1",
|
||||||
default_pay_delay: {
|
default_pay_delay: {
|
||||||
d_us: 1000000,
|
d_us: 1000 * 1000, //one second
|
||||||
},
|
},
|
||||||
default_wire_fee_amortization: 1,
|
default_wire_fee_amortization: 1,
|
||||||
default_wire_transfer_delay: {
|
default_wire_transfer_delay: {
|
||||||
d_us: 100000,
|
d_us: 1000 * 1000, //one second
|
||||||
},
|
},
|
||||||
merchant_pub: "ASDWQEKASJDKSADJ",
|
merchant_pub: "ASDWQEKASJDKSADJ",
|
||||||
},
|
},
|
||||||
|
@ -45,7 +45,7 @@ export const Example = createExample(TestedComponent, {
|
|||||||
default_max_deposit_fee: "",
|
default_max_deposit_fee: "",
|
||||||
default_max_wire_fee: "",
|
default_max_wire_fee: "",
|
||||||
default_pay_delay: {
|
default_pay_delay: {
|
||||||
d_us: 1000 * 60 * 60,
|
d_us: 1000 * 1000 * 60 * 60, //one hour
|
||||||
},
|
},
|
||||||
default_wire_fee_amortization: 1,
|
default_wire_fee_amortization: 1,
|
||||||
},
|
},
|
||||||
|
@ -61,7 +61,9 @@ function with_defaults(config: InstanceConfig): Partial<Entity> {
|
|||||||
const defaultPayDeadline =
|
const defaultPayDeadline =
|
||||||
!config.default_pay_delay || config.default_pay_delay.d_us === "forever"
|
!config.default_pay_delay || config.default_pay_delay.d_us === "forever"
|
||||||
? undefined
|
? 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 {
|
return {
|
||||||
inventoryProducts: {},
|
inventoryProducts: {},
|
||||||
|
@ -72,7 +72,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
|
|||||||
? i18n.str`can't be empty`
|
? i18n.str`can't be empty`
|
||||||
: state.template_contract.pay_duration.d_us === "forever"
|
: state.template_contract.pay_duration.d_us === "forever"
|
||||||
? undefined
|
? 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`
|
? i18n.str`to short`
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
|
@ -31,6 +31,7 @@ export interface Props {
|
|||||||
onCreate: () => void;
|
onCreate: () => void;
|
||||||
onDelete: (e: MerchantBackend.Template.TemplateEntry) => void;
|
onDelete: (e: MerchantBackend.Template.TemplateEntry) => void;
|
||||||
onSelect: (e: MerchantBackend.Template.TemplateEntry) => void;
|
onSelect: (e: MerchantBackend.Template.TemplateEntry) => void;
|
||||||
|
onNewOrder: (e: MerchantBackend.Template.TemplateEntry) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ListPage({
|
export function ListPage({
|
||||||
@ -38,6 +39,7 @@ export function ListPage({
|
|||||||
onCreate,
|
onCreate,
|
||||||
onDelete,
|
onDelete,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
onNewOrder,
|
||||||
onLoadMoreBefore,
|
onLoadMoreBefore,
|
||||||
onLoadMoreAfter,
|
onLoadMoreAfter,
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
@ -54,6 +56,7 @@ export function ListPage({
|
|||||||
onCreate={onCreate}
|
onCreate={onCreate}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
|
onNewOrder={onNewOrder}
|
||||||
onLoadMoreBefore={onLoadMoreBefore}
|
onLoadMoreBefore={onLoadMoreBefore}
|
||||||
hasMoreBefore={!onLoadMoreBefore}
|
hasMoreBefore={!onLoadMoreBefore}
|
||||||
onLoadMoreAfter={onLoadMoreAfter}
|
onLoadMoreAfter={onLoadMoreAfter}
|
||||||
|
@ -30,6 +30,7 @@ interface Props {
|
|||||||
templates: Entity[];
|
templates: Entity[];
|
||||||
onDelete: (e: Entity) => void;
|
onDelete: (e: Entity) => void;
|
||||||
onSelect: (e: Entity) => void;
|
onSelect: (e: Entity) => void;
|
||||||
|
onNewOrder: (e: Entity) => void;
|
||||||
onCreate: () => void;
|
onCreate: () => void;
|
||||||
onLoadMoreBefore?: () => void;
|
onLoadMoreBefore?: () => void;
|
||||||
hasMoreBefore?: boolean;
|
hasMoreBefore?: boolean;
|
||||||
@ -42,6 +43,7 @@ export function CardTable({
|
|||||||
onCreate,
|
onCreate,
|
||||||
onDelete,
|
onDelete,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
onNewOrder,
|
||||||
onLoadMoreAfter,
|
onLoadMoreAfter,
|
||||||
onLoadMoreBefore,
|
onLoadMoreBefore,
|
||||||
hasMoreAfter,
|
hasMoreAfter,
|
||||||
@ -81,6 +83,7 @@ export function CardTable({
|
|||||||
instances={templates}
|
instances={templates}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
|
onNewOrder={onNewOrder}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
rowSelectionHandler={rowSelectionHandler}
|
rowSelectionHandler={rowSelectionHandler}
|
||||||
onLoadMoreAfter={onLoadMoreAfter}
|
onLoadMoreAfter={onLoadMoreAfter}
|
||||||
@ -101,6 +104,7 @@ interface TableProps {
|
|||||||
rowSelection: string[];
|
rowSelection: string[];
|
||||||
instances: Entity[];
|
instances: Entity[];
|
||||||
onDelete: (e: Entity) => void;
|
onDelete: (e: Entity) => void;
|
||||||
|
onNewOrder: (e: Entity) => void;
|
||||||
onSelect: (e: Entity) => void;
|
onSelect: (e: Entity) => void;
|
||||||
rowSelectionHandler: StateUpdater<string[]>;
|
rowSelectionHandler: StateUpdater<string[]>;
|
||||||
onLoadMoreBefore?: () => void;
|
onLoadMoreBefore?: () => void;
|
||||||
@ -118,6 +122,7 @@ function Table({
|
|||||||
instances,
|
instances,
|
||||||
onLoadMoreAfter,
|
onLoadMoreAfter,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
onNewOrder,
|
||||||
onSelect,
|
onSelect,
|
||||||
onLoadMoreBefore,
|
onLoadMoreBefore,
|
||||||
hasMoreAfter,
|
hasMoreAfter,
|
||||||
@ -164,14 +169,23 @@ function Table({
|
|||||||
>
|
>
|
||||||
{i.template_description}
|
{i.template_description}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="is-actions-cell right-sticky">
|
||||||
<button
|
<div class="buttons is-right">
|
||||||
class="button is-danger is-small has-tooltip-left"
|
<button
|
||||||
data-tooltip={i18n.str`delete selected templates from the database`}
|
class="button is-danger is-small has-tooltip-left"
|
||||||
onClick={() => onDelete(i)}
|
data-tooltip={i18n.str`delete selected templates from the database`}
|
||||||
>
|
onClick={() => onDelete(i)}
|
||||||
Delete
|
>
|
||||||
</button>
|
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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
@ -39,6 +39,7 @@ interface Props {
|
|||||||
onNotFound: () => VNode;
|
onNotFound: () => VNode;
|
||||||
onCreate: () => void;
|
onCreate: () => void;
|
||||||
onSelect: (id: string) => void;
|
onSelect: (id: string) => void;
|
||||||
|
onNewOrder: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ListTemplates({
|
export default function ListTemplates({
|
||||||
@ -46,6 +47,7 @@ export default function ListTemplates({
|
|||||||
onLoadError,
|
onLoadError,
|
||||||
onCreate,
|
onCreate,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
onNewOrder,
|
||||||
onNotFound,
|
onNotFound,
|
||||||
}: Props): VNode {
|
}: Props): VNode {
|
||||||
const [position, setPosition] = useState<string | undefined>(undefined);
|
const [position, setPosition] = useState<string | undefined>(undefined);
|
||||||
@ -73,6 +75,9 @@ export default function ListTemplates({
|
|||||||
onSelect={(e) => {
|
onSelect={(e) => {
|
||||||
onSelect(e.template_id);
|
onSelect(e.template_id);
|
||||||
}}
|
}}
|
||||||
|
onNewOrder={(e) => {
|
||||||
|
onNewOrder(e.template_id);
|
||||||
|
}}
|
||||||
onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
|
onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
|
||||||
deleteTemplate(e.template_id)
|
deleteTemplate(e.template_id)
|
||||||
.then(() =>
|
.then(() =>
|
||||||
|
@ -65,7 +65,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
|
|||||||
? i18n.str`can't be empty`
|
? i18n.str`can't be empty`
|
||||||
: state.template_contract.pay_duration.d_us === "forever"
|
: state.template_contract.pay_duration.d_us === "forever"
|
||||||
? undefined
|
? 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`
|
? i18n.str`to short`
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
|
@ -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,
|
||||||
|
};
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
@ -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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -50,11 +50,11 @@ export const Example = createExample(TestedComponent, {
|
|||||||
default_max_deposit_fee: "TESTKUDOS:2",
|
default_max_deposit_fee: "TESTKUDOS:2",
|
||||||
default_max_wire_fee: "TESTKUDOS:1",
|
default_max_wire_fee: "TESTKUDOS:1",
|
||||||
default_pay_delay: {
|
default_pay_delay: {
|
||||||
d_us: 1000000,
|
d_us: 1000 * 1000, //one second
|
||||||
},
|
},
|
||||||
default_wire_fee_amortization: 1,
|
default_wire_fee_amortization: 1,
|
||||||
default_wire_transfer_delay: {
|
default_wire_transfer_delay: {
|
||||||
d_us: 100000,
|
d_us: 1000 * 1000, //one second
|
||||||
},
|
},
|
||||||
merchant_pub: "ASDWQEKASJDKSADJ",
|
merchant_pub: "ASDWQEKASJDKSADJ",
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user