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

Sign In feedback + Frontend componentization #137

Merged
merged 13 commits into from
Feb 9, 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
2 changes: 2 additions & 0 deletions .devcontainer/post-start.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/bin/bash

# Silence warnings about the git repos not being owned by this user (since it's bind mounted from host)
# This runs inside the container, so it's okay to do global git config
git config --global --add safe.directory "/workspaces/Monorepo"
Expand Down
1 change: 1 addition & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"wix.vscode-import-cost",
"jock.svg",
"herrmannplatz.npm-dependency-links",
"doggy8088.quicktype-refresh",

// Backend
"KevinRose.vsc-python-indent",
Expand Down
2 changes: 1 addition & 1 deletion Monorepo.wiki
1 change: 1 addition & 0 deletions apps/backend/backend.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ RUN pip install pipenv
COPY Pipfile .
COPY Pipfile.lock .
# Args explanation: https://stackoverflow.com/a/49705601
# https://pipenv-fork.readthedocs.io/en/latest/basics.html#pipenv-install
RUN pipenv install --system --deploy --ignore-pipfile

WORKDIR /app
Expand Down
68 changes: 68 additions & 0 deletions apps/frontend/components/auth/AlreadySignedInModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Link from 'next/link';
import Router from 'next/router';
import { useAuth } from '../../firebase/fbAuth';

export interface AlreadySignedInModalProps {
continueButtonMessage: string,
linkTarget: string
}

export const AlreadySignedInModal = ({ continueButtonMessage, linkTarget }: AlreadySignedInModalProps) => {
const { authService } = useAuth();

return <div className='mt-16 sm:mt-24 lg:mt-0 lg:col-span-6'>
<div className='bg-white sm:max-w-md sm:w-full sm:mx-auto sm:rounded-lg sm:overflow-hidden'>
<div className='px-4 py-8 sm:px-10'>
<div>
<p className='text-sm font-medium text-gray-700'>
{"You're already signed in as"}
</p>
<span className='px-2 bg-white text-gray-500'>
{authService.userEmail}
</span>
</div>

<div className='mt-3'>
<Link href={linkTarget}>
<a
className='space-x-6 flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
{continueButtonMessage}
</a>
</Link>
</div>

<div className='mt-3 relative'>
<div
className='absolute inset-0 flex items-center'
aria-hidden='true'
>
<div className='w-full border-t border-gray-300' />
</div>
<div className='relative flex justify-center text-sm'>
<span className='px-2 bg-white text-gray-500'>
Or
</span>
</div>
</div>
<div className='mt-3'>
<a
className='space-x-6 flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
onClick={() => {
return (
authService
.signOut()
.then(() => {
Router.reload();
})
.catch((err) => console.log('Sign out error', err))
);
}}
>
Log Out
</a>
</div>
</div>
</div>
</div>;
};
217 changes: 217 additions & 0 deletions apps/frontend/components/auth/SignInModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { joiResolver, useForm } from '@mantine/form';
import Link from 'next/link';
import { useState } from 'react';
import { useAuth } from '../../firebase/fbAuth';
import { emailSchema, signInSchema } from '../../utils/validators';

export const DEFAULT_SIGN_IN_TEXT = 'Sign in';
export const SIGN_IN_LOADING_TEXT = 'Loading...';

export interface SignInModalProps {
afterSignIn: () => void
}

export const SignInModal = ({ afterSignIn }: SignInModalProps) => {
const form = useForm({
initialValues: {
email: '',
password: '',
},
schema: joiResolver(signInSchema),
});

const { authService } = useAuth();
const [buttonDisabled, setButtonDisabled] = useState(false);
const [buttonText, setButtonText] = useState(DEFAULT_SIGN_IN_TEXT);

return <div className='mt-8 sm:mx-auto sm:w-full sm:max-w-md'>
<div className='bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10'>
<form
className='space-y-6'
onSubmit={form.onSubmit(async (values) => {
const { email, password } = values;
setButtonDisabled(true);
setButtonText(SIGN_IN_LOADING_TEXT);
await authService.signInWithEmailAndPassword(
email,
password
).then(() => {
afterSignIn();
}).catch((error) => {
alert(`Problem logging in: ${error}`);
setButtonText(DEFAULT_SIGN_IN_TEXT);
setButtonDisabled(false);
});
})}
>
<div>
<label
htmlFor='email'
className='block text-sm font-medium text-gray-700'
>
Email address
</label>
<div className='mt-1'>
<input
{...form.getInputProps('email')}
type='email'
autoComplete='email'
required
className='appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm' />
</div>
</div>

<div>
<label
htmlFor='password'
className='block text-sm font-medium text-gray-700'
>
Password
</label>
<div className='mt-1'>
<input
{...form.getInputProps('password')}
type='password'
autoComplete='current-password'
required
className='appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm' />
</div>
</div>

<div>
<button
type='submit'
disabled={buttonDisabled}
className='w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
{buttonText}
</button>
</div>

<div className='flex items-center justify-between'>
<div className='text-sm'>
<a
href='#'
className='font-medium text-blue-600 hover:text-blue-500'
onClick={async () => {
const entryEmail = form.getInputProps('email').value;
const { error: validationError, value: validatedEmail } = emailSchema.validate(entryEmail);
if (validationError) {
alert('Please enter a valid email address');
} else {
try {
await authService.sendPasswordResetEmail(validatedEmail);
alert('Password reset email sent');
} catch (error) {
alert(`Problem sending password reset email: ${error}`);
}
}
}}
>
Forgot your password?
</a>
</div>
</div>
</form>

<div className='mt-6'>
<div className='relative'>
<div className='absolute inset-0 flex items-center'>
<div className='w-full border-t border-gray-300' />
</div>
<div className='relative flex justify-center text-sm'>
<span className='px-2 bg-white text-gray-500'>
Or continue with
</span>
</div>
</div>

<span className='px-2 bg-white text-gray-500'>
(OAuth login support coming soon™)
</span>

{/* TODO implement OAuth sign in */}
{/* <div className='mt-6 grid grid-cols-3 gap-3'>
<div>
<a
href='#'
className='w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-500 hover:bg-gray-50'
>
<span className='sr-only'>Sign in with Facebook</span>
<svg
className='w-5 h-5'
aria-hidden='true'
fill='currentColor'
viewBox='0 0 20 20'
>
<path
fillRule='evenodd'
d='M20 10c0-5.523-4.477-10-10-10S0 4.477 0 10c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V10h2.54V7.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V10h2.773l-.443 2.89h-2.33v6.988C16.343 19.128 20 14.991 20 10z'
clipRule='evenodd' />
</svg>
</a>
</div>

<div>
<a
href='#'
className='w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-500 hover:bg-gray-50'
>
<span className='sr-only'>Sign in with Twitter</span>
<svg
className='w-5 h-5'
aria-hidden='true'
fill='currentColor'
viewBox='0 0 20 20'
>
<path d='M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84' />
</svg>
</a>
</div>

<div>
<a
href='#'
className='w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-500 hover:bg-gray-50'
>
<span className='sr-only'>Sign in with GitHub</span>
<svg
className='w-5 h-5'
aria-hidden='true'
fill='currentColor'
viewBox='0 0 20 20'
>
<path
fillRule='evenodd'
d='M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z'
clipRule='evenodd' />
</svg>
</a>
</div>
</div> */}
</div>
<div className='mt-3 relative'>
<div
className='absolute inset-0 flex items-center'
aria-hidden='true'
>
<div className='w-full border-t border-gray-300' />
</div>
<div className='relative flex justify-center text-sm'>
<span className='px-2 bg-white text-gray-500'>
No account yet?
</span>
</div>
</div>
<div className='mt-3'>
<Link href={'/'}>
<a
className='space-x-6 flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
>
Go to the Sign Up Page
</a>
</Link>
</div>
</div>
</div>;
};
Loading