commit c5b383417d887d3918ead8f612ad051da57faee9
parent 6f6e9a6fab4ecbf05264766e9b2661bdebad49eb
Author: Florian Dold <florian@dold.me>
Date: Tue, 27 Aug 2024 02:02:10 +0200
DCE
Diffstat:
3 files changed, 23 insertions(+), 435 deletions(-)
diff --git a/packages/auditor-backoffice-ui/src/components/modal/index.tsx b/packages/auditor-backoffice-ui/src/components/modal/index.tsx
@@ -19,14 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+/**
+ * Imports.
+ */
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
-import { useState } from "preact/hooks";
-import { useEntityContext } from "../../context/entity.js";
-import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
-import { Spinner } from "../exception/loading.js";
-import { FormProvider } from "../forms/FormProvider.js";
-import { Input } from "../forms/Input.js";
interface Props {
active?: boolean;
@@ -95,402 +92,3 @@ export function ConfirmModal({
</div>
);
}
-
-export function ContinueModal({
- active,
- description,
- onCancel,
- onConfirm,
- children,
- disabled,
-}: Props): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class={active ? "modal is-active" : "modal"}>
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head has-background-success">
- {!description ? null : <p class="modal-card-title">{description}</p>}
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body">{children}</section>
- <footer class="modal-card-foot">
- <div class="buttons is-right" style={{ width: "100%" }}>
- <button
- class="button is-success "
- disabled={disabled}
- onClick={onConfirm}
- >
- <i18n.Translate>Continue</i18n.Translate>
- </button>
- </div>
- </footer>
- </div>
- <button
- class="modal-close is-large "
- aria-label="close"
- onClick={onCancel}
- />
- </div>
- );
-}
-
-export function SimpleModal({ onCancel, children }: any): VNode {
- return (
- <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <section class="modal-card-body is-main-section">{children}</section>
- </div>
- <button
- class="modal-close is-large "
- aria-label="close"
- onClick={onCancel}
- />
- </div>
- );
-}
-
-export function ClearConfirmModal({
- description,
- onCancel,
- onClear,
- onConfirm,
- children,
-}: Props & { onClear?: () => void }): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head">
- {!description ? null : <p class="modal-card-title">{description}</p>}
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body is-main-section">{children}</section>
- <footer class="modal-card-foot">
- {onClear && (
- <button
- class="button is-danger"
- onClick={onClear}
- disabled={onClear === undefined}
- >
- <i18n.Translate>Clear</i18n.Translate>
- </button>
- )}
- <div class="buttons is-right" style={{ width: "100%" }}>
- <button class="button " onClick={onCancel}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <button
- class="button is-info"
- onClick={onConfirm}
- disabled={onConfirm === undefined}
- >
- <i18n.Translate>Confirm</i18n.Translate>
- </button>
- </div>
- </footer>
- </div>
- <button
- class="modal-close is-large "
- aria-label="close"
- onClick={onCancel}
- />
- </div>
- );
-}
-
-interface DeleteModalProps {
- element: { id: string; name: string };
- onCancel: () => void;
- onConfirm: (id: string) => void;
-}
-
-export function DeleteModal({
- element,
- onCancel,
- onConfirm,
-}: DeleteModalProps): VNode {
- return (
- <ConfirmModal
- label={`Delete instance`}
- description={`Delete the instance "${element.name}"`}
- danger
- active
- onCancel={onCancel}
- onConfirm={() => onConfirm(element.id)}
- >
- <p>
- If you delete the instance named <b>"{element.name}"</b> (ID:{" "}
- <b>{element.id}</b>), the merchant will no longer be able to process
- orders or refunds
- </p>
- <p>
- This action deletes the instance private key, but preserves all
- transaction data. You can still access that data after deleting the
- instance.
- </p>
- <p class="warning">
- Deleting an instance <b>cannot be undone</b>.
- </p>
- </ConfirmModal>
- );
-}
-
-export function PurgeModal({
- element,
- onCancel,
- onConfirm,
-}: DeleteModalProps): VNode {
- return (
- <ConfirmModal
- label={`Purge the instance`}
- description={`Purge the instance "${element.name}"`}
- danger
- active
- onCancel={onCancel}
- onConfirm={() => onConfirm(element.id)}
- >
- <p>
- If you purge the instance named <b>"{element.name}"</b> (ID:{" "}
- <b>{element.id}</b>), you will also delete all it's transaction
- data.
- </p>
- <p>
- The instance will disappear from your list, and you will no longer be
- able to access it's data.
- </p>
- <p class="warning">
- Purging an instance <b>cannot be undone</b>.
- </p>
- </ConfirmModal>
- );
-}
-
-interface UpdateTokenModalProps {
- oldToken?: string;
- onCancel: () => void;
- onConfirm: (value: string) => void;
- onClear: () => void;
-}
-
-//FIXME: merge UpdateTokenModal with SetTokenNewInstanceModal
-export function UpdateTokenModal({
- onCancel,
- onClear,
- onConfirm,
- oldToken,
-}: UpdateTokenModalProps): 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 hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token;
- const errors = {
- old_token: hasInputTheCorrectOldToken
- ? i18n.str`is not the same as the current access token`
- : 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 = useEntityContext();
-
- const text = i18n.str`You are updating the access token from instance with id `;
-
- return (
- <ClearConfirmModal
- description={text}
- onCancel={onCancel}
- onConfirm={!hasErrors ? () => onConfirm(form.new_token!) : undefined}
- onClear={!hasInputTheCorrectOldToken && oldToken ? onClear : undefined}
- >
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider errors={errors} object={form} valueHandler={setValue}>
- {oldToken && (
- <Input<State>
- name="old_token"
- label={i18n.str`Old access token`}
- tooltip={i18n.str`access token currently in use`}
- inputType="password"
- />
- )}
- <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"
- />
- </FormProvider>
- <p>
- <i18n.Translate>
- Clearing the access token will mean public access to the instance
- </i18n.Translate>
- </p>
- </div>
- <div class="column" />
- </div>
- </ClearConfirmModal>
- );
-}
-
-export function SetTokenNewInstanceModal({
- onCancel,
- onClear,
- onConfirm,
-}: UpdateTokenModalProps): VNode {
- type State = { old_token: string; new_token: string; repeat_token: string };
- const [form, setValue] = useState<Partial<State>>({
- new_token: "",
- repeat_token: "",
- });
- const { i18n } = useTranslationContext();
-
- const errors = {
- 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 access 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,
- );
-
- return (
- <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head">
- <p class="modal-card-title">{i18n.str`You are setting the access token for the new instance`}</p>
- <button class="delete " aria-label="close" onClick={onCancel} />
- </header>
- <section class="modal-card-body is-main-section">
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- <FormProvider
- errors={errors}
- object={form}
- valueHandler={setValue}
- >
- <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"
- />
- </FormProvider>
- <p>
- <i18n.Translate>
- With external authorization method no check will be done by
- the merchant backend
- </i18n.Translate>
- </p>
- </div>
- <div class="column" />
- </div>
- </section>
- <footer class="modal-card-foot">
- {onClear && (
- <button
- class="button is-danger"
- onClick={onClear}
- disabled={onClear === undefined}
- >
- <i18n.Translate>Set external authorization</i18n.Translate>
- </button>
- )}
- <div class="buttons is-right" style={{ width: "100%" }}>
- <button class="button " onClick={onCancel}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <button
- class="button is-info"
- onClick={() => onConfirm(form.new_token!)}
- disabled={hasErrors}
- >
- <i18n.Translate>Set access token</i18n.Translate>
- </button>
- </div>
- </footer>
- </div>
- <button
- class="modal-close is-large "
- aria-label="close"
- onClick={onCancel}
- />
- </div>
- );
-}
-
-export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
- const { i18n } = useTranslationContext();
- return (
- <div class="modal is-active">
- <div class="modal-background " onClick={onCancel} />
- <div class="modal-card">
- <header class="modal-card-head">
- <p class="modal-card-title">
- <i18n.Translate>Operation in progress...</i18n.Translate>
- </p>
- </header>
- <section class="modal-card-body">
- <div class="columns">
- <div class="column" />
- <Spinner />
- <div class="column" />
- </div>
- <p>{i18n.str`The operation will be automatically canceled after ${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
- </section>
- <footer class="modal-card-foot">
- <div class="buttons is-right" style={{ width: "100%" }}>
- <button class="button " onClick={onCancel}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- </div>
- </footer>
- </div>
- <button
- class="modal-close is-large "
- aria-label="close"
- onClick={onCancel}
- />
- </div>
- );
-}
diff --git a/packages/auditor-backoffice-ui/src/hooks/entity.ts b/packages/auditor-backoffice-ui/src/hooks/entity.ts
@@ -31,8 +31,6 @@ import { useEntityContext } from "../context/entity.js";
const useSWR = _useSWR as unknown as SWRHook;
-type YesOrNo = "yes" | "no";
-
interface Props {
endpoint: string;
entity: any;
diff --git a/packages/auditor-backoffice-ui/src/paths/default/index.tsx b/packages/auditor-backoffice-ui/src/paths/default/index.tsx
@@ -19,41 +19,40 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { HttpStatusCode } from "@gnu-taler/taler-util";
import {
ErrorType,
HttpError,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
-import { useState } from "preact/hooks";
+import { route } from "preact-router";
+import { useMemo, useState } from "preact/hooks";
import { Loading } from "../../components/exception/loading.js";
import { NotificationCard } from "../../components/menu/index.js";
+import { ConfirmModal } from "../../components/modal/index.js";
+import {
+ EntityDataContextProvider,
+ useEntityContext,
+} from "../../context/entity.js";
import { AuditorBackend, WithId } from "../../declaration.js";
-import { Notification } from "../../utils/types.js";
-import { CardTable } from "./Table.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { EntityDataContextProvider, useEntityContext } from "../../context/entity.js";
import { getEntityList, useEntityAPI } from "../../hooks/entity.js";
-import { useMemo } from "preact/hooks";
-import { ConfirmModal, DeleteModal } from "../../components/modal/index.js";
-import { route } from "preact-router";
import { Paths } from "../../InstanceRoutes.js";
-
+import { Notification } from "../../utils/types.js";
+import { CardTable } from "./Table.js";
interface Props {
onNotFound: () => VNode;
onLoadError: (e: HttpError<AuditorBackend.ErrorDetail>) => VNode;
}
-export default function DefaultList({
- onLoadError,
- onNotFound,
- }: Props): VNode {
+export default function DefaultList({ onLoadError, onNotFound }: Props): VNode {
const { endpoint, entity } = useEntityContext();
const result = getEntityList({ endpoint, entity });
const { updateEntity } = useEntityAPI();
- const [suppressing, setSuppressing] =
- useState<typeof entity & WithId | null>(null);
+ const [suppressing, setSuppressing] = useState<
+ (typeof entity & WithId) | null
+ >(null);
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
@@ -68,31 +67,24 @@ export default function DefaultList({
}
let data = result.data;
- const value = useMemo(
- () => ({ data }),
- [data],
- );
+ const value = useMemo(() => ({ data }), [data]);
function onReturn(): void {
route(Paths.detail_view);
}
return (
-
<section class="section is-main-section">
- <button
- class="button is-fullwidth"
- onClick={onReturn}
- >Back
- </button><br />
+ <button class="button is-fullwidth" onClick={onReturn}>
+ Back
+ </button>
+ <br />
<NotificationCard notification={notif} />
<EntityDataContextProvider value={value}>
<CardTable
- onSuppress={(e: typeof entity & WithId) =>
- setSuppressing(e)
- }
+ onSuppress={(e: typeof entity & WithId) => setSuppressing(e)}
/>
</EntityDataContextProvider>