CLASSROOMIO-T-1: Login/Signup UI page

This commit is contained in:
Rotimi Best
2022-11-28 11:23:46 +01:00
parent fa38cca0ba
commit e14af6b1ad
12 changed files with 397 additions and 157 deletions
+25 -3
View File
@@ -1946,9 +1946,9 @@
"integrity": "sha512-S18o4Y9PqI/uabdlT/jI3MY7XBJjNxnfapFIkjkMwpz6qNxLFZOm2b22OMf4ZYDL9lpNWI+Ih4fEMVPwO1KHFQ==" "integrity": "sha512-S18o4Y9PqI/uabdlT/jI3MY7XBJjNxnfapFIkjkMwpz6qNxLFZOm2b22OMf4ZYDL9lpNWI+Ih4fEMVPwO1KHFQ=="
}, },
"carbon-icons-svelte": { "carbon-icons-svelte": {
"version": "10.28.0", "version": "10.45.1",
"resolved": "https://registry.npmjs.org/carbon-icons-svelte/-/carbon-icons-svelte-10.28.0.tgz", "resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/carbon-icons-svelte/-/carbon-icons-svelte-10.45.1.tgz",
"integrity": "sha512-C3K+U2PRy63WHFZme1HQNju89rhVIQmUkfgBrZzcezdutkhXbSPOY+o/MI0B2mDcQ7G9gfhEpqsXET8gBTWfJQ==", "integrity": "sha512-Uz3byPkSBzjpvLmjt/ZWYJkQG1omfqKh1vzF1FeFYOYqbGOCwc4sHbyF8oet2CseT2cECn1BbEyY4yWvlWYd/w==",
"dev": true "dev": true
}, },
"caseless": { "caseless": {
@@ -3846,6 +3846,15 @@
"pinkie": "^2.0.0" "pinkie": "^2.0.0"
} }
}, },
"polka": {
"version": "1.0.0-next.22",
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/polka/-/polka-1.0.0-next.22.tgz",
"integrity": "sha512-a7tsZy5gFbJr0aUltZS97xCkbPglXuD67AMvTyZX7BTDBH384FWf0ZQF6rPvdutSxnO1vUlXM2zSLf5tCKk5RA==",
"requires": {
"@polka/url": "^1.0.0-next.21",
"trouter": "^3.1.0"
}
},
"postcss": { "postcss": {
"version": "8.3.0", "version": "8.3.0",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz",
@@ -4166,6 +4175,11 @@
"@babel/runtime": "^7.8.4" "@babel/runtime": "^7.8.4"
} }
}, },
"regexparam": {
"version": "1.3.0",
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/regexparam/-/regexparam-1.3.0.tgz",
"integrity": "sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g=="
},
"regexpu-core": { "regexpu-core": {
"version": "4.7.1", "version": "4.7.1",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
@@ -4929,6 +4943,14 @@
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
"integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
}, },
"trouter": {
"version": "3.2.0",
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/trouter/-/trouter-3.2.0.tgz",
"integrity": "sha512-rLLXbhTObLy2MBVjLC+jTnoIKw99n0GuJs9ov10J870vDw5qhTurPzsDrudNtBf5w/CZ9ctZy2p2IMmhGcel2w==",
"requires": {
"regexparam": "^1.3.0"
}
},
"true-case-path": { "true-case-path": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
+68
View File
@@ -0,0 +1,68 @@
<script>
import PrimaryButton from '../PrimaryButton/index.svelte';
import { VARIANTS } from '../PrimaryButton/constants';
import GoogleIconColored from '../Icons/GoogleIconColored.svelte';
export let supabase;
export let handleSubmit;
export let isLogin = true;
async function signInWithGoogle() {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
});
}
</script>
<div class="w-full min-h-screen flex items-center justify-center mt-4">
<div class="container border border-gray bg-white">
<div class="flex items-center flex-col p-2 lg:px-8 lg:py-4">
<div class="flex items-center w-full justify-center">
<img
src="/logo-192.png"
alt="ClassroomIO logo"
height="50"
width="50"
data-atf="1"
/>
<h4 class="text-xl">ClassroomIO</h4>
</div>
<form
on:submit|preventDefault={handleSubmit}
class="flex items-center flex-col w-10/12"
>
<slot />
</form>
<div class="w-10/12 mb-3">
<p class="text-sm mb-5">or sign up with:</p>
<PrimaryButton
variant={VARIANTS.OUTLINED}
onClick={signInWithGoogle}
className="py-3 sm:w-full w-full"
>
<GoogleIconColored />
<span class="ml-2">Sign up with Google</span>
</PrimaryButton>
</div>
</div>
<div class="w-full p-6 border-t border-grey text-center">
{#if isLogin}
Not registered yet? <a
class="hover:underline text-blue-700"
href="/signup">Sign up</a
>
{:else}
Already have an account? <a
class="hover:underline text-blue-700"
href="/login">Log In</a
>
{/if}
</div>
</div>
</div>
<style>
.container {
width: 450px;
}
</style>
+35 -4
View File
@@ -1,5 +1,8 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import IconButton from '../IconButton/index.svelte';
import PasswordEye from '../Icons/PasswordEye.svelte';
export let label = ''; export let label = '';
export let placeholder = ''; export let placeholder = '';
export let value = ''; export let value = '';
@@ -7,6 +10,7 @@
export let className = ''; export let className = '';
export let inputClassName = ''; export let inputClassName = '';
export let type = 'text'; export let type = 'text';
export let isPassword = false;
export let autoFocus = false; export let autoFocus = false;
export let isRequired = false; export let isRequired = false;
export let isDisabled = false; export let isDisabled = false;
@@ -16,6 +20,7 @@
export let onChange = () => {}; // This is to know if element is 'dirty' export let onChange = () => {}; // This is to know if element is 'dirty'
let ref; let ref;
let fieldNode;
function typeAction(node) { function typeAction(node) {
node.type = type; node.type = type;
@@ -23,18 +28,29 @@
if (isRequired) { if (isRequired) {
node.required = ''; node.required = '';
} }
fieldNode = node;
}
function handlePasswordEye() {
type = type === 'password' ? 'text' : 'password';
typeAction(fieldNode);
} }
onMount(() => { onMount(() => {
if (autoFocus) { if (autoFocus) {
ref.focus(); ref.focus();
} }
if (type === 'password') {
isPassword = true;
}
}); });
</script> </script>
<label class="block {className}"> <label class="block relative {className}">
{#if label} {#if label}
<label for="text-field" class="m-0"> <label for="text-field" class="m-0 font-light">
{label} {label}
{#if isRequired} {#if isRequired}
@@ -44,9 +60,9 @@
{/if} {/if}
<input <input
use:typeAction use:typeAction
class="form-input p-2 mt-1 block w-full bg-white border {inputClassName} {errorMessage class="form-input p-3 mt-1 block w-full border-none bg-gray-100 {inputClassName} {errorMessage
? 'border-red-500' ? 'border-red-500'
: 'border-gray-300'}" : ''}"
on:keydown={onKeyDown} on:keydown={onKeyDown}
{placeholder} {placeholder}
bind:value bind:value
@@ -56,6 +72,13 @@
autocomplete={autoComplete ? 'on' : 'off'} autocomplete={autoComplete ? 'on' : 'off'}
on:blur={onChange} on:blur={onChange}
/> />
{#if isPassword}
<span class="password-eye">
<IconButton value="write-code" onClick={handlePasswordEye}>
<PasswordEye isClosed={type === 'password' ? true : false} />
</IconButton>
</span>
{/if}
{#if errorMessage} {#if errorMessage}
<p class="text-sm text-red-500">{errorMessage}</p> <p class="text-sm text-red-500">{errorMessage}</p>
{:else if helperMessage} {:else if helperMessage}
@@ -64,3 +87,11 @@
</p> </p>
{/if} {/if}
</label> </label>
<style>
.password-eye {
position: absolute;
top: 33px;
right: 6px;
}
</style>
+2
View File
@@ -6,6 +6,7 @@
export let buttonClassName = ''; export let buttonClassName = '';
export let contained = false; export let contained = false;
export let value = ''; export let value = '';
export let type = 'button';
export let size = 'large'; export let size = 'large';
export let color = ''; export let color = '';
export let toolTipProps = { export let toolTipProps = {
@@ -28,6 +29,7 @@
class="root {color} {selected && 'active'} {size} {contained && class="root {color} {selected && 'active'} {size} {contained &&
'contained'} {buttonClassName}" 'contained'} {buttonClassName}"
{disabled} {disabled}
{type}
on:click={handleClick} on:click={handleClick}
> >
<slot /> <slot />
@@ -0,0 +1,32 @@
<svg
width="16"
height="17"
viewBox="0 0 16 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.75 8.76233C3.75 7.96961 3.96841 7.22701 4.34784 6.59117V3.89404H1.65072C0.58025 5.28429 0 6.97864 0 8.76233C0 10.546 0.58025 12.2404 1.65072 13.6306H4.34784V10.9335C3.96841 10.2976 3.75 9.55504 3.75 8.76233Z"
fill="#FBBD00"
/>
<path
d="M8 13.0124L6.125 14.8874L8 16.7624C9.78372 16.7624 11.478 16.1822 12.8683 15.1117V12.4175H10.174C9.53262 12.7983 8.78691 13.0124 8 13.0124Z"
fill="#0F9D58"
/>
<path
d="M4.34739 10.9336L1.65027 13.6307C1.86221 13.906 2.09299 14.1696 2.34271 14.4193C3.85371 15.9303 5.86267 16.7624 7.99955 16.7624V13.0124C6.4488 13.0124 5.08964 12.1774 4.34739 10.9336Z"
fill="#31AA52"
/>
<path
d="M16 8.76258C16 8.27586 15.9559 7.78824 15.869 7.3133L15.7987 6.92896H8V10.679H11.7954C11.4268 11.4121 10.8601 12.0103 10.174 12.4176L12.8682 15.1119C13.1435 14.9 13.4071 14.6692 13.6568 14.4195C15.1678 12.9084 16 10.8995 16 8.76258Z"
fill="#3C79E6"
/>
<path
d="M11.0052 5.75723L11.3367 6.08867L13.9883 3.43705L13.6569 3.10561C12.1459 1.59461 10.1369 0.762451 8 0.762451L6.125 2.63745L8 4.51245C9.13519 4.51245 10.2025 4.95451 11.0052 5.75723Z"
fill="#CF2D48"
/>
<path
d="M7.99958 4.51245V0.762451C5.86271 0.762451 3.85374 1.59461 2.34271 3.10558C2.09299 3.3553 1.86221 3.61889 1.65027 3.89417L4.34739 6.5913C5.08967 5.34745 6.44883 4.51245 7.99958 4.51245Z"
fill="#EB4132"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+43
View File
@@ -0,0 +1,43 @@
<script>
export let isClosed = true;
</script>
{#if isClosed}
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.6 11.3L3.3 10.6C2.6 9.8 1.9 9 1.5 8C2.5 5.5 5.3 3.5 8 3.5C8.7 3.5 9.4 3.6 10 3.9L10.8 3.1C9.9 2.7 9 2.5 8 2.5C4.7 2.6 1.7 4.7 0.5 7.8V8.1C1 9.3 1.7 10.4 2.6 11.3ZM12.6 4.1C13.9 5 14.9 6.3 15.5 7.8V8.2C14.3 11.3 11.3 13.4 8 13.5C6.7 13.4 5.4 13.1 4.3 12.4L1.7 15L1 14.3L14.3 1L15 1.7L12.6 4.1ZM7.8 6.1C6.9 6.1 6.1 6.9 6 7.9L5.1 8.8C5 8.3 5 7.7 5.1 7.2C5.5 5.6 7.2 4.7 8.7 5.2L7.8 6.1ZM10 8C10 9.1 9.1 10 8 10C7.7 10 7.3 9.9 7 9.7L9.7 7C9.9 7.3 10 7.6 10 8ZM5 11.7C5.9 12.2 7 12.5 8 12.5C10.6 12.5 13.4 10.5 14.5 8C13.9 6.7 13 5.6 11.9 4.8L10.5 6.2C11.2 7.2 11.2 8.6 10.5 9.6C9.5 11 7.7 11.3 6.3 10.4L5 11.7Z"
fill="#ADADAD"
/>
</svg>
{:else}
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.666748 8.00008C0.666748 8.00008 3.33341 2.66675 8.00008 2.66675C12.6667 2.66675 15.3334 8.00008 15.3334 8.00008C15.3334 8.00008 12.6667 13.3334 8.00008 13.3334C3.33341 13.3334 0.666748 8.00008 0.666748 8.00008Z"
stroke="#ADADAD"
stroke-width="1.33333"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z"
stroke="#ADADAD"
stroke-width="1.33333"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
{/if}
+11 -1
View File
@@ -3,6 +3,7 @@
import Avatar from '../Avatar/index.svelte'; import Avatar from '../Avatar/index.svelte';
import { user, profile } from '../../utils/store/user'; import { user, profile } from '../../utils/store/user';
import PrimaryButton from '../PrimaryButton/index.svelte'; import PrimaryButton from '../PrimaryButton/index.svelte';
import { VARIANTS } from '../PrimaryButton/constants';
export let segment; export let segment;
export let disableLogin; export let disableLogin;
@@ -88,11 +89,20 @@
/> />
</a> </a>
{:else if !disableLogin} {:else if !disableLogin}
<div class="flex">
<PrimaryButton <PrimaryButton
label="Login" label="Login"
className="py-2 px-4" variant={VARIANTS.NONE}
className="py-2 px-6 mx-2 app-background-color"
onClick={() => goto('/login')} onClick={() => goto('/login')}
/> />
<PrimaryButton
label="Sign Up"
variant={VARIANTS.CONTAINED}
className="py-2 px-6 mx-2"
onClick={() => goto('/signup')}
/>
</div>
{/if} {/if}
</li> </li>
<!-- <li class="hidden"> <!-- <li class="hidden">
+7 -6
View File
@@ -3,14 +3,15 @@ export const VARIANTS = {
CONTAINED_DANGER: 'CONTAINED_DANGER', CONTAINED_DANGER: 'CONTAINED_DANGER',
CONTAINED_SUCCESS: 'CONTAINED_SUCCESS', CONTAINED_SUCCESS: 'CONTAINED_SUCCESS',
OUTLINED: 'OUTLINED', OUTLINED: 'OUTLINED',
NONE: 'NONE',
}; };
export const VARIANTS_CLASS = { export const VARIANTS_CLASS = {
[VARIANTS.CONTAINED]: 'bg-blue-700 hover:bg-blue-900 text-white font-bold', [VARIANTS.CONTAINED]: 'border-none bg-blue-700 hover:bg-blue-900 text-white',
[VARIANTS.CONTAINED_SUCCESS]: [VARIANTS.CONTAINED_SUCCESS]: 'bg-green-700 hover:bg-green-900 text-white',
'bg-green-700 hover:bg-green-900 text-white font-bold', [VARIANTS.CONTAINED_DANGER]: 'bg-red-700 hover:bg-red-900 text-white',
[VARIANTS.CONTAINED_DANGER]:
'bg-red-700 hover:bg-red-900 text-white font-bold',
[VARIANTS.OUTLINED]: [VARIANTS.OUTLINED]:
'border border-gray-700 hover:border-black-300 hover:bg-gray-200 text-black', 'border border-gray hover:border-black-300 hover:bg-gray-200 text-black',
[VARIANTS.NONE]:
'border-none hover:border-black-300 hover:bg-gray-200 text-black',
}; };
+2 -2
View File
@@ -6,7 +6,7 @@
export let className = ''; export let className = '';
export let onClick = () => {}; export let onClick = () => {};
export let name = ''; export let name = '';
export let type = 'text'; export let type = 'button';
export let variant = VARIANTS.CONTAINED; export let variant = VARIANTS.CONTAINED;
export let disablePadding = false; export let disablePadding = false;
export let isDisabled = false; export let isDisabled = false;
@@ -21,7 +21,7 @@
'bg-opacity-25 hover:bg-opacity-25 cursor-not-allowed'} flex items-center h-auto {VARIANTS_CLASS[ 'bg-opacity-25 hover:bg-opacity-25 cursor-not-allowed'} flex items-center h-auto {VARIANTS_CLASS[
variant variant
]} {!disablePadding && ]} {!disablePadding &&
'py-1 px-5'} rounded {className} py-2 w-full justify-center sm:w-auto {isLoading && 'py-1 px-5'} rounded-none {className} py-2 w-full justify-center sm:w-auto {isLoading &&
'py-4 border-blue-700'} transition delay-150 duration-300 ease-in-out" 'py-4 border-blue-700'} transition delay-150 duration-300 ease-in-out"
on:click={onClick} on:click={onClick}
{name} {name}
+5
View File
@@ -197,5 +197,10 @@
:global(:root) { :global(:root) {
--main-primary-color: rgba(29, 78, 216, 1); --main-primary-color: rgba(29, 78, 216, 1);
--border-color: #eaecef; --border-color: #eaecef;
--app-background-color: #fafbfc;
}
:global(.app-background-color) {
background-color: var(--app-background-color);
} }
</style> </style>
+28 -36
View File
@@ -10,11 +10,13 @@
import PrimaryButton from '../components/PrimaryButton/index.svelte'; import PrimaryButton from '../components/PrimaryButton/index.svelte';
import { getSupabase } from '../utils/functions/supabase'; import { getSupabase } from '../utils/functions/supabase';
import { user } from '../utils/store/user'; import { user } from '../utils/store/user';
import AuthUI from '../components/AuthUI/index.svelte';
export let config; export let config;
let supabase = getSupabase(config); let supabase = getSupabase(config);
let email; let email;
let password;
let loading = false; let loading = false;
let success = false; let success = false;
let errors = {}; let errors = {};
@@ -23,6 +25,7 @@
if (!email) { if (!email) {
errors = { errors = {
email: 'Email is required', email: 'Email is required',
password: 'Password is required',
}; };
return; return;
} }
@@ -50,26 +53,11 @@
</script> </script>
<svelte:head> <svelte:head>
<title>Join ClassroomIO</title> <title>Login - ClassroomIO</title>
</svelte:head> </svelte:head>
<div class="w-full h-screen flex items-center justify-center "> <AuthUI {supabase} isLogin={true} {handleSubmit}>
<div class="container border border-gray-300 rounded-md p-2 lg:p-12"> <div class="mt-4 w-full">
<form
on:submit|preventDefault={handleSubmit}
class="flex items-center flex-col w-full"
>
<img
src="/logo-192.png"
alt="ClassroomIO logo"
height="70"
width="70"
data-atf="1"
/>
<h4 class="text-lg m-0 mt-3">Login</h4>
<p class="text-sm">into ClassroomIO</p>
<div class="my-4 w-full">
{#if $user.fetchingUser} {#if $user.fetchingUser}
<div class="h-40 text-md flex items-center justify-center"> <div class="h-40 text-md flex items-center justify-center">
<Chasing size="60" color="#ff3e00" unit="px" duration="1s" /> <Chasing size="60" color="#ff3e00" unit="px" duration="1s" />
@@ -81,24 +69,36 @@
<strong>spam </strong>and your<strong>&nbsp; inbox</strong>. <strong>spam </strong>and your<strong>&nbsp; inbox</strong>.
</p> </p>
<p class="mt-2 underline"> <p class="mt-2 underline">
If our email is in your spam, do make sure you mark our email If our email is in your spam, do make sure you mark our email address
address as safe, then move it from your spam to your inbox. as safe, then move it from your spam to your inbox.
</p> </p>
</div> </div>
{:else} {:else}
<p class="text-lg font-semibold mb-6">Welcome back</p>
<TextField <TextField
label="Email address" label="Your email"
bind:value={email} bind:value={email}
type="email" type="email"
autoFocus={true} autoFocus={true}
placeholder="[email protected]" placeholder="[email protected]"
inputClassName="rounded-md w-full px-2 py-4" className="mb-6"
inputClassName="w-full"
isDisabled={loading} isDisabled={loading}
errorMessage={errors.email} errorMessage={errors.email}
/> />
<p class="py-3 text-md"> <TextField
A magic link will be sent to your email, please open the link label="Your password"
</p> bind:value={password}
type="password"
placeholder="************"
className="mb-6"
inputClassName="w-full"
isDisabled={loading}
errorMessage={errors.password}
/>
<div class="w-full text-right">
<a class="text-md text-blue-700" href="/forgot"> Forgot password? </a>
</div>
{/if} {/if}
</div> </div>
@@ -106,19 +106,11 @@
<div class="my-4 w-full flex justify-end items-center"> <div class="my-4 w-full flex justify-end items-center">
<!-- <a href="/login" class="text-blue-700 text-sm">Create an account</a> --> <!-- <a href="/login" class="text-blue-700 text-sm">Create an account</a> -->
<PrimaryButton <PrimaryButton
label={loading ? 'Creating...' : 'Next'} label={loading ? 'Creating...' : 'Log In'}
type="submit" type="submit"
className="py-2 px-4" className="py-3 sm:w-full w-full"
isDisabled={loading} isDisabled={loading}
/> />
</div> </div>
{/if} {/if}
</form> </AuthUI>
</div>
</div>
<style>
.container {
width: 450px;
}
</style>
+104 -70
View File
@@ -5,39 +5,49 @@
</script> </script>
<script> <script>
import { Chasing } from 'svelte-loading-spinners';
import TextField from '../components/Form/TextField.svelte'; import TextField from '../components/Form/TextField.svelte';
import Select from '../components/Form/Select.svelte';
import PrimaryButton from '../components/PrimaryButton/index.svelte'; import PrimaryButton from '../components/PrimaryButton/index.svelte';
import { getSupabase } from '../utils/functions/supabase'; import { getSupabase } from '../utils/functions/supabase';
import { user } from '../utils/store/user';
import AuthUI from '../components/AuthUI/index.svelte';
export let config; export let config;
let supabase = getSupabase(config); let supabase = getSupabase(config);
let email; let email;
let userType = 0; let name;
let password;
let passwordAgain;
let loading = false;
let success = false;
let errors = {};
const userTypes = [
{
text: '--Select an option--',
value: 0,
},
{
text: 'Student',
value: 1,
},
{
text: 'Organization/Teacher',
value: 2,
},
];
async function handleSubmit() { async function handleSubmit() {
if (!email) {
errors = {
email: 'Email is required',
password: 'Password is required',
};
return;
}
try { try {
loading = true; loading = true;
const { error } = await supabase.auth.signIn({ email }); const { data, error } = await supabase.auth.signIn(
{ email },
{
redirectTo: window.location.origin + '/login',
}
);
if (error) throw error; if (error) throw error;
alert('Check your email for the login link!');
success = true;
email = {};
} catch (error) { } catch (error) {
alert(error.error_description || error.message); errors = {
email: error.error_description || error.message,
};
} finally { } finally {
loading = false; loading = false;
} }
@@ -45,60 +55,84 @@
</script> </script>
<svelte:head> <svelte:head>
<title>Join ClassroomIO</title> <title>Sign Up - ClassroomIO</title>
</svelte:head> </svelte:head>
<div class="w-full h-screen flex items-center justify-center "> <AuthUI {supabase} isLogin={false} {handleSubmit}>
<div class="container border border-gray-300 rounded-md p-12"> <div class="mt-4 w-full">
<form {#if $user.fetchingUser}
on:submit|preventDefault={handleSubmit} <div class="h-40 text-md flex items-center justify-center">
class="flex items-center flex-col w-full" <Chasing size="60" color="#ff3e00" unit="px" duration="1s" />
> </div>
<img {:else if success}
src="/logo-192.png" <div class="h-40 text-md flex items-center justify-center flex-col">
alt="ClassroomIO logo" <p class="">
height="70" A link has been sent to your email. Please check both your
width="70" <strong>spam </strong>and your<strong>&nbsp; inbox</strong>.
data-atf="1" </p>
/> <p class="mt-2 underline">
<h4 class="text-lg m-0 mt-3">Signup</h4> If our email is in your spam, do make sure you mark our email address
<p class="text-sm">for ClassroomIO</p> as safe, then move it from your spam to your inbox.
<!-- <div class="my-4 w-full">
<Select
label="Who are you?"
bind:value={userType}
options={userTypes}
valueKey="value"
isRequired={true}
/>
</div> -->
<div class="my-4 w-full">
<TextField
bind:value={email}
autofocus={true}
label="Email"
type="email"
placeholder="Enter your email"
inputClassName="rounded-md w-full px-2 py-4"
isRequired={true}
/>
<p class="py-3 text-sm">
A magic link will be sent to your email, please open the link
</p> </p>
</div> </div>
{:else}
<div class="my-4 w-full flex justify-between items-center"> <p class="text-lg font-semibold mb-6">Create a free account</p>
<a href="/login" class="text-blue-700 text-sm">I have an account</a> <TextField
<PrimaryButton label="Next" type="submit" className="py-2 px-4" /> label="Full Name"
</div> bind:value={name}
</form> type="text"
autoFocus={true}
placeholder="e.g Joke Silva"
className="mb-6"
inputClassName="w-full"
isDisabled={loading}
errorMessage={errors.name}
/>
<TextField
label="Your Email"
bind:value={email}
type="email"
placeholder="[email protected]"
className="mb-6"
inputClassName="w-full"
isDisabled={loading}
errorMessage={errors.email}
/>
<TextField
label="Your Password"
bind:value={password}
type="password"
placeholder="************"
className="mb-6"
inputClassName="w-full"
isDisabled={loading}
errorMessage={errors.password}
/>
<TextField
label="Confirm Password"
bind:value={passwordAgain}
type="password"
placeholder="************"
className="mb-6"
inputClassName="w-full"
isDisabled={loading}
errorMessage={errors.passwordAgain}
/>
<div class="w-full text-right">
<a class="text-md text-blue-700" href="/forgot"> Forgot password? </a>
</div> </div>
{/if}
</div> </div>
<style> {#if !success}
.container { <div class="my-4 w-full flex justify-end items-center">
width: 450px; <!-- <a href="/login" class="text-blue-700 text-sm">Create an account</a> -->
} <PrimaryButton
</style> label={loading ? 'Creating...' : 'Log In'}
type="submit"
className="py-3 sm:w-full w-full"
isDisabled={loading}
/>
</div>
{/if}
</AuthUI>