taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit c5b383417d887d3918ead8f612ad051da57faee9
parent 6f6e9a6fab4ecbf05264766e9b2661bdebad49eb
Author: Florian Dold <florian@dold.me>
Date:   Tue, 27 Aug 2024 02:02:10 +0200

DCE

Diffstat:
Mpackages/auditor-backoffice-ui/src/components/modal/index.tsx | 408+------------------------------------------------------------------------------
Mpackages/auditor-backoffice-ui/src/hooks/entity.ts | 2--
Mpackages/auditor-backoffice-ui/src/paths/default/index.tsx | 48++++++++++++++++++++----------------------------
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>&quot;{element.name}&quot;</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>&quot;{element.name}&quot;</b> (ID:{" "} - <b>{element.id}</b>), you will also delete all it&apos;s transaction - data. - </p> - <p> - The instance will disappear from your list, and you will no longer be - able to access it&apos;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>