diff options
author | Sebastian <sebasjm@gmail.com> | 2023-03-10 01:25:22 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-03-10 01:25:22 -0300 |
commit | 8ddc551cc83a81ade3206e6e04c516ff15215993 (patch) | |
tree | 31145c0f17472b38cdce5520bd038e7c4d6d8df9 /packages/merchant-backoffice-ui/src/paths | |
parent | 2291d460e833bd6b63df0a7e9a9a16f801f39007 (diff) | |
download | wallet-core-8ddc551cc83a81ade3206e6e04c516ff15215993.tar.xz |
remove webui from login url, ad qr for template, fix navbar size,
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths')
9 files changed, 305 insertions, 57 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx index 7376a88cb..fd1b8dc05 100644 --- a/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx +++ b/packages/merchant-backoffice-ui/src/paths/admin/list/View.tsx @@ -57,56 +57,54 @@ export function View({ : instances; return ( - <div id="app"> - <section class="section is-main-section"> - <div class="columns"> - <div class="column is-two-thirds"> - <div class="tabs" style={{ overflow: "inherit" }}> - <ul> - <li class={showIsActive}> - <div - class="has-tooltip-right" - data-tooltip={i18n.str`Only show active instances`} - > - <a onClick={() => setShow("active")}> - <i18n.Translate>Active</i18n.Translate> - </a> - </div> - </li> - <li class={showIsDeleted}> - <div - class="has-tooltip-right" - data-tooltip={i18n.str`Only show deleted instances`} - > - <a onClick={() => setShow("deleted")}> - <i18n.Translate>Deleted</i18n.Translate> - </a> - </div> - </li> - <li class={showAll}> - <div - class="has-tooltip-right" - data-tooltip={i18n.str`Show all instances`} - > - <a onClick={() => setShow(null)}> - <i18n.Translate>All</i18n.Translate> - </a> - </div> - </li> - </ul> - </div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column is-two-thirds"> + <div class="tabs" style={{ overflow: "inherit" }}> + <ul> + <li class={showIsActive}> + <div + class="has-tooltip-right" + data-tooltip={i18n.str`Only show active instances`} + > + <a onClick={() => setShow("active")}> + <i18n.Translate>Active</i18n.Translate> + </a> + </div> + </li> + <li class={showIsDeleted}> + <div + class="has-tooltip-right" + data-tooltip={i18n.str`Only show deleted instances`} + > + <a onClick={() => setShow("deleted")}> + <i18n.Translate>Deleted</i18n.Translate> + </a> + </div> + </li> + <li class={showAll}> + <div + class="has-tooltip-right" + data-tooltip={i18n.str`Show all instances`} + > + <a onClick={() => setShow(null)}> + <i18n.Translate>All</i18n.Translate> + </a> + </div> + </li> + </ul> </div> </div> - <CardTableActive - instances={showingInstances} - onDelete={onDelete} - onPurge={onPurge} - setInstanceName={setInstanceName} - onUpdate={onUpdate} - selected={selected} - onCreate={onCreate} - /> - </section> - </div> + </div> + <CardTableActive + instances={showingInstances} + onDelete={onDelete} + onPurge={onPurge} + setInstanceName={setInstanceName} + onUpdate={onUpdate} + selected={selected} + onCreate={onCreate} + /> + </section> ); } 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 b23c52362..22f86002a 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 @@ -114,13 +114,13 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <Input name="template_contract.summary" inputType="multiline" - label={i18n.str`Order summary`} - tooltip={i18n.str`Title of the order to be shown to the customer`} + label={i18n.str`Fixed summary`} + tooltip={i18n.str`If specified, this template will create order with the same summary`} /> <InputCurrency name="template_contract.amount" - label={i18n.str`Order price`} - tooltip={i18n.str`Order price`} + label={i18n.str`Fixed price`} + tooltip={i18n.str`If specified, this template will create order with the same price`} /> <InputNumber name="template_contract.minimum_age" 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 8482f7f52..708720818 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 @@ -32,6 +32,7 @@ export interface Props { onDelete: (e: MerchantBackend.Template.TemplateEntry) => void; onSelect: (e: MerchantBackend.Template.TemplateEntry) => void; onNewOrder: (e: MerchantBackend.Template.TemplateEntry) => void; + onQR: (e: MerchantBackend.Template.TemplateEntry) => void; } export function ListPage({ @@ -40,6 +41,7 @@ export function ListPage({ onDelete, onSelect, onNewOrder, + onQR, onLoadMoreBefore, onLoadMoreAfter, }: Props): VNode { @@ -53,6 +55,7 @@ export function ListPage({ ...o, id: String(o.template_id), }))} + onQR={onQR} onCreate={onCreate} onDelete={onDelete} onSelect={onSelect} 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 57d328d39..700c332d7 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 @@ -31,6 +31,7 @@ interface Props { onDelete: (e: Entity) => void; onSelect: (e: Entity) => void; onNewOrder: (e: Entity) => void; + onQR: (e: Entity) => void; onCreate: () => void; onLoadMoreBefore?: () => void; hasMoreBefore?: boolean; @@ -43,6 +44,7 @@ export function CardTable({ onCreate, onDelete, onSelect, + onQR, onNewOrder, onLoadMoreAfter, onLoadMoreBefore, @@ -84,6 +86,7 @@ export function CardTable({ onDelete={onDelete} onSelect={onSelect} onNewOrder={onNewOrder} + onQR={onQR} rowSelection={rowSelection} rowSelectionHandler={rowSelectionHandler} onLoadMoreAfter={onLoadMoreAfter} @@ -105,6 +108,7 @@ interface TableProps { instances: Entity[]; onDelete: (e: Entity) => void; onNewOrder: (e: Entity) => void; + onQR: (e: Entity) => void; onSelect: (e: Entity) => void; rowSelectionHandler: StateUpdater<string[]>; onLoadMoreBefore?: () => void; @@ -123,6 +127,7 @@ function Table({ onLoadMoreAfter, onDelete, onNewOrder, + onQR, onSelect, onLoadMoreBefore, hasMoreAfter, @@ -185,6 +190,13 @@ function Table({ > New order </button> + <button + class="button is-info is-small has-tooltip-left" + data-tooltip={i18n.str`create qr code for the template`} + onClick={() => onQR(i)} + > + QR + </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 0b7c191bd..ea8f4e7e3 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 @@ -42,12 +42,14 @@ interface Props { onCreate: () => void; onSelect: (id: string) => void; onNewOrder: (id: string) => void; + onQR: (id: string) => void; } export default function ListTemplates({ onUnauthorized, onLoadError, onCreate, + onQR, onSelect, onNewOrder, onNotFound, @@ -80,6 +82,9 @@ export default function ListTemplates({ onNewOrder={(e) => { onNewOrder(e.template_id); }} + onQR={(e) => { + onQR(e.template_id); + }} onDelete={(e: MerchantBackend.Template.TemplateEntry) => deleteTemplate(e.template_id) .then(() => diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx new file mode 100644 index 000000000..eb853c8ff --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx @@ -0,0 +1,27 @@ +/* + 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 { QrPage as TestedComponent } from "./QrPage.js"; + +export default { + title: "Pages/Templates/QR", + component: TestedComponent, +}; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx new file mode 100644 index 000000000..756909d15 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx @@ -0,0 +1,133 @@ +/* + 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 { buildPayto, classifyTalerUri } from "@gnu-taler/taler-util"; +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 { QR } from "../../../../components/exception/QR.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; +import { Input } from "../../../../components/form/Input.js"; +import { InputCurrency } from "../../../../components/form/InputCurrency.js"; +import { useBackendContext } from "../../../../context/backend.js"; +import { useConfigContext } from "../../../../context/config.js"; +import { MerchantBackend } from "../../../../declaration.js"; + +type Entity = MerchantBackend.Template.UsingTemplateDetails; + +interface Props { + template: MerchantBackend.Template.TemplateDetails; + id: string; + onBack?: () => void; +} + +export function QrPage({ template, id: templateId, onBack }: Props): VNode { + const { i18n } = useTranslationContext(); + const { url: backendUrl } = useBackendContext(); + const config = useConfigContext(); + + const [state, setState] = useState<Partial<Entity>>({ + amount: template.template_contract.amount, + summary: template.template_contract.summary, + }); + + const errors: FormErrors<Entity> = {}; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined, + ); + + const fixedAmount = !!template.template_contract.amount; + const fixedSummary = !!template.template_contract.summary; + + const params = new URLSearchParams(); + if (!fixedAmount) { + if (state.amount) { + params.append("amount", state.amount); + } else { + params.append("amount", config.currency); + } + } + if (!fixedSummary) { + params.append("summary", state.summary ?? ""); + } + + const paramsStr = fixedAmount && fixedSummary ? "" : "?" + params.toString(); + const merchantURL = new URL(backendUrl); + + const talerProto = + merchantURL.protocol === "http:" ? "taler+http:" : "taler:"; + + const payTemplateUri = `${talerProto}//pay-template/${merchantURL.hostname}/${templateId}${paramsStr}`; + + 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={fixedAmount} + tooltip={i18n.str`Amount of the order`} + /> + <Input<Entity> + name="summary" + inputType="multiline" + readonly={fixedSummary} + label={i18n.str`Order 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> + )} + <button class="button is-info" onClick={onBack}> + <i18n.Translate>Print</i18n.Translate> + </button> + </div> + </div> + <div class="column" /> + </div> + </section> + <section> + <pre> + <a href={payTemplateUri}>{payTemplateUri}</a> + </pre> + <QR text={payTemplateUri} /> + </section> + </div> + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx new file mode 100644 index 000000000..97d25b700 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx @@ -0,0 +1,70 @@ +/* + 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 { + HttpError, + 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 { Notification } from "../../../../utils/types.js"; +import { QrPage } from "./QrPage.js"; + +export type Entity = MerchantBackend.Transfers.TransferInformation; +interface Props { + onBack?: () => void; + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode; + tid: string; +} + +export default function TemplateQrPage({ + tid, + 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} /> + <QrPage template={result.data} id={tid} onBack={onBack} /> + </> + ); +} 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 5bd9bd38d..eba212517 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 @@ -123,13 +123,13 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { <Input name="template_contract.summary" inputType="multiline" - label={i18n.str`Order summary`} - tooltip={i18n.str`Title of the order to be shown to the customer`} + label={i18n.str`Fixed summary`} + tooltip={i18n.str`If specified, this template will create order with the same summary`} /> <InputCurrency name="template_contract.amount" - label={i18n.str`Order price`} - tooltip={i18n.str`total product price added up`} + label={i18n.str`Fixed price`} + tooltip={i18n.str`If specified, this template will create order with the same price`} /> <InputNumber name="template_contract.minimum_age" |