mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-12 19:02:53 +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;
|
||||
}
|
||||
|
||||
location /reset-password {
|
||||
try_files $uri /reset-password.html;
|
||||
location /forgot-password {
|
||||
try_files $uri /forgot-password.html;
|
||||
}
|
||||
|
||||
location /sign-in {
|
||||
|
@ -16,7 +16,6 @@
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@mantine/core": "^6.0.17",
|
||||
"@mantine/form": "^6.0.17",
|
||||
"@mantine/hooks": "^6.0.17",
|
||||
"@mantine/next": "^6.0.21",
|
||||
"@mantine/prism": "^6.0.21",
|
||||
|
@ -6,5 +6,5 @@ Allow: /
|
||||
# Block all crawlers for /accounts
|
||||
User-agent: *
|
||||
Disallow: /*?*
|
||||
Disallow: /reset-password
|
||||
Disallow: /forgot-password
|
||||
Disallow: /widget
|
@ -94,7 +94,7 @@ export const Navbar = () => {
|
||||
radius="md"
|
||||
className="hide-mobile"
|
||||
>
|
||||
Sign In
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
<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,
|
||||
Anchor,
|
||||
Stack,
|
||||
Alert,
|
||||
Center,
|
||||
} from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { useToggle, upperFirst } from "@mantine/hooks";
|
||||
import { useSession } from "@supabase/auth-helpers-react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { AiOutlineGithub, AiOutlineGoogle } from "react-icons/ai";
|
||||
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";
|
||||
import useUser from "src/store/useUser";
|
||||
@ -29,59 +26,26 @@ import useUser from "src/store/useUser";
|
||||
export function AuthenticationForm(props: PaperProps) {
|
||||
const setSession = useUser(state => state.setSession);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [type, toggle] = useToggle<"login" | "register">(["login", "register"]);
|
||||
const [done, setDone] = React.useState(false);
|
||||
|
||||
const form = useForm({
|
||||
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 [userData, setUserData] = React.useState({
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const validate = form.validate();
|
||||
|
||||
if (validate.hasErrors) return;
|
||||
|
||||
setLoading(true);
|
||||
if (type === "login") {
|
||||
supabase.auth
|
||||
.signInWithPassword({
|
||||
email: form.values.email,
|
||||
password: form.values.password,
|
||||
})
|
||||
.then(({ data, error }) => {
|
||||
if (error) return toast.error(error.message);
|
||||
setSession(data.session);
|
||||
})
|
||||
.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));
|
||||
}
|
||||
|
||||
supabase.auth
|
||||
.signInWithPassword({
|
||||
email: userData.email,
|
||||
password: userData.password,
|
||||
})
|
||||
.then(({ data, error }) => {
|
||||
if (error) return toast.error(error.message);
|
||||
setSession(data.session);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
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 (
|
||||
<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">
|
||||
<Button
|
||||
radius="sm"
|
||||
size="md"
|
||||
radius="md"
|
||||
leftIcon={<AiOutlineGoogle size="20" />}
|
||||
onClick={() => handleLoginClick("google")}
|
||||
color="red"
|
||||
variant="outline"
|
||||
>
|
||||
Sign In with Google
|
||||
Sign in with Google
|
||||
</Button>
|
||||
<Button
|
||||
radius="sm"
|
||||
size="md"
|
||||
radius="md"
|
||||
leftIcon={<AiOutlineGithub size="20" />}
|
||||
onClick={() => handleLoginClick("github")}
|
||||
color="dark"
|
||||
variant="outline"
|
||||
>
|
||||
Sign In with GitHub
|
||||
Sign in with GitHub
|
||||
</Button>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@ -241,7 +145,7 @@ function ResetPassword(props: PaperProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper radius="sm" {...props}>
|
||||
<Paper radius="md" {...props}>
|
||||
<Text size="lg" weight={500}>
|
||||
Reset Password
|
||||
</Text>
|
||||
@ -253,14 +157,14 @@ function ResetPassword(props: PaperProps) {
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
required
|
||||
label="Password"
|
||||
radius="sm"
|
||||
radius="md"
|
||||
/>
|
||||
<PasswordInput
|
||||
value={password2}
|
||||
onChange={e => setPassword2(e.target.value)}
|
||||
required
|
||||
label="Validate Password"
|
||||
radius="sm"
|
||||
radius="md"
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@ -278,7 +182,6 @@ const SignIn = () => {
|
||||
const { isReady, push, query } = useRouter();
|
||||
const session = useSession();
|
||||
const isPasswordReset = query?.type === "recovery" && !query?.error;
|
||||
const [alertVisible, setAlertVisible] = React.useState(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isReady && session && !isPasswordReset) {
|
||||
@ -292,27 +195,15 @@ const SignIn = () => {
|
||||
<title>Sign In - JSON Crack</title>
|
||||
</Head>
|
||||
<Navbar />
|
||||
{alertVisible && (
|
||||
<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 />
|
||||
<Paper mt={50} shadow="xs" mx="auto" maw={400} p="lg" withBorder>
|
||||
{isPasswordReset ? <ResetPassword /> : <AuthenticationForm />}
|
||||
</Paper>
|
||||
<Center my="xl">
|
||||
<Anchor component={Link} prefetch={false} href="/sign-up" color="dark" fw="bold">
|
||||
Don't have an account?
|
||||
</Anchor>
|
||||
</Center>
|
||||
|
||||
<Footer />
|
||||
</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-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":
|
||||
version "6.0.21"
|
||||
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"
|
||||
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:
|
||||
version "0.3.22"
|
||||
resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz"
|
||||
|
Loading…
x
Reference in New Issue
Block a user