From 3cd8468c8f12075e66f3878ac36c482d8536f6c8 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Sun, 9 Apr 2023 17:48:06 +0100 Subject: [PATCH] Add login page --- .../AuthenticationFlow/EmailLoginForm.tsx | 102 +++++++++ .../EmailSignupForm.tsx | 6 +- .../VerificationEmailMessage.tsx | 4 +- src/components/Login/EmailLoginForm.tsx | 210 ------------------ src/components/Login/LoginCopmponent.astro | 2 +- src/pages/login.astro | 50 ++--- src/pages/signup.astro | 15 +- 7 files changed, 136 insertions(+), 253 deletions(-) create mode 100644 src/components/AuthenticationFlow/EmailLoginForm.tsx rename src/components/{Login => AuthenticationFlow}/EmailSignupForm.tsx (98%) delete mode 100644 src/components/Login/EmailLoginForm.tsx diff --git a/src/components/AuthenticationFlow/EmailLoginForm.tsx b/src/components/AuthenticationFlow/EmailLoginForm.tsx new file mode 100644 index 000000000..9f4a7e133 --- /dev/null +++ b/src/components/AuthenticationFlow/EmailLoginForm.tsx @@ -0,0 +1,102 @@ +import Cookies from 'js-cookie'; +import type { FunctionComponent } from 'preact'; +import { useState } from 'preact/hooks'; +import { TOKEN_COOKIE_NAME } from '../../lib/constants'; +import Spinner from '../Spinner'; + +const EmailLoginForm: FunctionComponent<{}> = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + + const [isLoading, setIsLoading] = useState(false); + + const handleFormSubmit = async (e: Event) => { + e.preventDefault(); + setIsLoading(true); + setError(''); + + const res = await fetch('http://localhost:8080/v1-login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email, + password, + }), + }); + + const json = await res.json(); + + if (json.type === 'user_not_verified') { + window.location.href = `/verification-pending?email=${encodeURIComponent( + email + )}`; + return; + } + + if (!json.token) { + setIsLoading(false); + setError(json.message || 'Something went wrong. Please try again later.'); + + return; + } + + // If the response is ok, we'll set the token in a cookie + Cookies.set(TOKEN_COOKIE_NAME, json.token); + window.location.href = '/'; + }; + + return ( +
+ + setEmail(String((e.target as any).value))} + /> + + setPassword(String((e.target as any).value))} + /> + +

+ + Reset your password? + +

+ + {error &&

{error}

} + + +
+ ); +}; + +export default EmailLoginForm; diff --git a/src/components/Login/EmailSignupForm.tsx b/src/components/AuthenticationFlow/EmailSignupForm.tsx similarity index 98% rename from src/components/Login/EmailSignupForm.tsx rename to src/components/AuthenticationFlow/EmailSignupForm.tsx index 7dbb51b4c..448f3f52e 100644 --- a/src/components/Login/EmailSignupForm.tsx +++ b/src/components/AuthenticationFlow/EmailSignupForm.tsx @@ -19,13 +19,17 @@ const EmailSignupForm: FunctionComponent = () => { return; } - window.location.href = `/verification-pending?email=${encodeURIComponent(email)}`; + window.location.href = `/verification-pending?email=${encodeURIComponent( + email + )}`; }; const onSubmit = (e: Event) => { e.preventDefault(); setIsLoading(true); + setError(''); + fetch(`${import.meta.env.PUBLIC_API_URL}/v1-register`, { method: 'POST', headers: { diff --git a/src/components/AuthenticationFlow/VerificationEmailMessage.tsx b/src/components/AuthenticationFlow/VerificationEmailMessage.tsx index 9a7c43d07..1cb4d6f26 100644 --- a/src/components/AuthenticationFlow/VerificationEmailMessage.tsx +++ b/src/components/AuthenticationFlow/VerificationEmailMessage.tsx @@ -49,7 +49,7 @@ export function VerificationEmailMessage() {

- We have you an email at {email}. + We have sent you an email at {email}. Please click the link to verify your account. This link will expire shortly, so please verify soon!

@@ -77,7 +77,7 @@ export function VerificationEmailMessage() { )} - {isEmailResent &&

Email sent!

} + {isEmailResent &&

Verification email has been sent!

}
); diff --git a/src/components/Login/EmailLoginForm.tsx b/src/components/Login/EmailLoginForm.tsx deleted file mode 100644 index f9c3f3cf0..000000000 --- a/src/components/Login/EmailLoginForm.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import Cookies from 'js-cookie'; -import type { FunctionComponent } from 'preact'; -import { useState } from 'preact/hooks'; -import { TOKEN_COOKIE_NAME } from '../../lib/constants'; -import Spinner from '../Spinner'; - -const EmailLoginForm: FunctionComponent<{}> = () => { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - - const [isLoading, setIsLoading] = useState(false); - const [message, setMessage] = useState<{ - type: 'success' | 'error' | 'verification' | 'warning'; - message: string; - } | null>(null); - - const handleFormSubmit = async (e: Event) => { - e.preventDefault(); - setIsLoading(true); - setMessage(null); - // Check if the verification-email-sent-at is less than 5 seconds ago - const verificationEmailSentAt = localStorage.getItem( - 'verification-email-sent-at' - ); - - if (verificationEmailSentAt) { - const now = new Date(); - if (Number(verificationEmailSentAt) > now.getTime()) { - setIsLoading(false); - setEmail(''); - setPassword(''); - return setMessage({ - type: 'error', - message: 'Please wait before sending another verification email.', - }); - } - } - - const res = await fetch('http://localhost:8080/v1-login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email, - password, - }), - }); - - // TODO: Remove this on production - // Simulate slow network - 2 seconds - // await new Promise((resolve) => setTimeout(resolve, 2000)); - - const json = await res.json(); - - // If the response isn't ok, we'll throw an error - if (json.type === 'user_not_verified') { - setIsLoading(false); - return setMessage({ - type: 'verification', - message: - 'Your account is not verified. Please click the verification link in your email. Or resend verification email.', - }); - } - - if (json.token) { - // If the response is ok, we'll set the token in a cookie - Cookies.set(TOKEN_COOKIE_NAME, json.token); - window.location.href = '/'; - } else { - setMessage({ - type: 'error', - message: json.message, - }); - } - - setIsLoading(false); - }; - - const handleResendVerificationEmail = async () => { - setIsLoading(true); - // Check if the verification-email-sent-at is less than 5 seconds ago - const verificationEmailSentAt = localStorage.getItem( - 'verification-email-sent-at' - ); - - if (verificationEmailSentAt) { - const now = new Date(); - if (Number(verificationEmailSentAt) > now.getTime()) { - setIsLoading(false); - return setMessage({ - type: 'error', - message: 'Please wait before sending another verification email.', - }); - } - } - - const res = await fetch( - 'http://localhost:8080/v1-send-verification-email', - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email, - }), - } - ); - const json = await res.json(); - - // If the response isn't ok, we'll throw an error - if (!res.ok) { - return setMessage({ - type: 'error', - message: json.message, - }); - } - // If the response is ok, we'll set the token in a cookie - setEmail(''); - setPassword(''); - setMessage({ - type: 'success', - message: 'Verification instructions have been sent to your email.', - }); - - // Current time + 5 seconds, save it to localStorage - const now = new Date(); - const time = now.getTime(); - const expireTime = time + 5000; - now.setTime(expireTime); - localStorage.setItem( - 'verification-email-sent-at', - now.getTime().toString() - ); - - setIsLoading(false); - }; - - return ( -
- - setEmail(String((e.target as any).value))} - /> - - setPassword(String((e.target as any).value))} - /> - - {message && ( - <> - {message.type === 'verification' ? ( -
- Your account is not verified. Please click the verification link - in your email. Or{' '} - -
- ) : ( -
- {message.message} -
- )} - - )} - - -
- ); -}; - -export default EmailLoginForm; diff --git a/src/components/Login/LoginCopmponent.astro b/src/components/Login/LoginCopmponent.astro index 20b3f49ba..0fb57cb8e 100644 --- a/src/components/Login/LoginCopmponent.astro +++ b/src/components/Login/LoginCopmponent.astro @@ -1,6 +1,6 @@ --- import Divider from './Divider.astro'; -import EmailLoginForm from './EmailLoginForm'; +import EmailLoginForm from '../AuthenticationFlow/EmailLoginForm'; import GithubLogin from './GithubLogin.astro'; import GoogleLogin from './GoogleLogin.astro'; --- diff --git a/src/pages/login.astro b/src/pages/login.astro index d704919ac..b896b5458 100644 --- a/src/pages/login.astro +++ b/src/pages/login.astro @@ -1,33 +1,34 @@ --- -import CaptchaFields from '../components/Captcha/CaptchaFields.astro'; -import CaptchaScripts from '../components/Captcha/CaptchaScripts.astro'; import Divider from '../components/Login/Divider.astro'; -import GithubLogin from '../components/Login/GithubLogin.astro'; -import GoogleLogin from '../components/Login/GoogleLogin.astro'; -import EmailLoginForm from '../components/Login/EmailLoginForm'; -import BaseLayout from '../layouts/BaseLayout.astro'; +import { GitHubButton } from '../components/AuthenticationFlow/GitHubButton'; +import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton'; +import EmailLoginForm from '../components/AuthenticationFlow/EmailLoginForm'; +import SettingLayout from '../layouts/SettingLayout.astro'; --- -
-

Login

-

- Welcome back! Login to your account. +

Login

+ +

+ Welcome back! Let's take you to your account.

-
- - +
+ +
@@ -37,22 +38,9 @@ import BaseLayout from '../layouts/BaseLayout.astro';
Don't have an account?{' '} - Sign up / Forgot password? + Sign up +
- - - + diff --git a/src/pages/signup.astro b/src/pages/signup.astro index 8e666d370..e33c0e675 100644 --- a/src/pages/signup.astro +++ b/src/pages/signup.astro @@ -1,14 +1,13 @@ --- -import CaptchaScripts from '../components/Captcha/CaptchaScripts.astro'; import Divider from '../components/Login/Divider.astro'; import GoogleLogin from '../components/Login/GoogleLogin.astro'; -import EmailSignupForm from '../components/Login/EmailSignupForm'; -import BaseLayout from '../layouts/BaseLayout.astro'; +import EmailSignupForm from '../components/AuthenticationFlow/EmailSignupForm'; +import SettingLayout from '../layouts/SettingLayout.astro'; import { GitHubButton } from '../components/AuthenticationFlow/GitHubButton'; import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton'; --- -
-

Sign Up

-

Sign Up

+

@@ -46,7 +45,7 @@ import { GoogleButton } from '../components/AuthenticationFlow/GoogleButton';

-
+