From 6e2e3811574d5d8749591b6d421848f4de38949b Mon Sep 17 00:00:00 2001 From: suvarnakale Date: Mon, 6 May 2024 17:03:20 +0530 Subject: [PATCH 1/2] Ticket #PS-318 feat: Login using password and username --- src/pages/Login.tsx | 324 +++++++++++++++++++++++++++++++++++++++ src/pages/index.tsx | 14 +- src/styles/globals.css | 12 ++ src/translations/en.json | 14 +- src/translations/hi.json | 4 +- src/translations/mr.json | 4 +- 6 files changed, 357 insertions(+), 15 deletions(-) create mode 100644 src/pages/Login.tsx diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx new file mode 100644 index 00000000..9ef7c811 --- /dev/null +++ b/src/pages/Login.tsx @@ -0,0 +1,324 @@ +import React, { useEffect, useMemo } from 'react'; +import { + Box, + Button, + FormControl, + IconButton, + InputAdornment, + InputLabel, + OutlinedInput, + fabClasses, +} from '@mui/material'; +import Image from 'next/image'; +import appLogo2 from '../../public/appLogo.png'; +import { + CloseFullscreen, + Visibility, + VisibilityOff, +} from '@mui/icons-material'; +// import { useLocation } from 'react-router-dom'; +import { useRouter } from 'next/router'; +import CloseIcon from '@mui/icons-material/Close'; +import { useState } from 'react'; +// import '../App.css'; +import { login } from '../services/LoginService'; +import { useTranslation } from 'react-i18next'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import { useTheme } from '@mui/material/styles'; +import MenuItem from '@mui/material/MenuItem'; +import config from '../../config.json'; +// import { getUserId } from '../services/ProfileService'; +// import Loader from '../components/Loader.tsx'; +import Snackbar, { SnackbarOrigin } from '@mui/material/Snackbar'; +import Link from '@mui/material/Link'; +import Checkbox from '@mui/material/Checkbox'; + +interface State extends SnackbarOrigin { + openModal: boolean; +} + +const LoginPage = () => { + const { t, i18n } = useTranslation(); + const [showPassword, setShowPassword] = useState(false); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [showToastMessage, setShowToastMessage] = useState(false); + const [usernameError, setUsernameError] = useState(false); + const [passwordError, setPasswordError] = useState(false); + const [loading, setLoading] = useState(false); + + // const [selectedLanguage, setSelectedLanguage] = useState( + // localStorage.getItem('preferredLanguage') || 'en' + // ); + const [selectedLanguage, setSelectedLanguage] = useState('en'); + const [language, setLanguage] = useState(selectedLanguage); + const router = useRouter(); + // const location = useLocation(); + const theme = useTheme(); + const [state, setState] = React.useState({ + openModal: false, + vertical: 'top', + horizontal: 'center', + }); + const { vertical, horizontal, openModal } = state; + + useEffect(() => { + const token = localStorage.getItem('token'); + if (token) { + // router.push('/dashboard'); + } + }, []); + + const handleUsernameChange = (event: React.ChangeEvent) => { + const { value } = event.target; + const trimmedValue = value.trim(); + setUsername(trimmedValue); + if (trimmedValue.includes(' ')) { + setUsernameError(true); + } else { + setUsernameError(false); + } + }; + + const handlePasswordChange = (event: React.ChangeEvent) => { + const { value } = event.target; + setPassword(value); + }; + + const handleClickShowPassword = () => setShowPassword((show) => !show); + + const handleMouseDownPassword = ( + event: React.MouseEvent + ) => { + event.preventDefault(); + }; + + const handleFormSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + if (!usernameError && !passwordError) { + // loginButtonClick(event); + setLoading(true); + event.preventDefault(); + try { + const response = await login({ + username: username, + password: password, + }); + console.log(response); + if (response) { + setTimeout(() => { + setState({ + openModal: false, + vertical: 'top', + horizontal: 'center', + }); + setShowToastMessage(false); + }); + + console.log(state); + const token = response?.access_token; + const refreshToken = response?.refresh_token; + + localStorage.setItem('token', token); + localStorage.setItem('refreshToken', refreshToken); + // const userResponse = await getUserId(); + // localStorage.setItem('userId', userResponse?.userId); + } + setLoading(false); + // router.push('/dashboard'); + } catch (error: any) { + setLoading(false); + if (error.response && error.response.status === 404) { + handleClick({ vertical: 'top', horizontal: 'center' })(); + error?.response?.data?.statusCode === 404; + setShowToastMessage(true); + } else { + console.error('Error:', error); + console.error('Error:', error?.response?.data?.message); + } + } + } + }; + + const isButtonDisabled = + !username || !password || usernameError || passwordError; + + // const loginButtonClick = async (event: React.FormEvent) => {}; + const handleChange = (event: SelectChangeEvent) => { + setLanguage(event.target.value); + i18n.changeLanguage(event.target.value); + }; + + const handleClick = (newState: SnackbarOrigin) => () => { + setState({ ...newState, openModal: true }); + }; + + const handleClose = () => { + setState({ ...state, openModal: false }); + }; + const action = useMemo( + () => ( + + {/* {t('LOGIN_PAGE.USERNAME_PASSWORD_NOT_CORRECT')} */} + + + + + + ), + [t] + ); + + return ( +
+ + {/* {loading && ( + + )} */} + + App Logo{' '} + + + + + + + + + + + + {t('LOGIN_PAGE.USERNAME')} + + + + + + + + {t('LOGIN_PAGE.PASSWORD')} + + + + {showPassword ? : } + + + } + label={t('LOGIN_PAGE.PASSWORD')} + placeholder={t('LOGIN_PAGE.PASSWORD_PLACEHOLDER')} + value={password} + onChange={handlePasswordChange} + error={passwordError} + /> + + + + + + {t('LOGIN_PAGE.FORGOT_PASSWORD')} + + + + + {t('LOGIN_PAGE.REMEMBER_ME')} + + + + + + + {showToastMessage && ( + + )} + +
+ ); +}; + +export default LoginPage; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 31378b5b..18b3fd1d 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,19 +1,19 @@ -// import Head from 'next/head'; -// import Image from 'next/image'; -// import { Inter } from 'next/font/google'; -// import styles from '@/styles/Home.module.css'; -import LoginPage from './LoginPage'; +import Head from 'next/head'; +import Image from 'next/image'; +import { Inter } from 'next/font/google'; +import styles from '@/styles/Home.module.css'; import i18n from '../i18n'; import { I18nextProvider } from 'react-i18next'; +import Login from './Login'; // const inter = Inter({ subsets: ['latin'] }); export default function Home() { - + console.log('hi'); return ( <> - + ); diff --git a/src/styles/globals.css b/src/styles/globals.css index e69de29b..5daf509d 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -0,0 +1,12 @@ +.alert .MuiSnackbarContent-root { + background-color: #ffdad6 !important; + color: #ba1a1a; + font-size: 0.75rem; + margin-top: 35%; +} +.RememberMecheckbox span svg { + fill: black !important; +} +.RememberMecheckbox span { + padding: 6px 7px 6px 0 !important; +} diff --git a/src/translations/en.json b/src/translations/en.json index 7884742b..5b1fcbbe 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -30,11 +30,11 @@ "MARITAL_STATUS": "Marital Status", "DROPOUT_YEAR": "Dropout Year", "EMPLOYMENT_STATUS": "Employment Status", - "ENROLLMENT_DATE":"Enrollment Date", - "AS_OF_TODAY":"As of today", - "AS_OF_LAST_WEEK":"As of last week", - "AS_OF_LAST_SIX_MONTH":"As of last six month", - "NONE":"None" + "ENROLLMENT_DATE": "Enrollment Date", + "AS_OF_TODAY": "As of today", + "AS_OF_LAST_WEEK": "As of last week", + "AS_OF_LAST_SIX_MONTH": "As of last six month", + "NONE": "None" }, "LOGIN_PAGE": { "USERNAME": "Username", @@ -42,7 +42,9 @@ "LOGIN": "Login", "USERNAME_PLACEHOLDER": "Enter username", "PASSWORD_PLACEHOLDER": "Enter password", - "USERNAME_PASSWORD_NOT_CORRECT": "The username or password you entered is incorrect" + "USERNAME_PASSWORD_NOT_CORRECT": "The username or password you entered is incorrect", + "FORGOT_PASSWORD": "Forgot Password?", + "REMEMBER_ME": "Remember Me" }, "DASHBOARD": { "DASHBOARD": "Dashboard", diff --git a/src/translations/hi.json b/src/translations/hi.json index a8a23bf6..981af60a 100644 --- a/src/translations/hi.json +++ b/src/translations/hi.json @@ -41,7 +41,9 @@ "LOGIN": "लॉग इन करें", "USERNAME_PLACEHOLDER": "उपयोगकर्ता नाम दर्ज करें", "PASSWORD_PLACEHOLDER": "पासवर्ड दर्ज करें", - "USERNAME_PASSWORD_NOT_CORRECT": "आपका यूजरनेम या पासवर्ड गलत है" + "USERNAME_PASSWORD_NOT_CORRECT": "आपका यूजरनेम या पासवर्ड गलत है", + "FORGOT_PASSWORD": "पासवर्ड भूल गए?", + "REMEMBER_ME": "मुझे याद रखें" }, "DASHBOARD": { "DASHBOARD": "डैशबोर्ड", diff --git a/src/translations/mr.json b/src/translations/mr.json index bc696b5b..c2b63305 100644 --- a/src/translations/mr.json +++ b/src/translations/mr.json @@ -39,7 +39,9 @@ "LOGIN": "लॉग इन करा", "USERNAME_PLACEHOLDER": "वापरकर्तानाव प्रविष्ट करा", "PASSWORD_PLACEHOLDER": "पासवर्ड टाका", - "USERNAME_PASSWORD_NOT_CORRECT": "आपलं वापरकर्तानाव किंवा संकेतशब्द चुकीचं आहे" + "USERNAME_PASSWORD_NOT_CORRECT": "आपलं वापरकर्तानाव किंवा संकेतशब्द चुकीचं आहे", + "FORGOT_PASSWORD": "पासवर्ड विसरलात?", + "REMEMBER_ME": "मला लक्षात ठेवा" }, "DASHBOARD": { "DASHBOARD": "डॅशबोर्ड", From 0549e9944fc69e3d8a5063689b0fde270e895536 Mon Sep 17 00:00:00 2001 From: suvarnakale Date: Tue, 7 May 2024 12:52:15 +0530 Subject: [PATCH 2/2] Ticket PS-318 chore: resolved comments --- src/pages/Login.tsx | 69 ++++------ src/pages/LoginPage.tsx | 293 ---------------------------------------- src/pages/_app.tsx | 2 +- src/pages/index.tsx | 4 +- 4 files changed, 33 insertions(+), 335 deletions(-) delete mode 100644 src/pages/LoginPage.tsx diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 9ef7c811..aa877d93 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,3 +1,4 @@ +'use client'; import React, { useEffect, useMemo } from 'react'; import { Box, @@ -7,20 +8,14 @@ import { InputAdornment, InputLabel, OutlinedInput, - fabClasses, } from '@mui/material'; import Image from 'next/image'; import appLogo2 from '../../public/appLogo.png'; -import { - CloseFullscreen, - Visibility, - VisibilityOff, -} from '@mui/icons-material'; +import { Visibility, VisibilityOff } from '@mui/icons-material'; // import { useLocation } from 'react-router-dom'; -import { useRouter } from 'next/router'; +// import { useRouter } from 'next/router'; import CloseIcon from '@mui/icons-material/Close'; import { useState } from 'react'; -// import '../App.css'; import { login } from '../services/LoginService'; import { useTranslation } from 'react-i18next'; import Select, { SelectChangeEvent } from '@mui/material/Select'; @@ -28,7 +23,7 @@ import { useTheme } from '@mui/material/styles'; import MenuItem from '@mui/material/MenuItem'; import config from '../../config.json'; // import { getUserId } from '../services/ProfileService'; -// import Loader from '../components/Loader.tsx'; +import Loader from '../components/Loader'; import Snackbar, { SnackbarOrigin } from '@mui/material/Snackbar'; import Link from '@mui/material/Link'; import Checkbox from '@mui/material/Checkbox'; @@ -46,19 +41,21 @@ const LoginPage = () => { const [usernameError, setUsernameError] = useState(false); const [passwordError, setPasswordError] = useState(false); const [loading, setLoading] = useState(false); - - // const [selectedLanguage, setSelectedLanguage] = useState( - // localStorage.getItem('preferredLanguage') || 'en' - // ); - const [selectedLanguage, setSelectedLanguage] = useState('en'); + const [selectedLanguage, setSelectedLanguage] = useState( + localStorage.getItem('preferredLanguage') || 'en' + ); const [language, setLanguage] = useState(selectedLanguage); - const router = useRouter(); - // const location = useLocation(); const theme = useTheme(); - const [state, setState] = React.useState({ - openModal: false, + // const router = useRouter(); + // const location = useLocation(); + + const DEFAULT_POSITION: Pick = { vertical: 'top', horizontal: 'center', + }; + const [state, setState] = React.useState({ + openModal: false, + ...DEFAULT_POSITION, }); const { vertical, horizontal, openModal } = state; @@ -73,11 +70,8 @@ const LoginPage = () => { const { value } = event.target; const trimmedValue = value.trim(); setUsername(trimmedValue); - if (trimmedValue.includes(' ')) { - setUsernameError(true); - } else { - setUsernameError(false); - } + const containsSpace = /\s/.test(trimmedValue); + setUsernameError(containsSpace); }; const handlePasswordChange = (event: React.ChangeEvent) => { @@ -96,7 +90,6 @@ const LoginPage = () => { const handleFormSubmit = async (event: React.FormEvent) => { event.preventDefault(); if (!usernameError && !passwordError) { - // loginButtonClick(event); setLoading(true); event.preventDefault(); try { @@ -109,18 +102,16 @@ const LoginPage = () => { setTimeout(() => { setState({ openModal: false, - vertical: 'top', - horizontal: 'center', + ...DEFAULT_POSITION, }); setShowToastMessage(false); }); - console.log(state); const token = response?.access_token; const refreshToken = response?.refresh_token; - localStorage.setItem('token', token); localStorage.setItem('refreshToken', refreshToken); + // const userResponse = await getUserId(); // localStorage.setItem('userId', userResponse?.userId); } @@ -129,12 +120,10 @@ const LoginPage = () => { } catch (error: any) { setLoading(false); if (error.response && error.response.status === 404) { - handleClick({ vertical: 'top', horizontal: 'center' })(); - error?.response?.data?.statusCode === 404; + handleClick({ ...DEFAULT_POSITION })(); setShowToastMessage(true); } else { console.error('Error:', error); - console.error('Error:', error?.response?.data?.message); } } } @@ -143,7 +132,6 @@ const LoginPage = () => { const isButtonDisabled = !username || !password || usernameError || passwordError; - // const loginButtonClick = async (event: React.FormEvent) => {}; const handleChange = (event: SelectChangeEvent) => { setLanguage(event.target.value); i18n.changeLanguage(event.target.value); @@ -159,8 +147,6 @@ const LoginPage = () => { const action = useMemo( () => ( - {/* {t('LOGIN_PAGE.USERNAME_PASSWORD_NOT_CORRECT')} */} - { bgcolor={theme.palette.warning.A200} minHeight={'100vh'} > - {/* {loading && ( + {loading && ( - )} */} + )} { fullWidth={true} className="CssTextField" > - + {t('LOGIN_PAGE.USERNAME')} { - + {t('LOGIN_PAGE.PASSWORD')} { variant="contained" type="submit" fullWidth={true} - // onClick={(event) => loginButtonClick(event)} disabled={isButtonDisabled} > {t('LOGIN_PAGE.LOGIN')} diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx deleted file mode 100644 index 1d843724..00000000 --- a/src/pages/LoginPage.tsx +++ /dev/null @@ -1,293 +0,0 @@ -import React, { useEffect, useMemo } from 'react'; -import { - Box, - Button, - FormControl, - IconButton, - InputAdornment, - InputLabel, - OutlinedInput, -} from '@mui/material'; -import Image from 'next/image'; -import appLogo2 from '../../public/appLogo.png'; -import { Visibility, VisibilityOff } from '@mui/icons-material'; -// import { useLocation } from 'react-router-dom'; -import { useRouter } from 'next/router'; -import CloseIcon from '@mui/icons-material/Close'; -import { useState } from 'react'; -// import '../App.css'; -import { login } from '../services/LoginService'; -import { useTranslation } from 'react-i18next'; -import Select, { SelectChangeEvent } from '@mui/material/Select'; -import { useTheme } from '@mui/material/styles'; -import MenuItem from '@mui/material/MenuItem'; -import config from '../../config.json'; -import { getUserId } from '../services/ProfileService'; -import Loader from '../components/Loader'; -import Snackbar, { SnackbarOrigin } from '@mui/material/Snackbar'; - -interface State extends SnackbarOrigin { - openModal: boolean; -} - -const LoginPage = () => { - const { t, i18n } = useTranslation(); - const [showPassword, setShowPassword] = useState(false); - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [usernameError, setUsernameError] = useState(false); - const [passwordError, setPasswordError] = useState(false); - const [loading, setLoading] = useState(false); - - // const [selectedLanguage, setSelectedLanguage] = useState( - // localStorage.getItem('preferredLanguage') || 'en' - // ); - const [selectedLanguage, setSelectedLanguage] = useState('en'); - const [language, setLanguage] = useState(selectedLanguage); - const router = useRouter(); - // const location = useLocation(); - const theme = useTheme(); - const [state, setState] = React.useState({ - openModal: false, - vertical: 'top', - horizontal: 'center', - }); - const { vertical, horizontal, openModal } = state; - - useEffect(() => { - const token = localStorage.getItem('token'); - if (token) { - router.push('/dashboard'); - } - }, []); - - const handleUsernameChange = (event: React.ChangeEvent) => { - const { value } = event.target; - const trimmedValue = value.trim(); - setUsername(trimmedValue); - if (trimmedValue.includes(' ')) { - setUsernameError(true); - } else { - setUsernameError(false); - } - }; - - const handlePasswordChange = (event: React.ChangeEvent) => { - const { value } = event.target; - setPassword(value); - }; - - const handleClickShowPassword = () => setShowPassword((show) => !show); - - const handleMouseDownPassword = ( - event: React.MouseEvent - ) => { - event.preventDefault(); - }; - - const handleFormSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - if (!usernameError && !passwordError) { - // loginButtonClick(event); - setLoading(true); - event.preventDefault(); - try { - const response = await login({ - username: username, - password: password, - }); - console.log(response); - if (response) { - const token = response?.access_token; - const refreshToken = response?.refresh_token; - - localStorage.setItem('token', token); - localStorage.setItem('refreshToken', refreshToken); - const userResponse = await getUserId(); - localStorage.setItem('userId', userResponse?.userId); - } - setLoading(false); - router.push('/dashboard'); - } catch (error: any) { - setLoading(false); - if (error.response && error.response.status === 401) { - handleClick({ vertical: 'top', horizontal: 'center' })(); - } else { - console.error('Error:', error); - } - } - } - }; - - const isButtonDisabled = - !username || !password || usernameError || passwordError; - - // const loginButtonClick = async (event: React.FormEvent) => {}; - const handleChange = (event: SelectChangeEvent) => { - setLanguage(event.target.value); - i18n.changeLanguage(event.target.value); - }; - - const handleClick = (newState: SnackbarOrigin) => () => { - setState({ ...newState, openModal: true }); - }; - - const handleClose = () => { - setState({ ...state, openModal: false }); - }; - const action = useMemo( - () => ( - - {/* {t('LOGIN_PAGE.USERNAME_PASSWORD_NOT_CORRECT')} */} - - - - - - ), - [t] - ); - - return ( -
- - {loading && ( - - )} - - App Logo{' '} - - - - - - - - - - - - {t('LOGIN_PAGE.USERNAME')} - - - - - - - - {t('LOGIN_PAGE.PASSWORD')} - - - - {showPassword ? : } - - - } - label={t('LOGIN_PAGE.PASSWORD')} - placeholder={t('LOGIN_PAGE.PASSWORD_PLACEHOLDER')} - value={password} - onChange={handlePasswordChange} - error={passwordError} - /> - - - - - - - - - - - -
- ); -}; - -export default LoginPage; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index d5d41e23..db15ec04 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -55,7 +55,7 @@ export default function App({ Component, pageProps }: AppProps) { {/* */} - ; + ); diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d1e5a860..b9f01571 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,11 +2,11 @@ // import Image from 'next/image'; // import { Inter } from 'next/font/google'; // import styles from '@/styles/Home.module.css'; -const LoginPage = dynamic(() => import('./LoginPage'), { ssr: false }); +const Login = dynamic(() => import('./Login'), { ssr: false }); import dynamic from 'next/dynamic'; import i18n from '../i18n'; import { I18nextProvider } from 'react-i18next'; -import Login from './Login'; +// import Login from './Login'; // const inter = Inter({ subsets: ['latin'] });