forked from github/plane
style: auth screens (#478)
* style: sign in page * style: github and google sign * style: sign with code and password * style: not a member and not authorized for project setting * style: join project icon * chore: comment removed
This commit is contained in:
parent
68150a9d2b
commit
b96d40f106
@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
// ui
|
||||
import { CheckCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { Input, SecondaryButton } from "components/ui";
|
||||
import { Input, PrimaryButton, SecondaryButton } from "components/ui";
|
||||
// services
|
||||
import authenticationService from "services/authentication.service";
|
||||
import useToast from "hooks/use-toast";
|
||||
@ -90,7 +90,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<form className="mt-5 space-y-5">
|
||||
<form className="space-y-5 py-5 px-5">
|
||||
{(codeSent || codeResent) && (
|
||||
<div className="rounded-md bg-green-50 p-4">
|
||||
<div className="flex">
|
||||
@ -121,7 +121,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
||||
) || "Email ID is not valid",
|
||||
}}
|
||||
error={errors.email}
|
||||
placeholder="Enter your Email ID"
|
||||
placeholder="Enter you email Id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -169,18 +169,20 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
||||
)}
|
||||
<div>
|
||||
{codeSent ? (
|
||||
<SecondaryButton
|
||||
<PrimaryButton
|
||||
type="submit"
|
||||
className="w-full text-center"
|
||||
size="md"
|
||||
onClick={handleSubmit(handleSignin)}
|
||||
loading={isSubmitting || (!isValid && isDirty)}
|
||||
>
|
||||
{isSubmitting ? "Signing in..." : "Sign in"}
|
||||
</SecondaryButton>
|
||||
</PrimaryButton>
|
||||
) : (
|
||||
<SecondaryButton
|
||||
<PrimaryButton
|
||||
type="submit"
|
||||
className="w-full text-center"
|
||||
size="md"
|
||||
onClick={() => {
|
||||
handleSubmit(onSubmit)().then(() => {
|
||||
setResendCodeTimer(30);
|
||||
@ -189,7 +191,7 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
|
||||
loading={isSubmitting || (!isValid && isDirty)}
|
||||
>
|
||||
{isSubmitting ? "Sending code..." : "Send code"}
|
||||
</SecondaryButton>
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
@ -60,7 +60,7 @@ export const EmailPasswordForm = ({ onSuccess }: any) => {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<form className="mt-5" onSubmit={handleSubmit(onSubmit)}>
|
||||
<form className="mt-5 py-5 px-5" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div>
|
||||
<Input
|
||||
id="email"
|
||||
|
@ -19,28 +19,6 @@ export const EmailSignInForm: FC<EmailSignInFormProps> = (props) => {
|
||||
) : (
|
||||
<EmailPasswordForm onSuccess={handleSuccess} />
|
||||
)}
|
||||
<div className="mt-6">
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-gray-300" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="bg-white px-2 text-gray-500">or</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="mt-6 flex w-full flex-col items-stretch gap-y-2">
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center rounded border border-gray-300 px-3 py-2 text-sm duration-300 hover:bg-gray-100"
|
||||
onClick={() => setUseCode((prev) => !prev)}
|
||||
>
|
||||
<KeyIcon className="h-[25px] w-[25px]" />
|
||||
<span className="w-full text-center font-medium">
|
||||
{useCode ? "Continue with Password" : "Continue with Code"}
|
||||
</span>
|
||||
</button>
|
||||
</div> */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
// images
|
||||
import githubImage from "/public/logos/github.png";
|
||||
import githubImage from "/public/logos/github-black.png";
|
||||
|
||||
const { NEXT_PUBLIC_GITHUB_ID } = process.env;
|
||||
|
||||
@ -33,19 +33,15 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="px-1 w-full">
|
||||
<Link
|
||||
href={`https://github.com/login/oauth/authorize?client_id=${NEXT_PUBLIC_GITHUB_ID}&redirect_uri=${loginCallBackURL}&scope=read:user,user:email`}
|
||||
>
|
||||
<button className="flex w-full items-center rounded bg-black px-3 py-2 text-sm text-white opacity-90 duration-300 hover:opacity-100">
|
||||
<Image
|
||||
src={githubImage}
|
||||
height={25}
|
||||
width={25}
|
||||
className="flex-shrink-0"
|
||||
alt="GitHub Logo"
|
||||
/>
|
||||
<span className="w-full text-center font-medium">Continue with GitHub</span>
|
||||
<button className="flex w-full items-center justify-center gap-3 rounded-md border border-gray-200 p-2 text-sm font-medium text-gray-600 duration-300 hover:bg-gray-50">
|
||||
<Image src={githubImage} height={22} width={22} color="#000" alt="GitHub Logo" />
|
||||
<span>Sign In with Github</span>
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ export const GoogleLoginButton: FC<IGoogleLoginButton> = (props) => {
|
||||
theme: "outline",
|
||||
size: "large",
|
||||
logo_alignment: "center",
|
||||
width: document.getElementById("googleSignInButton")?.offsetWidth,
|
||||
width: "410",
|
||||
text: "continue_with",
|
||||
} as GsiButtonConfiguration // customization attributes
|
||||
);
|
||||
@ -47,7 +47,7 @@ export const GoogleLoginButton: FC<IGoogleLoginButton> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://accounts.google.com/gsi/client" async defer onLoad={loadScript} />
|
||||
<div className="w-full" id="googleSignInButton" ref={googleSignInButton} />
|
||||
<div className="h-12" id="googleSignInButton" ref={googleSignInButton} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,13 +1,14 @@
|
||||
import React from "react";
|
||||
// next
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// icons
|
||||
import { LockIcon } from "components/icons";
|
||||
// img
|
||||
import ProjectSettingImg from "public/project-setting.svg";
|
||||
|
||||
type TNotAuthorizedViewProps = {
|
||||
actionButton?: React.ReactNode;
|
||||
@ -27,25 +28,27 @@ export const NotAuthorizedView: React.FC<TNotAuthorizedViewProps> = (props) => {
|
||||
}}
|
||||
>
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-y-5 text-center">
|
||||
<LockIcon className="h-16 w-16 text-gray-400" />
|
||||
<div className="h-44 w-72">
|
||||
<Image src={ProjectSettingImg} height="176" width="288" alt="ProjectSettingImg" />
|
||||
</div>
|
||||
<h1 className="text-xl font-medium text-gray-900">
|
||||
Oops! You are not authorized to view this page
|
||||
</h1>
|
||||
|
||||
<div className="w-full md:w-1/3">
|
||||
<div className="w-full text-base text-gray-500 max-w-md ">
|
||||
{user ? (
|
||||
<p className="text-base font-light">
|
||||
You have signed in as <span className="font-medium">{user.email}</span>.{" "}
|
||||
<p className="">
|
||||
You have signed in as {user.email}.{" "}
|
||||
<Link href={`/signin?next=${currentPath}`}>
|
||||
<a className="font-medium">Sign in</a>
|
||||
<a className="text-gray-900 font-medium">Sign in</a>
|
||||
</Link>{" "}
|
||||
with different account that has access to this page.
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-base font-light">
|
||||
<p className="">
|
||||
You need to{" "}
|
||||
<Link href={`/signin?next=${currentPath}`}>
|
||||
<a className="font-medium">Sign in</a>
|
||||
<a className="text-gray-900 font-medium">Sign in</a>
|
||||
</Link>{" "}
|
||||
with an account that has access to this page.
|
||||
</p>
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { FC } from "react";
|
||||
// next
|
||||
import Image from "next/image";
|
||||
|
||||
// ui
|
||||
import { SecondaryButton } from "components/ui";
|
||||
import { PrimaryButton } from "components/ui";
|
||||
// icon
|
||||
import { AssignmentClipboardIcon } from "components/icons";
|
||||
// img
|
||||
import JoinProjectImg from "public/join-project.svg";
|
||||
|
||||
export interface JoinProjectProps {
|
||||
isJoiningProject: boolean;
|
||||
@ -9,18 +15,27 @@ export interface JoinProjectProps {
|
||||
}
|
||||
|
||||
export const JoinProject: FC<JoinProjectProps> = ({ isJoiningProject, handleJoin }) => (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<div className="space-y-4 text-center">
|
||||
<h1 className="text-2xl font-bold">You are not a member of this project</h1>
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-y-5 text-center">
|
||||
<div className="h-44 w-72">
|
||||
<Image src={JoinProjectImg} height="176" width="288" alt="JoinProject" />
|
||||
</div>
|
||||
<h1 className="text-xl font-medium text-gray-900">You are not a member of this project</h1>
|
||||
|
||||
<div className="w-full max-w-md text-base text-gray-500 ">
|
||||
<p className="mx-auto w-full text-sm md:w-3/4">
|
||||
You are not a member of this project, but you can join this project by clicking the button
|
||||
below.
|
||||
</p>
|
||||
<div>
|
||||
<SecondaryButton loading={isJoiningProject} onClick={handleJoin}>
|
||||
{isJoiningProject ? "Joining..." : "Click to join"}
|
||||
</SecondaryButton>
|
||||
</div>
|
||||
<div>
|
||||
<PrimaryButton
|
||||
className="flex items-center gap-1"
|
||||
loading={isJoiningProject}
|
||||
onClick={handleJoin}
|
||||
>
|
||||
<AssignmentClipboardIcon height={16} width={16} color="white" />
|
||||
{isJoiningProject ? "Joining..." : "Click to join"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -10,7 +10,9 @@ import projectService from "services/project.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// ui
|
||||
import { SecondaryButton, Spinner } from "components/ui";
|
||||
import { PrimaryButton, Spinner } from "components/ui";
|
||||
// icon
|
||||
import { LayerDiagonalIcon } from "components/icons";
|
||||
// components
|
||||
import { NotAuthorizedView } from "components/core";
|
||||
import { CommandPalette } from "components/command-palette";
|
||||
@ -103,13 +105,17 @@ const AppLayout: FC<AppLayoutProps> = ({
|
||||
actionButton={
|
||||
(memberType?.isViewer || memberType?.isGuest) && projectId ? (
|
||||
<Link href={`/${workspaceSlug}/projects/${projectId}/issues`}>
|
||||
<SecondaryButton>Go to Issues</SecondaryButton>
|
||||
<PrimaryButton className="flex items-center gap-1">
|
||||
<LayerDiagonalIcon height={16} width={16} color="white" /> Go to Issues
|
||||
</PrimaryButton>
|
||||
</Link>
|
||||
) : (
|
||||
(memberType?.isViewer || memberType?.isGuest) &&
|
||||
workspaceSlug && (
|
||||
<Link href={`/${workspaceSlug}`}>
|
||||
<SecondaryButton>Go to workspace</SecondaryButton>
|
||||
<PrimaryButton className="flex items-center gap-1">
|
||||
<LayerDiagonalIcon height={16} width={16} color="white" /> Go to workspace
|
||||
</PrimaryButton>
|
||||
</Link>
|
||||
)
|
||||
)
|
||||
|
@ -20,7 +20,7 @@ import {
|
||||
// ui
|
||||
import { Spinner } from "components/ui";
|
||||
// icons
|
||||
import Logo from "public/logo-with-text.png";
|
||||
import Logo from "public/logo.png";
|
||||
// types
|
||||
import type { NextPage } from "next";
|
||||
|
||||
@ -105,31 +105,28 @@ const SignInPage: NextPage = () => {
|
||||
)}
|
||||
<div className="flex h-screen w-full items-center justify-center overflow-auto bg-gray-50">
|
||||
<div className="flex min-h-full w-full flex-col justify-center py-12 px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="text-center">
|
||||
<Image src={Logo} height={40} width={179} alt="Plane Web Logo" />
|
||||
</div>
|
||||
<h2 className="mt-3 text-center text-3xl font-bold text-gray-900">
|
||||
Sign in to your account
|
||||
<div className="flex flex-col gap-10 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex flex-col items-center justify-center gap-10">
|
||||
<Image src={Logo} height={80} width={80} alt="Plane Web Logo" />
|
||||
<h2 className="text-center text-xl font-medium text-black">
|
||||
Sign In to your Plane Account
|
||||
</h2>
|
||||
<div className="mt-16 bg-white py-8 px-4 sm:rounded-lg sm:px-10">
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col rounded-[10px] bg-white shadow-md">
|
||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
||||
<>
|
||||
<div className="mb-4">
|
||||
<EmailSignInForm handleSuccess={onSignInSuccess} />
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
|
||||
<div className="flex flex-col gap-3 py-5 px-5 border-t items-center justify-center border-gray-300 ">
|
||||
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
|
||||
<GithubLoginButton handleSignIn={handleGithubSignIn} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mb-4">
|
||||
<EmailPasswordForm onSuccess={onSignInSuccess} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
52
apps/app/public/join-project.svg
Normal file
52
apps/app/public/join-project.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 27 KiB |
44
apps/app/public/project-setting.svg
Normal file
44
apps/app/public/project-setting.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 33 KiB |
Loading…
Reference in New Issue
Block a user