diff options
Diffstat (limited to 'packages/demobank-ui/src/pages/RegistrationPage.tsx')
-rw-r--r-- | packages/demobank-ui/src/pages/RegistrationPage.tsx | 311 |
1 files changed, 190 insertions, 121 deletions
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx b/packages/demobank-ui/src/pages/RegistrationPage.tsx index e52a5b11b..b912b9060 100644 --- a/packages/demobank-ui/src/pages/RegistrationPage.tsx +++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx @@ -13,19 +13,21 @@ 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, Logger } from "@gnu-taler/taler-util"; +import { HttpStatusCode, Logger, TranslatedString } from "@gnu-taler/taler-util"; import { RequestError, + notify, + notifyError, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { useBackendContext } from "../context/backend.js"; import { useTestingAPI } from "../hooks/access.js"; -import { notifyError } from "../hooks/notification.js"; import { bankUiSettings } from "../settings.js"; import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js"; import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; +import { getRandomPassword, getRandomUsername } from "./rnd.js"; const logger = new Logger("RegistrationPage"); @@ -61,147 +63,214 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode { username: !username ? i18n.str`Missing username` : !USERNAME_REGEX.test(username) - ? i18n.str`Use letters and numbers only, and start with a lowercase letter` - : undefined, + ? i18n.str`Use letters and numbers only, and start with a lowercase letter` + : undefined, password: !password ? i18n.str`Missing password` : undefined, repeatPassword: !repeatPassword ? i18n.str`Missing password` : repeatPassword !== password - ? i18n.str`Passwords don't match` - : undefined, + ? i18n.str`Passwords don't match` + : undefined, }); + async function doRegistrationStep() { + if (!username || !password) return; + try { + await register({ username, password }); + setUsername(undefined); + backend.logIn({ username, password }); + onComplete(); + } catch (error) { + if (error instanceof RequestError) { + notify( + buildRequestErrorMessage(i18n, error.cause, { + onClientError: (status) => + status === HttpStatusCode.Conflict + ? i18n.str`That username is already taken` + : undefined, + }), + ); + } else { + notifyError( + i18n.str`Operation failed, please report`, + (error instanceof Error + ? error.message + : JSON.stringify(error)) as TranslatedString + ) +} + } + setPassword(undefined); + setRepeatPassword(undefined); +} + + async function delay(ms: number):Promise<void> { + return new Promise((resolve) => { + setTimeout(() => { + resolve(undefined); + }, ms) + }) + } + async function doRandomRegistration(tries: number = 3) { + const user = getRandomUsername(); + const pass = getRandomPassword(); + try { + setUsername(undefined); + setPassword(undefined); + setRepeatPassword(undefined); + await register({ username: user, password: pass }); + backend.logIn({ username: user, password: pass }); + onComplete(); + } catch (error) { + if (error instanceof RequestError) { + if (tries > 0) { + await delay(200) + await doRandomRegistration(tries-1) + } else { + notify( + buildRequestErrorMessage(i18n, error.cause, { + onClientError: (status) => + status === HttpStatusCode.Conflict + ? i18n.str`Could not create a random user` + : undefined, + }), + ); + } + } else { + notifyError( + i18n.str`Operation failed, please report`, + (error instanceof Error + ? error.message + : JSON.stringify(error)) as TranslatedString + ) +} + } + } + return ( <Fragment> - <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1> - <article> - <div class="register-div"> - <form - class="register-form" - noValidate + <h1 class="nav"></h1> + + <div class="flex min-h-full flex-col justify-center"> + <div class="sm:mx-auto sm:w-full sm:max-w-sm"> + <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Sign up!`}</h2> + </div> + + <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm"> + <form class="space-y-6" noValidate onSubmit={(e) => { e.preventDefault(); }} autoCapitalize="none" autoCorrect="off" > - <div class="pure-form"> - <h2>{i18n.str`Please register!`}</h2> - <p class="unameFieldLabel registerFieldLabel formFieldLabel"> - <label for="register-un">{i18n.str`Username:`}</label> - </p> - <input - id="register-un" - name="register-un" - type="text" - placeholder="Username" - autocomplete="username" - value={username ?? ""} - onInput={(e): void => { - setUsername(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errors?.username} - isDirty={username !== undefined} - /> - <p class="unameFieldLabel registerFieldLabel formFieldLabel"> - <label for="register-pw">{i18n.str`Password:`}</label> - </p> - <input - type="password" - name="register-pw" - id="register-pw" - placeholder="Password" - autocomplete="new-password" - value={password ?? ""} - required - onInput={(e): void => { - setPassword(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errors?.password} - isDirty={password !== undefined} - /> - <p class="unameFieldLabel registerFieldLabel formFieldLabel"> - <label for="register-repeat">{i18n.str`Repeat Password:`}</label> - </p> - <input - type="password" - style={{ marginBottom: 8 }} - name="register-repeat" - id="register-repeat" - autocomplete="new-password" - placeholder="Same password" - value={repeatPassword ?? ""} - required - onInput={(e): void => { - setRepeatPassword(e.currentTarget.value); - }} - /> - <ShowInputErrorLabel - message={errors?.repeatPassword} - isDirty={repeatPassword !== undefined} - /> - <br /> - <button - class="pure-button pure-button-primary btn-register" - type="submit" + <div> + <label for="username" class="block text-sm font-medium leading-6 text-gray-900"> + <i18n.Translate>Username</i18n.Translate> + </label> + <div class="mt-2"> + <input + autoFocus + type="text" + name="username" + id="username" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + value={username ?? ""} + enterkeyhint="next" + placeholder="identification" + autocomplete="username" + required + onInput={(e): void => { + setUsername(e.currentTarget.value); + }} + /> + <ShowInputErrorLabel + message={errors?.username} + isDirty={username !== undefined} + /> + </div> + </div> + + <div> + <div class="flex items-center justify-between"> + <label for="password" class="block text-sm font-medium leading-6 text-gray-900">Password</label> + </div> + <div class="mt-2"> + <input + type="password" + name="password" + id="password" + autocomplete="current-password" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + enterkeyhint="send" + value={password ?? ""} + placeholder="Password" + required + onInput={(e): void => { + setPassword(e.currentTarget.value); + }} + /> + <ShowInputErrorLabel + message={errors?.password} + isDirty={password !== undefined} + /> + </div> + </div> + + <div> + <div class="flex items-center justify-between"> + <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">Repeat assword</label> + </div> + <div class="mt-2"> + <input + type="password" + name="register-repeat" + id="register-repeat" + autocomplete="current-password" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + enterkeyhint="send" + value={repeatPassword ?? ""} + placeholder="Same password" + required + onInput={(e): void => { + setRepeatPassword(e.currentTarget.value); + }} + /> + <ShowInputErrorLabel + message={errors?.repeatPassword} + isDirty={repeatPassword !== undefined} + /> + </div> + </div> + + <div> + <button type="submit" + class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" disabled={!!errors} - onClick={async (e) => { - e.preventDefault(); - - if (!username || !password) return; - try { - const credentials = { username, password }; - await register(credentials); - setUsername(undefined); - setPassword(undefined); - setRepeatPassword(undefined); - backend.logIn(credentials); - onComplete(); - } catch (error) { - if (error instanceof RequestError) { - notifyError( - buildRequestErrorMessage(i18n, error.cause, { - onClientError: (status) => - status === HttpStatusCode.Conflict - ? i18n.str`That username is already taken` - : undefined, - }), - ); - } else { - notifyError({ - title: i18n.str`Operation failed, please report`, - description: - error instanceof Error - ? error.message - : JSON.stringify(error), - }); - } - } - }} - > - {i18n.str`Register`} - </button> - {/* FIXME: should use a different color */} - <button - class="pure-button pure-button-secondary btn-cancel" onClick={(e) => { - e.preventDefault(); - setUsername(undefined); - setPassword(undefined); - setRepeatPassword(undefined); - onComplete(); + e.preventDefault() + doRegistrationStep() }} > - {i18n.str`Cancel`} + <i18n.Translate>Confirm</i18n.Translate> </button> </div> + </form> + + <p class="mt-10 text-center text-sm text-gray-500 border-t"> + <button type="submit" + class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600" + onClick={(e) => { + e.preventDefault() + doRandomRegistration() + }} + > + <i18n.Translate>Create a random user</i18n.Translate> + </button> + </p> </div> - </article> + </div> + </Fragment> ); } |