summaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/LoginForm.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/pages/LoginForm.tsx')
-rw-r--r--packages/demobank-ui/src/pages/LoginForm.tsx335
1 files changed, 179 insertions, 156 deletions
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx b/packages/demobank-ui/src/pages/LoginForm.tsx
index f0ae97d60..46039005a 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -14,16 +14,14 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { ErrorType, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useBackendContext } from "../context/backend.js";
import { useCredentialsChecker } from "../hooks/backend.js";
-import { ErrorMessage } from "../hooks/notification.js";
import { bankUiSettings } from "../settings.js";
import { undefinedIfEmpty } from "../utils.js";
-import { ErrorBannerFloat } from "./BankFrame.js";
import { USERNAME_REGEX } from "./RegistrationPage.js";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
@@ -36,177 +34,202 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
const [password, setPassword] = useState<string | undefined>();
const { i18n } = useTranslationContext();
const testLogin = useCredentialsChecker();
- const [error, saveError] = useState<ErrorMessage | undefined>();
const ref = useRef<HTMLInputElement>(null);
useEffect(function focusInput() {
ref.current?.focus();
}, []);
+ const [busy, setBusy] = useState<Record<string, undefined>>()
const errors = undefinedIfEmpty({
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,
- });
+ }) ?? busy;
+
+ function saveError({ title, description, debug }: { title: TranslatedString, description?: TranslatedString, debug?: any }) {
+ notifyError(title, description, debug)
+ }
+
+ async function doLogin() {
+ if (!username || !password) return;
+ setBusy({})
+ const testResult = await testLogin(username, password);
+ if (testResult.valid) {
+ backend.logIn({ username, password });
+ } else {
+ if (testResult.requestError) {
+ const { cause } = testResult;
+ switch (cause.type) {
+ case ErrorType.CLIENT: {
+ if (cause.status === HttpStatusCode.Unauthorized) {
+ saveError({
+ title: i18n.str`Wrong credentials for "${username}"`,
+ });
+ } else
+ if (cause.status === HttpStatusCode.NotFound) {
+ saveError({
+ title: i18n.str`Account not found`,
+ });
+ } else {
+ saveError({
+ title: i18n.str`Could not load due to a client error`,
+ description: cause.payload.error.description,
+ debug: JSON.stringify(cause.payload),
+ });
+ }
+ break;
+ }
+ case ErrorType.SERVER: {
+ saveError({
+ title: i18n.str`Server had a problem, try again later or report.`,
+ description: cause.payload.error.description,
+ debug: JSON.stringify(cause.payload),
+ });
+ break;
+ }
+ case ErrorType.TIMEOUT: {
+ saveError({
+ title: i18n.str`Request timeout, try again later.`,
+ });
+ break;
+ }
+ case ErrorType.UNREADABLE: {
+ saveError({
+ title: i18n.str`Unexpected error.`,
+ description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}` as TranslatedString,
+ debug: JSON.stringify(cause),
+ });
+ break;
+ }
+ default: {
+ saveError({
+ title: i18n.str`Unexpected error, please report.`,
+ description: `Diagnostic from ${cause.info?.url} is "${cause.message}"` as TranslatedString,
+ debug: JSON.stringify(cause),
+ });
+ break;
+ }
+ }
+ } else {
+ saveError({
+ title: i18n.str`Unexpected error, please report.`,
+ debug: JSON.stringify(testResult.error),
+ });
+ }
+ backend.logOut();
+ }
+ setPassword(undefined);
+ setBusy(undefined)
+ }
return (
<Fragment>
- <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
- {error && (
+ <h1 class="nav"></h1>
+ {/* {error && (
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
- )}
- <div class="login-div">
- <form
- class="login-form"
- noValidate
- onSubmit={(e) => {
- e.preventDefault();
- }}
- autoCapitalize="none"
- autoCorrect="off"
- >
- <div class="pure-form">
- <h2>{i18n.str`Please login!`}</h2>
- <p class="unameFieldLabel loginFieldLabel formFieldLabel">
- <label for="username">{i18n.str`Username:`}</label>
- </p>
- <input
- ref={ref}
- autoFocus
- type="text"
- name="username"
- id="username"
- value={username ?? ""}
- enterkeyhint="next"
- placeholder="Username"
- autocomplete="username"
- required
- onInput={(e): void => {
- setUsername(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.username}
- isDirty={username !== undefined}
- />
- <p class="passFieldLabel loginFieldLabel formFieldLabel">
- <label for="password">{i18n.str`Password:`}</label>
- </p>
- <input
- type="password"
- name="password"
- id="password"
- autocomplete="current-password"
- enterkeyhint="send"
- value={password ?? ""}
- placeholder="Password"
- required
- onInput={(e): void => {
- setPassword(e.currentTarget.value);
- }}
- />
- <ShowInputErrorLabel
- message={errors?.password}
- isDirty={password !== undefined}
- />
- <br />
- <button
- type="submit"
- class="pure-button pure-button-primary"
- disabled={!!errors}
- onClick={async (e) => {
- e.preventDefault();
- if (!username || !password) return;
- const testResult = await testLogin(username, password);
- if (testResult.valid) {
- backend.logIn({ username, password });
- } else {
- if (testResult.requestError) {
- const { cause } = testResult;
- switch (cause.type) {
- case ErrorType.CLIENT: {
- if (cause.status === HttpStatusCode.Unauthorized) {
- saveError({
- title: i18n.str`Wrong credentials for "${username}"`,
- });
- }
- if (cause.status === HttpStatusCode.NotFound) {
- saveError({
- title: i18n.str`Account not found`,
- });
- } else {
- saveError({
- title: i18n.str`Could not load due to a client error`,
- description: cause.payload.error.description,
- debug: JSON.stringify(cause.payload),
- });
- }
- break;
- }
- case ErrorType.SERVER: {
- saveError({
- title: i18n.str`Server had a problem, try again later or report.`,
- description: cause.payload.error.description,
- debug: JSON.stringify(cause.payload),
- });
- break;
- }
- case ErrorType.TIMEOUT: {
- saveError({
- title: i18n.str`Request timeout, try again later.`,
- });
- break;
- }
- case ErrorType.UNREADABLE: {
- saveError({
- title: i18n.str`Unexpected error.`,
- description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}`,
- debug: JSON.stringify(cause),
- });
- break;
- }
- default: {
- saveError({
- title: i18n.str`Unexpected error, please report.`,
- description: `Diagnostic from ${cause.info?.url} is "${cause.message}"`,
- debug: JSON.stringify(cause),
- });
- break;
- }
- }
- } else {
- saveError({
- title: i18n.str`Unexpected error, please report.`,
- debug: JSON.stringify(testResult.error),
- });
- }
- backend.logOut();
- }
- setUsername(undefined);
- setPassword(undefined);
- }}
- >
- {i18n.str`Login`}
- </button>
-
- {bankUiSettings.allowRegistrations && onRegister ? (
- <button
- class="pure-button pure-button-secondary btn-cancel"
+ )} */}
+
+ <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`Welcome to ${bankUiSettings.bankName}!`}</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>
+ <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
+ ref={ref}
+ 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>
+ <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={(e) => {
- e.preventDefault();
- onRegister();
+ e.preventDefault()
+ doLogin()
}}
>
- {i18n.str`Register`}
+ <i18n.Translate>Log in</i18n.Translate>
</button>
- ) : (
- <div />
- )}
- </div>
- </form>
+ </div>
+ </form>
+
+ {bankUiSettings.allowRegistrations && onRegister &&
+ <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()
+ onRegister()
+ }}
+ >
+ <i18n.Translate>Register</i18n.Translate>
+ </button>
+ </p>
+ }
+ </div>
</div>
+
+
</Fragment>
);
}