aboutsummaryrefslogtreecommitdiff
path: root/packages/auditor-backoffice-ui/src/paths
diff options
context:
space:
mode:
Diffstat (limited to 'packages/auditor-backoffice-ui/src/paths')
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/create/Create.stories.tsx57
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/create/CreatePage.tsx257
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx74
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/create/index.tsx82
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/create/stories.tsx52
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/index.stories.ts18
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/list/TableActive.tsx287
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/list/View.stories.tsx90
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/list/View.tsx110
-rw-r--r--packages/auditor-backoffice-ui/src/paths/admin/list/index.tsx140
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx173
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/create/index.tsx65
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx64
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/list/Table.tsx385
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/list/index.tsx107
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx32
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx195
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/accounts/update/index.tsx96
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/Create.stories.tsx43
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatePage.tsx80
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatedSuccessfully.tsx69
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/index.tsx46
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/List.stories.tsx43
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/Table.tsx249
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/Update.stories.tsx73
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/UpdatePage.tsx99
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/details/DetailPage.tsx83
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/details/index.tsx87
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/details/stories.tsx68
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/index.stories.ts19
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx58
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx208
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/kyc/list/index.tsx63
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx71
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx705
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx114
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/create/index.tsx114
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx135
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx770
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx129
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx95
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx107
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx226
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/list/Table.tsx417
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/orders/list/index.tsx231
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx179
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx104
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx70
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx64
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx211
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx106
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx32
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx186
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx102
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx43
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx80
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx72
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/create/index.tsx47
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/list/List.stories.tsx61
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/list/Table.tsx496
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/list/index.tsx150
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx73
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx99
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx43
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx277
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx120
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx190
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/create/index.tsx71
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx266
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx126
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx94
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/details/index.tsx68
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx124
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx102
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx96
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/list/Table.tsx320
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/reserves/list/index.tsx171
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/create/Create.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx270
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/create/index.tsx61
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx68
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/list/Table.tsx235
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/list/index.tsx152
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx27
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx172
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx32
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx269
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/update/index.tsx99
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx27
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx143
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/use/index.tsx101
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/token/DetailPage.tsx183
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/token/index.tsx106
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/token/stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx45
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx146
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/create/index.tsx68
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx93
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx134
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/list/Table.tsx229
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/transfers/list/index.tsx118
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/update/Update.stories.tsx59
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/update/UpdatePage.tsx176
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/update/index.tsx118
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx183
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/index.tsx61
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx28
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx64
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx218
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/index.tsx109
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx32
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx146
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/index.tsx99
119 files changed, 0 insertions, 15224 deletions
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/admin/create/Create.stories.tsx
deleted file mode 100644
index b3d0bdb18..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/create/Create.stories.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ConfigContextProvider } from "../../../context/config.js";
-import { CreatePage as TestedComponent } from "./CreatePage.js";
-
-export default {
- title: "Pages/Instance/Create",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- goBack: { action: "goBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => (
- <ConfigContextProvider
- value={{
- currency: "ARS",
- version: "1",
- }}
- >
- <Component {...args} />
- </ConfigContextProvider>
- );
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {});
-// export const Example = (a: any): VNode => <CreatePage {...a} />;
-// Example.args = {
-// isLoading: false
-// }
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/admin/create/CreatePage.tsx
deleted file mode 100644
index b13b0e5dd..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/create/CreatePage.tsx
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/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 { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
-import { MerchantBackend } from "../../../declaration.js";
-import { INSTANCE_ID_REGEX } from "../../../utils/constants.js";
-import { undefinedIfEmpty } from "../../../utils/table.js";
-import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
-import { Duration } from "@gnu-taler/taler-util";
-
-export type Entity = Omit<Omit<MerchantBackend.Instances.InstanceConfigurationMessage, "default_pay_delay">, "default_wire_transfer_delay"> & {
- auth_token?: string;
- default_pay_delay: Duration,
- default_wire_transfer_delay: Duration,
-};
-
-interface Props {
- onCreate: (d: MerchantBackend.Instances.InstanceConfigurationMessage) => Promise<void>;
- onBack?: () => void;
- forceId?: string;
-}
-
-function with_defaults(id?: string): Partial<Entity> {
- return {
- id,
- // accounts: [],
- user_type: "business",
- use_stefan: true,
- default_pay_delay: { d_ms: 2 * 60 * 60 * 1000 }, // two hours
- default_wire_transfer_delay: { d_ms: 2 * 60 * 60 * 24 * 1000 }, // two days
- };
-}
-
-export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
- const [value, valueHandler] = useState(with_defaults(forceId));
- const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
- const [isTokenDialogActive, updateIsTokenDialogActive] =
- useState<boolean>(false);
-
- const { i18n } = useTranslationContext();
-
- const errors: FormErrors<Entity> = {
- id: !value.id
- ? i18n.str`required`
- : !INSTANCE_ID_REGEX.test(value.id)
- ? i18n.str`is not valid`
- : undefined,
- name: !value.name ? i18n.str`required` : undefined,
-
- user_type: !value.user_type
- ? i18n.str`required`
- : value.user_type !== "business" && value.user_type !== "individual"
- ? i18n.str`should be business or individual`
- : undefined,
- // accounts:
- // !value.accounts || !value.accounts.length
- // ? i18n.str`required`
- // : undefinedIfEmpty(
- // value.accounts.map((p) => {
- // return !PAYTO_REGEX.test(p.payto_uri)
- // ? i18n.str`is not valid`
- // : undefined;
- // }),
- // ),
- default_pay_delay: !value.default_pay_delay
- ? i18n.str`required`
- : !!value.default_wire_transfer_delay &&
- value.default_wire_transfer_delay.d_ms !== "forever" &&
- value.default_pay_delay.d_ms !== "forever" &&
- value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms ?
- i18n.str`pay delay can't be greater than wire transfer delay` : undefined,
- default_wire_transfer_delay: !value.default_wire_transfer_delay
- ? i18n.str`required`
- : undefined,
- address: undefinedIfEmpty({
- address_lines:
- value.address?.address_lines && value.address?.address_lines.length > 7
- ? i18n.str`max 7 lines`
- : undefined,
- }),
- jurisdiction: undefinedIfEmpty({
- address_lines:
- value.address?.address_lines && value.address?.address_lines.length > 7
- ? i18n.str`max 7 lines`
- : undefined,
- }),
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submit = (): Promise<void> => {
- // use conversion instead of this
- const newToken = value.auth_token;
- value.auth_token = undefined;
- value.auth = newToken === null || newToken === undefined
- ? { method: "external" }
- : { method: "token", token: `secret-token:${newToken}` };
- if (!value.address) value.address = {};
- if (!value.jurisdiction) value.jurisdiction = {};
- // remove above use conversion
- // schema.validateSync(value, { abortEarly: false })
- value.default_pay_delay = Duration.toTalerProtocolDuration(value.default_pay_delay!) as any
- value.default_wire_transfer_delay = Duration.toTalerProtocolDuration(value.default_wire_transfer_delay!) as any
- // delete value.default_pay_delay;
- // delete value.default_wire_transfer_delay;
-
- return onCreate(value as any as MerchantBackend.Instances.InstanceConfigurationMessage);
- };
-
- function updateToken(token: string | null) {
- valueHandler((old) => ({
- ...old,
- auth_token: token === null ? undefined : token,
- }));
- }
-
- return (
- <div>
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- {isTokenDialogActive && (
- <SetTokenNewInstanceModal
- onCancel={() => {
- updateIsTokenDialogActive(false);
- updateIsTokenSet(false);
- }}
- onClear={() => {
- updateToken(null);
- updateIsTokenDialogActive(false);
- updateIsTokenSet(true);
- }}
- onConfirm={(newToken) => {
- updateToken(newToken);
- updateIsTokenDialogActive(false);
- updateIsTokenSet(true);
- }}
- />
- )}
- </div>
- <div class="column" />
- </div>
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider<Entity>
- errors={errors}
- object={value}
- valueHandler={valueHandler}
- >
- <DefaultInstanceFormFields readonlyId={!!forceId} showId={true} />
- </FormProvider>
-
- <div class="level">
- <div class="level-item has-text-centered">
- <h1 class="title">
- <button
- class={
- !isTokenSet
- ? "button is-danger has-tooltip-bottom"
- : !value.auth_token
- ? "button has-tooltip-bottom"
- : "button is-info has-tooltip-bottom"
- }
- data-tooltip={i18n.str`change authorization configuration`}
- onClick={() => updateIsTokenDialogActive(true)}
- >
- <div class="icon is-centered">
- <i class="mdi mdi-lock-reset" />
- </div>
- <span>
- <i18n.Translate>Set access token</i18n.Translate>
- </span>
- </button>
- </h1>
- </div>
- </div>
- <div class="level">
- <div class="level-item has-text-centered">
- {!isTokenSet ? (
- <p class="is-size-6">
- <i18n.Translate>
- Access token is not yet configured. This instance can't be
- created.
- </i18n.Translate>
- </p>
- ) : value.auth_token === undefined ? (
- <p class="is-size-6">
- <i18n.Translate>
- No access token. Authorization must be handled externally.
- </i18n.Translate>
- </p>
- ) : (
- <p class="is-size-6">
- <i18n.Translate>
- Access token is set. Authorization is handled by the
- merchant backend.
- </i18n.Translate>
- </p>
- )}
- </div>
- </div>
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <AsyncButton
- onClick={submit}
- disabled={hasErrors || !isTokenSet}
- data-tooltip={
- hasErrors
- ? i18n.str`Need to complete marked fields and choose authorization method`
- : "confirm operation"
- }
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx
deleted file mode 100644
index 939f9b06a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/create/InstanceCreatedSuccessfully.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 } from "preact";
-import { CreatedSuccessfully } from "../../../components/notifications/CreatedSuccessfully.js";
-import { Entity } from "./index.js";
-
-export function InstanceCreatedSuccessfully({
- entity,
- onConfirm,
-}: {
- entity: Entity;
- onConfirm: () => void;
-}): VNode {
- return (
- <CreatedSuccessfully onConfirm={onConfirm}>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">ID</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input class="input" readonly value={entity.id} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Business Name</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input class="input" readonly value={entity.name} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Access token</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- {entity.auth.method === "external" && "external"}
- {entity.auth.method === "token" && (
- <input class="input" readonly value={entity.auth.token} />
- )}
- </p>
- </div>
- </div>
- </div>
- </CreatedSuccessfully>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/admin/create/index.tsx
deleted file mode 100644
index 19f857ad9..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/create/index.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../components/menu/index.js";
-import { AccessToken, MerchantBackend } from "../../../declaration.js";
-import { useAdminAPI, useInstanceAPI } from "../../../hooks/instance.js";
-import { Notification } from "../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-import { useCredentialsChecker } from "../../../hooks/backend.js";
-import { useBackendContext } from "../../../context/backend.js";
-
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
- forceId?: string;
-}
-export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage;
-
-export default function Create({ onBack, onConfirm, forceId }: Props): VNode {
- const { createInstance } = useAdminAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const { requestNewLoginToken } = useCredentialsChecker()
- const { url: backendURL, updateToken } = useBackendContext()
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- <CreatePage
- onBack={onBack}
- forceId={forceId}
- onCreate={async (
- d: MerchantBackend.Instances.InstanceConfigurationMessage,
- ) => {
- try {
- await createInstance(d)
- if (d.auth.token) {
- const resp = await requestNewLoginToken(backendURL, d.auth.token as AccessToken)
- if (resp.valid) {
- const { token, expiration } = resp
- updateToken({ token, expiration });
- } else {
- updateToken(undefined)
- }
- }
- onConfirm();
- } catch (ex) {
- if (ex instanceof Error) {
- setNotif({
- message: i18n.str`Failed to create instance`,
- type: "ERROR",
- description: ex.message,
- });
- } else {
- console.error(ex)
- }
- }
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/create/stories.tsx b/packages/auditor-backoffice-ui/src/paths/admin/create/stories.tsx
deleted file mode 100644
index 4091eea42..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/create/stories.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ConfigContextProvider } from "../../../context/config.js";
-import { CreatePage as TestedComponent } from "./CreatePage.js";
-
-export default {
- title: "Pages/Instance/Create",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- goBack: { action: "goBack" },
- },
-};
-
-function createExample<Props>(
- Internal: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const component = (args: any) => (
- <ConfigContextProvider
- value={{
- currency: "TESTKUDOS",
- version: "1",
- }}
- >
- <Internal {...(props as any)} />
- </ConfigContextProvider>
- );
- return { component, props };
-}
-
-export const Example = createExample(TestedComponent, {});
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/index.stories.ts b/packages/auditor-backoffice-ui/src/paths/admin/index.stories.ts
deleted file mode 100644
index 3c16c0e57..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/index.stories.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-
-// export * as list from "./list/stories.js";
-export * as create from "./create/stories.js";
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/list/TableActive.tsx b/packages/auditor-backoffice-ui/src/paths/admin/list/TableActive.tsx
deleted file mode 100644
index e6530ec25..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/list/TableActive.tsx
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
-import { MerchantBackend } from "../../../declaration.js";
-
-interface Props {
- instances: MerchantBackend.Instances.Instance[];
- onUpdate: (id: string) => void;
- onDelete: (id: MerchantBackend.Instances.Instance) => void;
- onPurge: (id: MerchantBackend.Instances.Instance) => void;
- onCreate: () => void;
- selected?: boolean;
- setInstanceName: (s: string) => void;
-}
-
-export function CardTable({
- instances,
- onCreate,
- onUpdate,
- onPurge,
- setInstanceName,
- onDelete,
- selected,
-}: Props): VNode {
- const [actionQueue, actionQueueHandler] = useState<Actions[]>([]);
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- useEffect(() => {
- if (
- actionQueue.length > 0 &&
- !selected &&
- actionQueue[0].type == "DELETE"
- ) {
- onDelete(actionQueue[0].element);
- actionQueueHandler(actionQueue.slice(1));
- }
- }, [actionQueue, selected, onDelete]);
-
- useEffect(() => {
- if (
- actionQueue.length > 0 &&
- !selected &&
- actionQueue[0].type == "UPDATE"
- ) {
- onUpdate(actionQueue[0].element.id);
- actionQueueHandler(actionQueue.slice(1));
- }
- }, [actionQueue, selected, onUpdate]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-desktop-mac" />
- </span>
- <i18n.Translate>Instances</i18n.Translate>
- </p>
-
- <div class="card-header-icon" aria-label="more options">
- <button
- class={rowSelection.length > 0 ? "button is-danger" : "is-hidden"}
- type="button"
- onClick={(): void =>
- actionQueueHandler(
- buildActions(instances, rowSelection, "DELETE"),
- )
- }
- >
- <i18n.Translate>Delete</i18n.Translate>
- </button>
- </div>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new instance`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {instances.length > 0 ? (
- <Table
- instances={instances}
- onPurge={onPurge}
- onUpdate={onUpdate}
- setInstanceName={setInstanceName}
- onDelete={onDelete}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- instances: MerchantBackend.Instances.Instance[];
- onUpdate: (id: string) => void;
- onDelete: (id: MerchantBackend.Instances.Instance) => void;
- onPurge: (id: MerchantBackend.Instances.Instance) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- setInstanceName: (s: string) => void;
-}
-
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
-function Table({
- rowSelection,
- rowSelectionHandler,
- setInstanceName,
- instances,
- onUpdate,
- onDelete,
- onPurge,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th class="is-checkbox-cell">
- <label class="b-checkbox checkbox">
- <input
- type="checkbox"
- checked={rowSelection.length === instances.length}
- onClick={(): void =>
- rowSelectionHandler(
- rowSelection.length === instances.length
- ? []
- : instances.map((i) => i.id),
- )
- }
- />
- <span class="check" />
- </label>
- </th>
- <th>
- <i18n.Translate>ID</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Name</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.id}>
- <td class="is-checkbox-cell">
- <label class="b-checkbox checkbox">
- <input
- type="checkbox"
- checked={rowSelection.indexOf(i.id) != -1}
- onClick={(): void =>
- rowSelectionHandler(toggleSelected(i.id))
- }
- />
- <span class="check" />
- </label>
- </td>
- <td>
- <a
- href={`#/orders?instance=${i.id}`}
- onClick={(e) => {
- setInstanceName(i.id);
- }}
- >
- {i.id}
- </a>
- </td>
- <td>{i.name}</td>
- <td class="is-actions-cell right-sticky">
- <div class="buttons is-right">
- <button
- class="button is-small is-success jb-modal"
- type="button"
- onClick={(): void => onUpdate(i.id)}
- >
- <i18n.Translate>Edit</i18n.Translate>
- </button>
- {!i.deleted && (
- <button
- class="button is-small is-danger jb-modal is-outlined"
- type="button"
- onClick={(): void => onDelete(i)}
- >
- <i18n.Translate>Delete</i18n.Translate>
- </button>
- )}
- {i.deleted && (
- <button
- class="button is-small is-danger jb-modal"
- type="button"
- onClick={(): void => onPurge(i)}
- >
- <i18n.Translate>Purge</i18n.Translate>
- </button>
- )}
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no instances yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
-
-interface Actions {
- element: MerchantBackend.Instances.Instance;
- type: "DELETE" | "UPDATE";
-}
-
-function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
- return value !== null && value !== undefined;
-}
-
-function buildActions(
- instances: MerchantBackend.Instances.Instance[],
- selected: string[],
- action: "DELETE",
-): Actions[] {
- return selected
- .map((id) => instances.find((i) => i.id === id))
- .filter(notEmpty)
- .map((id) => ({ element: id, type: action }));
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/list/View.stories.tsx b/packages/auditor-backoffice-ui/src/paths/admin/list/View.stories.tsx
deleted file mode 100644
index c4c0996f6..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/list/View.stories.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 } from "preact";
-import { View } from "./View.js";
-
-export default {
- title: "Pages/Instance/List",
- component: View,
- argTypes: {
- onSelect: { action: "onSelect" },
- },
-};
-
-export const Empty = (a: any) => <View {...a} />;
-Empty.args = {
- instances: [],
-};
-
-export const WithDefaultInstance = (a: any) => <View {...a} />;
-WithDefaultInstance.args = {
- instances: [
- {
- id: "default",
- name: "the default instance",
- merchant_pub: "abcdef",
- payment_targets: [],
- },
- ],
-};
-
-export const WithFiveInstance = (a: any) => <View {...a} />;
-WithFiveInstance.args = {
- instances: [
- {
- id: "first",
- name: "the first instance",
- merchant_pub: "abcdefgh",
- payment_targets: ["asd"],
- },
- {
- id: "second",
- name: "the second instance",
- merchant_pub: "zxczxcz",
- payment_targets: ["asd"],
- },
- {
- id: "third",
- name: "the third instance",
- merchant_pub: "QWEQWEWQE",
- payment_targets: ["asd"],
- },
- {
- id: "other",
- name: "the other instance",
- merchant_pub: "FHJHGJGHJ",
- payment_targets: ["asd"],
- },
- {
- id: "another",
- name: "the another instance",
- merchant_pub: "abcd3423423efgh",
- payment_targets: ["asd"],
- },
- {
- id: "last",
- name: "last instance",
- merchant_pub: "zxcvvbnm",
- payment_targets: ["pay-to", "asd"],
- },
- ],
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/list/View.tsx b/packages/auditor-backoffice-ui/src/paths/admin/list/View.tsx
deleted file mode 100644
index 35b59633b..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/list/View.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { MerchantBackend } from "../../../declaration.js";
-import { CardTable as CardTableActive } from "./TableActive.js";
-
-interface Props {
- instances: MerchantBackend.Instances.Instance[];
- onCreate: () => void;
- onUpdate: (id: string) => void;
- onDelete: (id: MerchantBackend.Instances.Instance) => void;
- onPurge: (id: MerchantBackend.Instances.Instance) => void;
- selected?: boolean;
- setInstanceName: (s: string) => void;
-}
-
-export function View({
- instances,
- onCreate,
- onDelete,
- onPurge,
- onUpdate,
- setInstanceName,
- selected,
-}: Props): VNode {
- const [show, setShow] = useState<"active" | "deleted" | null>("active");
- const showIsActive = show === "active" ? "is-active" : "";
- const showIsDeleted = show === "deleted" ? "is-active" : "";
- const showAll = show === null ? "is-active" : "";
- const { i18n } = useTranslationContext();
-
- const showingInstances = showIsDeleted
- ? instances.filter((i) => i.deleted)
- : showIsActive
- ? instances.filter((i) => !i.deleted)
- : instances;
-
- return (
- <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>
- </div>
- <CardTableActive
- instances={showingInstances}
- onDelete={onDelete}
- onPurge={onPurge}
- setInstanceName={setInstanceName}
- onUpdate={onUpdate}
- selected={selected}
- onCreate={onCreate}
- />
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/admin/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/admin/list/index.tsx
deleted file mode 100644
index d01460ac9..000000000
--- a/packages/auditor-backoffice-ui/src/paths/admin/list/index.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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 { DeleteModal, PurgeModal } from "../../../components/modal/index.js";
-import { MerchantBackend } from "../../../declaration.js";
-import { useAdminAPI, useBackendInstances } from "../../../hooks/instance.js";
-import { Notification } from "../../../utils/types.js";
-import { View } from "./View.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-interface Props {
- onCreate: () => void;
- onUpdate: (id: string) => void;
- instances: MerchantBackend.Instances.Instance[];
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- setInstanceName: (s: string) => void;
-}
-
-export default function Instances({
- onUnauthorized,
- onLoadError,
- onNotFound,
- onCreate,
- onUpdate,
- setInstanceName,
-}: Props): VNode {
- const result = useBackendInstances();
- const [deleting, setDeleting] =
- useState<MerchantBackend.Instances.Instance | null>(null);
- const [purging, setPurging] =
- useState<MerchantBackend.Instances.Instance | null>(null);
- const { deleteInstance, purgeInstance } = useAdminAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <View
- instances={result.data.instances}
- onDelete={setDeleting}
- onCreate={onCreate}
- onPurge={setPurging}
- onUpdate={onUpdate}
- setInstanceName={setInstanceName}
- selected={!!deleting}
- />
- {deleting && (
- <DeleteModal
- element={deleting}
- onCancel={() => setDeleting(null)}
- onConfirm={async (): Promise<void> => {
- try {
- await deleteInstance(deleting.id);
- // pushNotification({ message: 'delete_success', type: 'SUCCESS' })
- setNotif({
- message: i18n.str`Instance "${deleting.name}" (ID: ${deleting.id}) has been deleted`,
- type: "SUCCESS",
- });
- } catch (error) {
- setNotif({
- message: i18n.str`Failed to delete instance`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- // pushNotification({ message: 'delete_error', type: 'ERROR' })
- }
- setDeleting(null);
- }}
- />
- )}
- {purging && (
- <PurgeModal
- element={purging}
- onCancel={() => setPurging(null)}
- onConfirm={async (): Promise<void> => {
- try {
- await purgeInstance(purging.id);
- setNotif({
- message: i18n.str`Instance '${purging.name}' (ID: ${purging.id}) has been disabled`,
- type: "SUCCESS",
- });
- } catch (error) {
- setNotif({
- message: i18n.str`Failed to purge instance`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- }
- setPurging(null);
- }}
- />
- )}
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
deleted file mode 100644
index 50cd801d8..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Accounts/Create",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
deleted file mode 100644
index 09b1d38ec..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, 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 { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { undefinedIfEmpty } from "../../../../utils/table.js";
-
-type Entity = MerchantBackend.BankAccounts.AccountAddDetails & { repeatPassword: string };
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-const accountAuthType = ["none", "basic"];
-
-function isValidURL(s: string): boolean {
- try {
- const u = new URL(s)
- return true;
- } catch (e) {
- return false;
- }
-}
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const [state, setState] = useState<Partial<Entity>>({});
- const errors: FormErrors<Entity> = {
- payto_uri: !state.payto_uri ? i18n.str`required` : undefined,
-
- credit_facade_credentials: !state.credit_facade_credentials
- ? undefined
- : undefinedIfEmpty({
- username:
- state.credit_facade_credentials.type === "basic" && !state.credit_facade_credentials.username
- ? i18n.str`required`
- : undefined,
- password:
- state.credit_facade_credentials.type === "basic" && !state.credit_facade_credentials.password
- ? i18n.str`required`
- : undefined,
- }),
- credit_facade_url: !state.credit_facade_url
- ? undefined
- : !isValidURL(state.credit_facade_url) ? i18n.str`not valid url`
- : undefined,
- repeatPassword:
- !state.credit_facade_credentials
- ? undefined
- : state.credit_facade_credentials.type === "basic" && (!state.credit_facade_credentials.password || state.credit_facade_credentials.password !== state.repeatPassword)
- ? i18n.str`is not the same`
- : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- delete state.repeatPassword
- return onCreate(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}
- >
- <InputPaytoForm<Entity>
- name="payto_uri"
- label={i18n.str`Account`}
- />
- <Input<Entity>
- name="credit_facade_url"
- label={i18n.str`Account info URL`}
- help="https://bank.com"
- expand
- tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
- />
- <InputSelector
- name="credit_facade_credentials.type"
- label={i18n.str`Auth type`}
- tooltip={i18n.str`Choose the authentication type for the account info URL`}
- values={accountAuthType}
- toStr={(str) => {
- if (str === "none") return "Without authentication";
- return "Username and password";
- }}
- />
- {state.credit_facade_credentials?.type === "basic" ? (
- <Fragment>
- <Input
- name="credit_facade_credentials.username"
- label={i18n.str`Username`}
- tooltip={i18n.str`Username to access the account information.`}
- />
- <Input
- name="credit_facade_credentials.password"
- inputType="password"
- label={i18n.str`Password`}
- tooltip={i18n.str`Password to access the account information.`}
- />
- <Input
- name="repeatPassword"
- inputType="password"
- label={i18n.str`Repeat password`}
- />
- </Fragment>
- ) : undefined}
- </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/auditor-backoffice-ui/src/paths/instance/accounts/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/index.tsx
deleted file mode 100644
index ad5d025d6..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/create/index.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useWebhookAPI } from "../../../../hooks/webhooks.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
-import { useBankAccountAPI } from "../../../../hooks/bank.js";
-
-export type Entity = MerchantBackend.BankAccounts.AccountAddDetails;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-
-export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
- const { createBankAccount } = useBankAccountAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- return (
- <>
- <NotificationCard notification={notif} />
- <CreatePage
- onBack={onBack}
- onCreate={(request: Entity) => {
- return createBankAccount(request)
- .then((d) => {
- onConfirm()
- })
- .catch((error) => {
- setNotif({
- message: i18n.str`could not create device`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
deleted file mode 100644
index 18e762642..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { FunctionalComponent, h } from "preact";
-import { ListPage as TestedComponent } from "./ListPage.js";
-
-export default {
- title: "Pages/Accounts/List",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
deleted file mode 100644
index 3359d1a95..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { CardTable } from "./Table.js";
-
-export interface Props {
- devices: MerchantBackend.BankAccounts.BankAccountEntry[];
- onLoadMoreBefore?: () => void;
- onLoadMoreAfter?: () => void;
- onCreate: () => void;
- onDelete: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
- onSelect: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
-}
-
-export function ListPage({
- devices,
- onCreate,
- onDelete,
- onSelect,
- onLoadMoreBefore,
- onLoadMoreAfter,
-}: Props): VNode {
- const form = { payto_uri: "" };
-
- const { i18n } = useTranslationContext();
- return (
- <section class="section is-main-section">
- <CardTable
- accounts={devices.map((o) => ({
- ...o,
- id: String(o.h_wire),
- }))}
- onCreate={onCreate}
- onDelete={onDelete}
- onSelect={onSelect}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreBefore={!onLoadMoreBefore}
- onLoadMoreAfter={onLoadMoreAfter}
- hasMoreAfter={!onLoadMoreAfter}
- />
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
deleted file mode 100644
index 77e820e04..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import { MerchantBackend } from "../../../../declaration.js";
-import { parsePaytoUri, PaytoType, PaytoUri, PaytoUriBitcoin, PaytoUriIBAN, PaytoUriTalerBank, PaytoUriUnknown } from "@gnu-taler/taler-util";
-
-type Entity = MerchantBackend.BankAccounts.BankAccountEntry;
-
-interface Props {
- accounts: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- onCreate: () => void;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-export function CardTable({
- accounts,
- onCreate,
- onDelete,
- onSelect,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-newspaper" />
- </span>
- <i18n.Translate>Bank accounts</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new accounts`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {accounts.length > 0 ? (
- <Table
- accounts={accounts}
- onDelete={onDelete}
- onSelect={onSelect}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- accounts: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
-function Table({
- accounts,
- onLoadMoreAfter,
- onDelete,
- onSelect,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const emptyList: Record<PaytoType | "unknown", { parsed: PaytoUri, acc: Entity }[]> = { "bitcoin": [], "x-taler-bank": [], "iban": [], "unknown": [], }
- const accountsByType = accounts.reduce((prev, acc) => {
- const parsed = parsePaytoUri(acc.payto_uri)
- if (!parsed) return prev //skip
- if (parsed.targetType !== "bitcoin" && parsed.targetType !== "x-taler-bank" && parsed.targetType !== "iban") {
- prev["unknown"].push({ parsed, acc })
- } else {
- prev[parsed.targetType].push({ parsed, acc })
- }
- return prev
- }, emptyList)
-
- const bitcoinAccounts = accountsByType["bitcoin"]
- const talerbankAccounts = accountsByType["x-taler-bank"]
- const ibanAccounts = accountsByType["iban"]
- const unkownAccounts = accountsByType["unknown"]
-
-
- return (
- <Fragment>
-
- {bitcoinAccounts.length > 0 && <div class="table-container">
- <p class="card-header-title"><i18n.Translate>Bitcoin type accounts</i18n.Translate></p>
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Address</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Sewgit 1</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Sewgit 2</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {bitcoinAccounts.map(({ parsed, acc }, idx) => {
- const ac = parsed as PaytoUriBitcoin
- return (
- <tr key={idx}>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.targetPath}
- </td>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.segwitAddrs[0]}
- </td>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.segwitAddrs[1]}
- </td>
- <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 accounts from the database`}
- onClick={() => onDelete(acc)}
- >
- Delete
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>}
-
-
-
- {talerbankAccounts.length > 0 && <div class="table-container">
- <p class="card-header-title"><i18n.Translate>Taler type accounts</i18n.Translate></p>
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Host</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Account name</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {talerbankAccounts.map(({ parsed, acc }, idx) => {
- const ac = parsed as PaytoUriTalerBank
- return (
- <tr key={idx}>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.host}
- </td>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.account}
- </td>
- <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 accounts from the database`}
- onClick={() => onDelete(acc)}
- >
- Delete
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>}
-
- {ibanAccounts.length > 0 && <div class="table-container">
- <p class="card-header-title"><i18n.Translate>IBAN type accounts</i18n.Translate></p>
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Account name</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>IBAN</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>BIC</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {ibanAccounts.map(({ parsed, acc }, idx) => {
- const ac = parsed as PaytoUriIBAN
- return (
- <tr key={idx}>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.params["receiver-name"]}
- </td>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.iban}
- </td>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.bic ?? ""}
- </td>
- <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 accounts from the database`}
- onClick={() => onDelete(acc)}
- >
- Delete
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>}
-
- {unkownAccounts.length > 0 && <div class="table-container">
- <p class="card-header-title"><i18n.Translate>Other type accounts</i18n.Translate></p>
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Type</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Path</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {unkownAccounts.map(({ parsed, acc }, idx) => {
- const ac = parsed as PaytoUriUnknown
- return (
- <tr key={idx}>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.targetType}
- </td>
- <td
- onClick={(): void => onSelect(acc)}
- style={{ cursor: "pointer" }}
- >
- {ac.targetPath}
- </td>
- <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 accounts from the database`}
- onClick={() => onDelete(acc)}
- >
- Delete
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>}
- </Fragment>
-
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no accounts yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/index.tsx
deleted file mode 100644
index cbe03d660..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/list/index.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { HttpStatusCode } from "@gnu-taler/taler-util";
-import {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } 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 { useInstanceOtpDevices, useOtpDeviceAPI } from "../../../../hooks/otp.js";
-import { Notification } from "../../../../utils/types.js";
-import { ListPage } from "./ListPage.js";
-import { useBankAccountAPI, useInstanceBankAccounts } from "../../../../hooks/bank.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onCreate: () => void;
- onSelect: (id: string) => void;
-}
-
-export default function ListOtpDevices({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const [position, setPosition] = useState<string | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { deleteBankAccount } = useBankAccountAPI();
- const result = useInstanceBankAccounts({ position }, (id) => setPosition(id));
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- <ListPage
- devices={result.data.accounts}
- onLoadMoreBefore={
- result.isReachingStart ? result.loadMorePrev : undefined
- }
- onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
- onCreate={onCreate}
- onSelect={(e) => {
- onSelect(e.h_wire);
- }}
- onDelete={(e: MerchantBackend.BankAccounts.BankAccountEntry) =>
- deleteBankAccount(e.h_wire)
- .then(() =>
- setNotif({
- message: i18n.str`bank account delete successfully`,
- type: "SUCCESS",
- }),
- )
- .catch((error) =>
- setNotif({
- message: i18n.str`could not delete the bank account`,
- type: "ERROR",
- description: error.message,
- }),
- )
- }
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
deleted file mode 100644
index 06ea9d07a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/OtpDevices/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
deleted file mode 100644
index 4efb822a3..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, 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 { MerchantBackend, WithId } from "../../../../declaration.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
-import { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js";
-import { undefinedIfEmpty } from "../../../../utils/table.js";
-
-type Entity = MerchantBackend.BankAccounts.BankAccountEntry
- & WithId;
-
-const accountAuthType = ["unedit", "none", "basic"];
-interface Props {
- onUpdate: (d: MerchantBackend.BankAccounts.AccountPatchDetails) => Promise<void>;
- onBack?: () => void;
- account: Entity;
-}
-
-
-export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const [state, setState] = useState<Partial<MerchantBackend.BankAccounts.AccountPatchDetails>>(account);
-
- const errors: FormErrors<MerchantBackend.BankAccounts.AccountPatchDetails> = {
- credit_facade_url: !state.credit_facade_url ? i18n.str`required` : !isValidURL(state.credit_facade_url) ? i18n.str`invalid url` : undefined,
- credit_facade_credentials: undefinedIfEmpty({
-
- username: state.credit_facade_credentials?.type !== "basic" ? undefined
- : !state.credit_facade_credentials.username ? i18n.str`required` : undefined,
-
- password: state.credit_facade_credentials?.type !== "basic" ? undefined
- : !state.credit_facade_credentials.password ? i18n.str`required` : undefined,
-
- repeatPassword: state.credit_facade_credentials?.type !== "basic" ? undefined
- : !(state.credit_facade_credentials as any).repeatPassword ? i18n.str`required` :
- (state.credit_facade_credentials as any).repeatPassword !== state.credit_facade_credentials.password ? i18n.str`doesn't match`
- : undefined,
- }),
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
-
- const creds: typeof state.credit_facade_credentials =
- state.credit_facade_credentials?.type === "basic" ? {
- type: "basic",
- password: state.credit_facade_credentials.password,
- username: state.credit_facade_credentials.username,
- } : state.credit_facade_credentials?.type === "none" ? {
- type: "none"
- } : undefined;
-
- return onUpdate({
- credit_facade_credentials: creds,
- credit_facade_url: state.credit_facade_url,
- });
- };
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- Account: <b>{account.id.substring(0, 8)}...</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column is-four-fifths">
- <FormProvider
- object={state}
- valueHandler={setState}
- errors={errors}
- >
- <InputPaytoForm<Entity>
- name="payto_uri"
- label={i18n.str`Account`}
- readonly
- />
- <Input<Entity>
- name="credit_facade_url"
- label={i18n.str`Account info URL`}
- help="https://bank.com"
- expand
- tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
- />
- <InputSelector
- name="credit_facade_credentials.type"
- label={i18n.str`Auth type`}
- tooltip={i18n.str`Choose the authentication type for the account info URL`}
- values={accountAuthType}
- toStr={(str) => {
- if (str === "none") return "Without authentication";
- if (str === "basic") return "With authentication";
- return "Do not change"
- }}
- />
- {state.credit_facade_credentials?.type === "basic" ? (
- <Fragment>
- <Input
- name="credit_facade_credentials.username"
- label={i18n.str`Username`}
- tooltip={i18n.str`Username to access the account information.`}
- />
- <Input
- name="credit_facade_credentials.password"
- inputType="password"
- label={i18n.str`Password`}
- tooltip={i18n.str`Password to access the account information.`}
- />
- <Input
- name="credit_facade_credentials.repeatPassword"
- inputType="password"
- label={i18n.str`Repeat password`}
- />
- </Fragment>
- ) : undefined}
- </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>
- </section>
- </section>
- </div>
- );
-}
-
-function isValidURL(s: string): boolean {
- try {
- const u = new URL(s)
- return true;
- } catch (e) {
- return false;
- }
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/index.tsx
deleted file mode 100644
index ca0b692a3..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/accounts/update/index.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { HttpStatusCode } from "@gnu-taler/taler-util";
-import {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { Loading } from "../../../../components/exception/loading.js";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { useBankAccountAPI, useBankAccountDetails } from "../../../../hooks/bank.js";
-import { Notification } from "../../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
-
-export type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
-
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- bid: string;
-}
-export default function UpdateValidator({
- bid,
- onConfirm,
- onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
-}: Props): VNode {
- const { updateBankAccount } = useBankAccountAPI();
- const result = useBankAccountDetails(bid);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <UpdatePage
- account={{ ...result.data, id: bid }}
- onBack={onBack}
- onUpdate={(data) => {
- return updateBankAccount(bid, data)
- .then(onConfirm)
- .catch((error) => {
- setNotif({
- message: i18n.str`could not update account`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/Create.stories.tsx
deleted file mode 100644
index 22bbfe28a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/Create.stories.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Product/Create",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatePage.tsx
deleted file mode 100644
index 6cbc26d8d..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatePage.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
-import { ProductForm } from "../../../../components/product/ProductForm.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useListener } from "../../../../hooks/listener.js";
-
-type Entity = MerchantBackend.Products.ProductAddDetail & {
- product_id: string;
-};
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const [submitForm, addFormSubmitter] = useListener<Entity | undefined>(
- (result) => {
- if (result) return onCreate(result);
- return Promise.reject();
- },
- );
-
- const { i18n } = useTranslationContext();
-
- return (
- <div>
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <ProductForm onSubscribe={addFormSubmitter} />
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <AsyncButton
- onClick={submitForm}
- data-tooltip={
- !submitForm
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={!submitForm}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatedSuccessfully.tsx
deleted file mode 100644
index 4f7da26da..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/CreatedSuccessfully.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { h, VNode } from "preact";
-import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { Entity } from "./index.js";
-import emptyImage from "../../assets/empty.png";
-
-interface Props {
- entity: Entity;
- onConfirm: () => void;
- onCreateAnother?: () => void;
-}
-
-export function CreatedSuccessfully({
- entity,
- onConfirm,
- onCreateAnother,
-}: Props): VNode {
- return (
- <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Image</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Description</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Price</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- </p>
- </div>
- </div>
- </div>
- </Template>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/index.tsx
deleted file mode 100644
index d537f6bf0..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/create/index.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { AuditorBackend } from "../../../../declaration.js";
-import { useDepositConfirmationAPI } from "../../../../hooks/deposit_confirmations.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-
-export type Entity = AuditorBackend.DepositConfirmation.DepositConfirmationDetail;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-export default function CreateProduct({ onConfirm, onBack }: Props): VNode {
- const { createDepositConfirmation } = useDepositConfirmationAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/List.stories.tsx
deleted file mode 100644
index f4368293b..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/List.stories.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { CardTable as TestedComponent } from "./Table.js";
-
-export default {
- title: "Pages/Product/List",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onSelect: { action: "onSelect" },
- onDelete: { action: "onDelete" },
- onUpdate: { action: "onUpdate" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/Table.tsx
deleted file mode 100644
index e71b1eeaa..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/list/Table.tsx
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { Amounts } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { ComponentChildren, Fragment, h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import emptyImage from "../../../../assets/empty.png";
-import {
- FormErrors,
- FormProvider,
-} from "../../../../components/form/FormProvider.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { AuditorBackend, WithId } from "../../../../declaration.js";
-import { dateFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = AuditorBackend.DepositConfirmation.DepositConfirmationDetail & WithId;
-
-interface Props {
- instances: Entity[];
- onDelete: (id: Entity) => void;
- onSelect: (depositConfirmation: Entity) => void;
- onUpdate: (
- id: string,
- data: AuditorBackend.DepositConfirmation.DepositConfirmationDetail,
- ) => Promise<void>;
- onCreate: () => void;
- selected?: boolean;
-}
-
-export function CardTable({
- instances,
- onCreate,
- onSelect,
- onUpdate,
- onDelete,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string | undefined>(
- undefined,
- );
- const { i18n } = useTranslationContext();
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-shopping" />
- </span>
- <i18n.Translate>Deposit Confirmations</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add deposit-confirmation`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {instances.length > 0 ? (
- <Table
- instances={instances}
- onSelect={onSelect}
- onDelete={onDelete}
- onUpdate={onUpdate}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string | undefined;
- instances: Entity[];
- onSelect: (id: Entity) => void;
- onUpdate: (
- id: string,
- data: AuditorBackend.DepositConfirmation.DepositConfirmationDetail,
- ) => Promise<void>;
- onDelete: (serial_id: Entity) => void;
- rowSelectionHandler: StateUpdater<string | undefined>;
-}
-
-function Table({
- rowSelection,
- rowSelectionHandler,
- instances,
- onSelect,
- onUpdate,
- onDelete,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = useSettings();
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Image</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Description</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Price per unit</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Taxes</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Sales</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Stock</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Sold</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
-
- return (
- <Fragment key={i.id}>
- <tr key="info">
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- </td>
-
- <td class="is-actions-cell right-sticky">
- <div class="buttons is-right">
- <span
- class="has-tooltip-bottom"
- data-tooltip={i18n.str`go to product update page`}
- >
- <button
- class="button is-small is-success "
- type="button"
- onClick={(): void => onSelect(i)}
- >
- <i18n.Translate>Update</i18n.Translate>
- </button>
- </span>
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`remove this product from the database`}
- >
- <button
- class="button is-small is-danger"
- type="button"
- onClick={(): void => onDelete(i)}
- >
- <i18n.Translate>Delete</i18n.Translate>
- </button>
- </span>
- </div>
- </td>
- </tr>
- {rowSelection === i.id && (
- <tr key="form">
- <td colSpan={10}>
- </td>
- </tr>
- )}
- </Fragment>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-interface FastProductUpdate {
- incoming: number;
- lost: number;
- price: string;
-}
-interface UpdatePrice {
- price: string;
-}
-
-
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no products yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
-
-function difference(price: string, tax: number) {
- if (!tax) return price;
- const ps = price.split(":");
- const p = parseInt(ps[1], 10);
- ps[1] = `${p - tax}`;
- return ps.join(":");
-} \ No newline at end of file
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/Update.stories.tsx
deleted file mode 100644
index d1dc9d540..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/Update.stories.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/Product/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const WithManagedStock = createExample(TestedComponent, {
- product: {
- product_id: "20102-ASDAS-QWE",
- description: "description1",
- description_i18n: {} as any,
- image: "",
- price: "TESTKUDOS:10",
- taxes: [],
- total_lost: 10,
- total_sold: 5,
- total_stock: 15,
- unit: "bar",
- address: {},
- },
-});
-
-export const WithInfiniteStock = createExample(TestedComponent, {
- product: {
- product_id: "20102-ASDAS-QWE",
- description: "description1",
- description_i18n: {} as any,
- image: "",
- price: "TESTKUDOS:10",
- taxes: [],
- total_lost: 10,
- total_sold: 5,
- total_stock: -1,
- unit: "bar",
- address: {},
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/UpdatePage.tsx
deleted file mode 100644
index 53aa9d61f..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/deposit_confirmations/update/UpdatePage.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
-import { ProductForm } from "../../../../components/product/ProductForm.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useListener } from "../../../../hooks/listener.js";
-
-type Entity = MerchantBackend.Products.ProductDetail & { product_id: string };
-
-interface Props {
- onUpdate: (d: Entity) => Promise<void>;
- onBack?: () => void;
- product: Entity;
-}
-
-export function UpdatePage({ product, onUpdate, onBack }: Props): VNode {
- const [submitForm, addFormSubmitter] = useListener<Entity | undefined>(
- (result) => {
- if (result) return onUpdate(result);
- return Promise.resolve();
- },
- );
-
- const { i18n } = useTranslationContext();
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- <i18n.Translate>Product id:</i18n.Translate>
- <b>{product.product_id}</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <ProductForm
- initial={product}
- onSubscribe={addFormSubmitter}
- alreadyExist
- />
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <AsyncButton
- onClick={submitForm}
- data-tooltip={
- !submitForm
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={!submitForm}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/details/DetailPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/details/DetailPage.tsx
deleted file mode 100644
index 6e9b51106..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/details/DetailPage.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { FormProvider } from "../../../components/form/FormProvider.js";
-import { Input } from "../../../components/form/Input.js";
-import { MerchantBackend } from "../../../declaration.js";
-
-type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage;
-interface Props {
- onUpdate: () => void;
- onDelete: () => void;
- selected: MerchantBackend.Instances.QueryInstancesResponse;
-}
-
-function convert(
- from: MerchantBackend.Instances.QueryInstancesResponse,
-): Entity {
- const defaults = {
- default_wire_fee_amortization: 1,
- use_stefan: true,
- 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, ...from };
-}
-
-export function DetailPage({ selected }: Props): VNode {
- const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
-
- const { i18n } = useTranslationContext();
-
- return (
- <div>
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <h1 class="title">Here goes the instance description</h1>
- </div>
- </div>
- <div class="level-right" style="display: none;">
- <div class="level-item" />
- </div>
- </div>
- </div>
- </section>
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-6">
- <FormProvider<Entity> object={value} valueHandler={valueHandler}>
- <Input<Entity> name="name" readonly label={i18n.str`Name`} />
- </FormProvider>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/details/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/details/index.tsx
deleted file mode 100644
index 13dd3a2f6..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/details/index.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { ErrorType, HttpError } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { Loading } from "../../../components/exception/loading.js";
-import { DeleteModal } from "../../../components/modal/index.js";
-import { useInstanceContext } from "../../../context/instance.js";
-import { MerchantBackend } from "../../../declaration.js";
-import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance.js";
-import { DetailPage } from "./DetailPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onUpdate: () => void;
- onNotFound: () => VNode;
- onDelete: () => void;
-}
-
-export default function Detail({
- onUpdate,
- onLoadError,
- onUnauthorized,
- onDelete,
- onNotFound,
-}: Props): VNode {
- const { id } = useInstanceContext();
- const result = useInstanceDetails();
- const [deleting, setDeleting] = useState<boolean>(false);
-
- const { deleteInstance } = useInstanceAPI();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <DetailPage
- selected={result.data}
- onUpdate={onUpdate}
- onDelete={() => setDeleting(true)}
- />
- {deleting && (
- <DeleteModal
- element={{ name: result.data.name, id }}
- onCancel={() => setDeleting(false)}
- onConfirm={async (): Promise<void> => {
- try {
- await deleteInstance();
- onDelete();
- } catch (error) {
- //FIXME: show message error
- }
- setDeleting(false);
- }}
- />
- )}
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/details/stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/details/stories.tsx
deleted file mode 100644
index fa6e9d763..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/details/stories.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ConfigContextProvider } from "../../../context/config.js";
-import { DetailPage as TestedComponent } from "./DetailPage.js";
-
-export default {
- title: "Pages/Instance/Detail",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Internal: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const component = (args: any) => (
- <ConfigContextProvider
- value={{
- currency: "TESTKUDOS",
- version: "1",
- }}
- >
- <Internal {...(props as any)} />
- </ConfigContextProvider>
- );
- return { component, props };
-}
-
-export const Example = createExample(TestedComponent, {
- selected: {
- name: "name",
- auth: { method: "external" },
- address: {},
- user_type: "business",
- jurisdiction: {},
- use_stefan: true,
- default_pay_delay: {
- d_us: 1000 * 1000, //one second
- },
- default_wire_transfer_delay: {
- d_us: 1000 * 1000, //one second
- },
- merchant_pub: "ASDWQEKASJDKSADJ",
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/index.stories.ts b/packages/auditor-backoffice-ui/src/paths/instance/index.stories.ts
deleted file mode 100644
index cd8f3d11c..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/index.stories.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-
-export * as details from "./details/stories.js";
-export * as kycList from "./kyc/list/ListPage.stories.js";
-export * as reserve from "./reserves/create/CreatedSuccessfully.stories.js";
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
deleted file mode 100644
index a914639e5..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ListPage as TestedComponent } from "./ListPage.js";
-import * as tests from "@gnu-taler/web-util/testing";
-import { MerchantBackend } from "../../../../declaration.js";
-
-export default {
- title: "Pages/KYC/List",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-export const Example = tests.createExample(TestedComponent, {
- status: {
- timeout_kycs: [],
- pending_kycs: [
- {
- aml_status: 0,
- exchange_url: "http://exchange.taler",
- payto_uri: "payto://iban/de123123123",
- kyc_url: "http://exchange.taler/kyc",
- },
- {
- aml_status: 1,
- exchange_url: "http://exchange.taler",
- payto_uri: "payto://iban/de123123123",
- },
- {
- aml_status: 2,
- exchange_url: "http://exchange.taler",
- payto_uri: "payto://iban/de123123123",
- },
- ],
- } as MerchantBackend.KYC.AccountKycRedirects,
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
deleted file mode 100644
index 2ec0137d9..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-
-export interface Props {
- status: MerchantBackend.KYC.AccountKycRedirects;
-}
-
-export function ListPage({ status }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- return (
- <section class="section is-main-section">
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-clock" />
- </span>
- <i18n.Translate>Pending KYC verification</i18n.Translate>
- </p>
-
- <div class="card-header-icon" aria-label="more options" />
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {status.pending_kycs.length > 0 ? (
- <PendingTable entries={status.pending_kycs} />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
-
- {status.timeout_kycs.length > 0 ? (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-clock" />
- </span>
- <i18n.Translate>Timed out</i18n.Translate>
- </p>
-
- <div class="card-header-icon" aria-label="more options" />
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {status.timeout_kycs.length > 0 ? (
- <TimedOutTable entries={status.timeout_kycs} />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- ) : undefined}
- </section>
- );
-}
-interface PendingTableProps {
- entries: MerchantBackend.KYC.MerchantAccountKycRedirect[];
-}
-
-interface TimedOutTableProps {
- entries: MerchantBackend.KYC.ExchangeKycTimeout[];
-}
-
-function PendingTable({ entries }: PendingTableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- <table class="table is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Exchange</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Target account</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Reason</i18n.Translate>
- </th>
- </tr>
- </thead>
- <tbody>
- {entries.map((e, i) => {
- if (e.kyc_url === undefined) {
- // blocked by AML
- return (
- <tr key={i}>
- <td>{e.exchange_url}</td>
- <td>{e.payto_uri}</td>
- <td>
- {e.aml_status === 1 ? (
- <i18n.Translate>
- There is an anti-money laundering process pending to
- complete.
- </i18n.Translate>
- ) : (
- <i18n.Translate>
- The account is frozen due to the anti-money laundering
- rules. Contact the exchange service provider for further
- instructions.
- </i18n.Translate>
- )}
- </td>
- </tr>
- );
- } else {
- // blocked by KYC
- return (
- <tr key={i}>
- <td>{e.exchange_url}</td>
- <td>{e.payto_uri}</td>
- <td>
- <a href={e.kyc_url} target="_black" rel="noreferrer">
- <i18n.Translate>
- Pending KYC process, click here to complete
- </i18n.Translate>
- </a>
- </td>
- </tr>
- );
- }
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-function TimedOutTable({ entries }: TimedOutTableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- <table class="table is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Exchange</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Code</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Http Status</i18n.Translate>
- </th>
- </tr>
- </thead>
- <tbody>
- {entries.map((e, i) => {
- return (
- <tr key={i}>
- <td>{e.exchange_url}</td>
- <td>{e.exchange_code}</td>
- <td>{e.exchange_http_status}</td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-happy mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>No pending kyc verification!</i18n.Translate>
- </p>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/index.tsx
deleted file mode 100644
index 664f05f66..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/kyc/list/index.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ErrorType, HttpError } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { Loading } from "../../../../components/exception/loading.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceKYCDetails } from "../../../../hooks/instance.js";
-import { ListPage } from "./ListPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
-}
-
-export default function ListKYC({
- onUnauthorized,
- onLoadError,
- onNotFound,
-}: Props): VNode {
- const result = useInstanceKYCDetails();
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- const status = result.data.type === "ok" ? undefined : result.data.status;
-
- if (!status) {
- return <div>no kyc required</div>;
- }
- return <ListPage status={status} />;
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
deleted file mode 100644
index fc814b68f..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Order/Create",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- goBack: { action: "goBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {
- instanceConfig: {
- default_pay_delay: {
- d_us: 1000 * 1000 * 60 * 60, //one hour
- },
- default_wire_transfer_delay: {
- d_us: 1000 * 1000 * 60 * 60, //one hour
- },
- use_stefan: true,
- },
- instanceInventory: [
- {
- id: "t-shirt-1",
- description: "a m size t-shirt",
- price: "TESTKUDOS:1",
- total_stock: -1,
- },
- {
- id: "t-shirt-2",
- price: "TESTKUDOS:1",
- description: "a xl size t-shirt",
- } as any,
- {
- id: "t-shirt-3",
- price: "TESTKUDOS:1",
- description: "a s size t-shirt",
- } as any,
- ],
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
deleted file mode 100644
index 92271f52f..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { AbsoluteTime, Amounts, Duration, TalerProtocolDuration } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format, isFuture } from "date-fns";
-import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import {
- FormErrors,
- FormProvider,
-} from "../../../../components/form/FormProvider.js";
-import { Input } from "../../../../components/form/Input.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputDate } from "../../../../components/form/InputDate.js";
-import { InputDuration } from "../../../../components/form/InputDuration.js";
-import { InputGroup } from "../../../../components/form/InputGroup.js";
-import { InputLocation } from "../../../../components/form/InputLocation.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { InputToggle } from "../../../../components/form/InputToggle.js";
-import { InventoryProductForm } from "../../../../components/product/InventoryProductForm.js";
-import { NonInventoryProductFrom } from "../../../../components/product/NonInventoryProductForm.js";
-import { ProductList } from "../../../../components/product/ProductList.js";
-import { useConfigContext } from "../../../../context/config.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { useSettings } from "../../../../hooks/useSettings.js";
-import { OrderCreateSchema as schema } from "../../../../schemas/index.js";
-import { rate } from "../../../../utils/amount.js";
-import { undefinedIfEmpty } from "../../../../utils/table.js";
-
-interface Props {
- onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
- onBack?: () => void;
- instanceConfig: InstanceConfig;
- instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[];
-}
-interface InstanceConfig {
- use_stefan: boolean;
- default_pay_delay: TalerProtocolDuration;
- default_wire_transfer_delay: TalerProtocolDuration;
-}
-
-function with_defaults(config: InstanceConfig, currency: string): Partial<Entity> {
- const defaultPayDeadline = Duration.fromTalerProtocolDuration(config.default_pay_delay);
- const defaultWireDeadline = Duration.fromTalerProtocolDuration(config.default_wire_transfer_delay);
-
- return {
- inventoryProducts: {},
- products: [],
- pricing: {},
- payments: {
- max_fee: undefined,
- createToken: true,
- pay_deadline: (defaultPayDeadline),
- refund_deadline: (defaultPayDeadline),
- wire_transfer_deadline: (defaultWireDeadline),
- },
- shipping: {},
- extra: {},
- };
-}
-
-interface ProductAndQuantity {
- product: MerchantBackend.Products.ProductDetail & WithId;
- quantity: number;
-}
-export interface ProductMap {
- [id: string]: ProductAndQuantity;
-}
-
-interface Pricing {
- products_price: string;
- order_price: string;
- summary: string;
-}
-interface Shipping {
- delivery_date?: Date;
- delivery_location?: MerchantBackend.Location;
- fullfilment_url?: string;
-}
-interface Payments {
- refund_deadline: Duration;
- pay_deadline: Duration;
- wire_transfer_deadline: Duration;
- auto_refund_deadline: Duration;
- max_fee?: string;
- createToken: boolean;
- minimum_age?: number;
-}
-interface Entity {
- inventoryProducts: ProductMap;
- products: MerchantBackend.Product[];
- pricing: Partial<Pricing>;
- payments: Partial<Payments>;
- shipping: Partial<Shipping>;
- extra: Record<string, string>;
-}
-
-const stringIsValidJSON = (value: string) => {
- try {
- JSON.parse(value.trim());
- return true;
- } catch {
- return false;
- }
-};
-
-export function CreatePage({
- onCreate,
- onBack,
- instanceConfig,
- instanceInventory,
-}: Props): VNode {
- const config = useConfigContext();
- const instance_default = with_defaults(instanceConfig, config.currency)
- const [value, valueHandler] = useState(instance_default);
- const zero = Amounts.zeroOfCurrency(config.currency);
- const [settings, updateSettings] = useSettings()
- const inventoryList = Object.values(value.inventoryProducts || {});
- const productList = Object.values(value.products || {});
-
- const { i18n } = useTranslationContext();
-
- const parsedPrice = !value.pricing?.order_price
- ? undefined
- : Amounts.parse(value.pricing.order_price);
-
- const errors: FormErrors<Entity> = {
- pricing: undefinedIfEmpty({
- summary: !value.pricing?.summary ? i18n.str`required` : undefined,
- order_price: !value.pricing?.order_price
- ? i18n.str`required`
- : !parsedPrice
- ? i18n.str`not valid`
- : Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
- }),
- payments: undefinedIfEmpty({
- refund_deadline: !value.payments?.refund_deadline
- ? undefined
- : value.payments.pay_deadline &&
- Duration.cmp(value.payments.refund_deadline, value.payments.pay_deadline) === -1
- ? i18n.str`refund deadline cannot be before pay deadline`
- : value.payments.wire_transfer_deadline &&
- Duration.cmp(
- value.payments.wire_transfer_deadline,
- value.payments.refund_deadline,
- ) === -1
- ? i18n.str`wire transfer deadline cannot be before refund deadline`
- : undefined,
- pay_deadline: !value.payments?.pay_deadline
- ? i18n.str`required`
- : value.payments.wire_transfer_deadline &&
- Duration.cmp(
- value.payments.wire_transfer_deadline,
- value.payments.pay_deadline,
- ) === -1
- ? i18n.str`wire transfer deadline cannot be before pay deadline`
- : undefined,
- wire_transfer_deadline: !value.payments?.wire_transfer_deadline
- ? i18n.str`required`
- : undefined,
- auto_refund_deadline: !value.payments?.auto_refund_deadline
- ? undefined
- : !value.payments?.refund_deadline
- ? i18n.str`should have a refund deadline`
- : Duration.cmp(
- value.payments.refund_deadline,
- value.payments.auto_refund_deadline,
- ) == -1
- ? i18n.str`auto refund cannot be after refund deadline`
- : undefined,
-
- }),
- shipping: undefinedIfEmpty({
- delivery_date: !value.shipping?.delivery_date
- ? undefined
- : !isFuture(value.shipping.delivery_date)
- ? i18n.str`should be in the future`
- : undefined,
- }),
- };
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submit = (): void => {
- const order = value as any; //schema.cast(value);
- if (!value.payments) return;
- if (!value.shipping) return;
-
- const request: MerchantBackend.Orders.PostOrderRequest = {
- order: {
- amount: order.pricing.order_price,
- summary: order.pricing.summary,
- products: productList,
- extra: undefinedIfEmpty(value.extra),
- pay_deadline: !value.payments.pay_deadline ?
- i18n.str`required` :
- AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.pay_deadline))
- ,// : undefined,
- wire_transfer_deadline: value.payments.wire_transfer_deadline
- ? AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.wire_transfer_deadline))
- : undefined,
- refund_deadline: value.payments.refund_deadline
- ? AbsoluteTime.toProtocolTimestamp(AbsoluteTime.addDuration(AbsoluteTime.now(), value.payments.refund_deadline))
- : undefined,
- auto_refund: value.payments.auto_refund_deadline
- ? Duration.toTalerProtocolDuration(value.payments.auto_refund_deadline)
- : undefined,
- max_fee: value.payments.max_fee as string,
-
- delivery_date: value.shipping.delivery_date
- ? { t_s: value.shipping.delivery_date.getTime() / 1000 }
- : undefined,
- delivery_location: value.shipping.delivery_location,
- fulfillment_url: value.shipping.fullfilment_url,
- minimum_age: value.payments.minimum_age,
- },
- inventory_products: inventoryList.map((p) => ({
- product_id: p.product.id,
- quantity: p.quantity,
- })),
- create_token: value.payments.createToken,
- };
-
- onCreate(request);
- };
-
- const addProductToTheInventoryList = (
- product: MerchantBackend.Products.ProductDetail & WithId,
- quantity: number,
- ) => {
- valueHandler((v) => {
- const inventoryProducts = { ...v.inventoryProducts };
- inventoryProducts[product.id] = { product, quantity };
- return { ...v, inventoryProducts };
- });
- };
-
- const removeProductFromTheInventoryList = (id: string) => {
- valueHandler((v) => {
- const inventoryProducts = { ...v.inventoryProducts };
- delete inventoryProducts[id];
- return { ...v, inventoryProducts };
- });
- };
-
- const addNewProduct = async (product: MerchantBackend.Product) => {
- return valueHandler((v) => {
- const products = v.products ? [...v.products, product] : [];
- return { ...v, products };
- });
- };
-
- const removeFromNewProduct = (index: number) => {
- valueHandler((v) => {
- const products = v.products ? [...v.products] : [];
- products.splice(index, 1);
- return { ...v, products };
- });
- };
-
- const [editingProduct, setEditingProduct] = useState<
- MerchantBackend.Product | undefined
- >(undefined);
-
- const totalPriceInventory = inventoryList.reduce((prev, cur) => {
- const p = Amounts.parseOrThrow(cur.product.price);
- return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount;
- }, zero);
-
- const totalPriceProducts = productList.reduce((prev, cur) => {
- if (!cur.price) return zero;
- const p = Amounts.parseOrThrow(cur.price);
- return Amounts.add(prev, Amounts.mult(p, cur.quantity).amount).amount;
- }, zero);
-
- const hasProducts = inventoryList.length > 0 || productList.length > 0;
- const totalPrice = Amounts.add(totalPriceInventory, totalPriceProducts);
-
- const totalAsString = Amounts.stringify(totalPrice.amount);
- const allProducts = productList.concat(inventoryList.map(asProduct));
-
- const [newField, setNewField] = useState("")
-
- useEffect(() => {
- valueHandler((v) => {
- return {
- ...v,
- pricing: {
- ...v.pricing,
- products_price: hasProducts ? totalAsString : undefined,
- order_price: hasProducts ? totalAsString : undefined,
- },
- };
- });
- }, [hasProducts, totalAsString]);
-
- const discountOrRise = rate(
- parsedPrice ?? Amounts.zeroOfCurrency(config.currency),
- totalPrice.amount,
- );
-
- const minAgeByProducts = allProducts.reduce(
- (cur, prev) =>
- !prev.minimum_age || cur > prev.minimum_age ? cur : prev.minimum_age,
- 0,
- );
-
- // if there is no default pay deadline
- const noDefault_payDeadline = !instance_default.payments || !instance_default.payments.pay_deadline
- // and there is no default wire deadline
- const noDefault_wireDeadline = !instance_default.payments || !instance_default.payments.wire_transfer_deadline
- // user required to set the taler options
- const requiresSomeTalerOptions = noDefault_payDeadline || noDefault_wireDeadline
-
-
- return (
- <div>
-
- <section class="section is-main-section">
- <div class="tabs is-toggle is-fullwidth is-small">
- <ul>
- <li class={!settings.advanceOrderMode ? "is-active" : ""} onClick={() => {
- updateSettings({
- ...settings,
- advanceOrderMode: false
- })
- }}>
- <a >
- <span><i18n.Translate>Simple</i18n.Translate></span>
- </a>
- </li>
- <li class={settings.advanceOrderMode ? "is-active" : ""} onClick={() => {
- updateSettings({
- ...settings,
- advanceOrderMode: true
- })
- }}>
- <a >
- <span><i18n.Translate>Advanced</i18n.Translate></span>
- </a>
- </li>
- </ul>
- </div>
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- {/* // FIXME: translating plural singular */}
- <InputGroup
- name="inventory_products"
- label={i18n.str`Manage products in order`}
- alternative={
- allProducts.length > 0 && (
- <p>
- {allProducts.length} products with a total price of{" "}
- {totalAsString}.
- </p>
- )
- }
- tooltip={i18n.str`Manage list of products in the order.`}
- >
- <InventoryProductForm
- currentProducts={value.inventoryProducts || {}}
- onAddProduct={addProductToTheInventoryList}
- inventory={instanceInventory}
- />
-
- {settings.advanceOrderMode &&
- <NonInventoryProductFrom
- productToEdit={editingProduct}
- onAddProduct={(p) => {
- setEditingProduct(undefined);
- return addNewProduct(p);
- }}
- />
- }
-
- {allProducts.length > 0 && (
- <ProductList
- list={allProducts}
- actions={[
- {
- name: i18n.str`Remove`,
- tooltip: i18n.str`Remove this product from the order.`,
- handler: (e, index) => {
- if (e.product_id) {
- removeProductFromTheInventoryList(e.product_id);
- } else {
- removeFromNewProduct(index);
- setEditingProduct(e);
- }
- },
- },
- ]}
- />
- )}
- </InputGroup>
-
- <FormProvider<Entity>
- errors={errors}
- object={value}
- valueHandler={valueHandler as any}
- >
- {hasProducts ? (
- <Fragment>
- <InputCurrency
- name="pricing.products_price"
- label={i18n.str`Total price`}
- readonly
- tooltip={i18n.str`total product price added up`}
- />
- <InputCurrency
- name="pricing.order_price"
- label={i18n.str`Total price`}
- addonAfter={
- discountOrRise > 0 &&
- (discountOrRise < 1
- ? `discount of %${Math.round(
- (1 - discountOrRise) * 100,
- )}`
- : `rise of %${Math.round((discountOrRise - 1) * 100)}`)
- }
- tooltip={i18n.str`Amount to be paid by the customer`}
- />
- </Fragment>
- ) : (
- <InputCurrency
- name="pricing.order_price"
- label={i18n.str`Order price`}
- tooltip={i18n.str`final order price`}
- />
- )}
-
- <Input
- name="pricing.summary"
- inputType="multiline"
- label={i18n.str`Summary`}
- tooltip={i18n.str`Title of the order to be shown to the customer`}
- />
-
- {settings.advanceOrderMode &&
- <InputGroup
- name="shipping"
- label={i18n.str`Shipping and Fulfillment`}
- initialActive
- >
- <InputDate
- name="shipping.delivery_date"
- label={i18n.str`Delivery date`}
- tooltip={i18n.str`Deadline for physical delivery assured by the merchant.`}
- />
- {value.shipping?.delivery_date && (
- <InputGroup
- name="shipping.delivery_location"
- label={i18n.str`Location`}
- tooltip={i18n.str`address where the products will be delivered`}
- >
- <InputLocation name="shipping.delivery_location" />
- </InputGroup>
- )}
- <Input
- name="shipping.fullfilment_url"
- label={i18n.str`Fulfillment URL`}
- tooltip={i18n.str`URL to which the user will be redirected after successful payment.`}
- />
- </InputGroup>
- }
-
- {(settings.advanceOrderMode || requiresSomeTalerOptions) &&
- <InputGroup
- name="payments"
- label={i18n.str`Taler payment options`}
- tooltip={i18n.str`Override default Taler payment settings for this order`}
- >
- {(settings.advanceOrderMode || noDefault_payDeadline) && <InputDuration
- name="payments.pay_deadline"
- label={i18n.str`Payment time`}
- help={<DeadlineHelp duration={value.payments?.pay_deadline} />}
- withForever
- withoutClear
- tooltip={i18n.str`Time for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline. Time start to run after the order is created.`}
- side={
- <span>
- <button class="button" onClick={() => {
- const c = {
- ...value,
- payments: {
- ...(value.payments ?? {}),
- pay_deadline: instance_default.payments?.pay_deadline
- }
- }
- valueHandler(c)
- }}>
- <i18n.Translate>default</i18n.Translate>
- </button>
- </span>
- }
- />}
- {settings.advanceOrderMode && <InputDuration
- name="payments.refund_deadline"
- label={i18n.str`Refund time`}
- help={<DeadlineHelp duration={value.payments?.refund_deadline} />}
- withForever
- withoutClear
- tooltip={i18n.str`Time while the order can be refunded by the merchant. Time starts after the order is created.`}
- side={
- <span>
- <button class="button" onClick={() => {
- valueHandler({
- ...value,
- payments: {
- ...(value.payments ?? {}),
- refund_deadline: instance_default.payments?.refund_deadline
- }
- })
- }}>
- <i18n.Translate>default</i18n.Translate>
- </button>
- </span>
- }
- />}
- {(settings.advanceOrderMode || noDefault_wireDeadline) && <InputDuration
- name="payments.wire_transfer_deadline"
- label={i18n.str`Wire transfer time`}
- help={<DeadlineHelp duration={value.payments?.wire_transfer_deadline} />}
- withoutClear
- withForever
- tooltip={i18n.str`Time for the exchange to make the wire transfer. Time starts after the order is created.`}
- side={
- <span>
- <button class="button" onClick={() => {
- valueHandler({
- ...value,
- payments: {
- ...(value.payments ?? {}),
- wire_transfer_deadline: instance_default.payments?.wire_transfer_deadline
- }
- })
- }}>
- <i18n.Translate>default</i18n.Translate>
- </button>
- </span>
- }
- />}
- {settings.advanceOrderMode && <InputDuration
- name="payments.auto_refund_deadline"
- label={i18n.str`Auto-refund time`}
- help={<DeadlineHelp duration={value.payments?.auto_refund_deadline} />}
- tooltip={i18n.str`Time until which the wallet will automatically check for refunds without user interaction.`}
- withForever
- />}
-
- {settings.advanceOrderMode && <InputCurrency
- name="payments.max_fee"
- label={i18n.str`Maximum fee`}
- tooltip={i18n.str`Maximum fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
- />}
- {settings.advanceOrderMode && <InputToggle
- name="payments.createToken"
- label={i18n.str`Create token`}
- tooltip={i18n.str`If the order ID is easy to guess the token will prevent user to steal orders from others.`}
- />}
- {settings.advanceOrderMode && <InputNumber
- name="payments.minimum_age"
- label={i18n.str`Minimum age required`}
- tooltip={i18n.str`Any value greater than 0 will limit the coins able be used to pay this contract. If empty the age restriction will be defined by the products`}
- help={
- minAgeByProducts > 0
- ? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
- : i18n.str`No product with age restriction in this order`
- }
- />}
- </InputGroup>
- }
-
- {settings.advanceOrderMode &&
- <InputGroup
- name="extra"
- label={i18n.str`Additional information`}
- tooltip={i18n.str`Custom information to be included in the contract for this order.`}
- >
- {Object.keys(value.extra ?? {}).map((key) => {
-
- return <Input
- name={`extra.${key}`}
- inputType="multiline"
- label={key}
- tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`}
- side={
- <button class="button" onClick={(e) => {
- if (value.extra && value.extra[key] !== undefined) {
- console.log(value.extra)
- delete value.extra[key]
- }
- valueHandler({
- ...value,
- })
- }}>remove</button>
- }
- />
- })}
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Custom field name</i18n.Translate>
- <span class="icon has-tooltip-right" data-tooltip={"new extra field"}>
- <i class="mdi mdi-information" />
- </span>
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input class="input " value={newField} onChange={(e) => setNewField(e.currentTarget.value)} />
- </p>
- </div>
- </div>
- <button class="button" onClick={(e) => {
- setNewField("")
- valueHandler({
- ...value,
- extra: {
- ...(value.extra ?? {}),
- [newField]: ""
- }
- })
- }}>add</button>
- </div>
- </InputGroup>
- }
- </FormProvider>
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <button
- class="button is-success"
- onClick={submit}
- disabled={hasErrors}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </button>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
-
-function asProduct(p: ProductAndQuantity): MerchantBackend.Product {
- return {
- product_id: p.product.id,
- image: p.product.image,
- price: p.product.price,
- unit: p.product.unit,
- quantity: p.quantity,
- description: p.product.description,
- taxes: p.product.taxes,
- minimum_age: p.product.minimum_age,
- };
-}
-
-
-function DeadlineHelp({ duration }: { duration?: Duration }): VNode {
- const { i18n } = useTranslationContext();
- const [now, setNow] = useState(AbsoluteTime.now())
- useEffect(() => {
- const iid = setInterval(() => {
- setNow(AbsoluteTime.now())
- }, 60 * 1000)
- return () => {
- clearInterval(iid)
- }
- })
- if (!duration) return <i18n.Translate>Disabled</i18n.Translate>
- const when = AbsoluteTime.addDuration(now, duration)
- if (when.t_ms === "never") return <i18n.Translate>No deadline</i18n.Translate>
- return <i18n.Translate>Deadline at {format(when.t_ms, "dd/MM/yy HH:mm")}</i18n.Translate>
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
deleted file mode 100644
index 3f7b20f52..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { CreatedSuccessfully } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { useOrderAPI } from "../../../../hooks/order.js";
-import { Entity } from "./index.js";
-
-interface Props {
- entity: Entity;
- onConfirm: () => void;
- onCreateAnother?: () => void;
-}
-
-export function OrderCreatedSuccessfully({
- entity,
- onConfirm,
- onCreateAnother,
-}: Props): VNode {
- const { getPaymentURL } = useOrderAPI();
- const [url, setURL] = useState<string | undefined>(undefined);
- const { i18n } = useTranslationContext();
- useEffect(() => {
- getPaymentURL(entity.response.order_id).then((response) => {
- setURL(response.data);
- });
- }, [getPaymentURL, entity.response.order_id]);
-
- return (
- <CreatedSuccessfully
- onConfirm={onConfirm}
- onCreateAnother={onCreateAnother}
- >
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Amount</i18n.Translate>
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- class="input"
- readonly
- value={entity.request.order.amount}
- />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Summary</i18n.Translate>
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- class="input"
- readonly
- value={entity.request.order.summary}
- />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Order ID</i18n.Translate>
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input class="input" readonly value={entity.response.order_id} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">
- <i18n.Translate>Payment URL</i18n.Translate>
- </label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input class="input" readonly value={url} />
- </p>
- </div>
- </div>
- </div>
- </CreatedSuccessfully>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/create/index.tsx
deleted file mode 100644
index d2a8619ce..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/create/index.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ErrorType, HttpError } from "@gnu-taler/web-util/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 { useInstanceDetails } from "../../../../hooks/instance.js";
-import { useOrderAPI } from "../../../../hooks/order.js";
-import { useInstanceProducts } from "../../../../hooks/product.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-export type Entity = {
- request: MerchantBackend.Orders.PostOrderRequest;
- response: MerchantBackend.Orders.PostOrderResponse;
-};
-interface Props {
- onBack?: () => void;
- onConfirm: (id: string) => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-}
-export default function OrderCreate({
- onConfirm,
- onBack,
- onLoadError,
- onNotFound,
- onUnauthorized,
-}: Props): VNode {
- const { createOrder } = useOrderAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const detailsResult = useInstanceDetails();
- const inventoryResult = useInstanceProducts();
-
- if (detailsResult.loading) return <Loading />;
- if (inventoryResult.loading) return <Loading />;
-
- if (!detailsResult.ok) {
- if (
- detailsResult.type === ErrorType.CLIENT &&
- detailsResult.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- detailsResult.type === ErrorType.CLIENT &&
- detailsResult.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(detailsResult);
- }
-
- if (!inventoryResult.ok) {
- if (
- inventoryResult.type === ErrorType.CLIENT &&
- inventoryResult.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- inventoryResult.type === ErrorType.CLIENT &&
- inventoryResult.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(inventoryResult);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- <CreatePage
- onBack={onBack}
- onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => {
- createOrder(request)
- .then((r) => {
- return onConfirm(r.data.order_id)
- })
- .catch((error) => {
- setNotif({
- message: "could not create order",
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- instanceConfig={detailsResult.data}
- instanceInventory={inventoryResult.data}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
deleted file mode 100644
index 22c6944b3..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { addDays } from "date-fns";
-import { h, VNode, FunctionalComponent } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-import { DetailPage as TestedComponent } from "./DetailPage.js";
-
-export default {
- title: "Pages/Order/Detail",
- component: TestedComponent,
- argTypes: {
- onRefund: { action: "onRefund" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-const defaultContractTerm = {
- amount: "TESTKUDOS:10",
- timestamp: {
- t_s: new Date().getTime() / 1000,
- },
- auditors: [],
- exchanges: [],
- max_fee: "TESTKUDOS:1",
- merchant: {} as any,
- merchant_base_url: "http://merchant.url/",
- order_id: "2021.165-03GDFC26Y1NNG",
- products: [],
- summary: "text summary",
- wire_transfer_deadline: {
- t_s: "never",
- },
- refund_deadline: { t_s: "never" },
- merchant_pub: "ASDASDASDSd",
- nonce: "QWEQWEQWE",
- pay_deadline: {
- t_s: "never",
- },
- wire_method: "x-taler-bank",
- h_wire: "asd",
-} as MerchantBackend.ContractTerms;
-
-// contract_terms: defaultContracTerm,
-export const Claimed = createExample(TestedComponent, {
- id: "2021.165-03GDFC26Y1NNG",
- selected: {
- order_status: "claimed",
- contract_terms: defaultContractTerm,
- },
-});
-
-export const PaidNotRefundable = createExample(TestedComponent, {
- id: "2021.165-03GDFC26Y1NNG",
- selected: {
- order_status: "paid",
- contract_terms: defaultContractTerm,
- refunded: false,
- deposit_total: "TESTKUDOS:10",
- exchange_ec: 0,
- order_status_url: "http://merchant.backend/status",
- exchange_hc: 0,
- refund_amount: "TESTKUDOS:0",
- refund_details: [],
- refund_pending: false,
- wire_details: [],
- wire_reports: [],
- wired: false,
- },
-});
-
-export const PaidRefundable = createExample(TestedComponent, {
- id: "2021.165-03GDFC26Y1NNG",
- selected: {
- order_status: "paid",
- contract_terms: {
- ...defaultContractTerm,
- refund_deadline: {
- t_s: addDays(new Date(), 2).getTime() / 1000,
- },
- },
- refunded: false,
- deposit_total: "TESTKUDOS:10",
- exchange_ec: 0,
- order_status_url: "http://merchant.backend/status",
- exchange_hc: 0,
- refund_amount: "TESTKUDOS:0",
- refund_details: [],
- refund_pending: false,
- wire_details: [],
- wire_reports: [],
- wired: false,
- },
-});
-
-export const Unpaid = createExample(TestedComponent, {
- id: "2021.165-03GDFC26Y1NNG",
- selected: {
- order_status: "unpaid",
- order_status_url: "http://merchant.backend/status",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- summary: "text summary",
- taler_pay_uri: "pay uri",
- total_amount: "TESTKUDOS:10",
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
deleted file mode 100644
index abcddb38a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { AmountJson, Amounts, stringifyRefundUri } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format, formatDistance } from "date-fns";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { FormProvider } from "../../../../components/form/FormProvider.js";
-import { Input } from "../../../../components/form/Input.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputDate } from "../../../../components/form/InputDate.js";
-import { InputDuration } from "../../../../components/form/InputDuration.js";
-import { InputGroup } from "../../../../components/form/InputGroup.js";
-import { InputLocation } from "../../../../components/form/InputLocation.js";
-import { TextField } from "../../../../components/form/TextField.js";
-import { ProductList } from "../../../../components/product/ProductList.js";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-import { mergeRefunds } from "../../../../utils/amount.js";
-import { RefundModal } from "../list/Table.js";
-import { Event, Timeline } from "./Timeline.js";
-
-type Entity = MerchantBackend.Orders.MerchantOrderStatusResponse;
-type CT = MerchantBackend.ContractTerms;
-
-interface Props {
- onBack: () => void;
- selected: Entity;
- id: string;
- onRefund: (id: string, value: MerchantBackend.Orders.RefundRequest) => void;
-}
-
-type Paid = MerchantBackend.Orders.CheckPaymentPaidResponse & {
- refund_taken: string;
-};
-type Unpaid = MerchantBackend.Orders.CheckPaymentUnpaidResponse;
-type Claimed = MerchantBackend.Orders.CheckPaymentClaimedResponse;
-
-function ContractTerms({ value }: { value: CT }) {
- const { i18n } = useTranslationContext();
-
- return (
- <InputGroup name="contract_terms" label={i18n.str`Contract Terms`}>
- <FormProvider<CT> object={value} valueHandler={null}>
- <Input<CT>
- readonly
- name="summary"
- label={i18n.str`Summary`}
- tooltip={i18n.str`human-readable description of the whole purchase`}
- />
- <InputCurrency<CT>
- readonly
- name="amount"
- label={i18n.str`Amount`}
- tooltip={i18n.str`total price for the transaction`}
- />
- {value.fulfillment_url && (
- <Input<CT>
- readonly
- name="fulfillment_url"
- label={i18n.str`Fulfillment URL`}
- tooltip={i18n.str`URL for this purchase`}
- />
- )}
- <Input<CT>
- readonly
- name="max_fee"
- label={i18n.str`Max fee`}
- tooltip={i18n.str`maximum total deposit fee accepted by the merchant for this contract`}
- />
- <InputDate<CT>
- readonly
- name="timestamp"
- label={i18n.str`Created at`}
- tooltip={i18n.str`time when this contract was generated`}
- />
- <InputDate<CT>
- readonly
- name="refund_deadline"
- label={i18n.str`Refund deadline`}
- tooltip={i18n.str`after this deadline has passed no refunds will be accepted`}
- />
- <InputDate<CT>
- readonly
- name="pay_deadline"
- label={i18n.str`Payment deadline`}
- tooltip={i18n.str`after this deadline, the merchant won't accept payments for the contract`}
- />
- <InputDate<CT>
- readonly
- name="wire_transfer_deadline"
- label={i18n.str`Wire transfer deadline`}
- tooltip={i18n.str`transfer deadline for the exchange`}
- />
- <InputDate<CT>
- readonly
- name="delivery_date"
- label={i18n.str`Delivery date`}
- tooltip={i18n.str`time indicating when the order should be delivered`}
- />
- {value.delivery_date && (
- <InputGroup
- name="delivery_location"
- label={i18n.str`Location`}
- tooltip={i18n.str`where the order will be delivered`}
- >
- <InputLocation name="payments.delivery_location" />
- </InputGroup>
- )}
- <InputDuration<CT>
- readonly
- name="auto_refund"
- label={i18n.str`Auto-refund delay`}
- tooltip={i18n.str`how long the wallet should try to get an automatic refund for the purchase`}
- />
- <Input<CT>
- readonly
- name="extra"
- label={i18n.str`Extra info`}
- tooltip={i18n.str`extra data that is only interpreted by the merchant frontend`}
- />
- </FormProvider>
- </InputGroup>
- );
-}
-
-function ClaimedPage({
- id,
- order,
-}: {
- id: string;
- order: MerchantBackend.Orders.CheckPaymentClaimedResponse;
-}) {
- const events: Event[] = [];
- if (order.contract_terms.timestamp.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.timestamp.t_s * 1000),
- description: "order created",
- type: "start",
- });
- }
- if (order.contract_terms.pay_deadline.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.pay_deadline.t_s * 1000),
- description: "pay deadline",
- type: "deadline",
- });
- }
- if (order.contract_terms.refund_deadline.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.refund_deadline.t_s * 1000),
- description: "refund deadline",
- type: "deadline",
- });
- }
- if (order.contract_terms.wire_transfer_deadline.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000),
- description: "wire deadline",
- type: "deadline",
- });
- }
- if (
- order.contract_terms.delivery_date &&
- order.contract_terms.delivery_date.t_s !== "never"
- ) {
- events.push({
- when: new Date(order.contract_terms.delivery_date?.t_s * 1000),
- description: "delivery",
- type: "delivery",
- });
- }
-
- const [value, valueHandler] = useState<Partial<Claimed>>(order);
- const { i18n } = useTranslationContext();
- const [settings] = useSettings()
-
- return (
- <div>
- <section class="section">
- <div class="columns">
- <div class="column" />
- <div class="column is-10">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <i18n.Translate>Order</i18n.Translate> #{id}
- <div class="tag is-info ml-4">
- <i18n.Translate>claimed</i18n.Translate>
- </div>
- </div>
- </div>
- </div>
-
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <h1 class="title">{order.contract_terms.amount}</h1>
- </div>
- </div>
- </div>
-
- <div class="level">
- <div class="level-left" style={{ maxWidth: "100%" }}>
- <div class="level-item" style={{ maxWidth: "100%" }}>
- <div
- class="content"
- style={{
- whiteSpace: "nowrap",
- overflow: "hidden",
- textOverflow: "ellipsis",
- }}
- >
- <p>
- <b>
- <i18n.Translate>claimed at</i18n.Translate>:
- </b>{" "}
- {format(
- new Date(order.contract_terms.timestamp.t_s * 1000),
- datetimeFormatForSettings(settings)
- )}
- </p>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <section class="section">
- <div class="columns">
- <div class="column is-4">
- <div class="title">
- <i18n.Translate>Timeline</i18n.Translate>
- </div>
- <Timeline events={events} />
- </div>
- <div class="column is-8">
- <div class="title">
- <i18n.Translate>Payment details</i18n.Translate>
- </div>
- <FormProvider<Claimed>
- object={value}
- valueHandler={valueHandler}
- >
- <Input
- name="contract_terms.summary"
- readonly
- inputType="multiline"
- label={i18n.str`Summary`}
- />
- <InputCurrency
- name="contract_terms.amount"
- readonly
- label={i18n.str`Amount`}
- />
- <Input<Claimed>
- name="order_status"
- readonly
- label={i18n.str`Order status`}
- />
- </FormProvider>
- </div>
- </div>
- </section>
-
- {order.contract_terms.products.length ? (
- <Fragment>
- <div class="title">
- <i18n.Translate>Product list</i18n.Translate>
- </div>
- <ProductList list={order.contract_terms.products} />
- </Fragment>
- ) : undefined}
-
- {value.contract_terms && (
- <ContractTerms value={value.contract_terms} />
- )}
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
-function PaidPage({
- id,
- order,
- onRefund,
-}: {
- id: string;
- order: MerchantBackend.Orders.CheckPaymentPaidResponse;
- onRefund: (id: string) => void;
-}) {
- const events: Event[] = [];
- if (order.contract_terms.timestamp.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.timestamp.t_s * 1000),
- description: "order created",
- type: "start",
- });
- }
- if (order.contract_terms.pay_deadline.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.pay_deadline.t_s * 1000),
- description: "pay deadline",
- type: "deadline",
- });
- }
- if (order.contract_terms.refund_deadline.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.refund_deadline.t_s * 1000),
- description: "refund deadline",
- type: "deadline",
- });
- }
- if (order.contract_terms.wire_transfer_deadline.t_s !== "never") {
- events.push({
- when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000),
- description: "wire deadline",
- type: "deadline",
- });
- }
- if (
- order.contract_terms.delivery_date &&
- order.contract_terms.delivery_date.t_s !== "never"
- ) {
- if (order.contract_terms.delivery_date)
- events.push({
- when: new Date(order.contract_terms.delivery_date?.t_s * 1000),
- description: "delivery",
- type: "delivery",
- });
- }
- order.refund_details.reduce(mergeRefunds, []).forEach((e) => {
- if (e.timestamp.t_s !== "never") {
- events.push({
- when: new Date(e.timestamp.t_s * 1000),
- description: `refund: ${e.amount}: ${e.reason}`,
- type: e.pending ? "refund" : "refund-taken",
- });
- }
- });
- if (order.wire_details && order.wire_details.length) {
- if (order.wire_details.length > 1) {
- let last: MerchantBackend.Orders.TransactionWireTransfer | null = null;
- let first: MerchantBackend.Orders.TransactionWireTransfer | null = null;
- let total: AmountJson | null = null;
-
- order.wire_details.forEach((w) => {
- if (last === null || last.execution_time.t_s < w.execution_time.t_s) {
- last = w;
- }
- if (first === null || first.execution_time.t_s > w.execution_time.t_s) {
- first = w;
- }
- total =
- total === null
- ? Amounts.parseOrThrow(w.amount)
- : Amounts.add(total, Amounts.parseOrThrow(w.amount)).amount;
- });
- const last_time = last!.execution_time.t_s;
- if (last_time !== "never") {
- events.push({
- when: new Date(last_time * 1000),
- description: `wired ${Amounts.stringify(total!)}`,
- type: "wired-range",
- });
- }
- const first_time = first!.execution_time.t_s;
- if (first_time !== "never") {
- events.push({
- when: new Date(first_time * 1000),
- description: `wire transfer started...`,
- type: "wired-range",
- });
- }
- } else {
- order.wire_details.forEach((e) => {
- if (e.execution_time.t_s !== "never") {
- events.push({
- when: new Date(e.execution_time.t_s * 1000),
- description: `wired ${e.amount}`,
- type: "wired",
- });
- }
- });
- }
- }
-
- const now = new Date()
- const nextEvent = events.find((e) => {
- return e.when.getTime() > now.getTime()
- })
-
- const [value, valueHandler] = useState<Partial<Paid>>(order);
- const { url: backendURL } = useBackendContext()
- const refundurl = stringifyRefundUri({
- merchantBaseUrl: backendURL,
- orderId: order.contract_terms.order_id
- })
- const refundable =
- new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000;
- const { i18n } = useTranslationContext();
-
- const amount = Amounts.parseOrThrow(order.contract_terms.amount);
- const refund_taken = order.refund_details.reduce((prev, cur) => {
- if (cur.pending) return prev;
- return Amounts.add(prev, Amounts.parseOrThrow(cur.amount)).amount;
- }, Amounts.zeroOfCurrency(amount.currency));
- value.refund_taken = Amounts.stringify(refund_taken);
-
- return (
- <div>
- <section class="section">
- <div class="columns">
- <div class="column" />
- <div class="column is-10">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <i18n.Translate>Order</i18n.Translate> #{id}
- <div class="tag is-success ml-4">
- <i18n.Translate>paid</i18n.Translate>
- </div>
- {order.wired ? (
- <div class="tag is-success ml-4">
- <i18n.Translate>wired</i18n.Translate>
- </div>
- ) : null}
- {order.refunded ? (
- <div class="tag is-danger ml-4">
- <i18n.Translate>refunded</i18n.Translate>
- </div>
- ) : null}
- </div>
- </div>
- </div>
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <h1 class="title">{order.contract_terms.amount}</h1>
- </div>
- </div>
- <div class="level-right">
- <div class="level-item">
- <h1 class="title">
- <div class="buttons">
- <span
- class="has-tooltip-left"
- data-tooltip={
- refundable
- ? i18n.str`refund order`
- : i18n.str`not refundable`
- }
- >
- <button
- class="button is-danger"
- disabled={!refundable}
- onClick={() => onRefund(id)}
- >
- <i18n.Translate>refund</i18n.Translate>
- </button>
- </span>
- </div>
- </h1>
- </div>
- </div>
- </div>
-
- <div class="level">
- <div class="level-left" style={{ maxWidth: "100%" }}>
- <div class="level-item" style={{ maxWidth: "100%" }}>
- <div
- class="content"
- style={{
- whiteSpace: "nowrap",
- overflow: "hidden",
- textOverflow: "ellipsis",
- }}
- >
- {nextEvent &&
- <p>
- <i18n.Translate>Next event in </i18n.Translate> {formatDistance(
- nextEvent.when,
- new Date(),
- // "yyyy/MM/dd HH:mm:ss",
- )}
- </p>
- }
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <section class="section">
- <div class="columns">
- <div class="column is-4">
- <div class="title">
- <i18n.Translate>Timeline</i18n.Translate>
- </div>
- <Timeline events={events} />
- </div>
- <div class="column is-8">
- <div class="title">
- <i18n.Translate>Payment details</i18n.Translate>
- </div>
- <FormProvider<Paid>
- object={value}
- valueHandler={valueHandler}
- >
- {/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n.str`Deposit total`} /> */}
- {order.refunded && (
- <InputCurrency<Paid>
- name="refund_amount"
- readonly
- label={i18n.str`Refunded amount`}
- />
- )}
- {order.refunded && (
- <InputCurrency<Paid>
- name="refund_taken"
- readonly
- label={i18n.str`Refund taken`}
- />
- )}
- <Input<Paid>
- name="order_status"
- readonly
- label={i18n.str`Order status`}
- />
- <TextField<Paid>
- name="order_status_url"
- label={i18n.str`Status URL`}
- >
- <a
- target="_blank"
- rel="noreferrer"
- href={order.order_status_url}
- >
- {order.order_status_url}
- </a>
- </TextField>
- {order.refunded && (
- <TextField<Paid>
- name="order_status_url"
- label={i18n.str`Refund URI`}
- >
- <a target="_blank" rel="noreferrer" href={refundurl}>
- {refundurl}
- </a>
- </TextField>
- )}
- </FormProvider>
- </div>
- </div>
- </section>
-
- {order.contract_terms.products.length ? (
- <Fragment>
- <div class="title">
- <i18n.Translate>Product list</i18n.Translate>
- </div>
- <ProductList list={order.contract_terms.products} />
- </Fragment>
- ) : undefined}
-
- {value.contract_terms && (
- <ContractTerms value={value.contract_terms} />
- )}
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
-
-function UnpaidPage({
- id,
- order,
-}: {
- id: string;
- order: MerchantBackend.Orders.CheckPaymentUnpaidResponse;
-}) {
- const [value, valueHandler] = useState<Partial<Unpaid>>(order);
- const { i18n } = useTranslationContext();
- const [settings] = useSettings()
- return (
- <div>
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <h1 class="title">
- <i18n.Translate>Order</i18n.Translate> #{id}
- </h1>
- </div>
- <div class="tag is-dark">
- <i18n.Translate>unpaid</i18n.Translate>
- </div>
- </div>
- </div>
-
- <div class="level">
- <div class="level-left" style={{ maxWidth: "100%" }}>
- <div class="level-item" style={{ maxWidth: "100%" }}>
- <div
- class="content"
- style={{
- whiteSpace: "nowrap",
- overflow: "hidden",
- textOverflow: "ellipsis",
- }}
- >
- <p>
- <b>
- <i18n.Translate>pay at</i18n.Translate>:
- </b>{" "}
- <a
- href={order.order_status_url}
- rel="nofollow"
- target="new"
- >
- {order.order_status_url}
- </a>
- </p>
- <p>
- <b>
- <i18n.Translate>created at</i18n.Translate>:
- </b>{" "}
- {order.creation_time.t_s === "never"
- ? "never"
- : format(
- new Date(order.creation_time.t_s * 1000),
- datetimeFormatForSettings(settings)
- )}
- </p>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider<Unpaid> object={value} valueHandler={valueHandler}>
- <Input<Unpaid>
- readonly
- name="summary"
- label={i18n.str`Summary`}
- tooltip={i18n.str`human-readable description of the whole purchase`}
- />
- <InputCurrency<Unpaid>
- readonly
- name="total_amount"
- label={i18n.str`Amount`}
- tooltip={i18n.str`total price for the transaction`}
- />
- <Input<Unpaid>
- name="order_status"
- readonly
- label={i18n.str`Order status`}
- />
- <Input<Unpaid>
- name="order_status_url"
- readonly
- label={i18n.str`Order status URL`}
- />
- <TextField<Unpaid>
- name="taler_pay_uri"
- label={i18n.str`Payment URI`}
- >
- <a target="_blank" rel="noreferrer" href={value.taler_pay_uri}>
- {value.taler_pay_uri}
- </a>
- </TextField>
- </FormProvider>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
-
-export function DetailPage({ id, selected, onRefund, onBack }: Props): VNode {
- const [showRefund, setShowRefund] = useState<string | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const DetailByStatus = function () {
- switch (selected.order_status) {
- case "claimed":
- return <ClaimedPage id={id} order={selected} />;
- case "paid":
- return <PaidPage id={id} order={selected} onRefund={setShowRefund} />;
- case "unpaid":
- return <UnpaidPage id={id} order={selected} />;
- default:
- return (
- <div>
- <i18n.Translate>
- Unknown order status. This is an error, please contact the
- administrator.
- </i18n.Translate>
- </div>
- );
- }
- };
-
- return (
- <Fragment>
- {DetailByStatus()}
- {showRefund && (
- <RefundModal
- order={selected}
- onCancel={() => setShowRefund(undefined)}
- onConfirm={(value) => {
- onRefund(showRefund, value);
- setShowRefund(undefined);
- }}
- />
- )}
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <div class="buttons is-right mt-5">
- <button class="button" onClick={onBack}>
- <i18n.Translate>Back</i18n.Translate>
- </button>
- </div>
- </div>
- <div class="column" />
- </div>
- </Fragment>
- );
-}
-
-async function copyToClipboard(text: string) {
- return navigator.clipboard.writeText(text);
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
deleted file mode 100644
index 1aae4da21..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { format } from "date-fns";
-import { h } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-interface Props {
- events: Event[];
-}
-
-export function Timeline({ events: e }: Props) {
- const events = [...e];
- events.push({
- when: new Date(),
- description: "now",
- type: "now",
- });
-
- events.sort((a, b) => a.when.getTime() - b.when.getTime());
- const [settings] = useSettings();
- const [state, setState] = useState(events);
- useEffect(() => {
- const handle = setTimeout(() => {
- const eventsWithoutNow = state.filter((e) => e.type !== "now");
- eventsWithoutNow.push({
- when: new Date(),
- description: "now",
- type: "now",
- });
- setState(eventsWithoutNow);
- }, 1000);
- return () => {
- clearTimeout(handle);
- };
- });
- return (
- <div class="timeline">
- {events.map((e, i) => {
- return (
- <div key={i} class="timeline-item">
- {(() => {
- switch (e.type) {
- case "deadline":
- return (
- <div class="timeline-marker is-icon ">
- <i class="mdi mdi-flag" />
- </div>
- );
- case "delivery":
- return (
- <div class="timeline-marker is-icon ">
- <i class="mdi mdi-delivery" />
- </div>
- );
- case "start":
- return (
- <div class="timeline-marker is-icon">
- <i class="mdi mdi-flag " />
- </div>
- );
- case "wired":
- return (
- <div class="timeline-marker is-icon is-success">
- <i class="mdi mdi-cash" />
- </div>
- );
- case "wired-range":
- return (
- <div class="timeline-marker is-icon is-success">
- <i class="mdi mdi-cash" />
- </div>
- );
- case "refund":
- return (
- <div class="timeline-marker is-icon is-danger">
- <i class="mdi mdi-cash" />
- </div>
- );
- case "refund-taken":
- return (
- <div class="timeline-marker is-icon is-success">
- <i class="mdi mdi-cash" />
- </div>
- );
- case "now":
- return (
- <div class="timeline-marker is-icon is-info">
- <i class="mdi mdi-clock" />
- </div>
- );
- }
- })()}
- <div class="timeline-content">
- {e.description !== "now" && <p class="heading">{format(e.when, datetimeFormatForSettings(settings))}</p>}
- <p>{e.description}</p>
- </div>
- </div>
- );
- })}
- </div>
- );
-}
-export interface Event {
- when: Date;
- description: string;
- type:
- | "start"
- | "refund"
- | "refund-taken"
- | "wired"
- | "wired-range"
- | "deadline"
- | "delivery"
- | "now";
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx
deleted file mode 100644
index dfeaa4447..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/details/index.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import {
- useTranslationContext,
- HttpError,
- ErrorType,
-} from "@gnu-taler/web-util/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 { useOrderAPI, useOrderDetails } from "../../../../hooks/order.js";
-import { Notification } from "../../../../utils/types.js";
-import { DetailPage } from "./DetailPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-export interface Props {
- oid: string;
-
- onBack: () => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-}
-
-export default function Update({
- oid,
- onBack,
- onLoadError,
- onNotFound,
- onUnauthorized,
-}: Props): VNode {
- const { refundOrder } = useOrderAPI();
- const result = useOrderDetails(oid);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- <DetailPage
- onBack={onBack}
- id={oid}
- onRefund={(id, value) =>
- refundOrder(id, value)
- .then(() =>
- setNotif({
- message: i18n.str`refund created successfully`,
- type: "SUCCESS",
- }),
- )
- .catch((error) =>
- setNotif({
- message: i18n.str`could not create the refund`,
- type: "ERROR",
- description: error.message,
- }),
- )
- }
- selected={result.data}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx
deleted file mode 100644
index 9df006083..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/List.stories.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ListPage as TestedComponent } from "./ListPage.js";
-
-export default {
- title: "Pages/Order/List",
- component: TestedComponent,
- argTypes: {
- onShowAll: { action: "onShowAll" },
- onShowPaid: { action: "onShowPaid" },
- onShowRefunded: { action: "onShowRefunded" },
- onShowNotWired: { action: "onShowNotWired" },
- onCopyURL: { action: "onCopyURL" },
- onSelectDate: { action: "onSelectDate" },
- onLoadMoreBefore: { action: "onLoadMoreBefore" },
- onLoadMoreAfter: { action: "onLoadMoreAfter" },
- onSelectOrder: { action: "onSelectOrder" },
- onRefundOrder: { action: "onRefundOrder" },
- onSearchOrderById: { action: "onSearchOrderById" },
- onCreate: { action: "onCreate" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {
- orders: [
- {
- id: "123",
- amount: "TESTKUDOS:10",
- paid: false,
- refundable: true,
- row_id: 1,
- summary: "summary",
- timestamp: {
- t_s: new Date().getTime() / 1000,
- },
- order_id: "123",
- },
- {
- id: "234",
- amount: "TESTKUDOS:12",
- paid: true,
- refundable: true,
- row_id: 2,
- summary:
- "summary with long text, very very long text that someone want to add as a description of the order",
- timestamp: {
- t_s: new Date().getTime() / 1000,
- },
- order_id: "234",
- },
- {
- id: "456",
- amount: "TESTKUDOS:1",
- paid: false,
- refundable: false,
- row_id: 3,
- summary:
- "summary with long text, very very long text that someone want to add as a description of the order",
- timestamp: {
- t_s: new Date().getTime() / 1000,
- },
- order_id: "456",
- },
- {
- id: "234",
- amount: "TESTKUDOS:12",
- paid: false,
- refundable: false,
- row_id: 4,
- summary:
- "summary with long text, very very long text that someone want to add as a description of the order",
- timestamp: {
- t_s: new Date().getTime() / 1000,
- },
- order_id: "234",
- },
- ],
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
deleted file mode 100644
index 187a1d0b4..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { format } from "date-fns";
-import { h, VNode, Fragment } from "preact";
-import { useState } from "preact/hooks";
-import { DatePicker } from "../../../../components/picker/DatePicker.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { CardTable } from "./Table.js";
-import { dateFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-export interface ListPageProps {
- onShowAll: () => void;
- onShowNotPaid: () => void;
- onShowPaid: () => void;
- onShowRefunded: () => void;
- onShowNotWired: () => void;
- onShowWired: () => void;
- onCopyURL: (id: string) => void;
- isAllActive: string;
- isPaidActive: string;
- isNotPaidActive: string;
- isRefundedActive: string;
- isNotWiredActive: string;
- isWiredActive: string;
-
- jumpToDate?: Date;
- onSelectDate: (date?: Date) => void;
-
- orders: (MerchantBackend.Orders.OrderHistoryEntry & WithId)[];
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-
- onSelectOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void;
- onRefundOrder: (o: MerchantBackend.Orders.OrderHistoryEntry & WithId) => void;
- onCreate: () => void;
-}
-
-export function ListPage({
- hasMoreAfter,
- hasMoreBefore,
- onLoadMoreAfter,
- onLoadMoreBefore,
- orders,
- isAllActive,
- onSelectOrder,
- onRefundOrder,
- jumpToDate,
- onCopyURL,
- onShowAll,
- onShowPaid,
- onShowNotPaid,
- onShowRefunded,
- onShowNotWired,
- onShowWired,
- onSelectDate,
- isPaidActive,
- isRefundedActive,
- isNotWiredActive,
- onCreate,
- isNotPaidActive,
- isWiredActive,
-}: ListPageProps): VNode {
- const { i18n } = useTranslationContext();
- const dateTooltip = i18n.str`select date to show nearby orders`;
- const [pickDate, setPickDate] = useState(false);
- const [settings] = useSettings();
-
- return (
- <Fragment>
- <div class="columns">
- <div class="column is-two-thirds">
- <div class="tabs" style={{ overflow: "inherit" }}>
- <ul>
- <li class={isNotPaidActive}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`only show paid orders`}
- >
- <a onClick={onShowNotPaid}>
- <i18n.Translate>New</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isPaidActive}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`only show paid orders`}
- >
- <a onClick={onShowPaid}>
- <i18n.Translate>Paid</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isRefundedActive}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`only show orders with refunds`}
- >
- <a onClick={onShowRefunded}>
- <i18n.Translate>Refunded</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isNotWiredActive}>
- <div
- class="has-tooltip-left"
- data-tooltip={i18n.str`only show orders where customers paid, but wire payments from payment provider are still pending`}
- >
- <a onClick={onShowNotWired}>
- <i18n.Translate>Not wired</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isWiredActive}>
- <div
- class="has-tooltip-left"
- data-tooltip={i18n.str`only show orders where customers paid, but wire payments from payment provider are still pending`}
- >
- <a onClick={onShowWired}>
- <i18n.Translate>Completed</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isAllActive}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`remove all filters`}
- >
- <a onClick={onShowAll}>
- <i18n.Translate>All</i18n.Translate>
- </a>
- </div>
- </li>
- </ul>
- </div>
- </div>
- <div class="column ">
- <div class="buttons is-right">
- <div class="field has-addons">
- {jumpToDate && (
- <div class="control">
- <a class="button is-fullwidth" onClick={() => onSelectDate(undefined)}>
- <span
- class="icon"
- data-tooltip={i18n.str`clear date filter`}
- >
- <i class="mdi mdi-close" />
- </span>
- </a>
- </div>
- )}
- <div class="control">
- <span class="has-tooltip-top" data-tooltip={dateTooltip}>
- <input
- class="input"
- type="text"
- readonly
- value={!jumpToDate ? "" : format(jumpToDate, dateFormatForSettings(settings))}
- placeholder={i18n.str`date (${dateFormatForSettings(settings)})`}
- onClick={() => {
- setPickDate(true);
- }}
- />
- </span>
- </div>
- <div class="control">
- <span class="has-tooltip-left" data-tooltip={dateTooltip}>
- <a
- class="button is-fullwidth"
- onClick={() => {
- setPickDate(true);
- }}
- >
- <span class="icon">
- <i class="mdi mdi-calendar" />
- </span>
- </a>
- </span>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <DatePicker
- opened={pickDate}
- closeFunction={() => setPickDate(false)}
- dateReceiver={onSelectDate}
- />
-
- <CardTable
- orders={orders}
- onCreate={onCreate}
- onCopyURL={onCopyURL}
- onSelect={onSelectOrder}
- onRefund={onRefundOrder}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/list/Table.tsx
deleted file mode 100644
index cbe8331ca..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/Table.tsx
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { Amounts } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import {
- FormErrors,
- FormProvider,
-} from "../../../../components/form/FormProvider.js";
-import { Input } from "../../../../components/form/Input.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputGroup } from "../../../../components/form/InputGroup.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
-import { ConfirmModal } from "../../../../components/modal/index.js";
-import { useConfigContext } from "../../../../context/config.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { mergeRefunds } from "../../../../utils/amount.js";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId;
-interface Props {
- orders: Entity[];
- onRefund: (value: Entity) => void;
- onCopyURL: (id: string) => void;
- onCreate: () => void;
- onSelect: (order: Entity) => void;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-export function CardTable({
- orders,
- onCreate,
- onRefund,
- onCopyURL,
- onSelect,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-cash-register" />
- </span>
- <i18n.Translate>Orders</i18n.Translate>
- </p>
-
- <div class="card-header-icon" aria-label="more options" />
-
- <div class="card-header-icon" aria-label="more options">
- <span class="has-tooltip-left" data-tooltip={i18n.str`create order`}>
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {orders.length > 0 ? (
- <Table
- instances={orders}
- onSelect={onSelect}
- onRefund={onRefund}
- onCopyURL={(o) => onCopyURL(o.id)}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- instances: Entity[];
- onRefund: (id: Entity) => void;
- onCopyURL: (id: Entity) => void;
- onSelect: (id: Entity) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-function Table({
- instances,
- onSelect,
- onRefund,
- onCopyURL,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = useSettings();
- return (
- <div class="table-container">
- {hasMoreBefore && (
- <button
- class="button is-fullwidth"
- onClick={onLoadMoreBefore}
- >
- <i18n.Translate>load newer orders</i18n.Translate>
- </button>
- )}
- <table class="table is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th style={{ minWidth: 100 }}>
- <i18n.Translate>Date</i18n.Translate>
- </th>
- <th style={{ minWidth: 100 }}>
- <i18n.Translate>Amount</i18n.Translate>
- </th>
- <th style={{ minWidth: 400 }}>
- <i18n.Translate>Summary</i18n.Translate>
- </th>
- <th style={{ minWidth: 50 }} />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.id}>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.timestamp.t_s === "never"
- ? "never"
- : format(
- new Date(i.timestamp.t_s * 1000),
- datetimeFormatForSettings(settings),
- )}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.amount}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.summary}
- </td>
- <td class="is-actions-cell right-sticky">
- <div class="buttons is-right">
- {i.refundable && (
- <button
- class="button is-small is-danger jb-modal"
- type="button"
- onClick={(): void => onRefund(i)}
- >
- <i18n.Translate>Refund</i18n.Translate>
- </button>
- )}
- {!i.paid && (
- <button
- class="button is-small is-info jb-modal"
- type="button"
- onClick={(): void => onCopyURL(i)}
- >
- <i18n.Translate>copy url</i18n.Translate>
- </button>
- )}
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- {hasMoreAfter && (
- <button
- class="button is-fullwidth"
- onClick={onLoadMoreAfter}
- >
- <i18n.Translate>load older orders</i18n.Translate>
- </button>
- )}
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- No orders have been found matching your query!
- </i18n.Translate>
- </p>
- </div>
- );
-}
-
-interface RefundModalProps {
- onCancel: () => void;
- onConfirm: (value: MerchantBackend.Orders.RefundRequest) => void;
- order: MerchantBackend.Orders.MerchantOrderStatusResponse;
-}
-
-export function RefundModal({
- order,
- onCancel,
- onConfirm,
-}: RefundModalProps): VNode {
- type State = { mainReason?: string; description?: string; refund?: string };
- const [form, setValue] = useState<State>({});
- const [settings] = useSettings();
- const { i18n } = useTranslationContext();
- // const [errors, setErrors] = useState<FormErrors<State>>({});
-
- const refunds = (
- order.order_status === "paid" ? order.refund_details : []
- ).reduce(mergeRefunds, []);
-
- const config = useConfigContext();
- const totalRefunded = refunds
- .map((r) => r.amount)
- .reduce(
- (p, c) => Amounts.add(p, Amounts.parseOrThrow(c)).amount,
- Amounts.zeroOfCurrency(config.currency),
- );
- const orderPrice =
- order.order_status === "paid"
- ? Amounts.parseOrThrow(order.contract_terms.amount)
- : undefined;
- const totalRefundable = !orderPrice
- ? Amounts.zeroOfCurrency(totalRefunded.currency)
- : refunds.length
- ? Amounts.sub(orderPrice, totalRefunded).amount
- : orderPrice;
-
- const isRefundable = Amounts.isNonZero(totalRefundable);
- const duplicatedText = i18n.str`duplicated`;
-
- const errors: FormErrors<State> = {
- mainReason: !form.mainReason ? i18n.str`required` : undefined,
- description:
- !form.description && form.mainReason !== duplicatedText
- ? i18n.str`required`
- : undefined,
- refund: !form.refund
- ? i18n.str`required`
- : !Amounts.parse(form.refund)
- ? i18n.str`invalid format`
- : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
- ? i18n.str`this value exceed the refundable amount`
- : undefined,
- };
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const validateAndConfirm = () => {
- try {
- if (!form.refund) return;
- onConfirm({
- refund: Amounts.stringify(
- Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount,
- ),
- reason:
- form.description === undefined
- ? form.mainReason || ""
- : `${form.mainReason}: ${form.description}`,
- });
- } catch (err) {
- console.log(err);
- }
- };
-
- //FIXME: parameters in the translation
- return (
- <ConfirmModal
- description="refund"
- danger
- active
- disabled={!isRefundable || hasErrors}
- onCancel={onCancel}
- onConfirm={validateAndConfirm}
- >
- {refunds.length > 0 && (
- <div class="columns">
- <div class="column is-12">
- <InputGroup
- name="asd"
- label={`${Amounts.stringify(totalRefunded)} was already refunded`}
- >
- <table class="table is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>date</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>amount</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>reason</i18n.Translate>
- </th>
- </tr>
- </thead>
- <tbody>
- {refunds.map((r) => {
- return (
- <tr key={r.timestamp.t_s}>
- <td>
- {r.timestamp.t_s === "never"
- ? "never"
- : format(
- new Date(r.timestamp.t_s * 1000),
- datetimeFormatForSettings(settings),
- )}
- </td>
- <td>{r.amount}</td>
- <td>{r.reason}</td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </InputGroup>
- </div>
- </div>
- )}
-
- {isRefundable && (
- <FormProvider<State>
- errors={errors}
- object={form}
- valueHandler={(d) => setValue(d as any)}
- >
- <InputCurrency<State>
- name="refund"
- label={i18n.str`Refund`}
- tooltip={i18n.str`amount to be refunded`}
- >
- <i18n.Translate>Max refundable:</i18n.Translate>{" "}
- {Amounts.stringify(totalRefundable)}
- </InputCurrency>
- <InputSelector
- name="mainReason"
- label={i18n.str`Reason`}
- values={[
- i18n.str`Choose one...`,
- duplicatedText,
- i18n.str`requested by the customer`,
- i18n.str`other`,
- ]}
- tooltip={i18n.str`why this order is being refunded`}
- />
- {form.mainReason && form.mainReason !== duplicatedText ? (
- <Input<State>
- label={i18n.str`Description`}
- name="description"
- tooltip={i18n.str`more information to give context`}
- />
- ) : undefined}
- </FormProvider>
- )}
- </ConfirmModal>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/orders/list/index.tsx
deleted file mode 100644
index 369ef8c8b..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/orders/list/index.tsx
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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 {
- InstanceOrderFilter,
- useInstanceOrders,
- useOrderAPI,
- useOrderDetails,
-} from "../../../../hooks/order.js";
-import { Notification } from "../../../../utils/types.js";
-import { ListPage } from "./ListPage.js";
-import { RefundModal } from "./Table.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { JumpToElementById } from "../../../../components/form/JumpToElementById.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onSelect: (id: string) => void;
- onCreate: () => void;
-}
-
-export default function OrderList({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const [filter, setFilter] = useState<InstanceOrderFilter>({ paid: "no" });
- const [orderToBeRefunded, setOrderToBeRefunded] = useState<
- MerchantBackend.Orders.OrderHistoryEntry | undefined
- >(undefined);
-
- const setNewDate = (date?: Date): void =>
- setFilter((prev) => ({ ...prev, date }));
-
- const result = useInstanceOrders(filter, setNewDate);
- const { refundOrder, getPaymentURL } = useOrderAPI();
-
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- const isNotPaidActive = filter.paid === "no" ? "is-active" : "";
- const isPaidActive = filter.paid === "yes" && filter.wired === undefined ? "is-active" : "";
- const isRefundedActive = filter.refunded === "yes" ? "is-active" : "";
- const isNotWiredActive = filter.wired === "no" && filter.paid === "yes" ? "is-active" : "";
- const isWiredActive = filter.wired === "yes" ? "is-active" : "";
- const isAllActive =
- filter.paid === undefined &&
- filter.refunded === undefined &&
- filter.wired === undefined
- ? "is-active"
- : "";
-
- return (
- <section class="section is-main-section">
- <NotificationCard notification={notif} />
-
- <JumpToElementById
- testIfExist={getPaymentURL}
- onSelect={onSelect}
- description={i18n.str`jump to order with the given product ID`}
- placeholder={i18n.str`order id`}
- />
-
- <ListPage
- orders={result.data.orders.map((o) => ({ ...o, id: o.order_id }))}
- onLoadMoreBefore={result.loadMorePrev}
- hasMoreBefore={!result.isReachingStart}
- onLoadMoreAfter={result.loadMore}
- hasMoreAfter={!result.isReachingEnd}
- onSelectOrder={(order) => onSelect(order.id)}
- onRefundOrder={(value) => setOrderToBeRefunded(value)}
- isAllActive={isAllActive}
- isNotWiredActive={isNotWiredActive}
- isWiredActive={isWiredActive}
- isPaidActive={isPaidActive}
- isNotPaidActive={isNotPaidActive}
- isRefundedActive={isRefundedActive}
- jumpToDate={filter.date}
- onCopyURL={(id) =>
- getPaymentURL(id).then((resp) => copyToClipboard(resp.data))
- }
- onCreate={onCreate}
- onSelectDate={setNewDate}
- onShowAll={() => setFilter({})}
- onShowNotPaid={() => setFilter({ paid: "no" })}
- onShowPaid={() => setFilter({ paid: "yes" })}
- onShowRefunded={() => setFilter({ refunded: "yes" })}
- onShowNotWired={() => setFilter({ wired: "no", paid: "yes" })}
- onShowWired={() => setFilter({ wired: "yes" })}
- />
-
- {orderToBeRefunded && (
- <RefundModalForTable
- id={orderToBeRefunded.order_id}
- onCancel={() => setOrderToBeRefunded(undefined)}
- onConfirm={(value) =>
- refundOrder(orderToBeRefunded.order_id, value)
- .then(() =>
- setNotif({
- message: i18n.str`refund created successfully`,
- type: "SUCCESS",
- }),
- )
- .catch((error) =>
- setNotif({
- message: i18n.str`could not create the refund`,
- type: "ERROR",
- description: error.message,
- }),
- )
- .then(() => setOrderToBeRefunded(undefined))
- }
- onLoadError={(error) => {
- setNotif({
- message: i18n.str`could not create the refund`,
- type: "ERROR",
- description: error.message,
- });
- setOrderToBeRefunded(undefined);
- return <div />;
- }}
- onUnauthorized={onUnauthorized}
- onNotFound={() => {
- setNotif({
- message: i18n.str`could not get the order to refund`,
- type: "ERROR",
- // description: error.message
- });
- setOrderToBeRefunded(undefined);
- return <div />;
- }}
- />
- )}
- </section>
- );
-}
-
-interface RefundProps {
- id: string;
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onCancel: () => void;
- onConfirm: (m: MerchantBackend.Orders.RefundRequest) => void;
-}
-
-function RefundModalForTable({
- id,
- onUnauthorized,
- onLoadError,
- onNotFound,
- onConfirm,
- onCancel,
-}: RefundProps): VNode {
- const result = useOrderDetails(id);
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <RefundModal
- order={result.data}
- onCancel={onCancel}
- onConfirm={onConfirm}
- />
- );
-}
-
-async function copyToClipboard(text: string): Promise<void> {
- return navigator.clipboard.writeText(text);
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx
deleted file mode 100644
index 36b31ebe8..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/Create.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/OtpDevices/Create",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
deleted file mode 100644
index e3ecb3243..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatePage.tsx
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { isRfc3548Base32Charset, randomRfc3548Base32Key } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, 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 { InputSelector } from "../../../../components/form/InputSelector.js";
-import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-const algorithms = [0, 1, 2];
-const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
- const backend = useBackendContext();
-
- const [state, setState] = useState<Partial<Entity>>({});
-
- const [showKey, setShowKey] = useState(false);
-
- const errors: FormErrors<Entity> = {
- otp_device_id: !state.otp_device_id
- ? i18n.str`required`
- : !/[a-zA-Z0-9]*/.test(state.otp_device_id)
- ? i18n.str`no valid. only characters and numbers`
- : undefined,
- otp_algorithm: !state.otp_algorithm ? i18n.str`required` : undefined,
- otp_key: !state.otp_key
- ? i18n.str`required`
- : !isRfc3548Base32Charset(state.otp_key)
- ? i18n.str`just letters and numbers from 2 to 7`
- : state.otp_key.length !== 32
- ? i18n.str`size of the key should be 32`
- : undefined,
- otp_device_description: !state.otp_device_description
- ? i18n.str`required`
- : !/[a-zA-Z0-9]*/.test(state.otp_device_description)
- ? i18n.str`no valid. only characters and numbers`
- : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- return onCreate(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}
- >
- <Input<Entity>
- name="otp_device_id"
- label={i18n.str`ID`}
- tooltip={i18n.str`Internal id on the system`}
- />
- <Input<Entity>
- name="otp_device_description"
- label={i18n.str`Descripiton`}
- tooltip={i18n.str`Useful to identify the device physically`}
- />
- <InputSelector<Entity>
- name="otp_algorithm"
- label={i18n.str`Verification algorithm`}
- tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
- values={algorithms}
- toStr={(v) => algorithmsNames[v]}
- fromStr={(v) => Number(v)}
- />
- {state.otp_algorithm && state.otp_algorithm > 0 ? (
- <Fragment>
- <InputWithAddon<Entity>
- expand
- name="otp_key"
- label={i18n.str`Device key`}
- inputType={showKey ? "text" : "password"}
- help="Be sure to be very hard to guess or use the random generator"
- tooltip={i18n.str`Your device need to have exactly the same value`}
- fromStr={(v) => v.toUpperCase()}
- addonAfterAction={() => {
- setShowKey(!showKey);
- }}
- addonAfter={
- <span class="icon">
- {showKey ? (
- <i class="mdi mdi-eye" />
- ) : (
- <i class="mdi mdi-eye-off" />
- )}
- </span>
- }
- side={
- <button
- data-tooltip={i18n.str`generate random secret key`}
- class="button is-info mr-3"
- onClick={(e) => {
- setState((s) => ({
- ...s,
- otp_key: randomRfc3548Base32Key(),
- }));
- }}
- >
- <i18n.Translate>random</i18n.Translate>
- </button>
- }
- />
- </Fragment>
- ) : undefined}
- </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/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx
deleted file mode 100644
index cf60925a4..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/CreatedSuccessfully.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { QR } from "../../../../components/exception/QR.js";
-import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { useInstanceContext } from "../../../../context/instance.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useBackendContext } from "../../../../context/backend.js";
-
-type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
-
-interface Props {
- entity: Entity;
- onConfirm: () => void;
-}
-
-function isNotUndefined<X>(x: X | undefined): x is X {
- return !!x;
-}
-
-export function CreatedSuccessfully({
- entity,
- onConfirm,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- const { url: backendURL } = useBackendContext()
- const { id: instanceId } = useInstanceContext();
- const issuer = new URL(backendURL).hostname;
- const qrText = `otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key}`;
- const qrTextSafe = `otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key.substring(0, 6)}...`;
-
- return (
- <Template onConfirm={onConfirm} >
- <p class="is-size-5">
- <i18n.Translate>
- You can scan the next QR code with your device or safe the key before continue.
- </i18n.Translate>
- </p>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">ID</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- readonly
- class="input"
- value={entity.otp_device_id}
- />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label"><i18n.Translate>Description</i18n.Translate></label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- class="input"
- readonly
- value={entity.otp_device_description}
- />
- </p>
- </div>
- </div>
- </div>
- <QR
- text={qrText}
- />
- <div
- style={{
- color: "grey",
- fontSize: "small",
- width: 200,
- textAlign: "center",
- margin: "auto",
- wordBreak: "break-all",
- }}
- >
- {qrTextSafe}
- </div>
- </Template>
- );
-}
-
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
deleted file mode 100644
index f0b419f68..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/create/index.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useWebhookAPI } from "../../../../hooks/webhooks.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
-import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
-
-export type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-
-export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
- const { createOtpDevice } = useOtpDeviceAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const [created, setCreated] = useState<MerchantBackend.OTP.OtpDeviceAddDetails | null>(null)
-
- if (created) {
- return <CreatedSuccessfully entity={created} onConfirm={onConfirm} />
- }
-
- return (
- <>
- <NotificationCard notification={notif} />
- <CreatePage
- onBack={onBack}
- onCreate={(request: Entity) => {
- return createOtpDevice(request)
- .then((d) => {
- setCreated(request)
- })
- .catch((error) => {
- setNotif({
- message: i18n.str`could not create device`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx
deleted file mode 100644
index 49032c80e..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/List.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { FunctionalComponent, h } from "preact";
-import { ListPage as TestedComponent } from "./ListPage.js";
-
-export default {
- title: "Pages/OtpDevices/List",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx
deleted file mode 100644
index f3b5a2088..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/ListPage.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { CardTable } from "./Table.js";
-
-export interface Props {
- devices: MerchantBackend.OTP.OtpDeviceEntry[];
- onLoadMoreBefore?: () => void;
- onLoadMoreAfter?: () => void;
- onCreate: () => void;
- onDelete: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
- onSelect: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
-}
-
-export function ListPage({
- devices,
- onCreate,
- onDelete,
- onSelect,
- onLoadMoreBefore,
- onLoadMoreAfter,
-}: Props): VNode {
- const form = { payto_uri: "" };
-
- const { i18n } = useTranslationContext();
- return (
- <section class="section is-main-section">
- <CardTable
- devices={devices.map((o) => ({
- ...o,
- id: String(o.otp_device_id),
- }))}
- onCreate={onCreate}
- onDelete={onDelete}
- onSelect={onSelect}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreBefore={!onLoadMoreBefore}
- onLoadMoreAfter={onLoadMoreAfter}
- hasMoreAfter={!onLoadMoreAfter}
- />
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
deleted file mode 100644
index d085d1c44..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import { MerchantBackend } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.OTP.OtpDeviceEntry;
-
-interface Props {
- devices: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- onCreate: () => void;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-export function CardTable({
- devices,
- onCreate,
- onDelete,
- onSelect,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-newspaper" />
- </span>
- <i18n.Translate>OTP Devices</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new devices`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {devices.length > 0 ? (
- <Table
- instances={devices}
- onDelete={onDelete}
- onSelect={onSelect}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- instances: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
-function Table({
- instances,
- onLoadMoreAfter,
- onDelete,
- onSelect,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- {hasMoreBefore && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more devices before the first one`}
- onClick={onLoadMoreBefore}
- >
- <i18n.Translate>load newer devices</i18n.Translate>
- </button>
- )}
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>ID</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Description</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.otp_device_id}>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.otp_device_id}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.otp_device_id}
- </td>
- <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 devices from the database`}
- onClick={() => onDelete(i)}
- >
- Delete
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- {hasMoreAfter && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more devices after the last one`}
- onClick={onLoadMoreAfter}
- >
- <i18n.Translate>load older devices</i18n.Translate>
- </button>
- )}
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no devices yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
deleted file mode 100644
index a3299326c..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { HttpStatusCode } from "@gnu-taler/taler-util";
-import {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } 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 { useInstanceOtpDevices, useOtpDeviceAPI } from "../../../../hooks/otp.js";
-import { Notification } from "../../../../utils/types.js";
-import { ListPage } from "./ListPage.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onCreate: () => void;
- onSelect: (id: string) => void;
-}
-
-export default function ListOtpDevices({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const [position, setPosition] = useState<string | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { deleteOtpDevice } = useOtpDeviceAPI();
- const result = useInstanceOtpDevices({ position }, (id) => setPosition(id));
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- <ListPage
- devices={result.data.otp_devices}
- onLoadMoreBefore={
- result.isReachingStart ? result.loadMorePrev : undefined
- }
- onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
- onCreate={onCreate}
- onSelect={(e) => {
- onSelect(e.otp_device_id);
- }}
- onDelete={(e: MerchantBackend.OTP.OtpDeviceEntry) =>
- deleteOtpDevice(e.otp_device_id)
- .then(() =>
- setNotif({
- message: i18n.str`validator delete successfully`,
- type: "SUCCESS",
- }),
- )
- .catch((error) =>
- setNotif({
- message: i18n.str`could not delete the validator`,
- type: "ERROR",
- description: error.message,
- }),
- )
- }
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx
deleted file mode 100644
index 06ea9d07a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/Update.stories.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/OtpDevices/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
deleted file mode 100644
index 310810576..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/UpdatePage.tsx
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { randomRfc3548Base32Key } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, 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 { InputSelector } from "../../../../components/form/InputSelector.js";
-import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
-
-interface Props {
- onUpdate: (d: Entity) => Promise<void>;
- onBack?: () => void;
- device: Entity;
-}
-const algorithms = [0, 1, 2];
-const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
-export function UpdatePage({ device, onUpdate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const [state, setState] = useState<Partial<Entity>>(device);
- const [showKey, setShowKey] = useState(false);
-
- const errors: FormErrors<Entity> = {};
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- return onUpdate(state as any);
- };
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- Device: <b>{device.id}</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column is-four-fifths">
- <FormProvider
- object={state}
- valueHandler={setState}
- errors={errors}
- >
- <Input<Entity>
- name="otp_device_description"
- label={i18n.str`Description`}
- tooltip={i18n.str`Useful to identify the device physically`}
- />
- <InputSelector<Entity>
- name="otp_algorithm"
- label={i18n.str`Verification algorithm`}
- tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
- values={algorithms}
- toStr={(v) => algorithmsNames[v]}
- fromStr={(v) => Number(v)}
- />
- {state.otp_algorithm && state.otp_algorithm > 0 ? (
- <Fragment>
- <InputWithAddon<Entity>
- name="otp_key"
- label={i18n.str`Device key`}
- readonly={state.otp_key === undefined}
- inputType={showKey ? "text" : "password"}
- help={
- state.otp_key === undefined
- ? "Not modified"
- : "Be sure to be very hard to guess or use the random generator"
- }
- tooltip={i18n.str`Your device need to have exactly the same value`}
- fromStr={(v) => v.toUpperCase()}
- addonAfterAction={() => {
- setShowKey(!showKey);
- }}
- addonAfter={
- <span
- class="icon"
- onClick={() => {
- setShowKey(!showKey);
- }}
- >
- {showKey ? (
- <i class="mdi mdi-eye" />
- ) : (
- <i class="mdi mdi-eye-off" />
- )}
- </span>
- }
- side={
- state.otp_key === undefined ? (
- <button
- onClick={(e) => {
- setState((s) => ({ ...s, otp_key: "" }));
- }}
- class="button"
- >
- change key
- </button>
- ) : (
- <button
- data-tooltip={i18n.str`generate random secret key`}
- class="button is-info mr-3"
- onClick={(e) => {
- setState((s) => ({
- ...s,
- otp_key: randomRfc3548Base32Key(),
- }));
- }}
- >
- <i18n.Translate>random</i18n.Translate>
- </button>
- )
- }
- />
- </Fragment>
- ) : undefined}{" "}
- </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>
- </section>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
deleted file mode 100644
index 3128608e1..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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, WithId } from "../../../../declaration.js";
-import { Notification } from "../../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { useOtpDeviceAPI, useOtpDeviceDetails } from "../../../../hooks/otp.js";
-
-export type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
-
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- vid: string;
-}
-export default function UpdateValidator({
- vid,
- onConfirm,
- onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
-}: Props): VNode {
- const { updateOtpDevice } = useOtpDeviceAPI();
- const result = useOtpDeviceDetails(vid);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <UpdatePage
- device={{
- id: vid,
- otp_algorithm: result.data.otp_algorithm,
- otp_device_description: result.data.device_description,
- otp_key: undefined,
- otp_ctr: result.data.otp_ctr
- }}
- onBack={onBack}
- onUpdate={(data) => {
- return updateOtpDevice(vid, data)
- .then(onConfirm)
- .catch((error) => {
- setNotif({
- message: i18n.str`could not update template`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx
deleted file mode 100644
index 22bbfe28a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/create/Create.stories.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Product/Create",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx
deleted file mode 100644
index 6cbc26d8d..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
-import { ProductForm } from "../../../../components/product/ProductForm.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useListener } from "../../../../hooks/listener.js";
-
-type Entity = MerchantBackend.Products.ProductAddDetail & {
- product_id: string;
-};
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const [submitForm, addFormSubmitter] = useListener<Entity | undefined>(
- (result) => {
- if (result) return onCreate(result);
- return Promise.reject();
- },
- );
-
- const { i18n } = useTranslationContext();
-
- return (
- <div>
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <ProductForm onSubscribe={addFormSubmitter} />
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <AsyncButton
- onClick={submitForm}
- data-tooltip={
- !submitForm
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={!submitForm}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx
deleted file mode 100644
index 2b6ebed45..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/create/CreatedSuccessfully.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { h, VNode } from "preact";
-import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { Entity } from "./index.js";
-import emptyImage from "../../assets/empty.png";
-
-interface Props {
- entity: Entity;
- onConfirm: () => void;
- onCreateAnother?: () => void;
-}
-
-export function CreatedSuccessfully({
- entity,
- onConfirm,
- onCreateAnother,
-}: Props): VNode {
- return (
- <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Image</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <img src={entity.image} style={{ width: 200, height: 200 }} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Description</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <textarea class="input" readonly value={entity.description} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Price</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input class="input" readonly value={entity.price} />
- </p>
- </div>
- </div>
- </div>
- </Template>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/create/index.tsx
deleted file mode 100644
index 0322f2796..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/create/index.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { AuditorBackend, MerchantBackend } from "../../../../declaration.js";
-import { useProductAPI } from "../../../../hooks/product.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-
-export type Entity = MerchantBackend.Products.ProductDetail;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-export default function CreateProduct({ onConfirm, onBack }: Props): VNode {
- const { createProduct } = useProductAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/list/List.stories.tsx
deleted file mode 100644
index a2f996221..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/list/List.stories.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { CardTable as TestedComponent } from "./Table.js";
-
-export default {
- title: "Pages/Product/List",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onSelect: { action: "onSelect" },
- onDelete: { action: "onDelete" },
- onUpdate: { action: "onUpdate" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {
- instances: [
- {
- id: "orderid",
- description: "description1",
- description_i18n: {} as any,
- image: "",
- price: "TESTKUDOS:10",
- taxes: [],
- total_lost: 10,
- total_sold: 5,
- total_stock: 15,
- unit: "bar",
- address: {},
- },
- ],
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/list/Table.tsx
deleted file mode 100644
index b02d2f23a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/list/Table.tsx
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { Amounts } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { ComponentChildren, Fragment, h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import emptyImage from "../../../../assets/empty.png";
-import {
- FormErrors,
- FormProvider,
-} from "../../../../components/form/FormProvider.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { dateFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Products.ProductDetail & WithId;
-
-interface Props {
- instances: Entity[];
- onDelete: (id: Entity) => void;
- onSelect: (product: Entity) => void;
- onUpdate: (
- id: string,
- data: MerchantBackend.Products.ProductPatchDetail,
- ) => Promise<void>;
- onCreate: () => void;
- selected?: boolean;
-}
-
-export function CardTable({
- instances,
- onCreate,
- onSelect,
- onUpdate,
- onDelete,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string | undefined>(
- undefined,
- );
- const { i18n } = useTranslationContext();
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-shopping" />
- </span>
- <i18n.Translate>Inventory</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add product to inventory`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {instances.length > 0 ? (
- <Table
- instances={instances}
- onSelect={onSelect}
- onDelete={onDelete}
- onUpdate={onUpdate}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string | undefined;
- instances: Entity[];
- onSelect: (id: Entity) => void;
- onUpdate: (
- id: string,
- data: MerchantBackend.Products.ProductPatchDetail,
- ) => Promise<void>;
- onDelete: (id: Entity) => void;
- rowSelectionHandler: StateUpdater<string | undefined>;
-}
-
-function Table({
- rowSelection,
- rowSelectionHandler,
- instances,
- onSelect,
- onUpdate,
- onDelete,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = useSettings();
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Image</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Description</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Price per unit</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Taxes</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Sales</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Stock</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Sold</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- const restStockInfo = !i.next_restock
- ? ""
- : i.next_restock.t_s === "never"
- ? "never"
- : `restock at ${format(
- new Date(i.next_restock.t_s * 1000),
- dateFormatForSettings(settings),
- )}`;
- let stockInfo: ComponentChildren = "";
- if (i.total_stock < 0) {
- stockInfo = "infinite";
- } else {
- const totalStock = i.total_stock - i.total_lost - i.total_sold;
- stockInfo = (
- <label title={restStockInfo}>
- {totalStock} {i.unit}
- </label>
- );
- }
-
- const isFree = Amounts.isZero(Amounts.parseOrThrow(i.price));
-
- return (
- <Fragment key={i.id}>
- <tr key="info">
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- <img
- src={i.image ? i.image : emptyImage}
- style={{
- border: "solid black 1px",
- maxHeight: "2em",
- width: "auto",
- height: "auto",
- }}
- />
- </td>
- <td
- class="has-tooltip-right"
- data-tooltip={i.description}
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- {i.description.length > 30 ? i.description.substring(0, 30) + "..." : i.description}
- </td>
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- {isFree ? i18n.str`free` : `${i.price} / ${i.unit}`}
- </td>
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- {sum(i.taxes)}
- </td>
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- {difference(i.price, sum(i.taxes))}
- </td>
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- {stockInfo}
- </td>
- <td
- onClick={() =>
- rowSelection !== i.id && rowSelectionHandler(i.id)
- }
- style={{ cursor: "pointer" }}
- >
- <span style={{"whiteSpace":"nowrap"}}>
-
- {i.total_sold} {i.unit}
- </span>
- </td>
- <td class="is-actions-cell right-sticky">
- <div class="buttons is-right">
- <span
- class="has-tooltip-bottom"
- data-tooltip={i18n.str`go to product update page`}
- >
- <button
- class="button is-small is-success "
- type="button"
- onClick={(): void => onSelect(i)}
- >
- <i18n.Translate>Update</i18n.Translate>
- </button>
- </span>
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`remove this product from the database`}
- >
- <button
- class="button is-small is-danger"
- type="button"
- onClick={(): void => onDelete(i)}
- >
- <i18n.Translate>Delete</i18n.Translate>
- </button>
- </span>
- </div>
- </td>
- </tr>
- {rowSelection === i.id && (
- <tr key="form">
- <td colSpan={10}>
- <FastProductUpdateForm
- product={i}
- onUpdate={(prod) =>
- onUpdate(i.id, prod).then((r) =>
- rowSelectionHandler(undefined),
- )
- }
- onCancel={() => rowSelectionHandler(undefined)}
- />
- </td>
- </tr>
- )}
- </Fragment>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-interface FastProductUpdateFormProps {
- product: Entity;
- onUpdate: (
- data: MerchantBackend.Products.ProductPatchDetail,
- ) => Promise<void>;
- onCancel: () => void;
-}
-interface FastProductUpdate {
- incoming: number;
- lost: number;
- price: string;
-}
-interface UpdatePrice {
- price: string;
-}
-
-function FastProductWithInfiniteStockUpdateForm({
- product,
- onUpdate,
- onCancel,
-}: FastProductUpdateFormProps) {
- const [value, valueHandler] = useState<UpdatePrice>({ price: product.price });
- const { i18n } = useTranslationContext();
-
- return (
- <Fragment>
- <FormProvider<FastProductUpdate>
- name="added"
- object={value}
- valueHandler={valueHandler as any}
- >
- <InputCurrency<FastProductUpdate>
- name="price"
- label={i18n.str`Price`}
- tooltip={i18n.str`update the product with new price`}
- />
- </FormProvider>
-
- <div class="buttons is-expanded">
-
- <div class="buttons mt-5">
-
- <button class="button mt-5" onClick={onCancel}>
- <i18n.Translate>Clone</i18n.Translate>
- </button>
- </div>
- <div class="buttons is-right mt-5">
- <button class="button" onClick={onCancel}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`update product with new price`}
- >
- <button
- class="button is-info"
- onClick={() =>
- onUpdate({
- ...product,
- price: value.price,
- })
- }
- >
- <i18n.Translate>Confirm update</i18n.Translate>
- </button>
- </span>
- </div>
- </div>
- </Fragment>
- );
-}
-
-function FastProductWithManagedStockUpdateForm({
- product,
- onUpdate,
- onCancel,
-}: FastProductUpdateFormProps) {
- const [value, valueHandler] = useState<FastProductUpdate>({
- incoming: 0,
- lost: 0,
- price: product.price,
- });
-
- const currentStock =
- product.total_stock - product.total_sold - product.total_lost;
-
- const errors: FormErrors<FastProductUpdate> = {
- lost:
- currentStock + value.incoming < value.lost
- ? `lost cannot be greater that current + incoming (max ${currentStock + value.incoming
- })`
- : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
- const { i18n } = useTranslationContext();
-
- return (
- <Fragment>
- <FormProvider<FastProductUpdate>
- name="added"
- errors={errors}
- object={value}
- valueHandler={valueHandler as any}
- >
- <InputNumber<FastProductUpdate>
- name="incoming"
- label={i18n.str`Incoming`}
- tooltip={i18n.str`add more elements to the inventory`}
- />
- <InputNumber<FastProductUpdate>
- name="lost"
- label={i18n.str`Lost`}
- tooltip={i18n.str`report elements lost in the inventory`}
- />
- <InputCurrency<FastProductUpdate>
- name="price"
- label={i18n.str`Price`}
- tooltip={i18n.str`new price for the product`}
- />
- </FormProvider>
-
- <div class="buttons is-right mt-5">
- <button class="button" onClick={onCancel}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <span
- class="has-tooltip-left"
- data-tooltip={
- hasErrors
- ? i18n.str`the are value with errors`
- : i18n.str`update product with new stock and price`
- }
- >
- <button
- class="button is-info"
- disabled={hasErrors}
- onClick={() =>
- onUpdate({
- ...product,
- total_stock: product.total_stock + value.incoming,
- total_lost: product.total_lost + value.lost,
- price: value.price,
- })
- }
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </button>
- </span>
- </div>
- </Fragment>
- );
-}
-
-function FastProductUpdateForm(props: FastProductUpdateFormProps) {
- return props.product.total_stock === -1 ? (
- <FastProductWithInfiniteStockUpdateForm {...props} />
- ) : (
- <FastProductWithManagedStockUpdateForm {...props} />
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no products yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
-
-function difference(price: string, tax: number) {
- if (!tax) return price;
- const ps = price.split(":");
- const p = parseInt(ps[1], 10);
- ps[1] = `${p - tax}`;
- return ps.join(":");
-}
-function sum(taxes: MerchantBackend.Tax[]) {
- return taxes.reduce((p, c) => p + parseInt(c.tax.split(":")[1], 10), 0);
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/list/index.tsx
deleted file mode 100644
index 6adc221da..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/list/index.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { 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, WithId } from "../../../../declaration.js";
-import {
- useInstanceProducts,
- useProductAPI,
-} from "../../../../hooks/product.js";
-import { Notification } from "../../../../utils/types.js";
-import { CardTable } from "./Table.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ConfirmModal, DeleteModal } from "../../../../components/modal/index.js";
-import { JumpToElementById } from "../../../../components/form/JumpToElementById.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onCreate: () => void;
- onSelect: (id: string) => void;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
-}
-export default function ProductList({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const result = useInstanceProducts();
- const { deleteProduct, updateProduct, getProduct } = useProductAPI();
- const [deleting, setDeleting] =
- useState<MerchantBackend.Products.ProductDetail & WithId | null>(null);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <section class="section is-main-section">
- <NotificationCard notification={notif} />
-
- <JumpToElementById
- testIfExist={getProduct}
- onSelect={onSelect}
- description={i18n.str`jump to product with the given product ID`}
- placeholder={i18n.str`product id`}
- />
-
- <CardTable
- instances={result.data}
- onCreate={onCreate}
- onUpdate={(id, prod) =>
- updateProduct(id, prod)
- .then(() =>
- setNotif({
- message: i18n.str`product updated successfully`,
- type: "SUCCESS",
- }),
- )
- .catch((error) =>
- setNotif({
- message: i18n.str`could not update the product`,
- type: "ERROR",
- description: error.message,
- }),
- )
- }
- onSelect={(product) => onSelect(product.id)}
- onDelete={(prod: MerchantBackend.Products.ProductDetail & WithId) =>
- setDeleting(prod)
- }
- />
-
- {deleting && (
- <ConfirmModal
- label={`Delete product`}
- description={`Delete the product "${deleting.description}"`}
- danger
- active
- onCancel={() => setDeleting(null)}
- onConfirm={async (): Promise<void> => {
- try {
- await deleteProduct(deleting.id);
- setNotif({
- message: i18n.str`Product "${deleting.description}" (ID: ${deleting.id}) has been deleted`,
- type: "SUCCESS",
- });
- } catch (error) {
- setNotif({
- message: i18n.str`Failed to delete product`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- }
- setDeleting(null);
- }}
- >
- <p>
- If you delete the product named <b>&quot;{deleting.description}&quot;</b> (ID:{" "}
- <b>{deleting.id}</b>), the stock and related information will be lost
- </p>
- <p class="warning">
- Deleting an product <b>cannot be undone</b>.
- </p>
- </ConfirmModal>
- )}
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx
deleted file mode 100644
index d1dc9d540..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/update/Update.stories.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/Product/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const WithManagedStock = createExample(TestedComponent, {
- product: {
- product_id: "20102-ASDAS-QWE",
- description: "description1",
- description_i18n: {} as any,
- image: "",
- price: "TESTKUDOS:10",
- taxes: [],
- total_lost: 10,
- total_sold: 5,
- total_stock: 15,
- unit: "bar",
- address: {},
- },
-});
-
-export const WithInfiniteStock = createExample(TestedComponent, {
- product: {
- product_id: "20102-ASDAS-QWE",
- description: "description1",
- description_i18n: {} as any,
- image: "",
- price: "TESTKUDOS:10",
- taxes: [],
- total_lost: 10,
- total_sold: 5,
- total_stock: -1,
- unit: "bar",
- address: {},
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx
deleted file mode 100644
index 53aa9d61f..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
-import { ProductForm } from "../../../../components/product/ProductForm.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useListener } from "../../../../hooks/listener.js";
-
-type Entity = MerchantBackend.Products.ProductDetail & { product_id: string };
-
-interface Props {
- onUpdate: (d: Entity) => Promise<void>;
- onBack?: () => void;
- product: Entity;
-}
-
-export function UpdatePage({ product, onUpdate, onBack }: Props): VNode {
- const [submitForm, addFormSubmitter] = useListener<Entity | undefined>(
- (result) => {
- if (result) return onUpdate(result);
- return Promise.resolve();
- },
- );
-
- const { i18n } = useTranslationContext();
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- <i18n.Translate>Product id:</i18n.Translate>
- <b>{product.product_id}</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <ProductForm
- initial={product}
- onSubscribe={addFormSubmitter}
- alreadyExist
- />
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <AsyncButton
- onClick={submitForm}
- data-tooltip={
- !submitForm
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={!submitForm}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx
deleted file mode 100644
index 3b6ad5fb2..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/Create.stories.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Reserve/Create",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
deleted file mode 100644
index 2ab12687e..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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, RequestError, useApiContext, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
-import { StateUpdater, useEffect, 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 { InputSelector } from "../../../../components/form/InputSelector.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import {
- PAYTO_WIRE_METHOD_LOOKUP,
- URL_REGEX,
-} from "../../../../utils/constants.js";
-import { useBackendBaseRequest } from "../../../../hooks/backend.js";
-import { parsePaytoUri } from "@gnu-taler/taler-util";
-
-type Entity = MerchantBackend.Rewards.ReserveCreateRequest;
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-enum Steps {
- EXCHANGE,
- WIRE_METHOD,
-}
-
-interface ViewProps {
- step: Steps;
- setCurrentStep: (s: Steps) => void;
- reserve: Partial<Entity>;
- onBack?: () => void;
- submitForm: () => Promise<void>;
- setReserve: StateUpdater<Partial<Entity>>;
-}
-function ViewStep({
- step,
- setCurrentStep,
- reserve,
- onBack,
- submitForm,
- setReserve,
-}: ViewProps): VNode {
- const { i18n } = useTranslationContext();
- const {request} = useApiContext()
- const [wireMethods, setWireMethods] = useState<Array<string>>([]);
- const [exchangeQueryError, setExchangeQueryError] = useState<
- string | undefined
- >(undefined);
-
- useEffect(() => {
- setExchangeQueryError(undefined);
- }, [reserve.exchange_url]);
-
- switch (step) {
- case Steps.EXCHANGE: {
- const errors: FormErrors<Entity> = {
- initial_balance: !reserve.initial_balance
- ? "cannot be empty"
- : !(parseInt(reserve.initial_balance.split(":")[1], 10) > 0)
- ? i18n.str`it should be greater than 0`
- : undefined,
- exchange_url: !reserve.exchange_url
- ? i18n.str`cannot be empty`
- : !URL_REGEX.test(reserve.exchange_url)
- ? i18n.str`must be a valid URL`
- : !exchangeQueryError
- ? undefined
- : exchangeQueryError,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- return (
- <Fragment>
- <FormProvider<Entity>
- object={reserve}
- errors={errors}
- valueHandler={setReserve}
- >
- <InputCurrency<Entity>
- name="initial_balance"
- label={i18n.str`Initial balance`}
- tooltip={i18n.str`balance prior to deposit`}
- />
- <Input<Entity>
- name="exchange_url"
- label={i18n.str`Exchange URL`}
- tooltip={i18n.str`URL of exchange`}
- />
- </FormProvider>
-
- <div class="buttons is-right mt-5">
- {onBack && (
- <button class="button" onClick={onBack}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- )}
- <AsyncButton
- class="has-tooltip-left"
- onClick={() => {
- if (!reserve.exchange_url) {
- return Promise.resolve();
- }
-
- return request<any>(reserve.exchange_url, "keys")
- .then((r) => {
- console.log(r)
- if (r.loading) return;
- if (r.ok) {
- const wireMethods = r.data.accounts.map((a: any) => {
- const p = parsePaytoUri(a.payto_uri);
- const r = p?.targetType
- return r
- }).filter((x:any) => !!x);
- setWireMethods(Array.from(new Set(wireMethods)));
- }
- setCurrentStep(Steps.WIRE_METHOD);
- return;
- })
- .catch((r: RequestError<{}>) => {
- console.log(r.cause)
- setExchangeQueryError(r.cause.message);
- });
- }}
- data-tooltip={
- hasErrors
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={hasErrors}
- >
- <i18n.Translate>Next</i18n.Translate>
- </AsyncButton>
- </div>
- </Fragment>
- );
- }
-
- case Steps.WIRE_METHOD: {
- const errors: FormErrors<Entity> = {
- wire_method: !reserve.wire_method
- ? i18n.str`cannot be empty`
- : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
- return (
- <Fragment>
- <FormProvider<Entity>
- object={reserve}
- errors={errors}
- valueHandler={setReserve}
- >
- <InputCurrency<Entity>
- name="initial_balance"
- label={i18n.str`Initial balance`}
- tooltip={i18n.str`balance prior to deposit`}
- readonly
- />
- <Input<Entity>
- name="exchange_url"
- label={i18n.str`Exchange URL`}
- tooltip={i18n.str`URL of exchange`}
- readonly
- />
- <InputSelector<Entity>
- name="wire_method"
- label={i18n.str`Wire method`}
- tooltip={i18n.str`method to use for wire transfer`}
- values={wireMethods}
- placeholder={i18n.str`Select one wire method`}
- />
- </FormProvider>
- <div class="buttons is-right mt-5">
- {onBack && (
- <button
- class="button"
- onClick={() => setCurrentStep(Steps.EXCHANGE)}
- >
- <i18n.Translate>Back</i18n.Translate>
- </button>
- )}
- <AsyncButton
- onClick={submitForm}
- data-tooltip={
- hasErrors
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={hasErrors}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </Fragment>
- );
- }
- }
-}
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const [reserve, setReserve] = useState<Partial<Entity>>({});
-
- const submitForm = () => {
- return onCreate(reserve as Entity);
- };
-
- const [currentStep, setCurrentStep] = useState(Steps.EXCHANGE);
-
- return (
- <div>
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <div class="tabs is-toggle is-fullwidth is-small">
- <ul>
- <li class={currentStep === Steps.EXCHANGE ? "is-active" : ""}>
- <a style={{ cursor: "initial" }}>
- <span>Step 1: Specify exchange</span>
- </a>
- </li>
- <li
- class={currentStep === Steps.WIRE_METHOD ? "is-active" : ""}
- >
- <a style={{ cursor: "initial" }}>
- <span>Step 2: Select wire method</span>
- </a>
- </li>
- </ul>
- </div>
-
- <ViewStep
- step={currentStep}
- reserve={reserve}
- setCurrentStep={setCurrentStep}
- setReserve={setReserve}
- submitForm={submitForm}
- onBack={onBack}
- />
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx
deleted file mode 100644
index 9f32a26b8..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.stories.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { CreatedSuccessfully as TestedComponent } from "./CreatedSuccessfully.js";
-import * as tests from "@gnu-taler/web-util/testing";
-
-export default {
- title: "Pages/Reserve/CreatedSuccessfully",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onBack: { action: "onBack" },
- },
-};
-
-export const OneBankAccount = tests.createExample(TestedComponent, {
- entity: {
- request: {
- exchange_url: "http://exchange.taler/",
- initial_balance: "TESTKUDOS:1",
- wire_method: "x-taler-bank",
- },
- response: {
- accounts: [
- {
- payto_uri: "payto://x-taler-bank/bank.taler:8080/exchange_account",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "asd",
- conversion_url: "",
- },
- ],
- reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
- },
- },
-});
-
-export const ThreeBankAccount = tests.createExample(TestedComponent, {
- entity: {
- request: {
- exchange_url: "http://exchange.taler/",
- initial_balance: "TESTKUDOS:1",
- wire_method: "x-taler-bank",
- },
- response: {
- accounts: [
- {
- payto_uri: "payto://x-taler-bank/bank.taler:8080/exchange_account",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "asd",
- conversion_url: "",
- },
- {
- payto_uri: "payto://x-taler-bank/bank1.taler:8080/asd",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "asd",
- conversion_url: "",
- },
- {
- payto_uri: "payto://x-taler-bank/bank2.taler:8080/qwe",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "asd",
- conversion_url: "",
- },
- ],
- reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
- },
- },
-});
-
-export const NoBankAccount = tests.createExample(TestedComponent, {
- entity: {
- request: {
- exchange_url: "http://exchange.taler/",
- initial_balance: "TESTKUDOS:1",
- wire_method: "x-taler-bank",
- },
- response: {
- accounts: [
- {
- payto_uri: "payo://x-talr-bank/bank.taler:8080/exchange_account",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "asd",
- conversion_url: "",
- },
- {
- payto_uri: "payto://x-taler-bank",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "asd",
- conversion_url: "",
- },
- ],
- reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
- },
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
deleted file mode 100644
index 336e52b5e..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-
-import { parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { QR } from "../../../../components/exception/QR.js";
-import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { MerchantBackend, WireAccount } from "../../../../declaration.js";
-
-type Entity = {
- request: MerchantBackend.Rewards.ReserveCreateRequest;
- response: MerchantBackend.Rewards.ReserveCreateConfirmation;
-};
-
-interface Props {
- entity: Entity;
- onConfirm: () => void;
- onCreateAnother?: () => void;
-}
-
-function isNotUndefined<X>(x: X | undefined): x is X {
- return !!x;
-}
-
-export function CreatedSuccessfully({
- entity,
- onConfirm,
- onCreateAnother,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- return (
- <Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Amount</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- readonly
- class="input"
- value={entity.request.initial_balance}
- />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Wire transfer subject</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- class="input"
- readonly
- value={entity.response.reserve_pub}
- />
- </p>
- </div>
- </div>
- </div>
- <ShowAccountsOfReserveAsQRWithLink
- accounts={entity.response.accounts ?? []}
- message={entity.response.reserve_pub}
- amount={entity.request.initial_balance}
- />
- </Template>
- );
-}
-
-export function ShowAccountsOfReserveAsQRWithLink({
- accounts,
- message,
- amount,
-}: {
- accounts: WireAccount[];
- message: string;
- amount: string;
-}): VNode {
- const { i18n } = useTranslationContext();
- const accountsInfo = !accounts
- ? []
- : accounts
- .map((acc) => {
- const p = parsePaytoUri(acc.payto_uri);
- if (p) {
- p.params["message"] = message;
- p.params["amount"] = amount;
- }
- return p;
- })
- .filter(isNotUndefined);
-
- const links = accountsInfo.map((a) => stringifyPaytoUri(a));
-
- if (links.length === 0) {
- return (
- <Fragment>
- <p class="is-size-5">
- The reserve have invalid accounts. List of invalid payto URIs below:
- </p>
- <ul>
- {accounts.map((a, idx) => {
- return <li key={idx}>{a.payto_uri}</li>;
- })}
- </ul>
- </Fragment>
- );
- }
-
- if (links.length === 1) {
- return (
- <Fragment>
- <p class="is-size-5">
- <i18n.Translate>
- To complete the setup of the reserve, you must now initiate a wire
- transfer using the given wire transfer subject and crediting the
- specified amount to the indicated account of the exchange.
- </i18n.Translate>
- </p>
- <p style={{ margin: 10 }}>
- <b>Exchange bank account</b>
- </p>
- <QR text={links[0]} />
- <p class="is-size-5">
- <i18n.Translate>
- If your system supports RFC 8905, you can do this by opening this
- URI:
- </i18n.Translate>
- </p>
- <pre>
- <a target="_blank" rel="noreferrer" href={links[0]}>
- {links[0]}
- </a>
- </pre>
- </Fragment>
- );
- }
-
- return (
- <div>
- <p class="is-size-5">
- <i18n.Translate>
- To complete the setup of the reserve, you must now initiate a wire
- transfer using the given wire transfer subject and crediting the
- specified amount to one of the indicated account of the exchange.
- </i18n.Translate>
- </p>
-
- <p style={{ margin: 10 }}>
- <b>Exchange bank accounts</b>
- </p>
- <p class="is-size-5">
- <i18n.Translate>
- If your system supports RFC 8905, you can do this by clicking on the
- URI below the QR code:
- </i18n.Translate>
- </p>
- {links.map((link) => {
- return (
- <Fragment>
- <QR text={link} />
- <pre>
- <a target="_blank" rel="noreferrer" href={link}>
- {link}
- </a>
- </pre>
- </Fragment>
- );
- })}
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/index.tsx
deleted file mode 100644
index 13ea2398c..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/create/index.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useReservesAPI } from "../../../../hooks/reserves.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
-import { CreatePage } from "./CreatePage.js";
-interface Props {
- onBack: () => void;
- onConfirm: () => void;
-}
-export default function CreateReserve({ onBack, onConfirm }: Props): VNode {
- const { createReserve } = useReservesAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- const [createdOk, setCreatedOk] = useState<
- | {
- request: MerchantBackend.Rewards.ReserveCreateRequest;
- response: MerchantBackend.Rewards.ReserveCreateConfirmation;
- }
- | undefined
- >(undefined);
-
- if (createdOk) {
- return <CreatedSuccessfully entity={createdOk} onConfirm={onConfirm} />;
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <CreatePage
- onBack={onBack}
- onCreate={(request: MerchantBackend.Rewards.ReserveCreateRequest) => {
- return createReserve(request)
- .then((r) => setCreatedOk({ request, response: r.data }))
- .catch((error) => {
- setNotif({
- message: i18n.str`could not create reserve`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
deleted file mode 100644
index d5698e6c7..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- Amounts,
- parsePaytoUri,
- stringifyPaytoUri,
-} from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { format } from "date-fns";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { QR } from "../../../../components/exception/QR.js";
-import { FormProvider } from "../../../../components/form/FormProvider.js";
-import { Input } from "../../../../components/form/Input.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import { InputDate } from "../../../../components/form/InputDate.js";
-import { TextField } from "../../../../components/form/TextField.js";
-import { SimpleModal } from "../../../../components/modal/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useRewardDetails } from "../../../../hooks/reserves.js";
-import { RewardInfo } from "./RewardInfo.js";
-import { ShowAccountsOfReserveAsQRWithLink } from "../create/CreatedSuccessfully.js";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Rewards.ReserveDetail;
-type CT = MerchantBackend.ContractTerms;
-
-interface Props {
- onBack: () => void;
- selected: Entity;
- id: string;
-}
-
-export function DetailPage({ id, selected, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
- const didExchangeAckTransfer = Amounts.isNonZero(
- Amounts.parseOrThrow(selected.exchange_initial_amount),
- );
-
- return (
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <div class="section main-section">
- <FormProvider object={{ ...selected, id }} valueHandler={null}>
- <InputDate<Entity>
- name="creation_time"
- label={i18n.str`Created at`}
- readonly
- />
- <InputDate<Entity>
- name="expiration_time"
- label={i18n.str`Valid until`}
- readonly
- />
- <InputCurrency<Entity>
- name="merchant_initial_amount"
- label={i18n.str`Created balance`}
- readonly
- />
- <TextField<Entity>
- name="exchange_url"
- label={i18n.str`Exchange URL`}
- readonly
- >
- <a target="_blank" rel="noreferrer" href={selected.exchange_url}>
- {selected.exchange_url}
- </a>
- </TextField>
-
- {didExchangeAckTransfer && (
- <Fragment>
- <InputCurrency<Entity>
- name="exchange_initial_amount"
- label={i18n.str`Exchange balance`}
- readonly
- />
- <InputCurrency<Entity>
- name="pickup_amount"
- label={i18n.str`Picked up`}
- readonly
- />
- <InputCurrency<Entity>
- name="committed_amount"
- label={i18n.str`Committed`}
- readonly
- />
- </Fragment>
- )}
- <Input name="id" label={i18n.str`Subject`} readonly />
- </FormProvider>
-
- {didExchangeAckTransfer ? (
- <Fragment>
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-cash-register" />
- </span>
- <i18n.Translate>Rewards</i18n.Translate>
- </p>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {selected.rewards && selected.rewards.length > 0 ? (
- <Table rewards={selected.rewards} />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- </Fragment>
- ) : selected.accounts ? (
- <ShowAccountsOfReserveAsQRWithLink
- accounts={selected.accounts}
- amount={selected.merchant_initial_amount}
- message={id}
- />
- ) : undefined}
-
- <div class="buttons is-right mt-5">
- <button class="button" onClick={onBack}>
- <i18n.Translate>Back</i18n.Translate>
- </button>
- </div>
- </div>
- </div>
- <div class="column" />
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- No reward has been authorized from this reserve
- </i18n.Translate>
- </p>
- </div>
- );
-}
-
-interface TableProps {
- rewards: MerchantBackend.Rewards.RewardStatusEntry[];
-}
-
-function Table({ rewards }: TableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Authorized</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Picked up</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Reason</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Expiration</i18n.Translate>
- </th>
- </tr>
- </thead>
- <tbody>
- {rewards.map((t, i) => {
- return <RewardRow id={t.reward_id} key={i} entry={t} />;
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-function RewardRow({
- id,
- entry,
-}: {
- id: string;
- entry: MerchantBackend.Rewards.RewardStatusEntry;
-}) {
- const [selected, setSelected] = useState(false);
- const result = useRewardDetails(id);
- const [settings] = useSettings();
- if (result.loading) {
- return (
- <tr>
- <td>...</td>
- <td>...</td>
- <td>...</td>
- <td>...</td>
- </tr>
- );
- }
- if (!result.ok) {
- return (
- <tr>
- <td>...</td> {/* authorized */}
- <td>{entry.total_amount}</td>
- <td>{entry.reason}</td>
- <td>...</td> {/* expired */}
- </tr>
- );
- }
- const info = result.data;
- function onSelect() {
- setSelected(true);
- }
- return (
- <Fragment>
- {selected && (
- <SimpleModal
- description="reward"
- active
- onCancel={() => setSelected(false)}
- >
- <RewardInfo id={id} amount={info.total_authorized} entity={info} />
- </SimpleModal>
- )}
- <tr>
- <td onClick={onSelect}>{info.total_authorized}</td>
- <td onClick={onSelect}>{info.total_picked_up}</td>
- <td onClick={onSelect}>{info.reason}</td>
- <td onClick={onSelect}>
- {info.expiration.t_s === "never"
- ? "never"
- : format(info.expiration.t_s * 1000, datetimeFormatForSettings(settings))}
- </td>
- </tr>
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
deleted file mode 100644
index e79b78027..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { DetailPage as TestedComponent } from "./DetailPage.js";
-
-export default {
- title: "Pages/Reserve/Detail",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Funded = createExample(TestedComponent, {
- id: "THISISTHERESERVEID",
- selected: {
- active: true,
- committed_amount: "TESTKUDOS:10",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- exchange_initial_amount: "TESTKUDOS:10",
- expiration_time: {
- t_s: new Date().getTime() / 1000,
- },
- merchant_initial_amount: "TESTKUDOS:10",
- pickup_amount: "TESTKUDOS:10",
- accounts: [
- {
- payto_uri: "payto://x-taler-bank/bank.taler:8080/account",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "",
- },
- ],
- exchange_url: "http://exchange.taler/",
- },
-});
-
-export const NotYetFunded = createExample(TestedComponent, {
- id: "THISISTHERESERVEID",
- selected: {
- active: true,
- committed_amount: "TESTKUDOS:10",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- exchange_initial_amount: "TESTKUDOS:0",
- expiration_time: {
- t_s: new Date().getTime() / 1000,
- },
- merchant_initial_amount: "TESTKUDOS:10",
- pickup_amount: "TESTKUDOS:10",
- accounts: [
- {
- payto_uri: "payto://x-taler-bank/bank.taler:8080/account",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "",
- },
- ],
- exchange_url: "http://exchange.taler/",
- },
-});
-
-export const FundedWithEmptyRewards = createExample(TestedComponent, {
- id: "THISISTHERESERVEID",
- selected: {
- active: true,
- committed_amount: "TESTKUDOS:10",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- exchange_initial_amount: "TESTKUDOS:10",
- expiration_time: {
- t_s: new Date().getTime() / 1000,
- },
- merchant_initial_amount: "TESTKUDOS:10",
- pickup_amount: "TESTKUDOS:10",
- accounts: [
- {
- payto_uri: "payto://x-taler-bank/bank.taler:8080/account",
- credit_restrictions: [],
- debit_restrictions: [],
- master_sig: "",
- },
- ],
- exchange_url: "http://exchange.taler/",
- rewards: [
- {
- reason: "asdasd",
- reward_id: "123",
- total_amount: "TESTKUDOS:1",
- },
- ],
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
deleted file mode 100644
index 01f2bf250..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { format } from "date-fns";
-import { Fragment, h, VNode } from "preact";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import {
- datetimeFormatForSettings,
- useSettings,
-} from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Rewards.RewardDetails;
-
-interface Props {
- id: string;
- entity: Entity;
- amount: string;
-}
-
-export function RewardInfo({
- id: merchantRewardId,
- amount,
- entity,
-}: Props): VNode {
- const { url: backendURL } = useBackendContext();
- const [settings] = useSettings();
- const rewardURL = "not-supported";
- return (
- <Fragment>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Amount</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input readonly class="input" value={amount} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">URL</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field" style={{ overflowWrap: "anywhere" }}>
- <p class="control">
- <a target="_blank" rel="noreferrer" href={rewardURL}>
- {rewardURL}
- </a>
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Valid until</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- class="input"
- readonly
- value={
- !entity.expiration || entity.expiration.t_s === "never"
- ? "never"
- : format(
- entity.expiration.t_s * 1000,
- datetimeFormatForSettings(settings),
- )
- }
- />
- </p>
- </div>
- </div>
- </div>
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/index.tsx
deleted file mode 100644
index 5146545ac..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/details/index.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ErrorType, HttpError } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
-import { Loading } from "../../../../components/exception/loading.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useReserveDetails } from "../../../../hooks/reserves.js";
-import { DetailPage } from "./DetailPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-interface Props {
- rid: string;
-
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onDelete: () => void;
- onBack: () => void;
-}
-export default function DetailReserve({
- rid,
- onUnauthorized,
- onLoadError,
- onNotFound,
- onBack,
- onDelete,
-}: Props): VNode {
- const result = useReserveDetails(rid);
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
- return (
- <Fragment>
- <DetailPage selected={result.data} onBack={onBack} id={rid} />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
deleted file mode 100644
index 4b688d932..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import * as yup from "yup";
-import {
- FormErrors,
- FormProvider,
-} from "../../../../components/form/FormProvider.js";
-import { Input } from "../../../../components/form/Input.js";
-import { InputCurrency } from "../../../../components/form/InputCurrency.js";
-import {
- ConfirmModal,
- ContinueModal,
-} from "../../../../components/modal/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { AuthorizeRewardSchema } from "../../../../schemas/index.js";
-import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
-
-interface AuthorizeRewardModalProps {
- onCancel: () => void;
- onConfirm: (value: MerchantBackend.Rewards.RewardCreateRequest) => void;
- rewardAuthorized?: {
- response: MerchantBackend.Rewards.RewardCreateConfirmation;
- request: MerchantBackend.Rewards.RewardCreateRequest;
- };
-}
-
-export function AuthorizeRewardModal({
- onCancel,
- onConfirm,
- rewardAuthorized,
-}: AuthorizeRewardModalProps): VNode {
- // const result = useOrderDetails(id)
- type State = MerchantBackend.Rewards.RewardCreateRequest;
- const [form, setValue] = useState<Partial<State>>({});
- const { i18n } = useTranslationContext();
-
- // const [errors, setErrors] = useState<FormErrors<State>>({})
- let errors: FormErrors<State> = {};
- try {
- AuthorizeRewardSchema.validateSync(form, { abortEarly: false });
- } catch (err) {
- if (err instanceof yup.ValidationError) {
- const yupErrors = err.inner as any[];
- errors = yupErrors.reduce(
- (prev, cur) =>
- !cur.path ? prev : { ...prev, [cur.path]: cur.message },
- {},
- );
- }
- }
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const validateAndConfirm = () => {
- onConfirm(form as State);
- };
- if (rewardAuthorized) {
- return (
- <ContinueModal description="reward" active onConfirm={onCancel}>
- <CreatedSuccessfully
- entity={rewardAuthorized.response}
- request={rewardAuthorized.request}
- onConfirm={onCancel}
- />
- </ContinueModal>
- );
- }
-
- return (
- <ConfirmModal
- description="New reward"
- active
- onCancel={onCancel}
- disabled={hasErrors}
- onConfirm={validateAndConfirm}
- >
- <FormProvider<State>
- errors={errors}
- object={form}
- valueHandler={setValue}
- >
- <InputCurrency<State>
- name="amount"
- label={i18n.str`Amount`}
- tooltip={i18n.str`amount of reward`}
- />
- <Input<State>
- name="justification"
- label={i18n.str`Justification`}
- inputType="multiline"
- tooltip={i18n.str`reason for the reward`}
- />
- <Input<State>
- name="next_url"
- label={i18n.str`URL after reward`}
- tooltip={i18n.str`URL to visit after reward payment`}
- />
- </FormProvider>
- </ConfirmModal>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
deleted file mode 100644
index bd6d64a71..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { format } from "date-fns";
-import { Fragment, h, VNode } from "preact";
-import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Rewards.RewardCreateConfirmation;
-
-interface Props {
- entity: Entity;
- request: MerchantBackend.Rewards.RewardCreateRequest;
- onConfirm: () => void;
- onCreateAnother?: () => void;
-}
-
-export function CreatedSuccessfully({
- request,
- entity,
- onConfirm,
- onCreateAnother,
-}: Props): VNode {
- const [settings] = useSettings();
- return (
- <Fragment>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Amount</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input readonly class="input" value={request.amount} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Justification</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input readonly class="input" value={request.justification} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">URL</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input readonly class="input" value={entity.reward_status_url} />
- </p>
- </div>
- </div>
- </div>
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label">Valid until</label>
- </div>
- <div class="field-body is-flex-grow-3">
- <div class="field">
- <p class="control">
- <input
- class="input"
- readonly
- value={
- !entity.reward_expiration ||
- entity.reward_expiration.t_s === "never"
- ? "never"
- : format(
- entity.reward_expiration.t_s * 1000,
- datetimeFormatForSettings(settings),
- )
- }
- />
- </p>
- </div>
- </div>
- </div>
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
deleted file mode 100644
index 18e86f401..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { CardTable as TestedComponent } from "./Table.js";
-
-export default {
- title: "Pages/Reserve/List",
- component: TestedComponent,
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const AllFunded = createExample(TestedComponent, {
- instances: [
- {
- id: "reseverId",
- active: true,
- committed_amount: "TESTKUDOS:10",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- exchange_initial_amount: "TESTKUDOS:10",
- expiration_time: {
- t_s: new Date().getTime() / 1000,
- },
- merchant_initial_amount: "TESTKUDOS:10",
- pickup_amount: "TESTKUDOS:10",
- reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
- },
- {
- id: "reseverId2",
- active: true,
- committed_amount: "TESTKUDOS:13",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- exchange_initial_amount: "TESTKUDOS:10",
- expiration_time: {
- t_s: new Date().getTime() / 1000,
- },
- merchant_initial_amount: "TESTKUDOS:10",
- pickup_amount: "TESTKUDOS:10",
- reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
- },
- ],
-});
-
-export const Empty = createExample(TestedComponent, {
- instances: [],
-});
-
-export const OneNotYetFunded = createExample(TestedComponent, {
- instances: [
- {
- id: "reseverId",
- active: true,
- committed_amount: "TESTKUDOS:0",
- creation_time: {
- t_s: new Date().getTime() / 1000,
- },
- exchange_initial_amount: "TESTKUDOS:0",
- expiration_time: {
- t_s: new Date().getTime() / 1000,
- },
- merchant_initial_amount: "TESTKUDOS:10",
- pickup_amount: "TESTKUDOS:10",
- reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
- },
- ],
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
deleted file mode 100644
index fbb30956c..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { format } from "date-fns";
-import { Fragment, h, VNode } from "preact";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Rewards.ReserveStatusEntry & WithId;
-
-interface Props {
- instances: Entity[];
- onNewReward: (id: Entity) => void;
- onSelect: (id: Entity) => void;
- onDelete: (id: Entity) => void;
- onCreate: () => void;
-}
-
-export function CardTable({
- instances,
- onCreate,
- onSelect,
- onNewReward,
- onDelete,
-}: Props): VNode {
- const [withoutFunds, withFunds] = instances.reduce((prev, current) => {
- const amount = current.exchange_initial_amount;
- if (amount.endsWith(":0")) {
- prev[0] = prev[0].concat(current);
- } else {
- prev[1] = prev[1].concat(current);
- }
- return prev;
- }, new Array<Array<Entity>>([], []));
-
- const { i18n } = useTranslationContext();
-
- return (
- <Fragment>
- {withoutFunds.length > 0 && (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-cash" />
- </span>
- <i18n.Translate>Reserves not yet funded</i18n.Translate>
- </p>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- <TableWithoutFund
- instances={withoutFunds}
- onNewReward={onNewReward}
- onSelect={onSelect}
- onDelete={onDelete}
- />
- </div>
- </div>
- </div>
- </div>
- )}
-
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-cash" />
- </span>
- <i18n.Translate>Reserves ready</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options" />
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new reserve`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {withFunds.length > 0 ? (
- <Table
- instances={withFunds}
- onNewReward={onNewReward}
- onSelect={onSelect}
- onDelete={onDelete}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- </Fragment>
- );
-}
-interface TableProps {
- instances: Entity[];
- onNewReward: (id: Entity) => void;
- onDelete: (id: Entity) => void;
- onSelect: (id: Entity) => void;
-}
-
-function Table({ instances, onNewReward, onSelect, onDelete }: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = useSettings();
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Created at</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Expires at</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Initial</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Picked up</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Committed</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.id}>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.creation_time.t_s === "never"
- ? "never"
- : format(i.creation_time.t_s * 1000, datetimeFormatForSettings(settings))}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.expiration_time.t_s === "never"
- ? "never"
- : format(
- i.expiration_time.t_s * 1000,
- datetimeFormatForSettings(settings),
- )}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.exchange_initial_amount}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.pickup_amount}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.committed_amount}
- </td>
- <td class="is-actions-cell right-sticky">
- <div class="buttons is-right">
- <button
- class="button is-small is-danger has-tooltip-left"
- data-tooltip={i18n.str`delete selected reserve from the database`}
- type="button"
- onClick={(): void => onDelete(i)}
- >
- Delete
- </button>
- <button
- class="button is-small is-info has-tooltip-left"
- data-tooltip={i18n.str`authorize new reward from selected reserve`}
- type="button"
- onClick={(): void => onNewReward(i)}
- >
- New Reward
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no ready reserves yet, add more pressing the + sign or fund
- them
- </i18n.Translate>
- </p>
- </div>
- );
-}
-
-function TableWithoutFund({
- instances,
- onSelect,
- onDelete,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = useSettings();
- return (
- <div class="table-container">
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>Created at</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Expires at</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Expected Balance</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.id}>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.creation_time.t_s === "never"
- ? "never"
- : format(i.creation_time.t_s * 1000, datetimeFormatForSettings(settings))}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.expiration_time.t_s === "never"
- ? "never"
- : format(
- i.expiration_time.t_s * 1000,
- datetimeFormatForSettings(settings),
- )}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.merchant_initial_amount}
- </td>
- <td class="is-actions-cell right-sticky">
- <div class="buttons is-right">
- <button
- class="button is-small is-danger jb-modal has-tooltip-left"
- type="button"
- data-tooltip={i18n.str`delete selected reserve from the database`}
- onClick={(): void => onDelete(i)}
- >
- Delete
- </button>
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/index.tsx
deleted file mode 100644
index c64d28502..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/reserves/list/index.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { 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 {
- useInstanceReserves,
- useReservesAPI,
-} from "../../../../hooks/reserves.js";
-import { Notification } from "../../../../utils/types.js";
-import { AuthorizeRewardModal } from "./AutorizeRewardModal.js";
-import { CardTable } from "./Table.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ConfirmModal } from "../../../../components/modal/index.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onSelect: (id: string) => void;
- onNotFound: () => VNode;
- onCreate: () => void;
-}
-
-interface RewardConfirmation {
- response: MerchantBackend.Rewards.RewardCreateConfirmation;
- request: MerchantBackend.Rewards.RewardCreateRequest;
-}
-
-export default function ListRewards({
- onUnauthorized,
- onLoadError,
- onNotFound,
- onSelect,
- onCreate,
-}: Props): VNode {
- const result = useInstanceReserves();
- const { deleteReserve, authorizeRewardReserve } = useReservesAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const [reserveForReward, setReserveForReward] = useState<string | undefined>(
- undefined,
- );
- const [deleting, setDeleting] =
- useState<MerchantBackend.Rewards.ReserveStatusEntry | null>(null);
- const [rewardAuthorized, setRewardAuthorized] = useState<
- RewardConfirmation | undefined
- >(undefined);
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <section class="section is-main-section">
- <NotificationCard notification={notif} />
-
- {reserveForReward && (
- <AuthorizeRewardModal
- onCancel={() => {
- setReserveForReward(undefined);
- setRewardAuthorized(undefined);
- }}
- rewardAuthorized={rewardAuthorized}
- onConfirm={async (request) => {
- try {
- const response = await authorizeRewardReserve(
- reserveForReward,
- request,
- );
- setRewardAuthorized({
- request,
- response: response.data,
- });
- } catch (error) {
- setNotif({
- message: i18n.str`could not create the reward`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- setReserveForReward(undefined);
- }
- }}
- />
- )}
-
- <CardTable
- instances={result.data.reserves
- .filter((r) => r.active)
- .map((o) => ({ ...o, id: o.reserve_pub }))}
- onCreate={onCreate}
- onDelete={(reserve) => {
- setDeleting(reserve)
- }}
- onSelect={(reserve) => onSelect(reserve.id)}
- onNewReward={(reserve) => setReserveForReward(reserve.id)}
- />
-
- {deleting && (
- <ConfirmModal
- label={`Delete reserve`}
- description={`Delete the reserve`}
- danger
- active
- onCancel={() => setDeleting(null)}
- onConfirm={async (): Promise<void> => {
- try {
- await deleteReserve(deleting.reserve_pub);
- setNotif({
- message: i18n.str`Reserve for "${deleting.merchant_initial_amount}" (ID: ${deleting.reserve_pub}) has been deleted`,
- type: "SUCCESS",
- });
- } catch (error) {
- setNotif({
- message: i18n.str`Failed to delete reserve`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- }
- setDeleting(null);
- }}
- >
- <p>
- If you delete the reserve for <b>&quot;{deleting.merchant_initial_amount}&quot;</b> you won't be able to create more rewards. <br />
- Reserve ID: <b>{deleting.reserve_pub}</b>
- </p>
- <p class="warning">
- Deleting an template <b>cannot be undone</b>.
- </p>
- </ConfirmModal>
- )}
-
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/create/Create.stories.tsx
deleted file mode 100644
index 53025f153..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/Create.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Templates/Create",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
deleted file mode 100644
index 44d023f36..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } 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 { InputDuration } from "../../../../components/form/InputDuration.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { InputSearchOnList } from "../../../../components/form/InputSearchOnList.js";
-import { InputTab } from "../../../../components/form/InputTab.js";
-import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
-import { undefinedIfEmpty } from "../../../../utils/table.js";
-
-enum Steps {
- BOTH_FIXED,
- FIXED_PRICE,
- FIXED_SUMMARY,
- NON_FIXED,
-}
-
-type Entity = MerchantBackend.Template.TemplateAddDetails & { type: Steps };
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
- const { url: backendURL } = useBackendContext();
- const devices = useInstanceOtpDevices();
-
- const [state, setState] = useState<Partial<Entity>>({
- template_contract: {
- minimum_age: 0,
- pay_duration: {
- d_us: 1000 * 1000 * 60 * 30, //30 min
- },
- },
- type: Steps.NON_FIXED,
- });
-
- const parsedPrice = !state.template_contract?.amount
- ? undefined
- : Amounts.parse(state.template_contract?.amount);
-
- const errors: FormErrors<Entity> = {
- template_id: !state.template_id
- ? i18n.str`should not be empty`
- : !/[a-zA-Z0-9]*/.test(state.template_id)
- ? i18n.str`no valid. only characters and numbers`
- : undefined,
- template_description: !state.template_description
- ? i18n.str`should not be empty`
- : undefined,
- template_contract: !state.template_contract
- ? undefined
- : undefinedIfEmpty({
- amount: !(
- state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED
- )
- ? undefined
- : !state.template_contract?.amount
- ? i18n.str`required`
- : !parsedPrice
- ? i18n.str`not valid`
- : Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
- summary: !(
- state.type === Steps.FIXED_SUMMARY ||
- state.type === Steps.BOTH_FIXED
- )
- ? undefined
- : !state.template_contract?.summary
- ? i18n.str`required`
- : undefined,
- minimum_age:
- state.template_contract.minimum_age < 0
- ? i18n.str`should be greater that 0`
- : undefined,
- pay_duration: !state.template_contract.pay_duration
- ? i18n.str`can't be empty`
- : state.template_contract.pay_duration.d_us === "forever"
- ? undefined
- : state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second
- ? i18n.str`to short`
- : undefined,
- } as Partial<TalerMerchantApi.TemplateContractDetails>),
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- if (state.template_contract) {
- if (state.type === Steps.NON_FIXED) {
- delete state.template_contract.amount;
- delete state.template_contract.summary;
- } else if (state.type === Steps.FIXED_SUMMARY) {
- delete state.template_contract.amount;
- } else if (state.type === Steps.FIXED_PRICE) {
- delete state.template_contract.summary;
- }
- }
- delete state.type;
- return onCreate(state as any);
- };
-
- const deviceList = !devices.ok ? [] : devices.data.otp_devices;
-
- 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}
- >
- <InputWithAddon<Entity>
- name="template_id"
- help={`${backendURL}/templates/${state.template_id ?? ""}`}
- label={i18n.str`Identifier`}
- tooltip={i18n.str`Name of the template in URLs.`}
- />
- <Input<Entity>
- name="template_description"
- label={i18n.str`Description`}
- help=""
- tooltip={i18n.str`Describe what this template stands for`}
- />
- <InputTab
- name="type"
- label={i18n.str`Type`}
- help={(() => {
- switch (state.type) {
- case Steps.NON_FIXED:
- return i18n.str`User will be able to input price and summary before payment.`;
- case Steps.FIXED_PRICE:
- return i18n.str`User will be able to add a summary before payment.`;
- case Steps.FIXED_SUMMARY:
- return i18n.str`User will be able to set the price before payment.`;
- case Steps.BOTH_FIXED:
- return i18n.str`User will not be able to change the price or the summary.`;
- }
- })()}
- tooltip={i18n.str`Define what the user be allowed to modify`}
- values={[
- Steps.NON_FIXED,
- Steps.FIXED_PRICE,
- Steps.FIXED_SUMMARY,
- Steps.BOTH_FIXED,
- ]}
- toStr={(v: Steps): string => {
- switch (v) {
- case Steps.NON_FIXED:
- return i18n.str`Simple`;
- case Steps.FIXED_PRICE:
- return i18n.str`With price`;
- case Steps.FIXED_SUMMARY:
- return i18n.str`With summary`;
- case Steps.BOTH_FIXED:
- return i18n.str`With price and summary`;
- }
- }}
- />
- {state.type === Steps.BOTH_FIXED ||
- state.type === Steps.FIXED_SUMMARY ? (
- <Input
- name="template_contract.summary"
- inputType="multiline"
- label={i18n.str`Fixed summary`}
- tooltip={i18n.str`If specified, this template will create order with the same summary`}
- />
- ) : undefined}
- {state.type === Steps.BOTH_FIXED ||
- state.type === Steps.FIXED_PRICE ? (
- <InputCurrency
- name="template_contract.amount"
- label={i18n.str`Fixed price`}
- tooltip={i18n.str`If specified, this template will create order with the same price`}
- />
- ) : undefined}
- <InputNumber
- name="template_contract.minimum_age"
- label={i18n.str`Minimum age`}
- help=""
- tooltip={i18n.str`Is this contract restricted to some age?`}
- />
- <InputDuration
- name="template_contract.pay_duration"
- label={i18n.str`Payment timeout`}
- help=""
- tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
- />
- <Input<Entity>
- name="otp_id"
- label={i18n.str`OTP device`}
- readonly
- tooltip={i18n.str`Use to verify transaction in offline mode.`}
- />
- <InputSearchOnList
- label={i18n.str`Search device`}
- onChange={(p) => setState((v) => ({ ...v, otp_id: p?.id }))}
- list={deviceList.map((e) => ({
- description: e.device_description,
- id: e.otp_device_id,
- }))}
- />
- </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/auditor-backoffice-ui/src/paths/instance/templates/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/create/index.tsx
deleted file mode 100644
index f76146812..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/index.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useTemplateAPI } from "../../../../hooks/templates.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-
-export type Entity = MerchantBackend.Transfers.TransferInformation;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-
-export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
- const { createTemplate } = useTemplateAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- return (
- <>
- <NotificationCard notification={notif} />
- <CreatePage
- onBack={onBack}
- onCreate={(request: MerchantBackend.Template.TemplateAddDetails) => {
- return createTemplate(request)
- .then(() => onConfirm())
- .catch((error) => {
- setNotif({
- message: i18n.str`could not inform template`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
deleted file mode 100644
index 707324d40..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/List.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { FunctionalComponent, h } from "preact";
-import { ListPage as TestedComponent } from "./ListPage.js";
-
-export default {
- title: "Pages/Templates/List",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
deleted file mode 100644
index c21f64776..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/ListPage.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { CardTable } from "./Table.js";
-
-export interface Props {
- templates: MerchantBackend.Template.TemplateEntry[];
- onLoadMoreBefore?: () => void;
- onLoadMoreAfter?: () => void;
- onCreate: () => void;
- 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({
- templates,
- onCreate,
- onDelete,
- onSelect,
- onNewOrder,
- onQR,
- onLoadMoreBefore,
- onLoadMoreAfter,
-}: Props): VNode {
- const form = { payto_uri: "" };
-
- const { i18n } = useTranslationContext();
- return (
- <CardTable
- templates={templates.map((o) => ({
- ...o,
- id: String(o.template_id),
- }))}
- onQR={onQR}
- onCreate={onCreate}
- onDelete={onDelete}
- onSelect={onSelect}
- onNewOrder={onNewOrder}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreBefore={!onLoadMoreBefore}
- onLoadMoreAfter={onLoadMoreAfter}
- hasMoreAfter={!onLoadMoreAfter}
- />
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/list/Table.tsx
deleted file mode 100644
index 42758962d..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/Table.tsx
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import { MerchantBackend } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.Template.TemplateEntry;
-
-interface Props {
- templates: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- onNewOrder: (e: Entity) => void;
- onQR: (e: Entity) => void;
- onCreate: () => void;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-export function CardTable({
- templates,
- onCreate,
- onDelete,
- onSelect,
- onQR,
- onNewOrder,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-newspaper" />
- </span>
- <i18n.Translate>Templates</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new templates`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {templates.length > 0 ? (
- <Table
- instances={templates}
- onDelete={onDelete}
- onSelect={onSelect}
- onNewOrder={onNewOrder}
- onQR={onQR}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- instances: Entity[];
- onDelete: (e: Entity) => void;
- onNewOrder: (e: Entity) => void;
- onQR: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
-function Table({
- instances,
- onLoadMoreAfter,
- onDelete,
- onNewOrder,
- onQR,
- onSelect,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- {hasMoreBefore && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more templates before the first one`}
- onClick={onLoadMoreBefore}
- >
- <i18n.Translate>load newer templates</i18n.Translate>
- </button>
- )}
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>ID</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Description</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.template_id}>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.template_id}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.template_description}
- </td>
- <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`use template to create new order`}
- onClick={() => onNewOrder(i)}
- >
- Use template
- </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>
- );
- })}
- </tbody>
- </table>
- {hasMoreAfter && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more templates after the last one`}
- onClick={onLoadMoreAfter}
- >
- <i18n.Translate>load older templates</i18n.Translate>
- </button>
- )}
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no templates yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/list/index.tsx
deleted file mode 100644
index bdb7faf9d..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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 {
- useInstanceTemplates,
- useTemplateAPI,
-} from "../../../../hooks/templates.js";
-import { Notification } from "../../../../utils/types.js";
-import { ListPage } from "./ListPage.js";
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { ConfirmModal } from "../../../../components/modal/index.js";
-import { JumpToElementById } from "../../../../components/form/JumpToElementById.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- 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,
-}: Props): VNode {
- const [position, setPosition] = useState<string | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { deleteTemplate, testTemplateExist } = useTemplateAPI();
- const result = useInstanceTemplates({ position }, (id) => setPosition(id));
- const [deleting, setDeleting] =
- useState<MerchantBackend.Template.TemplateEntry | null>(null);
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <section class="section is-main-section">
- <NotificationCard notification={notif} />
-
- <JumpToElementById
- testIfExist={testTemplateExist}
- onSelect={onSelect}
- description={i18n.str`jump to template with the given template ID`}
- placeholder={i18n.str`template id`}
- />
-
- <ListPage
- templates={result.data.templates}
- onLoadMoreBefore={
- result.isReachingStart ? result.loadMorePrev : undefined
- }
- onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
- onCreate={onCreate}
- onSelect={(e) => {
- onSelect(e.template_id);
- }}
- onNewOrder={(e) => {
- onNewOrder(e.template_id);
- }}
- onQR={(e) => {
- onQR(e.template_id);
- }}
- onDelete={(e: MerchantBackend.Template.TemplateEntry) => {
- setDeleting(e)
- }
- }
- />
-
- {deleting && (
- <ConfirmModal
- label={`Delete template`}
- description={`Delete the template "${deleting.template_description}"`}
- danger
- active
- onCancel={() => setDeleting(null)}
- onConfirm={async (): Promise<void> => {
- try {
- await deleteTemplate(deleting.template_id);
- setNotif({
- message: i18n.str`Template "${deleting.template_description}" (ID: ${deleting.template_id}) has been deleted`,
- type: "SUCCESS",
- });
- } catch (error) {
- setNotif({
- message: i18n.str`Failed to delete template`,
- type: "ERROR",
- description: error instanceof Error ? error.message : undefined,
- });
- }
- setDeleting(null);
- }}
- >
- <p>
- If you delete the template <b>&quot;{deleting.template_description}&quot;</b> (ID:{" "}
- <b>{deleting.template_id}</b>) you may loose information
- </p>
- <p class="warning">
- Deleting an template <b>cannot be undone</b>.
- </p>
- </ConfirmModal>
- )}
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
deleted file mode 100644
index c0059c7bc..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/Qr.stories.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
deleted file mode 100644
index 108ec617f..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { stringifyPayTemplateUri } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
-import { useState } from "preact/hooks";
-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 { useInstanceContext } from "../../../../context/instance.js";
-import { MerchantBackend } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.Template.UsingTemplateDetails;
-
-interface Props {
- contract: MerchantBackend.Template.TemplateContractDetails;
- id: string;
- onBack?: () => void;
-}
-
-export function QrPage({ contract, id: templateId, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
- const { url: backendURL } = useBackendContext()
- const { id: instanceId } = useInstanceContext();
- const config = useConfigContext();
-
- const [state, setState] = useState<Partial<Entity>>({
- amount: contract.amount,
- summary: contract.summary,
- });
-
- const errors: FormErrors<Entity> = {};
-
- const fixedAmount = !!contract.amount;
- const fixedSummary = !!contract.summary;
-
- const templateParams: Record<string, string> = {}
- if (!fixedAmount) {
- if (state.amount) {
- templateParams.amount = state.amount
- } else {
- templateParams.amount = config.currency
- }
- }
-
- if (!fixedSummary) {
- templateParams.summary = state.summary ?? ""
- }
-
- const merchantBaseUrl = new URL(backendURL).href;
-
- const payTemplateUri = stringifyPayTemplateUri({
- merchantBaseUrl,
- templateId,
- //templateParams
- })
-
- const issuer = encodeURIComponent(
- `${new URL(backendURL).host}/${instanceId}`,
- );
-
- return (
- <div>
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <p class="is-size-5 mt-5 mb-5">
- <i18n.Translate>
- Here you can specify a default value for fields that are not
- fixed. Default values can be edited by the customer before the
- payment.
- </i18n.Translate>
- </p>
-
- <p></p>
- <FormProvider
- object={state}
- valueHandler={setState}
- errors={errors}
- >
- <InputCurrency<Entity>
- name="amount"
- label={
- fixedAmount
- ? i18n.str`Fixed amount`
- : i18n.str`Default amount`
- }
- readonly={fixedAmount}
- tooltip={i18n.str`Amount of the order`}
- />
- <Input<Entity>
- name="summary"
- inputType="multiline"
- readonly={fixedSummary}
- label={
- fixedSummary
- ? i18n.str`Fixed summary`
- : i18n.str`Default 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={() => saveAsPDF(templateId)}
- >
- <i18n.Translate>Print</i18n.Translate>
- </button>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- <section id="printThis">
- <QR text={payTemplateUri} />
- <pre style={{ textAlign: "center" }}>
- <a href={payTemplateUri}>{payTemplateUri}</a>
- </pre>
- </section>
- </div>
- );
-}
-
-function saveAsPDF(name: string): void {
- const printWindow = window.open("", "", "height=400,width=800");
- if (!printWindow) return;
- const divContents = document.getElementById("printThis");
- if (!divContents) return;
- printWindow.document.write(
- `<html><head><title>Order template for ${name}</title><style>`,
- );
- printWindow.document.write("</style></head><body>&nbsp;</body></html>");
- printWindow.document.close();
- printWindow.document.body.appendChild(divContents.cloneNode(true));
- printWindow.addEventListener("load", () => {
- printWindow.print();
- printWindow.close();
- });
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
deleted file mode 100644
index 303d17b72..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/Update.stories.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/Templates/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
deleted file mode 100644
index 3aa3f6ddd..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } 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 { InputDuration } from "../../../../components/form/InputDuration.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { InputTab } from "../../../../components/form/InputTab.js";
-import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { undefinedIfEmpty } from "../../../../utils/table.js";
-
-enum Steps {
- BOTH_FIXED,
- FIXED_PRICE,
- FIXED_SUMMARY,
- NON_FIXED,
-}
-
-type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
-
-interface Props {
- onUpdate: (d: Entity) => Promise<void>;
- onBack?: () => void;
- template: Entity;
-}
-
-export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
- const { url: backendURL } = useBackendContext();
-
- const intialStep =
- template.template_contract?.amount === undefined &&
- template.template_contract?.summary === undefined
- ? Steps.NON_FIXED
- : template.template_contract?.summary === undefined
- ? Steps.FIXED_PRICE
- : template.template_contract?.amount === undefined
- ? Steps.FIXED_SUMMARY
- : Steps.BOTH_FIXED;
-
- const [state, setState] = useState<Partial<Entity & { type: Steps }>>({
- ...template,
- type: intialStep,
- });
-
- const parsedPrice = !state.template_contract?.amount
- ? undefined
- : Amounts.parse(state.template_contract?.amount);
-
- const errors: FormErrors<Entity> = {
- template_description: !state.template_description
- ? i18n.str`should not be empty`
- : undefined,
- template_contract: !state.template_contract
- ? undefined
- : undefinedIfEmpty({
- amount: !(
- state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED
- )
- ? undefined
- : !state.template_contract?.amount
- ? i18n.str`required`
- : !parsedPrice
- ? i18n.str`not valid`
- : Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
- summary: !(
- state.type === Steps.FIXED_SUMMARY ||
- state.type === Steps.BOTH_FIXED
- )
- ? undefined
- : !state.template_contract?.summary
- ? i18n.str`required`
- : undefined,
- minimum_age:
- state.template_contract.minimum_age < 0
- ? i18n.str`should be greater that 0`
- : undefined,
- pay_duration: !state.template_contract.pay_duration
- ? i18n.str`can't be empty`
- : state.template_contract.pay_duration.d_us === "forever"
- ? undefined
- : state.template_contract.pay_duration.d_us < 1000 * 1000 // less than one second
- ? i18n.str`to short`
- : undefined,
- } as Partial<TalerMerchantApi.TemplateContractDetails>),
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- if (state.template_contract) {
- if (state.type === Steps.NON_FIXED) {
- delete state.template_contract.amount;
- delete state.template_contract.summary;
- } else if (state.type === Steps.FIXED_SUMMARY) {
- delete state.template_contract.amount;
- } else if (state.type === Steps.FIXED_PRICE) {
- delete state.template_contract.summary;
- }
- }
- delete state.type;
- return onUpdate(state as any);
- };
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- {backendURL}/templates/{template.id}
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column is-four-fifths">
- <FormProvider
- object={state}
- valueHandler={setState}
- errors={errors}
- >
- <InputWithAddon<Entity>
- name="id"
- addonBefore={`templates/`}
- readonly
- label={i18n.str`Identifier`}
- tooltip={i18n.str`Name of the template in URLs.`}
- />
-
- <Input<Entity>
- name="template_description"
- label={i18n.str`Description`}
- help=""
- tooltip={i18n.str`Describe what this template stands for`}
- />
- <InputTab
- name="type"
- label={i18n.str`Type`}
- help={(() => {
- switch (state.type) {
- case Steps.NON_FIXED:
- return i18n.str`User will be able to input price and summary before payment.`;
- case Steps.FIXED_PRICE:
- return i18n.str`User will be able to add a summary before payment.`;
- case Steps.FIXED_SUMMARY:
- return i18n.str`User will be able to set the price before payment.`;
- case Steps.BOTH_FIXED:
- return i18n.str`User will not be able to change the price or the summary.`;
- }
- })()}
- tooltip={i18n.str`Define what the user be allowed to modify`}
- values={[
- Steps.NON_FIXED,
- Steps.FIXED_PRICE,
- Steps.FIXED_SUMMARY,
- Steps.BOTH_FIXED,
- ]}
- toStr={(v: Steps): string => {
- switch (v) {
- case Steps.NON_FIXED:
- return i18n.str`Simple`;
- case Steps.FIXED_PRICE:
- return i18n.str`With price`;
- case Steps.FIXED_SUMMARY:
- return i18n.str`With summary`;
- case Steps.BOTH_FIXED:
- return i18n.str`With price and summary`;
- }
- }}
- />
- {state.type === Steps.BOTH_FIXED ||
- state.type === Steps.FIXED_SUMMARY ? (
- <Input
- name="template_contract.summary"
- inputType="multiline"
- label={i18n.str`Fixed summary`}
- tooltip={i18n.str`If specified, this template will create order with the same summary`}
- />
- ) : undefined}
- {state.type === Steps.BOTH_FIXED ||
- state.type === Steps.FIXED_PRICE ? (
- <InputCurrency
- name="template_contract.amount"
- label={i18n.str`Fixed price`}
- tooltip={i18n.str`If specified, this template will create order with the same price`}
- />
- ) : undefined}
- <InputNumber
- name="template_contract.minimum_age"
- label={i18n.str`Minimum age`}
- help=""
- tooltip={i18n.str`Is this contract restricted to some age?`}
- />
- <InputDuration
- name="template_contract.pay_duration"
- label={i18n.str`Payment timeout`}
- help=""
- tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
- />
- </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>
- </section>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/update/index.tsx
deleted file mode 100644
index 035b22da9..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/index.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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, WithId } from "../../../../declaration.js";
-import {
- useTemplateAPI,
- useTemplateDetails,
-} from "../../../../hooks/templates.js";
-import { Notification } from "../../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-export type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId;
-
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- tid: string;
-}
-export default function UpdateTemplate({
- tid,
- onConfirm,
- onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
-}: Props): VNode {
- const { updateTemplate } = useTemplateAPI();
- const result = useTemplateDetails(tid);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <UpdatePage
- template={{ ...result.data, id: tid }}
- onBack={onBack}
- onUpdate={(data) => {
- return updateTemplate(tid, data)
- .then(onConfirm)
- .catch((error) => {
- setNotif({
- message: i18n.str`could not update template`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
deleted file mode 100644
index d91888b97..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/use/Use.stories.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UsePage as TestedComponent } from "./UsePage.js";
-
-export default {
- title: "Pages/Templates/Create",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
deleted file mode 100644
index 93f303537..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/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 {
- id: string;
- template: MerchantBackend.Template.TemplateDetails;
- onCreateOrder: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-export function UsePage({ id, 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">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- <i18n.Translate>New order for template</i18n.Translate>:{" "}
- <b>{id}</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- </section>
- <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/auditor-backoffice-ui/src/paths/instance/templates/use/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/use/index.tsx
deleted file mode 100644
index 441a5c5e0..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/templates/use/index.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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 { UsePage } from "./UsePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-export type Entity = MerchantBackend.Transfers.TransferInformation;
-interface Props {
- onBack?: () => void;
- onOrderCreated: (id: string) => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => 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.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <>
- <NotificationCard notification={notif} />
- <UsePage
- template={result.data}
- id={tid}
- 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/auditor-backoffice-ui/src/paths/instance/token/DetailPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/token/DetailPage.tsx
deleted file mode 100644
index 1e9186624..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/token/DetailPage.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { AsyncButton } from "../../../components/exception/AsyncButton.js";
-import { FormProvider } from "../../../components/form/FormProvider.js";
-import { Input } from "../../../components/form/Input.js";
-import { useInstanceContext } from "../../../context/instance.js";
-import { AccessToken } from "../../../declaration.js";
-import { NotificationCard } from "../../../components/menu/index.js";
-
-interface Props {
- instanceId: string;
- hasToken: boolean | undefined;
- onClearToken: (c: AccessToken | undefined) => void;
- onNewToken: (c: AccessToken | undefined, s: AccessToken) => void;
- onBack?: () => void;
-}
-
-export function DetailPage({ instanceId, hasToken, onBack, onNewToken, onClearToken }: Props): VNode {
- type State = { old_token: string; new_token: string; repeat_token: string };
- const [form, setValue] = useState<Partial<State>>({
- old_token: "",
- new_token: "",
- repeat_token: "",
- });
- const { i18n } = useTranslationContext();
-
- const errors = {
- old_token: hasToken && !form.old_token
- ? i18n.str`you need your access token to perform the operation`
- : undefined,
- new_token: !form.new_token
- ? i18n.str`cannot be empty`
- : form.new_token === form.old_token
- ? i18n.str`cannot be the same as the old token`
- : undefined,
- repeat_token:
- form.new_token !== form.repeat_token
- ? i18n.str`is not the same`
- : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const instance = useInstanceContext();
-
- const text = i18n.str`You are updating the access token from instance with id "${instance.id}"`;
-
- async function submitForm() {
- if (hasErrors) return;
- const oldToken = hasToken ? `secret-token:${form.old_token}` as AccessToken : undefined;
- const newToken = `secret-token:${form.new_token}` as AccessToken;
- onNewToken(oldToken, newToken)
- }
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- {text}
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- {!hasToken &&
- <NotificationCard
- notification={{
- message: i18n.str`This instance doesn't have authentication token.`,
- description: i18n.str`You can leave it empty if there is another layer of security.`,
- type: "WARN",
- }}
- />
- }
-
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider errors={errors} object={form} valueHandler={setValue}>
- <Fragment>
- {hasToken && (
- <Fragment>
- <Input<State>
- name="old_token"
- label={i18n.str`Current access token`}
- tooltip={i18n.str`access token currently in use`}
- inputType="password"
- />
- <p>
- <i18n.Translate>
- Clearing the access token will mean public access to the instance.
- </i18n.Translate>
- </p>
- <div class="buttons is-right mt-5">
- <button
- class="button"
- onClick={() => {
- if (hasToken) {
- const oldToken = `secret-token:${form.old_token}` as AccessToken;
- onClearToken(oldToken)
- } else {
- onClearToken(undefined)
- }
- }}
- >
- <i18n.Translate>Clear token</i18n.Translate>
- </button>
- </div>
- </Fragment>
- )}
-
-
- <Input<State>
- name="new_token"
- label={i18n.str`New access token`}
- tooltip={i18n.str`next access token to be used`}
- inputType="password"
- />
- <Input<State>
- name="repeat_token"
- label={i18n.str`Repeat access token`}
- tooltip={i18n.str`confirm the same access token`}
- inputType="password"
- />
- </Fragment>
- </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 change</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
-
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/token/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/token/index.tsx
deleted file mode 100644
index 13642ec22..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/token/index.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ErrorType, HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { Loading } from "../../../components/exception/loading.js";
-import { AccessToken, MerchantBackend } from "../../../declaration.js";
-import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance.js";
-import { DetailPage } from "./DetailPage.js";
-import { useInstanceContext } from "../../../context/instance.js";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../components/menu/index.js";
-import { Notification } from "../../../utils/types.js";
-import { useBackendContext } from "../../../context/backend.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onChange: () => void;
- onNotFound: () => VNode;
- onCancel: () => void;
-}
-
-export default function Token({
- onLoadError,
- onChange,
- onUnauthorized,
- onNotFound,
- onCancel,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { clearAccessToken, setNewAccessToken } = useInstanceAPI();
- const { id } = useInstanceContext();
- const result = useInstanceDetails()
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- const hasToken = result.data.auth.method === "token"
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <DetailPage
- instanceId={id}
- onBack={onCancel}
- hasToken={hasToken}
- onClearToken={async (currentToken): Promise<void> => {
- try {
- await clearAccessToken(currentToken);
- onChange();
- } catch (error) {
- if (error instanceof Error) {
- setNotif({
- message: i18n.str`Failed to clear token`,
- type: "ERROR",
- description: error.message,
- });
- }
- }
- }}
- onNewToken={async (currentToken, newToken): Promise<void> => {
- try {
- await setNewAccessToken(currentToken, newToken);
- onChange();
- } catch (error) {
- if (error instanceof Error) {
- setNotif({
- message: i18n.str`Failed to set new token`,
- type: "ERROR",
- description: error.message,
- });
- }
- }
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/token/stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/token/stories.tsx
deleted file mode 100644
index 581828657..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/token/stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { DetailPage as TestedComponent } from "./DetailPage.js";
-
-export default {
- title: "Pages/Token",
- component: TestedComponent,
-};
-
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx
deleted file mode 100644
index ca38defc3..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/Create.stories.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Transfer/Create",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {
- accounts: ["payto://x-taler-bank/account1", "payto://x-taler-bank/account2"],
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx
deleted file mode 100644
index eb25045a0..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/CreatePage.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/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 { InputSelector } from "../../../../components/form/InputSelector.js";
-import { useConfigContext } from "../../../../context/config.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import {
- CROCKFORD_BASE32_REGEX,
- URL_REGEX,
-} from "../../../../utils/constants.js";
-
-type Entity = MerchantBackend.Transfers.TransferInformation;
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
- accounts: string[];
-}
-
-export function CreatePage({ accounts, onCreate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
- const { currency } = useConfigContext();
-
- const [state, setState] = useState<Partial<Entity>>({
- wtid: "",
- // payto_uri: ,
- // exchange_url: 'http://exchange.taler:8081/',
- credit_amount: ``,
- });
-
- const errors: FormErrors<Entity> = {
- wtid: !state.wtid
- ? i18n.str`cannot be empty`
- : !CROCKFORD_BASE32_REGEX.test(state.wtid)
- ? i18n.str`check the id, does not look valid`
- : state.wtid.length !== 52
- ? i18n.str`should have 52 characters, current ${state.wtid.length}`
- : undefined,
- payto_uri: !state.payto_uri ? i18n.str`cannot be empty` : undefined,
- credit_amount: !state.credit_amount ? i18n.str`cannot be empty` : undefined,
- exchange_url: !state.exchange_url
- ? i18n.str`cannot be empty`
- : !URL_REGEX.test(state.exchange_url)
- ? i18n.str`URL doesn't have the right format`
- : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- return onCreate(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}
- >
- <InputSelector
- name="payto_uri"
- label={i18n.str`Credited bank account`}
- values={accounts}
- placeholder={i18n.str`Select one account`}
- tooltip={i18n.str`Bank account of the merchant where the payment was received`}
- />
- <Input<Entity>
- name="wtid"
- label={i18n.str`Wire transfer ID`}
- help=""
- tooltip={i18n.str`unique identifier of the wire transfer used by the exchange, must be 52 characters long`}
- />
- <Input<Entity>
- name="exchange_url"
- label={i18n.str`Exchange URL`}
- tooltip={i18n.str`Base URL of the exchange that made the transfer, should have been in the wire transfer subject`}
- help="http://exchange.taler:8081/"
- />
- <InputCurrency<Entity>
- name="credit_amount"
- label={i18n.str`Amount credited`}
- tooltip={i18n.str`Actual amount that was wired to the merchant's bank account`}
- />
- </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/auditor-backoffice-ui/src/paths/instance/transfers/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/index.tsx
deleted file mode 100644
index 77a8c65fb..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceDetails } from "../../../../hooks/instance.js";
-import { useTransferAPI } from "../../../../hooks/transfer.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-import { useBankAccountDetails, useInstanceBankAccounts } from "../../../../hooks/bank.js";
-
-export type Entity = MerchantBackend.Transfers.TransferInformation;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-
-export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
- const { informTransfer } = useTransferAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const instance = useInstanceBankAccounts();
- const accounts = !instance.ok
- ? []
- : instance.data.accounts.map((a) => a.payto_uri);
-
- return (
- <>
- <NotificationCard notification={notif} />
- <CreatePage
- onBack={onBack}
- accounts={accounts}
- onCreate={(request: MerchantBackend.Transfers.TransferInformation) => {
- return informTransfer(request)
- .then(() => onConfirm())
- .catch((error) => {
- setNotif({
- message: i18n.str`could not inform transfer`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx
deleted file mode 100644
index ba22cb7d5..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/List.stories.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ListPage as TestedComponent } from "./ListPage.js";
-
-export default {
- title: "Pages/Transfer/List",
- component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onDelete: { action: "onDelete" },
- onLoadMoreBefore: { action: "onLoadMoreBefore" },
- onLoadMoreAfter: { action: "onLoadMoreAfter" },
- onShowAll: { action: "onShowAll" },
- onShowVerified: { action: "onShowVerified" },
- onShowUnverified: { action: "onShowUnverified" },
- onChangePayTo: { action: "onChangePayTo" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {
- transfers: [
- {
- exchange_url: "http://exchange.url/",
- credit_amount: "TESTKUDOS:10",
- payto_uri: "payto//x-taler-bank/bank:8080/account",
- transfer_serial_id: 123123123,
- wtid: "!@KJELQKWEJ!L@K#!J@",
- confirmed: true,
- execution_time: {
- t_s: new Date().getTime() / 1000,
- },
- verified: false,
- },
- {
- exchange_url: "http://exchange.url/",
- credit_amount: "TESTKUDOS:10",
- payto_uri: "payto//x-taler-bank/bank:8080/account",
- transfer_serial_id: 123123123,
- wtid: "!@KJELQKWEJ!L@K#!J@",
- confirmed: true,
- execution_time: {
- t_s: new Date().getTime() / 1000,
- },
- verified: false,
- },
- {
- exchange_url: "http://exchange.url/",
- credit_amount: "TESTKUDOS:10",
- payto_uri: "payto//x-taler-bank/bank:8080/account",
- transfer_serial_id: 123123123,
- wtid: "!@KJELQKWEJ!L@K#!J@",
- confirmed: true,
- execution_time: {
- t_s: new Date().getTime() / 1000,
- },
- verified: false,
- },
- ],
- accounts: ["payto://x-taler-bank/bank/some_account"],
-});
-export const Empty = createExample(TestedComponent, {
- transfers: [],
- accounts: [],
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
deleted file mode 100644
index 794d37fe6..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/ListPage.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { FormProvider } from "../../../../components/form/FormProvider.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { CardTable } from "./Table.js";
-
-export interface Props {
- transfers: MerchantBackend.Transfers.TransferDetails[];
- onLoadMoreBefore?: () => void;
- onLoadMoreAfter?: () => void;
- onShowAll: () => void;
- onShowVerified: () => void;
- onShowUnverified: () => void;
- isVerifiedTransfers?: boolean;
- isNonVerifiedTransfers?: boolean;
- isAllTransfers?: boolean;
- accounts: string[];
- onChangePayTo: (p?: string) => void;
- payTo?: string;
- onCreate: () => void;
- onDelete: () => void;
-}
-
-export function ListPage({
- payTo,
- onChangePayTo,
- transfers,
- onCreate,
- onDelete,
- accounts,
- onLoadMoreBefore,
- onLoadMoreAfter,
- isAllTransfers,
- isNonVerifiedTransfers,
- isVerifiedTransfers,
- onShowAll,
- onShowUnverified,
- onShowVerified,
-}: Props): VNode {
- const form = { payto_uri: payTo };
-
- const { i18n } = useTranslationContext();
- return (
- <section class="section is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-10">
- <FormProvider
- object={form}
- valueHandler={(updater) => onChangePayTo(updater(form).payto_uri)}
- >
- <InputSelector
- name="payto_uri"
- label={i18n.str`Account URI`}
- values={accounts}
- placeholder={i18n.str`Select one account`}
- tooltip={i18n.str`filter by account address`}
- />
- </FormProvider>
- </div>
- <div class="column" />
- </div>
- <div class="tabs">
- <ul>
- <li class={isAllTransfers ? "is-active" : ""}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`remove all filters`}
- >
- <a onClick={onShowAll}>
- <i18n.Translate>All</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isVerifiedTransfers ? "is-active" : ""}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`only show wire transfers confirmed by the merchant`}
- >
- <a onClick={onShowVerified}>
- <i18n.Translate>Verified</i18n.Translate>
- </a>
- </div>
- </li>
- <li class={isNonVerifiedTransfers ? "is-active" : ""}>
- <div
- class="has-tooltip-right"
- data-tooltip={i18n.str`only show wire transfers claimed by the exchange`}
- >
- <a onClick={onShowUnverified}>
- <i18n.Translate>Unverified</i18n.Translate>
- </a>
- </div>
- </li>
- </ul>
- </div>
- <CardTable
- transfers={transfers.map((o) => ({
- ...o,
- id: String(o.transfer_serial_id),
- }))}
- accounts={accounts}
- onCreate={onCreate}
- onDelete={onDelete}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreBefore={!onLoadMoreBefore}
- onLoadMoreAfter={onLoadMoreAfter}
- hasMoreAfter={!onLoadMoreAfter}
- />
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
deleted file mode 100644
index 642666c4f..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { format } from "date-fns";
-import { h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-
-type Entity = MerchantBackend.Transfers.TransferDetails & WithId;
-
-interface Props {
- transfers: Entity[];
- onDelete: (id: Entity) => void;
- onCreate: () => void;
- accounts: string[];
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-export function CardTable({
- transfers,
- onCreate,
- onDelete,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-arrow-left-right" />
- </span>
- <i18n.Translate>Transfers</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new transfer`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {transfers.length > 0 ? (
- <Table
- instances={transfers}
- onDelete={onDelete}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- instances: Entity[];
- onDelete: (id: Entity) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
-function Table({
- instances,
- onLoadMoreAfter,
- onDelete,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- const [settings] = useSettings();
- return (
- <div class="table-container">
- {hasMoreBefore && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more transfers before the first one`}
- onClick={onLoadMoreBefore}
- >
- <i18n.Translate>load newer transfers</i18n.Translate>
- </button>
- )}
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>ID</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Credit</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Address</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Exchange URL</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Confirmed</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Verified</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Executed at</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.id}>
- <td>{i.id}</td>
- <td>{i.credit_amount}</td>
- <td>{i.payto_uri}</td>
- <td>{i.exchange_url}</td>
- <td>{i.confirmed ? i18n.str`yes` : i18n.str`no`}</td>
- <td>{i.verified ? i18n.str`yes` : i18n.str`no`}</td>
- <td>
- {i.execution_time
- ? i.execution_time.t_s == "never"
- ? i18n.str`never`
- : format(
- i.execution_time.t_s * 1000,
- datetimeFormatForSettings(settings),
- )
- : i18n.str`unknown`}
- </td>
- <td>
- {i.verified === undefined ? (
- <button
- class="button is-danger is-small has-tooltip-left"
- data-tooltip={i18n.str`delete selected transfer from the database`}
- onClick={() => onDelete(i)}
- >
- Delete
- </button>
- ) : undefined}
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- {hasMoreAfter && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more transfer after the last one`}
- onClick={onLoadMoreAfter}
- >
- <i18n.Translate>load older transfers</i18n.Translate>
- </button>
- )}
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no transfer yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/index.tsx
deleted file mode 100644
index 1402df4c1..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/transfers/list/index.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { ErrorType, HttpError } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { Loading } from "../../../../components/exception/loading.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useInstanceDetails } from "../../../../hooks/instance.js";
-import { useInstanceTransfers } from "../../../../hooks/transfer.js";
-import { ListPage } from "./ListPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { useInstanceBankAccounts } from "../../../../hooks/bank.js";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onCreate: () => void;
-}
-interface Form {
- verified?: "yes" | "no";
- payto_uri?: string;
-}
-
-export default function ListTransfer({
- onUnauthorized,
- onLoadError,
- onCreate,
- onNotFound,
-}: Props): VNode {
- const setFilter = (s?: "yes" | "no") => setForm({ ...form, verified: s });
-
- const [position, setPosition] = useState<string | undefined>(undefined);
-
- const instance = useInstanceBankAccounts();
- const accounts = !instance.ok
- ? []
- : instance.data.accounts.map((a) => a.payto_uri);
- const [form, setForm] = useState<Form>({ payto_uri: "" });
-
- const shoulUseDefaultAccount = accounts.length === 1
- useEffect(() => {
- if (shoulUseDefaultAccount) {
- setForm({...form, payto_uri: accounts[0]})
- }
- }, [shoulUseDefaultAccount])
-
- const isVerifiedTransfers = form.verified === "yes";
- const isNonVerifiedTransfers = form.verified === "no";
- const isAllTransfers = form.verified === undefined;
-
- const result = useInstanceTransfers(
- {
- position,
- payto_uri: form.payto_uri === "" ? undefined : form.payto_uri,
- verified: form.verified,
- },
- (id) => setPosition(id),
- );
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <ListPage
- accounts={accounts}
- transfers={result.data.transfers}
- onLoadMoreBefore={
- result.isReachingStart ? result.loadMorePrev : undefined
- }
- onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
- onCreate={onCreate}
- onDelete={() => {
- null;
- }}
- // position={position} setPosition={setPosition}
- onShowAll={() => setFilter(undefined)}
- onShowUnverified={() => setFilter("no")}
- onShowVerified={() => setFilter("yes")}
- isAllTransfers={isAllTransfers}
- isVerifiedTransfers={isVerifiedTransfers}
- isNonVerifiedTransfers={isNonVerifiedTransfers}
- payTo={form.payto_uri}
- onChangePayTo={(p) => setForm((v) => ({ ...v, payto_uri: p }))}
- />
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/update/Update.stories.tsx
deleted file mode 100644
index 2accb7685..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/update/Update.stories.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/Instance/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
-
-function createExample<Props>(
- Component: FunctionalComponent<Props>,
- props: Partial<Props>,
-) {
- const r = (args: any) => <Component {...args} />;
- r.args = props;
- return r;
-}
-
-export const Example = createExample(TestedComponent, {
- selected: {
- name: "name",
- auth: { method: "external" },
- address: {},
- user_type: "business",
- use_stefan: true,
- jurisdiction: {},
- default_pay_delay: {
- d_us: 1000 * 1000, //one second
- },
- default_wire_transfer_delay: {
- d_us: 1000 * 1000, //one second
- },
- merchant_pub: "ASDWQEKASJDKSADJ",
- },
-});
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
deleted file mode 100644
index ff0d55d2d..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/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 { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
-import { useInstanceContext } from "../../../context/instance.js";
-import { MerchantBackend } from "../../../declaration.js";
-import { undefinedIfEmpty } from "../../../utils/table.js";
-import { Duration } from "@gnu-taler/taler-util";
-
-export type Entity = Omit<Omit<MerchantBackend.Instances.InstanceReconfigurationMessage, "default_pay_delay">, "default_wire_transfer_delay"> & {
- default_pay_delay: Duration,
- default_wire_transfer_delay: Duration,
-};
-
-//MerchantBackend.Instances.InstanceAuthConfigurationMessage
-interface Props {
- onUpdate: (d: MerchantBackend.Instances.InstanceReconfigurationMessage) => void;
- selected: MerchantBackend.Instances.QueryInstancesResponse;
- isLoading: boolean;
- onBack: () => void;
-}
-
-function convert(
- from: MerchantBackend.Instances.QueryInstancesResponse,
-): Entity {
- const { default_pay_delay, default_wire_transfer_delay, ...rest } = from;
-
- const defaults = {
- use_stefan: false,
- default_pay_delay: Duration.fromTalerProtocolDuration(default_pay_delay),
- default_wire_transfer_delay: Duration.fromTalerProtocolDuration(default_wire_transfer_delay),
- };
- return { ...defaults, ...rest };
-}
-
-export function UpdatePage({
- onUpdate,
- selected,
- onBack,
-}: Props): VNode {
- const { id } = useInstanceContext();
-
- const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
-
- const { i18n } = useTranslationContext();
-
- const errors: FormErrors<Entity> = {
- name: !value.name ? i18n.str`required` : undefined,
- user_type: !value.user_type
- ? i18n.str`required`
- : value.user_type !== "business" && value.user_type !== "individual"
- ? i18n.str`should be business or individual`
- : undefined,
- default_pay_delay: !value.default_pay_delay
- ? i18n.str`required`
- : !!value.default_wire_transfer_delay &&
- value.default_wire_transfer_delay.d_ms !== "forever" &&
- value.default_pay_delay.d_ms !== "forever" &&
- value.default_pay_delay.d_ms > value.default_wire_transfer_delay.d_ms ?
- i18n.str`pay delay can't be greater than wire transfer delay` : undefined,
- default_wire_transfer_delay: !value.default_wire_transfer_delay
- ? i18n.str`required`
- : undefined,
- address: undefinedIfEmpty({
- address_lines:
- value.address?.address_lines && value.address?.address_lines.length > 7
- ? i18n.str`max 7 lines`
- : undefined,
- }),
- jurisdiction: undefinedIfEmpty({
- address_lines:
- value.address?.address_lines && value.address?.address_lines.length > 7
- ? i18n.str`max 7 lines`
- : undefined,
- }),
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submit = async (): Promise<void> => {
- const { default_pay_delay, default_wire_transfer_delay, ...rest } = value as Required<Entity>;
- const result: MerchantBackend.Instances.InstanceReconfigurationMessage = {
- default_pay_delay: Duration.toTalerProtocolDuration(default_pay_delay),
- default_wire_transfer_delay: Duration.toTalerProtocolDuration(default_wire_transfer_delay),
- ...rest,
- }
- await onUpdate(result);
- };
- // const [active, setActive] = useState(false);
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- <i18n.Translate>Instance id</i18n.Translate>: <b>{id}</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <hr />
-
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider<Entity>
- errors={errors}
- object={value}
- valueHandler={valueHandler}
- >
- <DefaultInstanceFormFields showId={false} />
- </FormProvider>
-
- <div class="buttons is-right mt-4">
- <button
- class="button"
- onClick={onBack}
- data-tooltip="cancel operation"
- >
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
-
- <AsyncButton
- onClick={submit}
- data-tooltip={
- hasErrors
- ? i18n.str`Need to complete marked fields`
- : "confirm operation"
- }
- disabled={hasErrors}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </AsyncButton>
- </div>
- </div>
- <div class="column" />
- </div>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/update/index.tsx
deleted file mode 100644
index be3793ac3..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/update/index.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/>
- */
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import {
- ErrorType,
- HttpError,
- HttpResponse,
- useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
-import { Loading } from "../../../components/exception/loading.js";
-import { NotificationCard } from "../../../components/menu/index.js";
-import { useInstanceContext } from "../../../context/instance.js";
-import { AccessToken, MerchantBackend } from "../../../declaration.js";
-import {
- useInstanceAPI,
- useInstanceDetails,
- useManagedInstanceDetails,
- useManagementAPI,
-} from "../../../hooks/instance.js";
-import { Notification } from "../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
-
-export interface Props {
- onBack: () => void;
- onConfirm: () => void;
-
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onUpdateError: (e: HttpError<MerchantBackend.ErrorDetail>) => void;
-}
-
-export default function Update(props: Props): VNode {
- const { updateInstance } = useInstanceAPI();
- const result = useInstanceDetails();
- return CommonUpdate(props, result, updateInstance, );
-}
-
-export function AdminUpdate(props: Props & { instanceId: string }): VNode {
- const { updateInstance } = useManagementAPI(
- props.instanceId,
- );
- const result = useManagedInstanceDetails(props.instanceId);
- return CommonUpdate(props, result, updateInstance, );
-}
-
-function CommonUpdate(
- {
- onBack,
- onConfirm,
- onLoadError,
- onNotFound,
- onUpdateError,
- onUnauthorized,
- }: Props,
- result: HttpResponse<
- MerchantBackend.Instances.QueryInstancesResponse,
- MerchantBackend.ErrorDetail
- >,
- updateInstance: any,
-): VNode {
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <UpdatePage
- onBack={onBack}
- isLoading={false}
- selected={result.data}
- onUpdate={(
- d: MerchantBackend.Instances.InstanceReconfigurationMessage,
- ): Promise<void> => {
- return updateInstance(d)
- .then(onConfirm)
- .catch((error: Error) =>
- setNotif({
- message: i18n.str`Failed to create instance`,
- type: "ERROR",
- description: error.message,
- }),
- );
- }}
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx
deleted file mode 100644
index 2e2a58b33..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 "./CreatePage.js";
-
-export default {
- title: "Pages/Webhooks/Create",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx
deleted file mode 100644
index b89e5e6bf..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, 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 { InputDuration } from "../../../../components/form/InputDuration.js";
-import { InputNumber } from "../../../../components/form/InputNumber.js";
-import { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { InputSelector } from "../../../../components/form/InputSelector.js";
-
-type Entity = MerchantBackend.Webhooks.WebhookAddDetails;
-
-interface Props {
- onCreate: (d: Entity) => Promise<void>;
- onBack?: () => void;
-}
-
-const validMethod = ["GET", "POST", "PUT", "PATCH", "HEAD"];
-
-export function CreatePage({ onCreate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const [state, setState] = useState<Partial<Entity>>({});
-
- const errors: FormErrors<Entity> = {
- webhook_id: !state.webhook_id ? i18n.str`required` : undefined,
- event_type: !state.event_type ? i18n.str`required`
- : state.event_type !== "pay" && state.event_type !== "refund" ? i18n.str`it should be "pay" or "refund"`
- : undefined,
- http_method: !state.http_method
- ? i18n.str`required`
- : !validMethod.includes(state.http_method)
- ? i18n.str`should be one of '${validMethod.join(", ")}'`
- : undefined,
- url: !state.url ? i18n.str`required` : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- return onCreate(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}
- >
- <Input<Entity>
- name="webhook_id"
- label={i18n.str`ID`}
- tooltip={i18n.str`Webhook ID to use`}
- />
- <InputSelector
- name="event_type"
- label={i18n.str`Event`}
- values={[
- i18n.str`Choose one...`,
- i18n.str`pay`,
- i18n.str`refund`,
- ]}
- tooltip={i18n.str`The event of the webhook: why the webhook is used`}
- />
- <InputSelector
- name="http_method"
- label={i18n.str`Method`}
- values={[
- i18n.str`Choose one...`,
- i18n.str`GET`,
- i18n.str`POST`,
- i18n.str`PUT`,
- i18n.str`PATCH`,
- i18n.str`HEAD`,
- ]}
- tooltip={i18n.str`Method used by the webhook`}
- />
-
- <Input<Entity>
- name="url"
- label={i18n.str`URL`}
- tooltip={i18n.str`URL of the webhook where the customer will be redirected`}
- />
-
- <p>
- The text below support <a target="_blank" rel="noreferrer" href="https://mustache.github.io/mustache.5.html">mustache</a> template engine. Any string
- between <pre style={{ display: "inline", padding: 0 }}>&#123;&#123;</pre> and <pre style={{ display: "inline", padding: 0 }}>&#125;&#125;</pre> will
- be replaced with replaced with the value of the corresponding variable.
- </p>
- <p>
- For example <pre style={{ display: "inline", padding: 0 }}>&#123;&#123;contract_terms.amount&#125;&#125;</pre> will be replaced
- with the the order's price
- </p>
- <p>
- The short list of variables are:
- </p>
- <div class="menu">
-
- <ul class="menu-list" style={{ listStyleType: "disc", marginLeft: 20 }}>
- <li><b>contract_terms.summary:</b> order's description </li>
- <li><b>contract_terms.amount:</b> order's price </li>
- <li><b>order_id:</b> order's unique identification </li>
- {state.event_type === "refund" && <Fragment>
- <li><b>refund_amout:</b> the amount that was being refunded</li>
- <li><b>reason:</b> the reason entered by the merchant staff for granting the refund</li>
- <li><b>timestamp:</b> time of the refund in nanoseconds since 1970</li>
- </Fragment>}
- </ul>
- </div>
- {/* <Input<Entity>
- name="header_template"
- label={i18n.str`Http header`}
- inputType="multiline"
- tooltip={i18n.str`Header template of the webhook`}
- /> */}
- <Input<Entity>
- name="body_template"
- inputType="multiline"
- label={i18n.str`Http body`}
- tooltip={i18n.str`Body template by the webhook`}
- />
- </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/auditor-backoffice-ui/src/paths/instance/webhooks/create/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
deleted file mode 100644
index e1b8aa88e..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/create/index.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { NotificationCard } from "../../../../components/menu/index.js";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useWebhookAPI } from "../../../../hooks/webhooks.js";
-import { Notification } from "../../../../utils/types.js";
-import { CreatePage } from "./CreatePage.js";
-
-export type Entity = MerchantBackend.Webhooks.WebhookAddDetails;
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
-}
-
-export default function CreateWebhook({ onConfirm, onBack }: Props): VNode {
- const { createWebhook } = useWebhookAPI();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { i18n } = useTranslationContext();
-
- return (
- <>
- <NotificationCard notification={notif} />
- <CreatePage
- onBack={onBack}
- onCreate={(request: MerchantBackend.Webhooks.WebhookAddDetails) => {
- return createWebhook(request)
- .then(() => onConfirm())
- .catch((error) => {
- setNotif({
- message: i18n.str`could not inform template`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx
deleted file mode 100644
index 707324d40..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { FunctionalComponent, h } from "preact";
-import { ListPage as TestedComponent } from "./ListPage.js";
-
-export default {
- title: "Pages/Templates/List",
- component: TestedComponent,
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx
deleted file mode 100644
index be7c575a0..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 } from "preact";
-import { MerchantBackend } from "../../../../declaration.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { CardTable } from "./Table.js";
-
-export interface Props {
- webhooks: MerchantBackend.Webhooks.WebhookEntry[];
- onLoadMoreBefore?: () => void;
- onLoadMoreAfter?: () => void;
- onCreate: () => void;
- onDelete: (e: MerchantBackend.Webhooks.WebhookEntry) => void;
- onSelect: (e: MerchantBackend.Webhooks.WebhookEntry) => void;
-}
-
-export function ListPage({
- webhooks,
- onCreate,
- onDelete,
- onSelect,
- onLoadMoreBefore,
- onLoadMoreAfter,
-}: Props): VNode {
- const form = { payto_uri: "" };
-
- const { i18n } = useTranslationContext();
- return (
- <section class="section is-main-section">
- <CardTable
- webhooks={webhooks.map((o) => ({
- ...o,
- id: String(o.webhook_id),
- }))}
- onCreate={onCreate}
- onDelete={onDelete}
- onSelect={onSelect}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreBefore={!onLoadMoreBefore}
- onLoadMoreAfter={onLoadMoreAfter}
- hasMoreAfter={!onLoadMoreAfter}
- />
- </section>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
deleted file mode 100644
index daa984382..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/browser";
-import { h, VNode } from "preact";
-import { StateUpdater, useState } from "preact/hooks";
-import { MerchantBackend } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.Webhooks.WebhookEntry;
-
-interface Props {
- webhooks: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- onCreate: () => void;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-export function CardTable({
- webhooks,
- onCreate,
- onDelete,
- onSelect,
- onLoadMoreAfter,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: Props): VNode {
- const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
-
- const { i18n } = useTranslationContext();
-
- return (
- <div class="card has-table">
- <header class="card-header">
- <p class="card-header-title">
- <span class="icon">
- <i class="mdi mdi-newspaper" />
- </span>
- <i18n.Translate>Webhooks</i18n.Translate>
- </p>
- <div class="card-header-icon" aria-label="more options">
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`add new webhooks`}
- >
- <button class="button is-info" type="button" onClick={onCreate}>
- <span class="icon is-small">
- <i class="mdi mdi-plus mdi-36px" />
- </span>
- </button>
- </span>
- </div>
- </header>
- <div class="card-content">
- <div class="b-table has-pagination">
- <div class="table-wrapper has-mobile-cards">
- {webhooks.length > 0 ? (
- <Table
- instances={webhooks}
- onDelete={onDelete}
- onSelect={onSelect}
- rowSelection={rowSelection}
- rowSelectionHandler={rowSelectionHandler}
- onLoadMoreAfter={onLoadMoreAfter}
- onLoadMoreBefore={onLoadMoreBefore}
- hasMoreAfter={hasMoreAfter}
- hasMoreBefore={hasMoreBefore}
- />
- ) : (
- <EmptyTable />
- )}
- </div>
- </div>
- </div>
- </div>
- );
-}
-interface TableProps {
- rowSelection: string[];
- instances: Entity[];
- onDelete: (e: Entity) => void;
- onSelect: (e: Entity) => void;
- rowSelectionHandler: StateUpdater<string[]>;
- onLoadMoreBefore?: () => void;
- hasMoreBefore?: boolean;
- hasMoreAfter?: boolean;
- onLoadMoreAfter?: () => void;
-}
-
-function toggleSelected<T>(id: T): (prev: T[]) => T[] {
- return (prev: T[]): T[] =>
- prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
-}
-
-function Table({
- instances,
- onLoadMoreAfter,
- onDelete,
- onSelect,
- onLoadMoreBefore,
- hasMoreAfter,
- hasMoreBefore,
-}: TableProps): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="table-container">
- {hasMoreBefore && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more webhooks before the first one`}
- onClick={onLoadMoreBefore}
- >
- <i18n.Translate>load newer webhooks</i18n.Translate>
- </button>
- )}
- <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
- <thead>
- <tr>
- <th>
- <i18n.Translate>ID</i18n.Translate>
- </th>
- <th>
- <i18n.Translate>Event type</i18n.Translate>
- </th>
- <th />
- </tr>
- </thead>
- <tbody>
- {instances.map((i) => {
- return (
- <tr key={i.webhook_id}>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.webhook_id}
- </td>
- <td
- onClick={(): void => onSelect(i)}
- style={{ cursor: "pointer" }}
- >
- {i.event_type}
- </td>
- <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 webhook from the database`}
- onClick={() => onDelete(i)}
- >
- Delete
- </button>
- {/* <button
- class="button is-info is-small has-tooltip-left"
- data-tooltip={i18n.str`test webhook`}
- onClick={() => onNewOrder(i)}
- >
- Test
- </button> */}
- </div>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
- {hasMoreAfter && (
- <button
- class="button is-fullwidth"
- data-tooltip={i18n.str`load more webhooks after the last one`}
- onClick={onLoadMoreAfter}
- >
- <i18n.Translate>load older webhooks</i18n.Translate>
- </button>
- )}
- </div>
- );
-}
-
-function EmptyTable(): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="content has-text-grey has-text-centered">
- <p>
- <span class="icon is-large">
- <i class="mdi mdi-emoticon-sad mdi-48px" />
- </span>
- </p>
- <p>
- <i18n.Translate>
- There is no webhooks yet, add more pressing the + sign
- </i18n.Translate>
- </p>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
deleted file mode 100644
index c4b773669..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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 {
- useInstanceWebhooks,
- useWebhookAPI,
-} from "../../../../hooks/webhooks.js";
-import { Notification } from "../../../../utils/types.js";
-import { ListPage } from "./ListPage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-interface Props {
- onUnauthorized: () => VNode;
- onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- onNotFound: () => VNode;
- onCreate: () => void;
- onSelect: (id: string) => void;
-}
-
-export default function ListWebhooks({
- onUnauthorized,
- onLoadError,
- onCreate,
- onSelect,
- onNotFound,
-}: Props): VNode {
- const [position, setPosition] = useState<string | undefined>(undefined);
- const { i18n } = useTranslationContext();
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
- const { deleteWebhook } = useWebhookAPI();
- const result = useInstanceWebhooks({ position }, (id) => setPosition(id));
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
-
- <ListPage
- webhooks={result.data.webhooks}
- onLoadMoreBefore={
- result.isReachingStart ? result.loadMorePrev : undefined
- }
- onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
- onCreate={onCreate}
- onSelect={(e) => {
- onSelect(e.webhook_id);
- }}
- onDelete={(e: MerchantBackend.Webhooks.WebhookEntry) =>
- deleteWebhook(e.webhook_id)
- .then(() =>
- setNotif({
- message: i18n.str`webhook delete successfully`,
- type: "SUCCESS",
- }),
- )
- .catch((error) =>
- setNotif({
- message: i18n.str`could not delete the webhook`,
- type: "ERROR",
- description: error.message,
- }),
- )
- }
- />
- </Fragment>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx
deleted file mode 100644
index 303d17b72..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 { UpdatePage as TestedComponent } from "./UpdatePage.js";
-
-export default {
- title: "Pages/Templates/Update",
- component: TestedComponent,
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
-};
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
deleted file mode 100644
index 304ac90f3..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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/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 { useBackendContext } from "../../../../context/backend.js";
-import { MerchantBackend, WithId } from "../../../../declaration.js";
-
-type Entity = MerchantBackend.Webhooks.WebhookPatchDetails & WithId;
-
-interface Props {
- onUpdate: (d: Entity) => Promise<void>;
- onBack?: () => void;
- webhook: Entity;
-}
-const validMethod = ["GET", "POST", "PUT", "PATCH", "HEAD"];
-
-export function UpdatePage({ webhook, onUpdate, onBack }: Props): VNode {
- const { i18n } = useTranslationContext();
-
- const [state, setState] = useState<Partial<Entity>>(webhook);
-
- const errors: FormErrors<Entity> = {
- event_type: !state.event_type ? i18n.str`required` : undefined,
- http_method: !state.http_method
- ? i18n.str`required`
- : !validMethod.includes(state.http_method)
- ? i18n.str`should be one of '${validMethod.join(", ")}'`
- : undefined,
- url: !state.url ? i18n.str`required` : undefined,
- };
-
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const submitForm = () => {
- if (hasErrors) return Promise.reject();
- return onUpdate(state as any);
- };
-
- return (
- <div>
- <section class="section">
- <section class="hero is-hero-bar">
- <div class="hero-body">
- <div class="level">
- <div class="level-left">
- <div class="level-item">
- <span class="is-size-4">
- Webhook: <b>{webhook.id}</b>
- </span>
- </div>
- </div>
- </div>
- </div>
- </section>
- <hr />
-
- <section class="section is-main-section">
- <div class="columns">
- <div class="column is-four-fifths">
- <FormProvider
- object={state}
- valueHandler={setState}
- errors={errors}
- >
- <Input<Entity>
- name="event_type"
- label={i18n.str`Event`}
- tooltip={i18n.str`The event of the webhook: why the webhook is used`}
- />
- <Input<Entity>
- name="http_method"
- label={i18n.str`Method`}
- tooltip={i18n.str`Method used by the webhook`}
- />
- <Input<Entity>
- name="url"
- label={i18n.str`URL`}
- tooltip={i18n.str`URL of the webhook where the customer will be redirected`}
- />
- <Input<Entity>
- name="header_template"
- label={i18n.str`Header`}
- inputType="multiline"
- tooltip={i18n.str`Header template of the webhook`}
- />
- <Input<Entity>
- name="body_template"
- inputType="multiline"
- label={i18n.str`Body`}
- tooltip={i18n.str`Body template by the webhook`}
- />
- </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>
- </section>
- </section>
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/index.tsx b/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
deleted file mode 100644
index 12374d82a..000000000
--- a/packages/auditor-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 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 {
- ErrorType,
- HttpError,
- useTranslationContext,
-} from "@gnu-taler/web-util/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, WithId } from "../../../../declaration.js";
-import {
- useWebhookAPI,
- useWebhookDetails,
-} from "../../../../hooks/webhooks.js";
-import { Notification } from "../../../../utils/types.js";
-import { UpdatePage } from "./UpdatePage.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-
-export type Entity = MerchantBackend.Webhooks.WebhookPatchDetails & WithId;
-
-interface Props {
- onBack?: () => void;
- onConfirm: () => void;
- onUnauthorized: () => VNode;
- onNotFound: () => VNode;
- onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
- tid: string;
-}
-export default function UpdateWebhook({
- tid,
- onConfirm,
- onBack,
- onUnauthorized,
- onNotFound,
- onLoadError,
-}: Props): VNode {
- const { updateWebhook } = useWebhookAPI();
- const result = useWebhookDetails(tid);
- const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
- const { i18n } = useTranslationContext();
-
- if (result.loading) return <Loading />;
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- )
- return onUnauthorized();
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.NotFound
- )
- return onNotFound();
- return onLoadError(result);
- }
-
- return (
- <Fragment>
- <NotificationCard notification={notif} />
- <UpdatePage
- webhook={{ ...result.data, id: tid }}
- onBack={onBack}
- onUpdate={(data) => {
- return updateWebhook(tid, data)
- .then(onConfirm)
- .catch((error) => {
- setNotif({
- message: i18n.str`could not update template`,
- type: "ERROR",
- description: error.message,
- });
- });
- }}
- />
- </Fragment>
- );
-}