-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: use FormData browser API and define simple auth component
- Loading branch information
Showing
25 changed files
with
284 additions
and
528 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,54 @@ | ||
import { ChangeEventHandler } from 'react'; | ||
import { SignInFields } from '../../../validators/SignInFields'; | ||
import { FormEventHandler, Fragment, useRef, useState } from 'react'; | ||
import { SignInFields, SignInFieldsSch } from '../../../validators/SignInFields'; | ||
import { Button } from '../../generic/Button/Button'; | ||
import { ErrorMessage } from '../../generic/ErrorMessage/ErrorMessage'; | ||
import { Input } from '../../generic/Input/Input'; | ||
|
||
type SignInFormProps = { | ||
fields: SignInFields; | ||
onInputChange: ChangeEventHandler<HTMLInputElement>; | ||
onSubmit: (fields: SignInFields) => void; | ||
}; | ||
|
||
// The submit button could be displayed here, but to make it | ||
// consistant with sign up page, we will use button higher in the hierarchy | ||
export const SignInForm = ({ fields, onInputChange }: SignInFormProps) => { | ||
export const SignInForm = ({ onSubmit }: SignInFormProps) => { | ||
const formRef = useRef<HTMLFormElement>(null); | ||
const [inputErrorMessage, setInputErrorMessage] = useState(false); | ||
|
||
const handleSubmit: FormEventHandler<HTMLFormElement> = (e) => { | ||
e.preventDefault(); | ||
|
||
// in a way this is a redundant check, but it is more | ||
// strict than regular browser email regex | ||
const formData = new FormData(e.currentTarget); | ||
const result = SignInFieldsSch.safeParse({ | ||
email: formData.get('email'), | ||
password: formData.get('password'), | ||
}); | ||
|
||
if (result.success) { | ||
onSubmit(result.data); | ||
setInputErrorMessage(false); | ||
} else { | ||
setInputErrorMessage(true); | ||
} | ||
}; | ||
|
||
return ( | ||
<form className="flex flex-col gap-y-6 sm:gap-y-8 sm:w-1/2 sm:pr-4"> | ||
<Input | ||
type="email" | ||
name="email" | ||
label="Adres email" | ||
placeholder="[email protected]" | ||
value={fields.email} | ||
onChange={onInputChange} | ||
/> | ||
<Input | ||
type="password" | ||
name="password" | ||
label="Hasło" | ||
value={fields.password} | ||
onChange={onInputChange} | ||
/> | ||
</form> | ||
<Fragment> | ||
{inputErrorMessage && ( | ||
<ErrorMessage | ||
className="mb-6" | ||
title="Błąd danych wejściowych!" | ||
description="Wystąpił błąd danych wejściowych, sprawdź poprawność wpisanych danych. Jeśli błąd nie zniknie, skontaktuj się z administracją serwisu." | ||
/> | ||
)} | ||
<form | ||
ref={formRef} | ||
onSubmit={handleSubmit} | ||
className="flex flex-col items-start gap-y-6 sm:gap-y-8 sm:w-1/2 sm:pr-4" | ||
> | ||
<Input type="email" name="email" label="Adres email" placeholder="[email protected]" /> | ||
<Input type="password" name="password" label="Hasło" /> | ||
<Button type="submit" content="Zaloguj się" variant="primary" /> | ||
</form> | ||
</Fragment> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,30 @@ | ||
import { ChangeEventHandler, Fragment, MouseEventHandler, useCallback, useState } from 'react'; | ||
import { Button } from '../../generic/Button/Button'; | ||
import { useRouter } from 'next/router'; | ||
|
||
import { Heading } from '../../generic/Heading/Heading'; | ||
import { Text } from '../../generic/Text/Text'; | ||
import { ErrorAlert } from '../../generic/ErrorAlert/ErrorAlert'; | ||
import { SignInFields } from '../../../validators/SignInFields'; | ||
import { SignInForm } from '../SignInForm/SignInForm'; | ||
import { useMutation } from 'react-query'; | ||
import { signIn } from '../../../lib/post/signIn'; | ||
import { ErrorMessage } from '../../generic/ErrorMessage/ErrorMessage'; | ||
|
||
export const SignInPage = () => { | ||
// const {mutate} = useMutation((f) => { | ||
// return signIn(f); | ||
// }); | ||
const [fields, setFields] = useState<SignInFields>({ | ||
email: '', | ||
password: '', | ||
}); | ||
|
||
const handleInputChange: ChangeEventHandler<HTMLInputElement | HTMLSelectElement> = useCallback( | ||
({ currentTarget }) => { | ||
setFields((previousFields) => ({ | ||
...previousFields, | ||
[currentTarget.name]: currentTarget.value, | ||
})); | ||
}, | ||
[], | ||
); | ||
|
||
const handleSubmit: MouseEventHandler<HTMLButtonElement> = useCallback(() => 1, []); | ||
const router = useRouter(); | ||
const login = useMutation(signIn, { onSuccess: () => router.push('/') }); | ||
|
||
return ( | ||
<Fragment> | ||
<section className="mb-8 sm:mb-12 lg:mb-20"> | ||
<Heading className="mb-3" as="h1" variant="base" content="Logowanie" /> | ||
<Text | ||
className="mb-12" | ||
content="Konto jest potrzebne, aby utworzyć lokalną społeczność lub do niej dołączyć." | ||
<section> | ||
{login.isError && ( | ||
<ErrorMessage | ||
title="Nie udało się zalogować!" | ||
description="Sprawdź czy wprowadzone dane są poprawne i spróbuj ponownie. Jeśli nie możesz rozwiązać problemu, skontakuj się z administracją serwisu." | ||
/> | ||
<SignInForm fields={fields} onInputChange={handleInputChange} /> | ||
</section> | ||
<Button variant="primary" content="Zaloguj się" onClick={handleSubmit} /> | ||
</Fragment> | ||
)} | ||
<Heading className="mb-3 lg:mb-4" as="h1" variant="base" content="Logowanie" /> | ||
<Text | ||
className="mb-8 sm:mb-12 sm:w-1/2 sm:pr-4" | ||
content="Konto jest potrzebne, aby utworzyć lokalną społeczność lub do niej dołączyć." | ||
/> | ||
<SignInForm onSubmit={login.mutate} /> | ||
</section> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { FormEventHandler, Fragment, useRef, useState } from 'react'; | ||
import { SignUpFields, SignUpFieldsSch } from '../../../validators/SignUpFields'; | ||
import { Badge } from '../../generic/Badge/Badge'; | ||
import { Button } from '../../generic/Button/Button'; | ||
import { Text } from '../../generic/Text/Text'; | ||
import { ErrorMessage } from '../../generic/ErrorMessage/ErrorMessage'; | ||
import { Heading } from '../../generic/Heading/Heading'; | ||
import { Input } from '../../generic/Input/Input'; | ||
|
||
type SignUpFormProps = { | ||
onSubmit: (fields: SignUpFields) => void; | ||
}; | ||
|
||
export const SignUpForm = ({ onSubmit }: SignUpFormProps) => { | ||
const formRef = useRef<HTMLFormElement>(null); | ||
const [inputErrorMessage, setInputErrorMessage] = useState(false); | ||
|
||
// @todo passwords match | ||
// | ||
// const passwordForm = z | ||
// .object({ | ||
// password: z.string(), | ||
// confirm: z.string(), | ||
// }) | ||
// .refine((data) => data.password === data.confirm, { | ||
// message: "Passwords don't match", | ||
// path: ["confirm"], // path of error | ||
// }) | ||
// .parse({ password: "asdf", confirm: "qwer" }); | ||
|
||
const handleSubmit: FormEventHandler<HTMLFormElement> = (e) => { | ||
e.preventDefault(); | ||
|
||
// in a way this is a redundant check, but it is more | ||
// strict than regular browser email regex | ||
const formData = new FormData(e.currentTarget); | ||
|
||
const result = SignUpFieldsSch.safeParse({ | ||
name: formData.get('fullName'), | ||
email: formData.get('email'), | ||
password: formData.get('password'), | ||
phoneNo: formData.get('phone'), | ||
address: formData.get('address'), | ||
}); | ||
|
||
if (result.success) { | ||
onSubmit(result.data); | ||
setInputErrorMessage(false); | ||
} else { | ||
setInputErrorMessage(true); | ||
} | ||
}; | ||
|
||
return ( | ||
<Fragment> | ||
{inputErrorMessage && ( | ||
<ErrorMessage | ||
className="mb-6" | ||
title="Błąd danych wejściowych!" | ||
description="Wystąpił błąd danych wejściowych, sprawdź poprawność wpisanych danych. Jeśli błąd nie zniknie, skontaktuj się z administracją serwisu." | ||
/> | ||
)} | ||
<form | ||
ref={formRef} | ||
onSubmit={handleSubmit} | ||
className="flex flex-col items-start gap-y-12 sm:gap-y-16" | ||
> | ||
<fieldset className="w-full flex flex-col gap-y-6 sm:grid sm:grid-cols-2 sm:gap-y-8 sm:gap-x-8"> | ||
<Input type="text" name="fullName" label="Imię i nazwisko" placeholder="Jan Kowalski" /> | ||
<Input type="email" name="email" label="Adres email" placeholder="[email protected]" /> | ||
<Input | ||
type="password" | ||
name="password" | ||
label="Hasło (minimalnie 9 znaków)" | ||
minLength={9} | ||
/> | ||
<Input type="password" name="passwordConfirmation" label="Powtórz hasło" minLength={9} /> | ||
</fieldset> | ||
|
||
<fieldset className="w-full flex flex-col"> | ||
<legend className="mb-8"> | ||
<div className="flex gap-3 items-center mb-3"> | ||
<Heading as="h2" variant="smBold" content="Dodatkowe informacje" /> | ||
<Badge color="blue" textContent="Polecamy!" /> | ||
</div> | ||
<Text content="Wypełnienie poniższych pól może okazać się przydatne dla Twoich społeczności." /> | ||
</legend> | ||
<div className="flex flex-col gap-6 sm:grid sm:grid-cols-2 sm:gap-8"> | ||
<Input | ||
type="tel" | ||
name="phone" | ||
label="Numer telefonu" | ||
placeholder="123 456 789" | ||
required={false} | ||
/> | ||
<Input | ||
type="text" | ||
name="address" | ||
label="Adres zamieszkania" | ||
placeholder="Ostrowiec Świętokrzyski, ul. Sandomierska 2" | ||
required={false} | ||
/> | ||
</div> | ||
</fieldset> | ||
|
||
<Button type="submit" content="Zarejestruj się" variant="primary" /> | ||
</form> | ||
</Fragment> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,23 @@ | ||
import { ChangeEventHandler, Fragment, MouseEventHandler, useCallback, useState } from 'react'; | ||
import { Badge } from '../../generic/Badge/Badge'; | ||
import { Button } from '../../generic/Button/Button'; | ||
import { useRouter } from 'next/router'; | ||
import { useMutation } from 'react-query'; | ||
|
||
import { signUp } from '../../../lib/post/signUp'; | ||
import { Heading } from '../../generic/Heading/Heading'; | ||
import { DetailsForm } from '../../generic/DetailsForm/DetailsForm'; | ||
import { SignUpForm } from '../../generic/SignUpForm/SignUpForm'; | ||
import { Text } from '../../generic/Text/Text'; | ||
import { formatPhoneInput } from './formatPhoneInput'; | ||
import { useUser } from '../../../hooks/useUser'; | ||
import { ErrorAlert } from '../../generic/ErrorAlert/ErrorAlert'; | ||
|
||
export type SignUpFormFields = { | ||
fullName: string; | ||
email: string; | ||
password: string; | ||
passwordConfirmation: string; | ||
phone: string; | ||
address: string; | ||
}; | ||
import { SignUpForm } from '../SignUpForm/SignUpForm'; | ||
|
||
export const SignUpPage = () => { | ||
const { user } = useUser(); | ||
|
||
console.log(user); | ||
|
||
const [fields, setFields] = useState<SignUpFormFields>({ | ||
fullName: '', | ||
email: '', | ||
password: '', | ||
passwordConfirmation: '', | ||
phone: '', | ||
address: '', | ||
}); | ||
|
||
const handleInputChange: ChangeEventHandler<HTMLInputElement | HTMLSelectElement> = useCallback( | ||
({ currentTarget }) => { | ||
const value = | ||
currentTarget.type === 'tel' ? formatPhoneInput(currentTarget.value) : currentTarget.value; | ||
|
||
setFields((previousFields) => ({ | ||
...previousFields, | ||
[currentTarget.name]: value, | ||
})); | ||
}, | ||
[], | ||
); | ||
|
||
const handleSubmit: MouseEventHandler<HTMLButtonElement> = useCallback(() => 1, []); | ||
const handleDataSave: MouseEventHandler<HTMLButtonElement> = useCallback(() => 1, []); | ||
const router = useRouter(); | ||
const register = useMutation(signUp, { onSuccess: () => router.push('/') }); | ||
|
||
return ( | ||
<Fragment> | ||
<section className="mb-12"> | ||
<Heading className="mb-3" as="h1" variant="base" content="Rejestracja" /> | ||
<Text | ||
className="mb-12" | ||
content="Konto jest potrzebne, aby utworzyć lokalną społeczność lub do niej dołączyć." | ||
/> | ||
<SignUpForm fields={fields} onInputChange={handleInputChange} /> | ||
</section> | ||
<section className="mb-16 sm:mb-20 lg:mb-24"> | ||
<div className="flex gap-3 items-center mb-3"> | ||
<Heading as="h2" variant="smBold" content="Dodatkowe informacje" /> | ||
<Badge color="blue" textContent="Polecamy!" /> | ||
</div> | ||
<Text | ||
className="mb-12" | ||
content="Wypełnienie poniższych pól może okazać się przydatne dla Twoich społeczności." | ||
/> | ||
<DetailsForm fields={fields} onInputChange={handleInputChange} /> | ||
</section> | ||
<div className="flex gap-4 sm:gap-6 lg:gap-8"> | ||
<Button variant="primary" content="Zarejestruj się" onClick={handleSubmit} /> | ||
<Button variant="plain" content="Zachowaj dane" onClick={handleDataSave} /> | ||
</div> | ||
</Fragment> | ||
<section> | ||
<Heading className="mb-3 lg:mb-4" as="h1" variant="base" content="Rejestracja" /> | ||
<Text | ||
className="mb-8 sm:mb-12 sm:w-1/2 sm:pr-4" | ||
content="Konto jest potrzebne, aby utworzyć lokalną społeczność lub do niej dołączyć." | ||
/> | ||
<SignUpForm onSubmit={register.mutate} /> | ||
</section> | ||
); | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { useRouter } from 'next/router'; | ||
import { Fragment, useEffect } from 'react'; | ||
import { useUser } from '../../../hooks/useUser'; | ||
|
||
type AuthProps = { | ||
children: React.ReactNode; | ||
}; | ||
|
||
export const Auth = ({ children }: AuthProps) => { | ||
const router = useRouter(); | ||
const { isLoading, logged } = useUser(); | ||
|
||
useEffect(() => { | ||
if (!isLoading && !logged) { | ||
void router.push('/signin'); | ||
} | ||
}, [isLoading, logged, router]); | ||
|
||
if (!logged) { | ||
return null; | ||
} | ||
|
||
return <Fragment>{children}</Fragment>; | ||
}; |
Oops, something went wrong.