mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-27 15:22:56 +08:00
feat: update login/signup page
This commit is contained in:
parent
8af1bef216
commit
0091a25efb
@ -28,8 +28,8 @@ server {
|
|||||||
try_files $uri /pricing.html;
|
try_files $uri /pricing.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /reset-password {
|
location /forgot-password {
|
||||||
try_files $uri /reset-password.html;
|
try_files $uri /forgot-password.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /sign-in {
|
location /sign-in {
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@mantine/core": "^6.0.17",
|
"@mantine/core": "^6.0.17",
|
||||||
"@mantine/form": "^6.0.17",
|
|
||||||
"@mantine/hooks": "^6.0.17",
|
"@mantine/hooks": "^6.0.17",
|
||||||
"@mantine/next": "^6.0.21",
|
"@mantine/next": "^6.0.21",
|
||||||
"@mantine/prism": "^6.0.21",
|
"@mantine/prism": "^6.0.21",
|
||||||
|
@ -6,5 +6,5 @@ Allow: /
|
|||||||
# Block all crawlers for /accounts
|
# Block all crawlers for /accounts
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /*?*
|
Disallow: /*?*
|
||||||
Disallow: /reset-password
|
Disallow: /forgot-password
|
||||||
Disallow: /widget
|
Disallow: /widget
|
@ -94,7 +94,7 @@ export const Navbar = () => {
|
|||||||
radius="md"
|
radius="md"
|
||||||
className="hide-mobile"
|
className="hide-mobile"
|
||||||
>
|
>
|
||||||
Sign In
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button component={Link} href="/editor" prefetch={false} color="pink" radius="md">
|
<Button component={Link} href="/editor" prefetch={false} color="pink" radius="md">
|
||||||
|
73
src/pages/forgot-password.tsx
Normal file
73
src/pages/forgot-password.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Button, Group, Paper, Stack, TextInput, Text, Anchor } from "@mantine/core";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
|
import { Footer } from "src/layout/Footer";
|
||||||
|
import { Navbar } from "src/layout/Navbar";
|
||||||
|
import { supabase } from "src/lib/api/supabase";
|
||||||
|
|
||||||
|
const ForgotPassword = () => {
|
||||||
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
const [email, setEmail] = React.useState("");
|
||||||
|
const [success, setSuccess] = React.useState(false);
|
||||||
|
|
||||||
|
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
supabase.auth
|
||||||
|
.resetPasswordForEmail(email)
|
||||||
|
.then(({ error }) => {
|
||||||
|
if (error) return toast.error(error.message);
|
||||||
|
setSuccess(true);
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>Reset Password - JSON Crack</title>
|
||||||
|
<meta name="robots" content="noindex,nofollow" />
|
||||||
|
</Head>
|
||||||
|
<Navbar />
|
||||||
|
<Paper mx="auto" mt={70} maw={400} p="lg" withBorder>
|
||||||
|
<Text size="lg" weight={500}>
|
||||||
|
Reset Password
|
||||||
|
</Text>
|
||||||
|
<Paper pt="lg">
|
||||||
|
{success ? (
|
||||||
|
<Text>We've sent an email to you, please check your inbox.</Text>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={onSubmit}>
|
||||||
|
<Stack>
|
||||||
|
<TextInput
|
||||||
|
value={email}
|
||||||
|
onChange={e => setEmail(e.target.value)}
|
||||||
|
required
|
||||||
|
label="Email"
|
||||||
|
placeholder="hello@herowand.com"
|
||||||
|
radius="sm"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Group position="apart" mt="xl">
|
||||||
|
<Button color="dark" type="submit" radius="sm" loading={loading} fullWidth>
|
||||||
|
Reset Password
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
<Stack mt="lg" align="center">
|
||||||
|
<Anchor component={Link} prefetch={false} href="/sign-in" color="dark" size="xs">
|
||||||
|
Don't have an account? Sign Up
|
||||||
|
</Anchor>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
</Paper>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ForgotPassword;
|
@ -1,83 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Head from "next/head";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { PaperProps, Button, Group, Paper, Stack, TextInput, Text, Anchor } from "@mantine/core";
|
|
||||||
import { toast } from "react-hot-toast";
|
|
||||||
import { Footer } from "src/layout/Footer";
|
|
||||||
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
|
||||||
import { Navbar } from "src/layout/Navbar";
|
|
||||||
import { supabase } from "src/lib/api/supabase";
|
|
||||||
|
|
||||||
export function AuthenticationForm(props: PaperProps) {
|
|
||||||
const [loading, setLoading] = React.useState(false);
|
|
||||||
const [email, setEmail] = React.useState("");
|
|
||||||
const [success, setSuccess] = React.useState(false);
|
|
||||||
|
|
||||||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setLoading(true);
|
|
||||||
supabase.auth
|
|
||||||
.resetPasswordForEmail(email)
|
|
||||||
.then(({ error }) => {
|
|
||||||
if (error) return toast.error(error.message);
|
|
||||||
setSuccess(true);
|
|
||||||
})
|
|
||||||
.finally(() => setLoading(false));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper pt="lg" {...props}>
|
|
||||||
<Text size="lg" weight={500}>
|
|
||||||
Reset Password
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
{success ? (
|
|
||||||
<Text>We've sent an email to you, please check your inbox.</Text>
|
|
||||||
) : (
|
|
||||||
<form onSubmit={onSubmit}>
|
|
||||||
<Stack>
|
|
||||||
<TextInput
|
|
||||||
value={email}
|
|
||||||
onChange={e => setEmail(e.target.value)}
|
|
||||||
required
|
|
||||||
label="Email"
|
|
||||||
placeholder="hello@herowand.com"
|
|
||||||
size="md"
|
|
||||||
radius="sm"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Group position="apart" mt="xl">
|
|
||||||
<Button type="submit" radius="sm" size="md" loading={loading} fullWidth>
|
|
||||||
Reset Password
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
<Stack mt="lg" align="center">
|
|
||||||
<Anchor component={Link} prefetch={false} href="/sign-in" color="dark" size="xs">
|
|
||||||
Don't have an account? Sign Up
|
|
||||||
</Anchor>
|
|
||||||
</Stack>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ResetPassword = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Head>
|
|
||||||
<title>Reset Password - JSON Crack</title>
|
|
||||||
<meta name="robots" content="noindex,nofollow" />
|
|
||||||
</Head>
|
|
||||||
<Navbar />
|
|
||||||
<Paper mx="auto" mt={70} maw={400} p="lg" withBorder>
|
|
||||||
<JSONCrackLogo />
|
|
||||||
<AuthenticationForm />
|
|
||||||
</Paper>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ResetPassword;
|
|
@ -13,15 +13,12 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
Anchor,
|
Anchor,
|
||||||
Stack,
|
Stack,
|
||||||
Alert,
|
Center,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useForm } from "@mantine/form";
|
|
||||||
import { useToggle, upperFirst } from "@mantine/hooks";
|
|
||||||
import { useSession } from "@supabase/auth-helpers-react";
|
import { useSession } from "@supabase/auth-helpers-react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { AiOutlineGithub, AiOutlineGoogle } from "react-icons/ai";
|
import { AiOutlineGithub, AiOutlineGoogle } from "react-icons/ai";
|
||||||
import { Footer } from "src/layout/Footer";
|
import { Footer } from "src/layout/Footer";
|
||||||
import { JSONCrackLogo } from "src/layout/JsonCrackLogo";
|
|
||||||
import { Navbar } from "src/layout/Navbar";
|
import { Navbar } from "src/layout/Navbar";
|
||||||
import { supabase } from "src/lib/api/supabase";
|
import { supabase } from "src/lib/api/supabase";
|
||||||
import useUser from "src/store/useUser";
|
import useUser from "src/store/useUser";
|
||||||
@ -29,59 +26,26 @@ import useUser from "src/store/useUser";
|
|||||||
export function AuthenticationForm(props: PaperProps) {
|
export function AuthenticationForm(props: PaperProps) {
|
||||||
const setSession = useUser(state => state.setSession);
|
const setSession = useUser(state => state.setSession);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
const [type, toggle] = useToggle<"login" | "register">(["login", "register"]);
|
const [userData, setUserData] = React.useState({
|
||||||
const [done, setDone] = React.useState(false);
|
name: "",
|
||||||
|
email: "",
|
||||||
const form = useForm({
|
password: "",
|
||||||
initialValues: {
|
|
||||||
email: "",
|
|
||||||
name: "",
|
|
||||||
password: "",
|
|
||||||
passwordAgain: "",
|
|
||||||
},
|
|
||||||
|
|
||||||
validate: {
|
|
||||||
email: val => (/^\S+@\S+$/.test(val) ? null : "Invalid email"),
|
|
||||||
password: val => (val.length <= 6 ? "Password should include at least 6 characters" : null),
|
|
||||||
passwordAgain: (val, { password }) =>
|
|
||||||
type === "register" && val !== password ? "Passwords doesn't match" : null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const validate = form.validate();
|
|
||||||
|
|
||||||
if (validate.hasErrors) return;
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (type === "login") {
|
|
||||||
supabase.auth
|
supabase.auth
|
||||||
.signInWithPassword({
|
.signInWithPassword({
|
||||||
email: form.values.email,
|
email: userData.email,
|
||||||
password: form.values.password,
|
password: userData.password,
|
||||||
})
|
})
|
||||||
.then(({ data, error }) => {
|
.then(({ data, error }) => {
|
||||||
if (error) return toast.error(error.message);
|
if (error) return toast.error(error.message);
|
||||||
setSession(data.session);
|
setSession(data.session);
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
} else {
|
|
||||||
supabase.auth
|
|
||||||
.signUp({
|
|
||||||
email: form.values.email,
|
|
||||||
password: form.values.password,
|
|
||||||
options: {
|
|
||||||
data: { name: form.values.name },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(({ error }) => {
|
|
||||||
if (error) return toast.error(error.message);
|
|
||||||
toast.success("Please check your inbox to confirm mail address!", { duration: 7000 });
|
|
||||||
setDone(true);
|
|
||||||
})
|
|
||||||
.finally(() => setLoading(false));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginClick = (provider: "github" | "google") => {
|
const handleLoginClick = (provider: "github" | "google") => {
|
||||||
@ -91,128 +55,68 @@ export function AuthenticationForm(props: PaperProps) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (done) {
|
|
||||||
return (
|
|
||||||
<Paper mih={100}>
|
|
||||||
<Text align="center" mt="lg">
|
|
||||||
Registration successul!
|
|
||||||
<br />
|
|
||||||
Please check your inbox for email confirmation.
|
|
||||||
</Text>
|
|
||||||
<Button radius="sm" size="md" mt="lg" onClick={() => window.location.reload()} fullWidth>
|
|
||||||
Back to login
|
|
||||||
</Button>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper {...props}>
|
<Paper {...props}>
|
||||||
|
<form onSubmit={onSubmit}>
|
||||||
|
<Stack>
|
||||||
|
<TextInput
|
||||||
|
required
|
||||||
|
label="Email"
|
||||||
|
placeholder="hello@jsoncrack.com"
|
||||||
|
value={userData.email}
|
||||||
|
onChange={event => setUserData(d => ({ ...d, email: event.target.value }))}
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PasswordInput
|
||||||
|
required
|
||||||
|
label="Password"
|
||||||
|
placeholder="∗∗∗∗∗∗∗∗∗∗∗"
|
||||||
|
value={userData.password}
|
||||||
|
onChange={event => setUserData(d => ({ ...d, password: event.target.value }))}
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button color="dark" type="submit" radius="md" loading={loading}>
|
||||||
|
Sign in
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Stack spacing="sm" mx="auto" align="center">
|
||||||
|
<Anchor
|
||||||
|
component={Link}
|
||||||
|
prefetch={false}
|
||||||
|
href="/forgot-password"
|
||||||
|
color="dark"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Forgot your password?
|
||||||
|
</Anchor>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<Divider my="lg" />
|
||||||
|
|
||||||
<Stack mb="md" mt="md">
|
<Stack mb="md" mt="md">
|
||||||
<Button
|
<Button
|
||||||
radius="sm"
|
radius="md"
|
||||||
size="md"
|
|
||||||
leftIcon={<AiOutlineGoogle size="20" />}
|
leftIcon={<AiOutlineGoogle size="20" />}
|
||||||
onClick={() => handleLoginClick("google")}
|
onClick={() => handleLoginClick("google")}
|
||||||
color="red"
|
color="red"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
Sign In with Google
|
Sign in with Google
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
radius="sm"
|
radius="md"
|
||||||
size="md"
|
|
||||||
leftIcon={<AiOutlineGithub size="20" />}
|
leftIcon={<AiOutlineGithub size="20" />}
|
||||||
onClick={() => handleLoginClick("github")}
|
onClick={() => handleLoginClick("github")}
|
||||||
color="dark"
|
color="dark"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
>
|
>
|
||||||
Sign In with GitHub
|
Sign in with GitHub
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Divider label="Or continue with email" labelPosition="center" my="lg" />
|
|
||||||
|
|
||||||
<form onSubmit={onSubmit}>
|
|
||||||
<Stack>
|
|
||||||
{type === "register" && (
|
|
||||||
<TextInput
|
|
||||||
required
|
|
||||||
size="md"
|
|
||||||
label="Name"
|
|
||||||
placeholder="John Doe"
|
|
||||||
value={form.values.name}
|
|
||||||
onChange={event => form.setFieldValue("name", event.currentTarget.value)}
|
|
||||||
error={form.errors.name && "This field cannot be left blank"}
|
|
||||||
radius="sm"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<TextInput
|
|
||||||
required
|
|
||||||
label="Email address"
|
|
||||||
size="md"
|
|
||||||
placeholder="hello@jsoncrack.com"
|
|
||||||
value={form.values.email}
|
|
||||||
onChange={event => form.setFieldValue("email", event.currentTarget.value)}
|
|
||||||
error={form.errors.email && "Invalid email"}
|
|
||||||
radius="sm"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PasswordInput
|
|
||||||
required
|
|
||||||
label="Your Password"
|
|
||||||
size="md"
|
|
||||||
placeholder="*********"
|
|
||||||
value={form.values.password}
|
|
||||||
onChange={event => form.setFieldValue("password", event.currentTarget.value)}
|
|
||||||
error={form.errors.password && "Password should include at least 6 characters"}
|
|
||||||
radius="sm"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{type === "register" && (
|
|
||||||
<PasswordInput
|
|
||||||
required
|
|
||||||
label="Validate Password"
|
|
||||||
placeholder="Your password"
|
|
||||||
value={form.values.passwordAgain}
|
|
||||||
onChange={event => form.setFieldValue("passwordAgain", event.currentTarget.value)}
|
|
||||||
error={form.errors.passwordAgain && "Passwords doesn't match"}
|
|
||||||
radius="sm"
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button type="submit" radius="sm" size="md" loading={loading}>
|
|
||||||
{upperFirst(type)}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Stack spacing="sm" mx="auto" align="center">
|
|
||||||
{type === "login" && (
|
|
||||||
<Anchor
|
|
||||||
component={Link}
|
|
||||||
prefetch={false}
|
|
||||||
href="/reset-password"
|
|
||||||
color="dark"
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Forgot your password?
|
|
||||||
</Anchor>
|
|
||||||
)}
|
|
||||||
<Anchor
|
|
||||||
color="dark"
|
|
||||||
component="button"
|
|
||||||
type="button"
|
|
||||||
onClick={() => toggle()}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
{type === "register"
|
|
||||||
? "Already have an account? Login"
|
|
||||||
: "Don't have an account? Sign Up"}
|
|
||||||
</Anchor>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</form>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -241,7 +145,7 @@ function ResetPassword(props: PaperProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper radius="sm" {...props}>
|
<Paper radius="md" {...props}>
|
||||||
<Text size="lg" weight={500}>
|
<Text size="lg" weight={500}>
|
||||||
Reset Password
|
Reset Password
|
||||||
</Text>
|
</Text>
|
||||||
@ -253,14 +157,14 @@ function ResetPassword(props: PaperProps) {
|
|||||||
onChange={e => setPassword(e.target.value)}
|
onChange={e => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
label="Password"
|
label="Password"
|
||||||
radius="sm"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
value={password2}
|
value={password2}
|
||||||
onChange={e => setPassword2(e.target.value)}
|
onChange={e => setPassword2(e.target.value)}
|
||||||
required
|
required
|
||||||
label="Validate Password"
|
label="Validate Password"
|
||||||
radius="sm"
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@ -278,7 +182,6 @@ const SignIn = () => {
|
|||||||
const { isReady, push, query } = useRouter();
|
const { isReady, push, query } = useRouter();
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const isPasswordReset = query?.type === "recovery" && !query?.error;
|
const isPasswordReset = query?.type === "recovery" && !query?.error;
|
||||||
const [alertVisible, setAlertVisible] = React.useState(true);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isReady && session && !isPasswordReset) {
|
if (isReady && session && !isPasswordReset) {
|
||||||
@ -292,27 +195,15 @@ const SignIn = () => {
|
|||||||
<title>Sign In - JSON Crack</title>
|
<title>Sign In - JSON Crack</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
{alertVisible && (
|
<Paper mt={50} shadow="xs" mx="auto" maw={400} p="lg" withBorder>
|
||||||
<Alert
|
|
||||||
color="orange"
|
|
||||||
radius="md"
|
|
||||||
mt={30}
|
|
||||||
mx="auto"
|
|
||||||
w={700}
|
|
||||||
variant="outline"
|
|
||||||
withCloseButton
|
|
||||||
onClose={() => setAlertVisible(false)}
|
|
||||||
>
|
|
||||||
We have transitioned to a new database. If you've been using JSON Crack for a while
|
|
||||||
and unable to login, kindly <strong>register a new account</strong> once more. (Rest
|
|
||||||
assured, your saved files remain intact.)
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Paper shadow="md" mx="auto" maw={400} mt={50} p="lg" withBorder>
|
|
||||||
<JSONCrackLogo />
|
|
||||||
{isPasswordReset ? <ResetPassword /> : <AuthenticationForm />}
|
{isPasswordReset ? <ResetPassword /> : <AuthenticationForm />}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
<Center my="xl">
|
||||||
|
<Anchor component={Link} prefetch={false} href="/sign-up" color="dark" fw="bold">
|
||||||
|
Don't have an account?
|
||||||
|
</Anchor>
|
||||||
|
</Center>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
167
src/pages/sign-up.tsx
Normal file
167
src/pages/sign-up.tsx
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import Link from "next/link";
|
||||||
|
import {
|
||||||
|
Anchor,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Divider,
|
||||||
|
Flex,
|
||||||
|
Paper,
|
||||||
|
PasswordInput,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import { AiOutlineGithub, AiOutlineGoogle } from "react-icons/ai";
|
||||||
|
import Layout from "src/layout/Layout";
|
||||||
|
import { supabase } from "src/lib/api/supabase";
|
||||||
|
|
||||||
|
const SignUp = () => {
|
||||||
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
const [done, setDone] = React.useState(false);
|
||||||
|
const [userData, setUserData] = React.useState({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
supabase.auth
|
||||||
|
.signUp({
|
||||||
|
email: userData.email,
|
||||||
|
password: userData.password,
|
||||||
|
options: {
|
||||||
|
data: { name: userData.name },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ error }) => {
|
||||||
|
if (error) return toast.error(error.message);
|
||||||
|
toast.success("Please check your inbox to confirm mail address!", { duration: 7000 });
|
||||||
|
setDone(true);
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLoginClick = (provider: "github" | "google") => {
|
||||||
|
supabase.auth.signInWithOAuth({
|
||||||
|
provider,
|
||||||
|
options: { redirectTo: "https://jsoncrack.com/editor" },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Head>
|
||||||
|
<title>JSON Crack | Sign Up</title>
|
||||||
|
</Head>
|
||||||
|
{done ? (
|
||||||
|
<Paper shadow="xs" mx="auto" maw={400} mt={50} p="lg" withBorder>
|
||||||
|
<Text align="center" mt="lg">
|
||||||
|
Registration successul!
|
||||||
|
<br />
|
||||||
|
Please check your inbox for email confirmation.
|
||||||
|
</Text>
|
||||||
|
<Anchor component={Link} href="/sign-in" underline={false}>
|
||||||
|
<Button color="dark" radius="md" mt="lg" fullWidth>
|
||||||
|
Back to login
|
||||||
|
</Button>
|
||||||
|
</Anchor>
|
||||||
|
</Paper>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Paper shadow="xs" mx="auto" maw={400} mt={50} p="lg" withBorder>
|
||||||
|
<form onSubmit={onSubmit}>
|
||||||
|
<Stack>
|
||||||
|
<TextInput
|
||||||
|
onChange={e => setUserData(d => ({ ...d, name: e.target.value }))}
|
||||||
|
required
|
||||||
|
label="Name"
|
||||||
|
placeholder="John Doe"
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
onChange={e => setUserData(d => ({ ...d, email: e.target.value }))}
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
label="Email"
|
||||||
|
placeholder="hello@jsoncrack.com"
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PasswordInput
|
||||||
|
onChange={e => setUserData(d => ({ ...d, password: e.target.value }))}
|
||||||
|
min={6}
|
||||||
|
required
|
||||||
|
label="Password"
|
||||||
|
placeholder="∗∗∗∗∗∗∗∗∗∗"
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button color="dark" type="submit" loading={loading}>
|
||||||
|
Sign up for free
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Divider label="OR CONTINUE WITH" labelPosition="center" />
|
||||||
|
|
||||||
|
<Flex gap="sm">
|
||||||
|
<Button
|
||||||
|
radius="md"
|
||||||
|
fullWidth
|
||||||
|
leftIcon={<AiOutlineGoogle size="20" />}
|
||||||
|
onClick={() => handleLoginClick("google")}
|
||||||
|
color="red"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Google
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
radius="md"
|
||||||
|
leftIcon={<AiOutlineGithub size="20" />}
|
||||||
|
onClick={() => handleLoginClick("github")}
|
||||||
|
color="dark"
|
||||||
|
variant="outline"
|
||||||
|
fullWidth
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Divider mx={-20} />
|
||||||
|
|
||||||
|
<Text fz="xs" c="gray">
|
||||||
|
By signing up, you agree to our{" "}
|
||||||
|
<Anchor component={Link} href="/legal/terms" c="gray" fw={500}>
|
||||||
|
Terms of Service
|
||||||
|
</Anchor>{" "}
|
||||||
|
and{" "}
|
||||||
|
<Anchor component={Link} href="/legal/privacy" c="gray" fw={500}>
|
||||||
|
Privacy Policy
|
||||||
|
</Anchor>
|
||||||
|
. Need help?{" "}
|
||||||
|
<Anchor component={Link} href="mailto:contact@jsoncrack.com" c="gray" fw={500}>
|
||||||
|
Get in touch.
|
||||||
|
</Anchor>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</form>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
<Center my="xl">
|
||||||
|
<Anchor component={Link} prefetch={false} href="/sign-in" color="dark" fw="bold">
|
||||||
|
Already have an account?
|
||||||
|
</Anchor>
|
||||||
|
</Center>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignUp;
|
13
yarn.lock
13
yarn.lock
@ -1362,14 +1362,6 @@
|
|||||||
react-remove-scroll "^2.5.5"
|
react-remove-scroll "^2.5.5"
|
||||||
react-textarea-autosize "8.3.4"
|
react-textarea-autosize "8.3.4"
|
||||||
|
|
||||||
"@mantine/form@^6.0.17":
|
|
||||||
version "6.0.19"
|
|
||||||
resolved "https://registry.npmjs.org/@mantine/form/-/form-6.0.19.tgz"
|
|
||||||
integrity sha512-5SFLZEzaBH7yKIDSDt1r9UiN4y7RkFvu+7J7CFPIQM+nTdXeGnugVFM8rASuZI7/FSYty/XoPY+Yymq3xDX+MQ==
|
|
||||||
dependencies:
|
|
||||||
fast-deep-equal "^3.1.3"
|
|
||||||
klona "^2.0.5"
|
|
||||||
|
|
||||||
"@mantine/hooks@^6.0.17":
|
"@mantine/hooks@^6.0.17":
|
||||||
version "6.0.21"
|
version "6.0.21"
|
||||||
resolved "https://registry.npmjs.org/@mantine/hooks/-/hooks-6.0.21.tgz"
|
resolved "https://registry.npmjs.org/@mantine/hooks/-/hooks-6.0.21.tgz"
|
||||||
@ -4358,11 +4350,6 @@ kld-polynomial@^0.3.0:
|
|||||||
resolved "https://registry.npmjs.org/kld-polynomial/-/kld-polynomial-0.3.0.tgz"
|
resolved "https://registry.npmjs.org/kld-polynomial/-/kld-polynomial-0.3.0.tgz"
|
||||||
integrity sha512-PEfxjQ6tsxL9DHBIhM2UZsSes0GI+OIMjbE0kj60jr80Biq/xXl1eGfnyzmfoackAMdKZtw2060L09HdjkPP5w==
|
integrity sha512-PEfxjQ6tsxL9DHBIhM2UZsSes0GI+OIMjbE0kj60jr80Biq/xXl1eGfnyzmfoackAMdKZtw2060L09HdjkPP5w==
|
||||||
|
|
||||||
klona@^2.0.5:
|
|
||||||
version "2.0.6"
|
|
||||||
resolved "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz"
|
|
||||||
integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==
|
|
||||||
|
|
||||||
language-subtag-registry@~0.3.2:
|
language-subtag-registry@~0.3.2:
|
||||||
version "0.3.22"
|
version "0.3.22"
|
||||||
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
|
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user