/* This file is part of GNU Taler (C) 2022-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 */ import { AccessToken, HttpStatusCode, TalerErrorCode, } from "@gnu-taler/taler-util"; import { LocalNotificationBanner, ShowInputErrorLabel, useLocalNotification, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { useBankCoreApiContext } from "@gnu-taler/web-util/browser"; import { useSettingsContext } from "../context/settings.js"; import { RouteDefinition } from "@gnu-taler/web-util/browser"; import { undefinedIfEmpty } from "../utils.js"; import { getRandomPassword, getRandomUsername } from "./rnd.js"; export function RegistrationPage({ onRegistrationSuccesful, routeCancel, }: { onRegistrationSuccesful: (user: string, password: string) => void; routeCancel: RouteDefinition; }): VNode { const { i18n } = useTranslationContext(); const { config } = useBankCoreApiContext(); if (!config.allow_registrations) { return (

{i18n.str`Currently, the bank is not accepting new registrations!`}

); } return ( ); } // eslint-disable-next-line no-useless-escape export const USERNAME_REGEX = /^[a-zA-Z0-9\-\.\_\~]*$/; export const PHONE_REGEX = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/; export const EMAIL_REGEX = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/; /** * Collect and submit registration data. */ function RegistrationForm({ onRegistrationSuccesful, routeCancel, }: { onRegistrationSuccesful: (user: string, password: string) => void; routeCancel: RouteDefinition; }): VNode { const [username, setUsername] = useState(); const [name, setName] = useState(); const [password, setPassword] = useState(); // const [phone, setPhone] = useState(); // const [email, setEmail] = useState(); const [repeatPassword, setRepeatPassword] = useState(); const [notification, , handleError] = useLocalNotification(); const settings = useSettingsContext(); const { lib: { bank: api } } = useBankCoreApiContext(); // const { register } = useTestingAPI(); const { i18n } = useTranslationContext(); const errors = undefinedIfEmpty({ name: !name ? i18n.str`Missing name` : undefined, username: !username ? i18n.str`Missing username` : !USERNAME_REGEX.test(username) ? i18n.str`Use letters, numbers or any of these characters: - . _ ~` : undefined, // phone: !phone // ? undefined // : !PHONE_REGEX.test(phone) // ? i18n.str`Use letters and numbers only, and start with a lowercase letter` // : undefined, // email: !email // ? undefined // : !EMAIL_REGEX.test(email) // ? 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, }); async function doRegistrationAndLogin( name: string, username: string, password: string, onComplete: () => void, ) { await handleError(async (onError) => { const resp = await api.createAccount(undefined, { name, username, password, }); if (resp.type === "ok") { onComplete(); } else { onError(resp, (_case) => { switch (_case) { case HttpStatusCode.BadRequest: return i18n.str`Server replied with invalid phone or email.`; case HttpStatusCode.Unauthorized: return i18n.str`No enough permission to create that account.`; case TalerErrorCode.BANK_UNALLOWED_DEBIT: return i18n.str`Registration is disabled because the bank ran out of bonus credit.`; case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT: return i18n.str`That username can't be used because is reserved.`; case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE: return i18n.str`That username is already taken.`; case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE: return i18n.str`That account id is already taken.`; case TalerErrorCode.BANK_MISSING_TAN_INFO: return i18n.str`No information for the selected authentication channel.`; case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: return i18n.str`Authentication channel is not supported.`; case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT: return i18n.str`Only admin is allow to set debt limit.`; case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL: return i18n.str`Only admin can create accounts with second factor authentication.`; } }); } }); } async function doRegistrationStep() { if (!username || !password || !name) return; await doRegistrationAndLogin(name, username, password, () => { setUsername(undefined); setPassword(undefined); setRepeatPassword(undefined); onRegistrationSuccesful(username, password); }); } async function doRandomRegistration() { const user = getRandomUsername(); const password = settings.simplePasswordForRandomAccounts ? "123" : getRandomPassword(); const username = `_${user.first}-${user.second}_`; const name = `${capitalizeFirstLetter(user.first)} ${capitalizeFirstLetter( user.second, )}`; await doRegistrationAndLogin(name, username, password, () => { onRegistrationSuccesful(username, password); }); } return (

{i18n.str`Account registration`}

{ e.preventDefault(); }} autoCapitalize="none" autoCorrect="off" >
{ setUsername(e.currentTarget.value); }} />
{ setPassword(e.currentTarget.value); }} />
{ setRepeatPassword(e.currentTarget.value); }} />
{ setName(e.currentTarget.value); }} /> {/* */}
{/*
{ setPhone(e.currentTarget.value); }} />
{ setEmail(e.currentTarget.value); }} />
*/}
Cancel
{settings.allowRandomAccountCreation && (

)}
); } function capitalizeFirstLetter(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); }