Add client UI to select JAMS as login method

GitLab: #136

Change-Id: I4062d83b9fd43657aaa4d2f60a448fc55f486e72
diff --git a/client/src/locale/en/translation.json b/client/src/locale/en/translation.json
index c8f05b0..c63ef9c 100644
--- a/client/src/locale/en/translation.json
+++ b/client/src/locale/en/translation.json
@@ -88,10 +88,13 @@
   "login_form_title": "Login",
   "login_form_username_tooltip": "The username you registered with",
   "login_form_password_tooltip": "The password you registered with",
+  "jami": "Jami",
+  "jams": "JAMS",
   "login_form_submit_button": "Log in",
   "login_form_to_registration_text": "Need an account?",
   "login_form_to_registration_link": "Register",
   "registration_success": "You've successfully registered! — Logging you in...",
+  "login_invalid_credentials": "Invalid credentials",
   "registration_form_title": "Registration",
   "registration_form_username_tooltip": "Username may be from 3 to 32 chraracters long and contain a-z, A-Z, -, _\n\nClick for more details",
   "registration_form_password_tooltip": "Choose a password hard to guess for others but easy to remember for you, it must be at least 10 characters. Your account won't be recovered if you forget it!\n\nClick for more details",
diff --git a/client/src/locale/fr/translation.json b/client/src/locale/fr/translation.json
index 016df24..1441ff9 100644
--- a/client/src/locale/fr/translation.json
+++ b/client/src/locale/fr/translation.json
@@ -88,10 +88,13 @@
   "login_form_title": "Connexion",
   "login_form_username_tooltip": "Le nom d'utilisateur avec lequel vous vous êtes inscrit(e)",
   "login_form_password_tooltip": "Le mot de passe avec lequel vous vous êtes inscrit(e)",
+  "jami": "Jami",
+  "jams": "JAMS",
   "login_form_submit_button": "Se connecter",
   "login_form_to_registration_text": "Besoin d'un compte?",
   "login_form_to_registration_link": "S'inscrire",
   "registration_success": "Inscription réussie! — Connexion en cours...",
+  "login_invalid_credentials": "Identifiants incorrects",
   "registration_form_title": "Inscription",
   "registration_form_username_tooltip": "Le nom d'utilisateur doit avoir de 3 à 32 caractères et contenir a-z, A-Z, -, _\n\nCliquer pour plus de détails",
   "registration_form_password_tooltip": "Choisissez un mot de passe difficile à deviner pour les autres, mais facile à retenir pour vous, il doit avoir au moins 10 caractères. Votre compte ne sera pas récupéré si vous l'oubliez!\n\nCliquer pour plus de détails",
diff --git a/client/src/pages/JamiLogin.tsx b/client/src/pages/JamiLogin.tsx
index 7bec6f0..3d06995 100644
--- a/client/src/pages/JamiLogin.tsx
+++ b/client/src/pages/JamiLogin.tsx
@@ -15,7 +15,17 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { Box, Button, Stack, Typography, useMediaQuery } from '@mui/material';
+import {
+  Box,
+  Button,
+  FormControl,
+  FormControlLabel,
+  Radio,
+  RadioGroup,
+  Stack,
+  Typography,
+  useMediaQuery,
+} from '@mui/material';
 import { Theme, useTheme } from '@mui/material/styles';
 import { ChangeEvent, FormEvent, MouseEvent, ReactNode, useState } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -39,6 +49,7 @@
 
   const [username, setUsername] = useState<string>('');
   const [password, setPassword] = useState<string>('');
+  const [isJams, setIsJams] = useState<boolean>(false);
   const [isLoggingInUser, setIsLoggingInUser] = useState<boolean>(false);
   const [errorAlertContent, setErrorAlertContent] = useState<ReactNode>(undefined);
 
@@ -50,6 +61,10 @@
     setPassword(event.target.value);
   };
 
+  const handleIsJams = (event: ChangeEvent<HTMLInputElement>) => {
+    setIsJams(event.target.value === 'true');
+  };
+
   const register = (event: MouseEvent<HTMLAnchorElement>) => {
     event.preventDefault();
     props.register();
@@ -61,7 +76,7 @@
       setIsLoggingInUser(true);
 
       try {
-        const accessToken = await loginUser(username, password);
+        const accessToken = await loginUser(username, password, isJams);
         setAccessToken(accessToken);
         navigate('/conversation', { replace: true });
       } catch (e) {
@@ -116,6 +131,20 @@
               sx={{ width: theme.typography.pxToRem(inputWidth) }}
             />
           </div>
+          <div>
+            <FormControl
+              sx={{
+                width: theme.typography.pxToRem(inputWidth),
+                alignItems: 'center',
+                justifyContent: 'space-between',
+              }}
+            >
+              <RadioGroup row onChange={handleIsJams} value={isJams}>
+                <FormControlLabel value="false" control={<Radio />} label={t('jami')} />
+                <FormControlLabel value="true" control={<Radio />} label={t('jams')} />
+              </RadioGroup>
+            </FormControl>
+          </div>
 
           <Button
             variant="contained"
diff --git a/client/src/pages/JamiRegistration.tsx b/client/src/pages/JamiRegistration.tsx
index 619b230..0cb2f2d 100644
--- a/client/src/pages/JamiRegistration.tsx
+++ b/client/src/pages/JamiRegistration.tsx
@@ -15,7 +15,17 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { Box, Button, Stack, Typography, useMediaQuery } from '@mui/material';
+import {
+  Box,
+  Button,
+  FormControl,
+  FormControlLabel,
+  Radio,
+  RadioGroup,
+  Stack,
+  Typography,
+  useMediaQuery,
+} from '@mui/material';
 import { Theme, useTheme } from '@mui/material/styles';
 import { ChangeEvent, FormEvent, MouseEvent, ReactNode, useEffect, useState } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -26,7 +36,7 @@
 import ProcessingRequest from '../components/ProcessingRequest';
 import { checkPasswordStrength, isNameRegistered, loginUser, registerUser, setAccessToken } from '../utils/auth';
 import { inputWidth, jamiUsernamePattern } from '../utils/constants';
-import { InvalidPassword, UsernameNotFound } from '../utils/errors';
+import { InvalidCredentials, InvalidPassword, UsernameNotFound } from '../utils/errors';
 
 type JamiRegistrationProps = {
   login: () => void;
@@ -41,6 +51,7 @@
 
   const [username, setUsername] = useState('');
   const [password, setPassword] = useState('');
+  const [isJams, setIsJams] = useState(false);
 
   const [usernameStatus, setUsernameStatus] = useState<NameStatus>('default');
   const [passwordStatus, setPasswordStatus] = useState<PasswordStatus>('default');
@@ -71,7 +82,7 @@
 
   const firstUserLogin = async () => {
     try {
-      const accessToken = await loginUser(username, password);
+      const accessToken = await loginUser(username, password, isJams);
       setAccessToken(accessToken);
       navigate('/conversation', { replace: true });
     } catch (e) {
@@ -87,9 +98,22 @@
   };
 
   const createAccount = async () => {
-    await registerUser(username, password);
-    setSuccessAlertContent(t('registration_success'));
-    await firstUserLogin();
+    try {
+      await registerUser(username, password, isJams);
+      setSuccessAlertContent(t('registration_success'));
+      await firstUserLogin();
+    } catch (e) {
+      setIsCreatingUser(false);
+      if (e instanceof UsernameNotFound) {
+        setErrorAlertContent(t('login_username_not_found'));
+      } else if (e instanceof InvalidPassword) {
+        setErrorAlertContent(t('login_invalid_password'));
+      } else if (e instanceof InvalidCredentials) {
+        setErrorAlertContent(t('login_invalid_credentials'));
+      } else {
+        throw e;
+      }
+    }
   };
 
   const handleUsername = async (event: ChangeEvent<HTMLInputElement>) => {
@@ -115,6 +139,10 @@
     }
   };
 
+  const handleIsJams = (event: ChangeEvent<HTMLInputElement>) => {
+    setIsJams(event.target.value === 'true');
+  };
+
   const login = (event: MouseEvent<HTMLAnchorElement>) => {
     event.preventDefault();
     props.login();
@@ -192,6 +220,20 @@
               tooltipTitle={t('registration_form_password_tooltip')}
             />
           </div>
+          <div>
+            <FormControl
+              sx={{
+                width: theme.typography.pxToRem(inputWidth),
+                alignItems: 'center',
+                justifyContent: 'space-between',
+              }}
+            >
+              <RadioGroup row onChange={handleIsJams} value={isJams}>
+                <FormControlLabel value="false" control={<Radio />} label={t('jami')} />
+                <FormControlLabel value="true" control={<Radio />} label={t('jams')} />
+              </RadioGroup>
+            </FormControl>
+          </div>
 
           <Button
             variant="contained"
diff --git a/client/src/utils/auth.ts b/client/src/utils/auth.ts
index b9ab8b7..7b3a088 100644
--- a/client/src/utils/auth.ts
+++ b/client/src/utils/auth.ts
@@ -21,7 +21,7 @@
 
 import { PasswordStrength } from '../enums/passwordStrength';
 import { apiUrl } from './constants';
-import { InvalidPassword, UsernameNotFound } from './errors';
+import { InvalidCredentials, InvalidPassword, UsernameNotFound } from './errors';
 
 interface PasswordStrengthResult {
   id: number;
@@ -36,6 +36,7 @@
 }
 
 export type StrengthValueCode = 'default' | 'too_weak' | 'weak' | 'medium' | 'strong';
+export type LoginMethod = 'Jami' | 'JAMS';
 
 const idToStrengthValueCode: StrengthValueCode[] = ['too_weak', 'weak', 'medium', 'strong'];
 
@@ -60,13 +61,21 @@
   };
 }
 
-export async function registerUser(username: string, password: string): Promise<void> {
-  await axios.post('/auth/new-account', { username, password }, { baseURL: apiUrl });
+export async function registerUser(username: string, password: string, isJams: boolean): Promise<void> {
+  try {
+    await axios.post('/auth/new-account', { username, password, isJams }, { baseURL: apiUrl });
+  } catch (e: any) {
+    if (e.response?.status === HttpStatusCode.Unauthorized) {
+      throw new InvalidCredentials();
+    } else {
+      throw e;
+    }
+  }
 }
 
-export async function loginUser(username: string, password: string): Promise<string> {
+export async function loginUser(username: string, password: string, isJams: boolean): Promise<string> {
   try {
-    const { data } = await axios.post('/auth/login', { username, password }, { baseURL: apiUrl });
+    const { data } = await axios.post('/auth/login', { username, password, isJams }, { baseURL: apiUrl });
     return data.accessToken;
   } catch (e: any) {
     switch (e.response?.status) {
diff --git a/client/src/utils/errors.ts b/client/src/utils/errors.ts
index 30ec7aa..7510fbd 100644
--- a/client/src/utils/errors.ts
+++ b/client/src/utils/errors.ts
@@ -18,3 +18,5 @@
 export class UsernameNotFound extends Error {}
 
 export class InvalidPassword extends Error {}
+
+export class InvalidCredentials extends Error {}