diff options
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx')
-rw-r--r-- | packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx new file mode 100644 index 000000000..f75ee89b8 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx @@ -0,0 +1,188 @@ +/* + 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 { NotificationCard } from "../../../components/menu/index.js"; +import { useSessionContext } from "../../../context/session.js"; +import { AccessToken, createAccessToken } from "@gnu-taler/taler-util"; + +interface Props { + hasToken: boolean | undefined; + onClearToken: (c: AccessToken | undefined) => void; + onNewToken: (c: AccessToken | undefined, s: AccessToken) => void; + onBack?: () => void; +} + +export function DetailPage({ + 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 Record<string, unknown>)[k] !== undefined, + ); + + const { state } = useSessionContext(); + + const text = i18n.str`You are updating the access token from instance with id "${state.instance}"`; + + async function submitForm() { + if (hasErrors) return; + const oldToken = + form.old_token !== undefined && hasToken + ? createAccessToken(form.old_token) + : undefined; + const newToken = createAccessToken(form.new_token!); + 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) { + onClearToken(form.old_token ? createAccessToken(form.old_token) : undefined); + } 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> + <div class="buttons is-right mt-5"> + {onBack && ( + <a class="button" onClick={onBack}> + <i18n.Translate>Cancel</i18n.Translate> + </a> + )} + <AsyncButton + type="submit" + disabled={hasErrors} + data-tooltip={ + hasErrors + ? i18n.str`Need to complete marked fields` + : "confirm operation" + } + onClick={submitForm} + > + <i18n.Translate>Confirm change</i18n.Translate> + </AsyncButton> + </div> + </FormProvider> + </div> + <div class="column" /> + </div> + </section> + </div> + ); +} |