mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: login process validation based on api config (#2361)
This commit is contained in:
parent
48c65c9c95
commit
ea2c1e2d06
@ -1,12 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
// react hook form
|
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
// components
|
|
||||||
import { EmailResetPasswordForm } from "components/account";
|
|
||||||
// ui
|
// ui
|
||||||
import { Input, PrimaryButton } from "components/ui";
|
import { Input, PrimaryButton } from "components/ui";
|
||||||
// types
|
// types
|
||||||
@ -18,14 +11,12 @@ type EmailPasswordFormValues = {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onSubmit: (formData: EmailPasswordFormValues) => Promise<void>;
|
onSubmit: (formData: EmailPasswordFormValues) => Promise<void>;
|
||||||
|
setIsResettingPassword: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
export const EmailPasswordForm: React.FC<Props> = (props) => {
|
||||||
const [isResettingPassword, setIsResettingPassword] = useState(false);
|
const { onSubmit, setIsResettingPassword } = props;
|
||||||
|
// form info
|
||||||
const router = useRouter();
|
|
||||||
const isSignUpPage = router.pathname === "/sign-up";
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -42,16 +33,6 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
|
||||||
{isResettingPassword
|
|
||||||
? "Reset your password"
|
|
||||||
: isSignUpPage
|
|
||||||
? "Sign up on Plane"
|
|
||||||
: "Sign in to Plane"}
|
|
||||||
</h1>
|
|
||||||
{isResettingPassword ? (
|
|
||||||
<EmailResetPasswordForm setIsResettingPassword={setIsResettingPassword} />
|
|
||||||
) : (
|
|
||||||
<form
|
<form
|
||||||
className="space-y-4 mt-10 w-full sm:w-[360px] mx-auto"
|
className="space-y-4 mt-10 w-full sm:w-[360px] mx-auto"
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
@ -89,13 +70,6 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right text-xs">
|
<div className="text-right text-xs">
|
||||||
{isSignUpPage ? (
|
|
||||||
<Link href="/">
|
|
||||||
<a className="text-custom-text-200 hover:text-custom-primary-100">
|
|
||||||
Already have an account? Sign in.
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsResettingPassword(true)}
|
onClick={() => setIsResettingPassword(true)}
|
||||||
@ -103,7 +77,6 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
|||||||
>
|
>
|
||||||
Forgot your password?
|
Forgot your password?
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
@ -112,24 +85,10 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
|
|||||||
disabled={!isValid && isDirty}
|
disabled={!isValid && isDirty}
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
{isSignUpPage
|
{isSubmitting ? "Signing in..." : "Sign in"}
|
||||||
? isSubmitting
|
|
||||||
? "Signing up..."
|
|
||||||
: "Sign up"
|
|
||||||
: isSubmitting
|
|
||||||
? "Signing in..."
|
|
||||||
: "Sign in"}
|
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
{!isSignUpPage && (
|
|
||||||
<Link href="/sign-up">
|
|
||||||
<a className="block text-custom-text-200 hover:text-custom-primary-100 text-xs mt-4">
|
|
||||||
Don{"'"}t have an account? Sign up.
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
114
web/components/account/email-signup-form.tsx
Normal file
114
web/components/account/email-signup-form.tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
// ui
|
||||||
|
import { Input, PrimaryButton } from "components/ui";
|
||||||
|
// types
|
||||||
|
type EmailPasswordFormValues = {
|
||||||
|
email: string;
|
||||||
|
password?: string;
|
||||||
|
confirm_password: string;
|
||||||
|
medium?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onSubmit: (formData: EmailPasswordFormValues) => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmailSignUpForm: React.FC<Props> = (props) => {
|
||||||
|
const { onSubmit } = props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
watch,
|
||||||
|
formState: { errors, isSubmitting, isValid, isDirty },
|
||||||
|
} = useForm<EmailPasswordFormValues>({
|
||||||
|
defaultValues: {
|
||||||
|
email: "",
|
||||||
|
password: "",
|
||||||
|
confirm_password: "",
|
||||||
|
medium: "email",
|
||||||
|
},
|
||||||
|
mode: "onChange",
|
||||||
|
reValidateMode: "onChange",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form
|
||||||
|
className="space-y-4 mt-10 w-full sm:w-[360px] mx-auto"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
register={register}
|
||||||
|
validations={{
|
||||||
|
required: "Email address is required",
|
||||||
|
validate: (value) =>
|
||||||
|
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
||||||
|
value
|
||||||
|
) || "Email address is not valid",
|
||||||
|
}}
|
||||||
|
error={errors.email}
|
||||||
|
placeholder="Enter your email address..."
|
||||||
|
className="border-custom-border-300 h-[46px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
register={register}
|
||||||
|
validations={{
|
||||||
|
required: "Password is required",
|
||||||
|
}}
|
||||||
|
error={errors.password}
|
||||||
|
placeholder="Enter your password..."
|
||||||
|
className="border-custom-border-300 h-[46px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Input
|
||||||
|
id="confirm_password"
|
||||||
|
type="password"
|
||||||
|
name="confirm_password"
|
||||||
|
register={register}
|
||||||
|
validations={{
|
||||||
|
required: "Password is required",
|
||||||
|
validate: (val: string) => {
|
||||||
|
if (watch("password") != val) {
|
||||||
|
return "Your passwords do no match";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
error={errors.confirm_password}
|
||||||
|
placeholder="Confirm your password..."
|
||||||
|
className="border-custom-border-300 h-[46px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-right text-xs">
|
||||||
|
<Link href="/">
|
||||||
|
<a className="text-custom-text-200 hover:text-custom-primary-100">
|
||||||
|
Already have an account? Sign in.
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<PrimaryButton
|
||||||
|
type="submit"
|
||||||
|
className="w-full text-center h-[46px]"
|
||||||
|
disabled={!isValid && isDirty}
|
||||||
|
loading={isSubmitting}
|
||||||
|
>
|
||||||
|
{isSubmitting ? "Signing up..." : "Sign up"}
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -1,29 +1,27 @@
|
|||||||
import { useEffect, useState, FC } from "react";
|
import { useEffect, useState, FC } from "react";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
// next-themes
|
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// images
|
// images
|
||||||
import githubBlackImage from "/public/logos/github-black.png";
|
import githubBlackImage from "/public/logos/github-black.png";
|
||||||
import githubWhiteImage from "/public/logos/github-white.png";
|
import githubWhiteImage from "/public/logos/github-white.png";
|
||||||
|
|
||||||
const { NEXT_PUBLIC_GITHUB_ID } = process.env;
|
|
||||||
|
|
||||||
export interface GithubLoginButtonProps {
|
export interface GithubLoginButtonProps {
|
||||||
handleSignIn: React.Dispatch<string>;
|
handleSignIn: React.Dispatch<string>;
|
||||||
|
clientId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GithubLoginButton: FC<GithubLoginButtonProps> = ({ handleSignIn }) => {
|
export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
|
||||||
|
const { handleSignIn, clientId } = props;
|
||||||
|
// states
|
||||||
const [loginCallBackURL, setLoginCallBackURL] = useState(undefined);
|
const [loginCallBackURL, setLoginCallBackURL] = useState(undefined);
|
||||||
const [gitCode, setGitCode] = useState<null | string>(null);
|
const [gitCode, setGitCode] = useState<null | string>(null);
|
||||||
|
// router
|
||||||
const {
|
const {
|
||||||
query: { code },
|
query: { code },
|
||||||
} = useRouter();
|
} = useRouter();
|
||||||
|
// theme
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -42,7 +40,7 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = ({ handleSignIn })
|
|||||||
return (
|
return (
|
||||||
<div className="w-full flex justify-center items-center">
|
<div className="w-full flex justify-center items-center">
|
||||||
<Link
|
<Link
|
||||||
href={`https://github.com/login/oauth/authorize?client_id=${NEXT_PUBLIC_GITHUB_ID}&redirect_uri=${loginCallBackURL}&scope=read:user,user:email`}
|
href={`https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${loginCallBackURL}&scope=read:user,user:email`}
|
||||||
>
|
>
|
||||||
<button className="flex w-full items-center justify-center gap-2 rounded border border-custom-border-300 p-2 text-sm font-medium text-custom-text-100 duration-300 hover:bg-custom-background-80 h-[46px]">
|
<button className="flex w-full items-center justify-center gap-2 rounded border border-custom-border-300 p-2 text-sm font-medium text-custom-text-100 duration-300 hover:bg-custom-background-80 h-[46px]">
|
||||||
<Image
|
<Image
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
import { FC, CSSProperties, useEffect, useRef, useCallback, useState } from "react";
|
import { FC, useEffect, useRef, useCallback, useState } from "react";
|
||||||
|
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
|
|
||||||
export interface IGoogleLoginButton {
|
export interface IGoogleLoginButton {
|
||||||
text?: string;
|
|
||||||
handleSignIn: React.Dispatch<any>;
|
handleSignIn: React.Dispatch<any>;
|
||||||
styles?: CSSProperties;
|
clientId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GoogleLoginButton: FC<IGoogleLoginButton> = ({ handleSignIn }) => {
|
export const GoogleLoginButton: FC<IGoogleLoginButton> = (props) => {
|
||||||
|
const { handleSignIn, clientId } = props;
|
||||||
|
// refs
|
||||||
const googleSignInButton = useRef<HTMLDivElement>(null);
|
const googleSignInButton = useRef<HTMLDivElement>(null);
|
||||||
|
// states
|
||||||
const [gsiScriptLoaded, setGsiScriptLoaded] = useState(false);
|
const [gsiScriptLoaded, setGsiScriptLoaded] = useState(false);
|
||||||
|
|
||||||
const loadScript = useCallback(() => {
|
const loadScript = useCallback(() => {
|
||||||
if (!googleSignInButton.current || gsiScriptLoaded) return;
|
if (!googleSignInButton.current || gsiScriptLoaded) return;
|
||||||
|
|
||||||
window?.google?.accounts.id.initialize({
|
window?.google?.accounts.id.initialize({
|
||||||
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENTID || "",
|
client_id: clientId,
|
||||||
callback: handleSignIn,
|
callback: handleSignIn,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ export const GoogleLoginButton: FC<IGoogleLoginButton> = ({ handleSignIn }) => {
|
|||||||
window?.google?.accounts.id.prompt(); // also display the One Tap dialog
|
window?.google?.accounts.id.prompt(); // also display the One Tap dialog
|
||||||
|
|
||||||
setGsiScriptLoaded(true);
|
setGsiScriptLoaded(true);
|
||||||
}, [handleSignIn, gsiScriptLoaded]);
|
}, [handleSignIn, gsiScriptLoaded, clientId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window?.google?.accounts?.id) {
|
if (window?.google?.accounts?.id) {
|
||||||
|
@ -3,3 +3,4 @@ export * from "./email-password-form";
|
|||||||
export * from "./email-reset-password-form";
|
export * from "./email-reset-password-form";
|
||||||
export * from "./github-login-button";
|
export * from "./github-login-button";
|
||||||
export * from "./google-login";
|
export * from "./google-login";
|
||||||
|
export * from "./email-signup-form";
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
// layouts
|
// layouts
|
||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// services
|
// services
|
||||||
import authenticationService from "services/authentication.service";
|
import authenticationService from "services/authentication.service";
|
||||||
|
import { AppConfigService } from "services/app-config.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useUserAuth from "hooks/use-user-auth";
|
import useUserAuth from "hooks/use-user-auth";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
@ -17,19 +18,19 @@ import {
|
|||||||
GithubLoginButton,
|
GithubLoginButton,
|
||||||
EmailCodeForm,
|
EmailCodeForm,
|
||||||
EmailPasswordForm,
|
EmailPasswordForm,
|
||||||
|
EmailResetPasswordForm,
|
||||||
} from "components/account";
|
} from "components/account";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// images
|
// images
|
||||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||||
// mobx react lite
|
|
||||||
import { observer } from "mobx-react-lite";
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// next themes
|
// types
|
||||||
import { useTheme } from "next-themes";
|
|
||||||
import { IUser } from "types";
|
import { IUser } from "types";
|
||||||
|
|
||||||
|
const appConfig = new AppConfigService();
|
||||||
|
|
||||||
// types
|
// types
|
||||||
type EmailPasswordFormValues = {
|
type EmailPasswordFormValues = {
|
||||||
email: string;
|
email: string;
|
||||||
@ -39,11 +40,16 @@ type EmailPasswordFormValues = {
|
|||||||
|
|
||||||
const HomePage: NextPage = observer(() => {
|
const HomePage: NextPage = observer(() => {
|
||||||
const store: any = useMobxStore();
|
const store: any = useMobxStore();
|
||||||
|
// theme
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
// user
|
||||||
const { isLoading, mutateUser } = useUserAuth("sign-in");
|
const { isLoading, mutateUser } = useUserAuth("sign-in");
|
||||||
|
// states
|
||||||
|
const [isResettingPassword, setIsResettingPassword] = useState(false);
|
||||||
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
// fetch app config
|
||||||
|
const { data } = useSWR("APP_CONFIG", () => appConfig.envConfig());
|
||||||
|
|
||||||
const handleTheme = (user: IUser) => {
|
const handleTheme = (user: IUser) => {
|
||||||
const currentTheme = user.theme.theme ?? "system";
|
const currentTheme = user.theme.theme ?? "system";
|
||||||
@ -173,26 +179,43 @@ const HomePage: NextPage = observer(() => {
|
|||||||
</>
|
</>
|
||||||
<div className="grid place-items-center h-full overflow-y-auto py-5 px-7">
|
<div className="grid place-items-center h-full overflow-y-auto py-5 px-7">
|
||||||
<div>
|
<div>
|
||||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
|
||||||
<>
|
|
||||||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
||||||
Sign in to Plane
|
{isResettingPassword ? "Reset your password" : "Sign in to Plane"}
|
||||||
</h1>
|
</h1>
|
||||||
|
{isResettingPassword ? (
|
||||||
|
<EmailResetPasswordForm setIsResettingPassword={setIsResettingPassword} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{data?.email_password_login && (
|
||||||
|
<EmailPasswordForm
|
||||||
|
onSubmit={handlePasswordSignIn}
|
||||||
|
setIsResettingPassword={setIsResettingPassword}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{data?.magic_login && (
|
||||||
<div className="flex flex-col divide-y divide-custom-border-200">
|
<div className="flex flex-col divide-y divide-custom-border-200">
|
||||||
<div className="pb-7">
|
<div className="pb-7">
|
||||||
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
|
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
|
|
||||||
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
|
||||||
<GithubLoginButton handleSignIn={handleGitHubSignIn} />
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
|
||||||
|
{data?.google && (
|
||||||
|
<GoogleLoginButton
|
||||||
|
clientId={data?.google}
|
||||||
|
handleSignIn={handleGoogleSignIn}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{data?.github && (
|
||||||
|
<GithubLoginButton
|
||||||
|
clientId={data?.github}
|
||||||
|
handleSignIn={handleGitHubSignIn}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<EmailPasswordForm onSubmit={handlePasswordSignIn} />
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
|
||||||
<p className="pt-16 text-custom-text-200 text-sm text-center">
|
<p className="pt-16 text-custom-text-200 text-sm text-center">
|
||||||
By signing up, you agree to the{" "}
|
By signing up, you agree to the{" "}
|
||||||
<a
|
<a
|
||||||
@ -204,7 +227,6 @@ const HomePage: NextPage = observer(() => {
|
|||||||
Terms & Conditions
|
Terms & Conditions
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
// next-themes
|
// next-themes
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// services
|
// services
|
||||||
@ -13,9 +11,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// layouts
|
// layouts
|
||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// components
|
// components
|
||||||
import { EmailPasswordForm } from "components/account";
|
import { EmailPasswordForm, EmailSignUpForm } from "components/account";
|
||||||
// ui
|
|
||||||
import { Spinner } from "components/ui";
|
|
||||||
// images
|
// images
|
||||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||||
// types
|
// types
|
||||||
@ -27,8 +23,6 @@ type EmailPasswordFormValues = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SignUp: NextPage = () => {
|
const SignUp: NextPage = () => {
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
@ -70,18 +64,6 @@ const SignUp: NextPage = () => {
|
|||||||
setTheme("system");
|
setTheme("system");
|
||||||
}, [setTheme]);
|
}, [setTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
|
||||||
else setIsLoading(false);
|
|
||||||
}, [router]);
|
|
||||||
|
|
||||||
if (isLoading)
|
|
||||||
return (
|
|
||||||
<div className="grid place-items-center h-screen w-full">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<>
|
<>
|
||||||
@ -96,7 +78,8 @@ const SignUp: NextPage = () => {
|
|||||||
</>
|
</>
|
||||||
<div className="grid place-items-center h-full w-full overflow-y-auto py-5 px-7">
|
<div className="grid place-items-center h-full w-full overflow-y-auto py-5 px-7">
|
||||||
<div>
|
<div>
|
||||||
<EmailPasswordForm onSubmit={handleSignUp} />
|
<h1 className="text-2xl text-center font-">SignUp on Plane</h1>
|
||||||
|
<EmailSignUpForm onSubmit={handleSignUp} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
30
web/services/app-config.service.ts
Normal file
30
web/services/app-config.service.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// services
|
||||||
|
import APIService from "services/api.service";
|
||||||
|
// helper
|
||||||
|
import { API_BASE_URL } from "helpers/common.helper";
|
||||||
|
|
||||||
|
export interface IEnvConfig {
|
||||||
|
github: string;
|
||||||
|
google: string;
|
||||||
|
github_app_name: string | null;
|
||||||
|
email_password_login: boolean;
|
||||||
|
magic_login: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppConfigService extends APIService {
|
||||||
|
constructor() {
|
||||||
|
super(API_BASE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
async envConfig(): Promise<IEnvConfig> {
|
||||||
|
return this.get("/api/configs/", {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => response?.data)
|
||||||
|
.catch((error) => {
|
||||||
|
throw error?.response?.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user