Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CU-8692r5rm4_Create-page-Register_Patryk-Kosiski #11

Merged
merged 3 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions frontend/src/Pages/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const Login = () => {
</Stack>
<Group position={"center"}>
<Button color={'green'} mt={5} onClick={() => {
navigate("/mainpage")
navigate("/register");
}}>{t('card.signup')}</Button>
</Group>
</form>
Expand All @@ -90,9 +90,9 @@ export const Login = () => {
</ContainerVhVw>
<Group position={"apart"} p={"md"} sx={{backgroundColor: "#191919"}}>
<Group>
{Languages().map((language) => {
{Languages().map((language, key) => {
return (
<Text sx={{cursor: 'pointer'}} onClick={() => {
<Text key={key} sx={{cursor: 'pointer'}} onClick={() => {
i18n.changeLanguage(language.code);
localStorage.setItem('language', language.code)
}}>{language.name}</Text>
Expand Down
249 changes: 249 additions & 0 deletions frontend/src/Pages/Register/Register.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import {ContainerVhVw} from "../../Components/ContainerVhVw";
import {
Box,
Button,
Card,
Center, Checkbox, Divider,
Grid,
Group,
NativeSelect,
PasswordInput,
Popover, Progress,
Text,
TextInput
} from "@mantine/core";
import {useForm} from "@mantine/form";
import {useTranslation} from "react-i18next";
import {useNavigate} from "react-router-dom";
import {DatePickerInput} from "@mantine/dates";
import React, {useState} from "react";
import 'dayjs/locale/pl';
import 'dayjs/locale/en';
import {Gender} from "../../Services/Constants";
import {useDisclosure} from "@mantine/hooks";
import {IconCheck, IconX} from "@tabler/icons-react";

function PasswordRequirement({ meets, label }: { meets: boolean; label: string }) {
return (
<Text
color={meets ? 'teal' : 'red'}
sx={{ display: 'flex', alignItems: 'center' }}
mt={7}
size="sm"
>
{meets ? <IconCheck size="0.9rem" /> : <IconX size="0.9rem" />} <Box ml={10}>{label}</Box>
</Text>
);
}



export const Register = () => {

const {t, i18n} = useTranslation('register');
const navigate = useNavigate();
const [date, setDate] = useState<Date | null>(null);
const [visible, { toggle }] = useDisclosure(false);
const [popoverOpened, setPopoverOpened] = useState(false);
const [passwordValue, setPasswordValue] = useState('');

const requirements = [
{ re: /[0-9]/, label: t('card.password.requirement.number') },
{ re: /[a-z]/, label: t('card.password.requirement.lowercase') },
{ re: /[A-Z]/, label: t('card.password.requirement.uppercase') },
{ re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: t('card.password.requirement.special') },
];

const checks = requirements.map((requirement, index) => (
<PasswordRequirement key={index} label={requirement.label} meets={requirement.re.test(passwordValue)} />
));

const form: any = useForm({
initialValues: {
name: '',
surname: '',
age: null,
gender: Gender().at(0),
email: '',
repeatEmail: '',
password: '',
repeatPassword: '',
agree: false,
},

validate: {
name: (value) => (/^\S+$/.test(value)? null : t('card.name.invalid')),
surname: (value) => (/^\S+$/.test(value) ? null : t('card.surname.invalid')),
email: (value) => (/^\S+@\S+$/.test(value) ? null : t('card.email.invalid')),
age: (value) => (calculateAge(value) >= 18 ? null : t('card.age.invalid')),
password: () => (strength === 100 ? null : t('card.password.invalid')),
repeatEmail: (value) => (value === form.values.email ? null : t('card.email.notmatch')),
repeatPassword: (value) => (value === passwordValue ? null : t('card.password.notmatch')),
agree: (value) => (value ? null : t('card.agree.invalid')),
},
});

function getStrength(password: string) {
let multiplier = password.length > 7 ? 0 : 1;

requirements.forEach((requirement) => {
if (!requirement.re.test(password)) {
multiplier += 1;
}
});

return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 10);
}

function calculateAge(birthday: any) {
if (!birthday) return 0;
const ageDifMs = Date.now() - birthday.getTime();
const ageDate = new Date(ageDifMs);
return Math.abs(ageDate.getUTCFullYear() - 1970);
}


const strength = getStrength(passwordValue);
const color = strength === 100 ? 'teal' : strength > 50 ? 'yellow' : 'red';

return(
<ContainerVhVw vh={100} vw={99}>
<Center h={"inherit"}>
<Card maw={500} radius={"md"} shadow={"lg"} sx={{position: "inherit"}}> {/*here is login form*/}
<form onSubmit={form.onSubmit((values: any) => {
console.log(values);
navigate('/');
})}>
<Grid columns={2}>
<Grid.Col span={1}>
<TextInput
withAsterisk
required
label={t('card.name.label')}
placeholder={t('card.name.placeholder')}
{...form.getInputProps('name')}
/>
</Grid.Col>
<Grid.Col span={1}>
<TextInput
withAsterisk
required
label={t('card.surname.label')}
placeholder={t('card.surname.placeholder')}
{...form.getInputProps('surname')}
/>
</Grid.Col>
<Grid.Col span={1}>
<DatePickerInput
withAsterisk
required
value={date}
onChange={
(value) => {
setDate(value);
form.setFieldValue('age', value);
}
}
{...form.getInputProps('age')}
label={t('card.age.label')}
valueFormat={"DD MMMM YYYY"}
placeholder={t('card.age.placeholder')}
locale={i18n.language}
/>
</Grid.Col>
<Grid.Col span={1}>
<NativeSelect
withAsterisk
required
data={Gender()}
label={t('card.sex.label')}
{...form.getInputProps('gender')}
/>
</Grid.Col>
<Grid.Col span={1}>
<TextInput
withAsterisk
required
label={t('card.email.label')}
placeholder={t('card.email.placeholder')}
{...form.getInputProps('email')}
/>
</Grid.Col>
<Grid.Col span={1}>
<TextInput
withAsterisk
required
label={t('card.repeatemail.label')}
placeholder={t('card.repeatemail.placeholder')}
{...form.getInputProps('repeatEmail')}
/>
</Grid.Col>
<Grid.Col span={1}>
<Popover opened={popoverOpened} position="bottom" width="target" transitionProps={{ transition: 'pop' }}>
<Popover.Target>
<div
onFocusCapture={() => setPopoverOpened(true)}
onBlurCapture={() => setPopoverOpened(false)}
>
<PasswordInput
withAsterisk
required
visible={visible}
onVisibilityChange={toggle}
label={t('card.password.label')}
placeholder={t('card.password.placeholder')}
{...form.getInputProps('password')}
onChange={(event) => {
setPasswordValue(event.currentTarget.value);
form.setFieldValue('password', event.currentTarget.value);
}}
/>
</div>
</Popover.Target>
<Popover.Dropdown>
<Progress color={color} value={strength} size={5} mb="xs" />
<PasswordRequirement label={t('card.password.requirement.length')} meets={passwordValue.length > 7} />
{checks}
</Popover.Dropdown>
</Popover>

</Grid.Col>
<Grid.Col span={1}>
<PasswordInput
withAsterisk
required
visible={visible}
onVisibilityChange={toggle}
label={t('card.repeatpassword.label')}
placeholder={t('card.repeatpassword.placeholder')}
{...form.getInputProps('repeatPassword')}
/>
</Grid.Col>
<Grid.Col span={2}>
<Checkbox
required
label={t('card.tos.label')}
{...form.getInputProps('agree')}
/>
</Grid.Col>
</Grid>
<Divider mt={"xl"} mb={"xs"} />
<Group position={"apart"}>
<Button color={'red'} mt={5} onClick={() => navigate('/')}>{t('card.cancel')}</Button>
<Button color={'green'} mt={5} type="submit">{t('card.signup')}</Button>
</Group>
</form>
</Card>
<Box sx={(theme) => ({
position: 'absolute',
backgroundImage: theme.fn.gradient({from: 'hotpink', to: 'cyan', deg: 45}),
zIndex: -1,
width: '35%',
height: '40%',
top: '0',
filter: 'blur(500px)',
})}/>
</Center>
</ContainerVhVw>
);
}
1 change: 1 addition & 0 deletions frontend/src/Pages/Register/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Register';
3 changes: 2 additions & 1 deletion frontend/src/Pages/Root/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import {Route, Routes} from "react-router-dom";
import { Layout } from "../../Components/Layout";
import { MainPage } from "../MainPage";
import {Login} from "../Login";
import {Register} from "../Register";
import {NotFound} from "../NotFound";

export const Root = () => {
return(
<Routes>
<Route path="/" element={ <Login/> }/>
<Route path="/register" element={ <Register/>} />
<Route element={<Layout/>}>
<Route path="/mainpage" element={ <MainPage/> }/>
{/*<Route path="/form" element={ <Form/>} />*/}
{/*<Route path="/admin" element={ <Admin/>} />*/}
</Route>
<Route path="*" element={ <NotFound/> }/>
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/Services/Constants/Gender.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {useTranslation} from "react-i18next";

export const Gender: () => string[] = () => {

const {t} = useTranslation('gender');
return (
[
t("male"),
t("female"),
t("other")
] as string[]);
}
3 changes: 2 additions & 1 deletion frontend/src/Services/Constants/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './Languages';
export * from './Languages';
export * from './Gender';
8 changes: 8 additions & 0 deletions frontend/src/Services/Utils/i18nInitializer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import i18next from "i18next";
import enLogin from "../../Translations/en/login.json";
import enFooter from "../../Translations/en/footer.json";
import enNotFound from "../../Translations/en/notfound.json";
import enRegister from "../../Translations/en/register.json";
import enGender from "../../Translations/en/gender.json";
// polish translations
import plLogin from "../../Translations/pl/login.json";
import plFooter from "../../Translations/pl/footer.json";
import plNotFound from "../../Translations/pl/notfound.json";
import plRegister from "../../Translations/pl/register.json";
import plGender from "../../Translations/pl/gender.json";

export const i18nInitializer = () => {
const language = localStorage.getItem('language') == null ? 'en' : localStorage.getItem('language');
Expand All @@ -19,11 +23,15 @@ export const i18nInitializer = () => {
login: enLogin,
footer: enFooter,
notfound: enNotFound,
register: enRegister,
gender: enGender,
},
pl: {
login: plLogin,
footer: plFooter,
notfound: plNotFound,
register: plRegister,
gender: plGender,
}
}
});
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/Translations/en/gender.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"female": "Female",
"male": "Male",
"other": "Other"
}
Loading