Add authentication popup

pull/3813/head
Kamran Ahmed 2 years ago
parent cdc7f081a3
commit a5866608e5
  1. 32
      src/components/AuthenticationFlow/LoginPopup.astro
  2. 2
      src/components/BestPracticeHeader.astro
  3. 123
      src/components/Login/GithubLogin.astro
  4. 127
      src/components/Login/GoogleLogin.astro
  5. 31
      src/components/Login/LoginCopmponent.astro
  6. 8
      src/components/Login/LoginPopup.astro
  7. 15
      src/components/RoadmapHeader.astro
  8. 2
      src/hooks/use-auth.ts
  9. 0
      src/lib/jwt.ts

@ -0,0 +1,32 @@
---
import Popup from '../Popup/Popup.astro';
import EmailLoginForm from './EmailLoginForm';
import Divider from './Divider.astro';
import { GitHubButton } from './GitHubButton';
import { GoogleButton } from './GoogleButton';
---
<Popup id='login-popup' title='' subtitle=''>
<div class='text-center'>
<h2 class='mb-3 text-2xl font-semibold leading-5 text-slate-900'>
Login to your account
</h2>
<p class='mt-2 text-sm leading-4 text-slate-600'>
You must be logged in to perform this action.
</p>
</div>
<div class='mt-7 flex flex-col gap-2'>
<GitHubButton client:load />
<GoogleButton client:load />
</div>
<Divider />
<EmailLoginForm client:load />
<div class='mt-6 text-center text-sm text-slate-600'>
Don't have an account?{' '}
<a href='/signup' class='font-medium text-[#4285f4]'> Sign up</a>
</div>
</Popup>

@ -2,7 +2,7 @@
import BestPracticeHint from './BestPracticeHint.astro'; import BestPracticeHint from './BestPracticeHint.astro';
import DownloadPopup from './DownloadPopup.astro'; import DownloadPopup from './DownloadPopup.astro';
import Icon from './AstroIcon.astro'; import Icon from './AstroIcon.astro';
import LoginPopup from './Login/LoginPopup.astro'; import LoginPopup from './AuthenticationFlow/LoginPopup.astro';
import SubscribePopup from './SubscribePopup.astro'; import SubscribePopup from './SubscribePopup.astro';
export interface Props { export interface Props {

@ -1,123 +0,0 @@
---
import Icon from '../AstroIcon.astro';
import Spinner from '../Spinner.astro';
---
<button
class='inline-flex h-10 w-full items-center justify-center rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none transition duration-150 ease-in-out focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:opacity-60 disabled:cursor-not-allowed'
id='github-login-button'
>
<Spinner class='hidden text-black' id='github-login-spinner' />
<span class='flex items-center' data-github-text>
<Icon icon='github' />
<span class='ml-2'>Continue with Github</span>
</span>
</button>
<p class='hidden text-sm font-medium text-red-600' data-github-error></p>
<script>
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/constants';
const githubLoginButton = document.getElementById('github-login-button');
const githubLoginSpinner = document.getElementById('github-login-spinner');
const githubLoginText = document.querySelector('[data-github-text]');
const githubErrorText = document.querySelector('[data-github-error]');
function addSpinner() {
githubLoginText?.classList.add('hidden');
githubLoginSpinner?.classList.remove('hidden');
// Disable button
githubLoginButton?.setAttribute('disabled', 'true');
}
function hideSpinner() {
githubLoginText?.classList.remove('hidden');
githubLoginSpinner?.classList.add('hidden');
// Enable button
githubLoginButton?.removeAttribute('disabled');
}
function showError(err: Error) {
githubErrorText?.classList.remove('hidden');
if (githubErrorText) {
githubErrorText.innerHTML = err.message;
}
}
githubLoginButton?.addEventListener('click', () => {
addSpinner();
fetch(`${import.meta.env.PUBLIC_API_URL}/v1-github-login`, {
credentials: 'include',
})
.then((res) => {
if (res.ok) {
return res.json();
} else {
throw new Error('Something went wrong.');
}
})
.then((data) => {
// Redirect to google login
if (data.loginUrl) {
window.location.href = data.loginUrl;
githubLoginButton?.removeAttribute('disabled');
} else {
// Else throw error
throw new Error('Something went wrong.');
}
})
.catch((err: Error) => {
hideSpinner();
// Show error in the UI
showError(err);
});
});
window.addEventListener('load', () => {
// Get all query params and send them to v1-github-callback
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
const provider = urlParams.get('provider');
if (code && state && provider === 'github') {
addSpinner();
fetch(
`http://localhost:8080/v1-github-callback${window.location.search}`,
{
method: 'GET',
credentials: 'include',
}
)
.then((res) => {
if (res.ok) {
return res.json();
} else {
throw new Error('Something went wrong.');
}
})
.then((data) => {
// Remove spinner
hideSpinner();
// Set token in cookie and redirect to home
if (data.token) {
Cookies.set(TOKEN_COOKIE_NAME, data.token);
// Redirect to the page the user was on before login
window.location.href = '/';
} else {
throw new Error('Something went wrong.');
}
})
.catch((err: Error) => {
hideSpinner();
showError(err);
});
}
});
</script>

@ -1,127 +0,0 @@
---
import Icon from '../AstroIcon.astro';
import Spinner from '../Spinner.astro';
---
<button
class='inline-flex h-10 w-full items-center justify-center rounded border border-slate-300 bg-white p-2 text-sm font-medium text-black outline-none transition duration-150 ease-in-out focus:ring-2 focus:ring-[#333] focus:ring-offset-1 disabled:opacity-60 disabled:cursor-not-allowed'
id='google-login-button'
>
<Spinner class='hidden text-black' data-google-login-spinner />
<div class='flex items-center' data-google-text>
<Icon icon='google' />
<span class='ml-2'>Continue with Google</span>
</div>
</button>
<p class='hidden text-sm font-medium text-red-600' data-google-error></p>
<script>
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/constants';
const googleLoginButton = document.getElementById('google-login-button');
const googleLoginSpinner = document.querySelector(
'[data-google-login-spinner]'
);
const googleLoginText = document.querySelector('[data-google-text]');
const googleErrorText = document.querySelector('[data-google-error]');
function addSpinner() {
googleLoginText?.classList.add('hidden');
googleLoginSpinner?.classList.remove('hidden');
// Disable button
googleLoginButton?.setAttribute('disabled', 'true');
}
function hideSpinner() {
googleLoginText?.classList.remove('hidden');
googleLoginSpinner?.classList.add('hidden');
// Enable button
googleLoginButton?.setAttribute('disabled', 'true');
}
function showError(err: Error) {
googleErrorText?.classList.remove('hidden');
if (googleErrorText) {
googleErrorText.innerHTML = err.message;
}
}
googleLoginButton?.addEventListener('click', () => {
// Show spinner
addSpinner();
fetch('http://localhost:8080/v1-google-login', {
credentials: 'include',
})
.then((res) => {
if (res.ok) {
return res.json();
} else {
throw new Error('Something went wrong.');
}
})
.then((data) => {
// Redirect to google login
if (data.loginUrl) {
window.location.href = data.loginUrl;
} else {
// Else throw error
throw new Error('Something went wrong.');
}
})
.catch((err: Error) => {
hideSpinner();
// Show error in the UI
showError(err);
});
});
window.addEventListener('load', () => {
// Get all query params and send them to v1-google-callback
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
const prompt = urlParams.get('prompt');
const provider = urlParams.get('provider');
if (code && state && prompt && provider === 'google') {
// Show spinner
addSpinner();
fetch(
`http://localhost:8080/v1-google-callback${window.location.search}`,
{
method: 'GET',
credentials: 'include',
}
)
.then((res) => {
if (res.ok) {
return res.json();
} else {
throw new Error('Something went wrong.');
}
})
.then((data) => {
// Remove spinner
hideSpinner();
// Set token in cookie and redirect to home
if (data.token) {
Cookies.set(TOKEN_COOKIE_NAME, data.token);
// Redirect to the page the user was on before login
history.go(-2);
} else {
throw new Error('Something went wrong.');
}
})
.catch((err: Error) => {
hideSpinner();
showError(err);
});
}
});
</script>

@ -1,31 +0,0 @@
---
import Divider from '../AuthenticationFlow/Divider.astro';
import EmailLoginForm from '../AuthenticationFlow/EmailLoginForm';
import GithubLogin from './GithubLogin.astro';
import GoogleLogin from './GoogleLogin.astro';
---
<div>
<div class='text-center'>
<h2 class='text-2xl font-semibold leading-5 text-slate-900'>
Welcome back
</h2>
<p class='mt-2 text-sm leading-4 text-slate-600'>
Please enter your details.
</p>
</div>
<div class='mt-10 space-y-2'>
<GithubLogin />
<GoogleLogin />
</div>
<Divider />
<EmailLoginForm client:load />
<div class='mt-6 text-center text-sm text-slate-600'>
Don't have an account?{' '}
<a href='/signup' class='font-medium text-[#4285f4]'> Sign up</a>
</div>
</div>

@ -1,8 +0,0 @@
---
import Popup from '../Popup/Popup.astro';
import LoginComponent from './LoginCopmponent.astro';
---
<Popup id='login-popup' title='' subtitle=''>
<LoginComponent />
</Popup>

@ -1,7 +1,7 @@
--- ---
import DownloadPopup from './DownloadPopup.astro'; import DownloadPopup from './DownloadPopup.astro';
import Icon from './AstroIcon.astro'; import Icon from './AstroIcon.astro';
import LoginPopup from './Login/LoginPopup.astro'; import LoginPopup from './AuthenticationFlow/LoginPopup.astro';
import RoadmapHint from './RoadmapHint.astro'; import RoadmapHint from './RoadmapHint.astro';
import RoadmapNote from './RoadmapNote.astro'; import RoadmapNote from './RoadmapNote.astro';
import SubscribePopup from './SubscribePopup.astro'; import SubscribePopup from './SubscribePopup.astro';
@ -62,34 +62,28 @@ const isRoadmapReady = !isUpcoming;
</a> </a>
{isRoadmapReady && ( {isRoadmapReady && (
<>
<button <button
data-guest-required data-guest-required
data-popup='login-popup' data-popup='login-popup'
class='inline-flex hidden items-center justify-center rounded-md bg-yellow-400 px-3 py-1.5 text-xs font-medium hover:bg-yellow-500 sm:text-sm' class='inline-flex hidden items-center justify-center rounded-md bg-yellow-400 px-3 py-1.5 text-xs font-medium hover:bg-yellow-500 sm:text-sm'
aria-label='Download Roadmap' aria-label='Download Roadmap'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Download Roadmap Popup'
> >
<Icon icon='download' /> <Icon icon='download' />
<span class='ml-2 hidden sm:inline'>Download</span> <span class='ml-2 hidden sm:inline'>Download</span>
</button> </button>
)}
{isRoadmapReady && (
<a <a
data-auth-required data-auth-required
class='inline-flex hidden items-center justify-center rounded-md bg-yellow-400 px-3 py-1.5 text-xs font-medium hover:bg-yellow-500 sm:text-sm' class='inline-flex hidden items-center justify-center rounded-md bg-yellow-400 px-3 py-1.5 text-xs font-medium hover:bg-yellow-500 sm:text-sm'
aria-label='Download Roadmap' aria-label='Download Roadmap'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Download Roadmap Popup'
target='_blank' target='_blank'
href={`/pdfs/roadmaps/${roadmapId}.pdf`} href={`/pdfs/roadmaps/${roadmapId}.pdf`}
> >
<Icon icon='download' /> <Icon icon='download' />
<span class='ml-2 hidden sm:inline'>Download</span> <span class='ml-2 hidden sm:inline'>Download</span>
</a> </a>
</>
)} )}
<button <button
@ -97,9 +91,6 @@ const isRoadmapReady = !isUpcoming;
data-popup='login-popup' data-popup='login-popup'
class='inline-flex hidden items-center justify-center rounded-md bg-yellow-400 px-3 py-1.5 text-xs font-medium hover:bg-yellow-500 sm:text-sm' class='inline-flex hidden items-center justify-center rounded-md bg-yellow-400 px-3 py-1.5 text-xs font-medium hover:bg-yellow-500 sm:text-sm'
aria-label='Subscribe for Updates' aria-label='Subscribe for Updates'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Subscribe Roadmap Popup'
> >
<Icon icon='email' /> <Icon icon='email' />
<span class='ml-2'>Subscribe</span> <span class='ml-2'>Subscribe</span>

@ -1,5 +1,5 @@
import { useEffect, useState } from 'preact/hooks'; import { useEffect, useState } from 'preact/hooks';
import { TokenPayload, decodeToken } from '../lib/utils'; import { TokenPayload, decodeToken } from '../lib/jwt';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../lib/constants'; import { TOKEN_COOKIE_NAME } from '../lib/constants';

Loading…
Cancel
Save