Authentication Workflow fixes. Redirection fixes (#832)
* auth integration fixes * auth integration fixes * auth integration fixes * auth integration fixes * dev: update user api to return fallback workspace and improve the structure of the response * dev: fix the issue keyerror and move onboarding logic to serializer method field * dev: use-user-auth hook imlemented for route access validation and build issues resolved effected by user payload * fix: global theme color fix * style: new onboarding ui , fix: use-user-auth hook implemented * fix: command palette, project invite modal and issue detail page mutation type fix * fix: onboarding redirection fix * dev: build isuue resolved * fix: use user auth hook fix * fix: sign in toast alert fix, sign out redirection fix and user theme error fix * fix: user response fix * fix: unAuthorizedStatus logic updated --------- Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: anmolsinghbhatia <anmolsinghbhatia@caravel.tech>
This commit is contained in:
parent
33db616767
commit
44f8ba407d
43 changed files with 821 additions and 593 deletions
|
|
@ -8,7 +8,7 @@ import { Controller, useForm } from "react-hook-form";
|
|||
import fileService from "services/file.service";
|
||||
import userService from "services/user.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
import useToast from "hooks/use-toast";
|
||||
// layouts
|
||||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
|
|
@ -50,7 +50,7 @@ const Profile: NextPage = () => {
|
|||
} = useForm<IUser>({ defaultValues });
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
const { user: myProfile, mutateUser } = useUser();
|
||||
const { user: myProfile, mutateUser } = useUserAuth();
|
||||
|
||||
useEffect(() => {
|
||||
reset({ ...defaultValues, ...myProfile });
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
|||
import { useTheme } from "next-themes";
|
||||
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// layouts
|
||||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
import SettingsNavbar from "layouts/settings-navbar";
|
||||
|
|
@ -15,7 +15,7 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
|||
import { ICustomTheme } from "types";
|
||||
|
||||
const ProfilePreferences = () => {
|
||||
const { user: myProfile } = useUser();
|
||||
const { user: myProfile } = useUserAuth();
|
||||
const { theme } = useTheme();
|
||||
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
|
||||
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
|
||||
|
|
|
|||
|
|
@ -78,12 +78,15 @@ const IssueDetailsPage: NextPage = () => {
|
|||
async (formData: Partial<IIssue>) => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
mutate(
|
||||
mutate<IIssue>(
|
||||
ISSUE_DETAILS(issueId as string),
|
||||
(prevData: IIssue) => ({
|
||||
...prevData,
|
||||
...formData,
|
||||
}),
|
||||
(prevData) => {
|
||||
if (!prevData) return prevData;
|
||||
return {
|
||||
...prevData,
|
||||
...formData,
|
||||
};
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -44,31 +44,14 @@ Router.events.on("routeChangeComplete", NProgress.done);
|
|||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<ThemeProvider themes={THEMES} defaultTheme="light">
|
||||
<UserProvider>
|
||||
<ToastContextProvider>
|
||||
<ThemeContextProvider>
|
||||
<CrispWithNoSSR />{" "}
|
||||
<Head>
|
||||
<title>{SITE_TITLE}</title>
|
||||
<meta property="og:site_name" content={SITE_NAME} />
|
||||
<meta property="og:title" content={SITE_TITLE} />
|
||||
<meta property="og:url" content={SITE_URL} />
|
||||
<meta name="description" content={SITE_DESCRIPTION} />
|
||||
<meta property="og:description" content={SITE_DESCRIPTION} />
|
||||
<meta name="keywords" content={SITE_KEYWORDS} />
|
||||
<meta name="twitter:site" content={`@${TWITTER_USER_NAME}`} />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest.json" />
|
||||
<link rel="shortcut icon" href="/favicon/favicon.ico" />
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</ThemeContextProvider>
|
||||
</ToastContextProvider>
|
||||
</UserProvider>
|
||||
</ThemeProvider>
|
||||
// <UserProvider>
|
||||
<ToastContextProvider>
|
||||
<ThemeContextProvider>
|
||||
<CrispWithNoSSR />
|
||||
<Component {...pageProps} />
|
||||
</ThemeContextProvider>
|
||||
</ToastContextProvider>
|
||||
// </UserProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const CustomErrorComponent = () => {
|
|||
message: "Failed to sign out. Please try again.",
|
||||
})
|
||||
)
|
||||
.finally(() => router.push("/signin"));
|
||||
.finally(() => router.push("/"));
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import React from "react";
|
|||
|
||||
import { useRouter } from "next/router";
|
||||
import Image from "next/image";
|
||||
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// components
|
||||
import { OnboardingLogo } from "components/onboarding";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper";
|
||||
|
|
@ -21,19 +24,36 @@ const CreateWorkspace: NextPage = () => {
|
|||
company_size: null,
|
||||
};
|
||||
|
||||
const { user } = useUser();
|
||||
return (
|
||||
<UserAuthorizationLayout>
|
||||
<DefaultLayout>
|
||||
<div className="grid h-full place-items-center p-5">
|
||||
<div className="w-full space-y-4">
|
||||
<div className="mb-8 text-center">
|
||||
<Image src={Logo} height="50" alt="Plane Logo" />
|
||||
<div className="relative grid h-full place-items-center p-5">
|
||||
<div className="h-full flex flex-col items-center justify-center w-full py-4">
|
||||
<div className="mb-7 flex items-center justify-center text-center">
|
||||
<OnboardingLogo className="h-12 w-48 fill-current text-brand-base" />
|
||||
</div>
|
||||
<CreateWorkspaceForm
|
||||
defaultValues={defaultValues}
|
||||
setDefaultValues={() => {}}
|
||||
onSubmit={(res) => router.push(`/${res.slug}`)}
|
||||
/>
|
||||
|
||||
<div className="flex h-[366px] w-full max-w-xl flex-col justify-between rounded-[10px] bg-brand-base shadow-md">
|
||||
<div className="flex items-center justify-start gap-3 px-7 pt-7 pb-3.5 text-gray-8 text-sm">
|
||||
<div className="flex flex-col gap-2 justify-center ">
|
||||
<h3 className="text-base font-semibold text-brand-base">Create Workspace</h3>
|
||||
<p className="text-sm text-brand-secondary">
|
||||
Create or join the workspace to get started with Plane.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<CreateWorkspaceForm
|
||||
defaultValues={defaultValues}
|
||||
setDefaultValues={() => {}}
|
||||
onSubmit={(res) => router.push(`/${res.slug}`)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute flex flex-col gap-1 justify-center items-start left-5 top-5">
|
||||
<span className="text-xs text-brand-secondary">Logged in:</span>
|
||||
<span className="text-sm text-brand-base">{user?.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,66 +1,116 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
import Router from "next/router";
|
||||
|
||||
import React from "react";
|
||||
// next imports
|
||||
import Image from "next/image";
|
||||
// next types
|
||||
import type { NextPage } from "next";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
// hooks
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
import userService from "services/user.service";
|
||||
import workspaceService from "services/workspace.service";
|
||||
import authenticationService from "services/authentication.service";
|
||||
// social auth buttons
|
||||
import {
|
||||
GoogleLoginButton,
|
||||
GithubLoginButton,
|
||||
EmailCodeForm,
|
||||
EmailPasswordForm,
|
||||
} from "components/account";
|
||||
// ui
|
||||
import { Spinner } from "components/ui";
|
||||
// types
|
||||
import type { NextPage } from "next";
|
||||
// icons
|
||||
import Logo from "public/logo.png";
|
||||
|
||||
const redirectUserTo = async () => {
|
||||
const user = await userService
|
||||
.currentUser()
|
||||
.then((res) => res)
|
||||
.catch(() => {
|
||||
Router.push("/signin");
|
||||
return;
|
||||
});
|
||||
const HomePage: NextPage = () => {
|
||||
const { user, isLoading, mutateUser } = useUserAuth("sign-in");
|
||||
|
||||
if (!user) {
|
||||
Router.push("/signin");
|
||||
return;
|
||||
} else if (!user.user.is_onboarded) {
|
||||
Router.push("/onboarding");
|
||||
return;
|
||||
} else {
|
||||
const userWorkspaces = await workspaceService.userWorkspaces();
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const lastActiveWorkspace = userWorkspaces.find(
|
||||
(workspace) => workspace.id === user.user.last_workspace_id
|
||||
);
|
||||
|
||||
if (lastActiveWorkspace) {
|
||||
Router.push(`/${lastActiveWorkspace.slug}`);
|
||||
return;
|
||||
} else if (userWorkspaces.length > 0) {
|
||||
Router.push(`/${userWorkspaces[0].slug}`);
|
||||
return;
|
||||
} else {
|
||||
const invitations = await workspaceService.userWorkspaceInvitations();
|
||||
if (invitations.length > 0) {
|
||||
Router.push(`/invitations`);
|
||||
return;
|
||||
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
||||
try {
|
||||
if (clientId && credential) {
|
||||
mutateUser(
|
||||
await authenticationService.socialAuth({
|
||||
medium: "google",
|
||||
credential,
|
||||
clientId,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
Router.push(`/create-workspace`);
|
||||
return;
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const Home: NextPage = () => {
|
||||
useEffect(() => {
|
||||
redirectUserTo();
|
||||
}, []);
|
||||
const handleGithubSignIn = async (credential: string) => {
|
||||
try {
|
||||
if (process.env.NEXT_PUBLIC_GITHUB_ID && credential) {
|
||||
mutateUser(
|
||||
await authenticationService.socialAuth({
|
||||
medium: "github",
|
||||
credential,
|
||||
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
throw Error("Cant find credentials");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid h-screen place-items-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
<DefaultLayout>
|
||||
{isLoading ? (
|
||||
<div className="grid h-screen place-items-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<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="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" />
|
||||
<div className="text-center text-xl font-medium text-black">
|
||||
Sign In to your Plane Account
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col rounded-[10px] bg-white shadow-md">
|
||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
||||
<>
|
||||
<EmailCodeForm />
|
||||
<div className="flex flex-col gap-3 py-5 px-5 border-t items-center justify-center border-gray-300 ">
|
||||
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
||||
<GithubLoginButton handleSignIn={handleGithubSignIn} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<EmailPasswordForm />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DefaultLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
export default HomePage;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import DefaultLayout from "layouts/default-layout";
|
|||
import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper";
|
||||
// components
|
||||
import SingleInvitation from "components/workspace/single-invitation";
|
||||
import { OnboardingLogo } from "components/onboarding";
|
||||
// ui
|
||||
import { Spinner, EmptySpace, EmptySpaceItem, SecondaryButton, PrimaryButton } from "components/ui";
|
||||
// icons
|
||||
|
|
@ -81,86 +82,93 @@ const OnBoard: NextPage = () => {
|
|||
return (
|
||||
<UserAuthorizationLayout>
|
||||
<DefaultLayout>
|
||||
<div className="flex min-h-full flex-col items-center justify-center p-4 sm:p-0">
|
||||
{user && (
|
||||
<div className="mb-10 w-96 rounded-lg bg-brand-accent/20 p-2 text-brand-accent">
|
||||
<p className="text-center text-sm">logged in as {user.email}</p>
|
||||
<div className="relative grid h-full place-items-center p-5">
|
||||
<div className="h-full flex flex-col items-center justify-center w-full py-4">
|
||||
<div className="mb-7 flex items-center justify-center text-center">
|
||||
<OnboardingLogo className="h-12 w-48 fill-current text-brand-base" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="w-full rounded-lg p-8 md:w-2/3 lg:w-1/3">
|
||||
{invitations && workspaces ? (
|
||||
invitations.length > 0 ? (
|
||||
<div>
|
||||
<h2 className="text-lg font-medium">Workspace Invitations</h2>
|
||||
<p className="mt-1 text-sm text-brand-secondary">
|
||||
Select invites that you want to accept.
|
||||
</p>
|
||||
<ul
|
||||
role="list"
|
||||
className="mt-6 divide-y divide-brand-base border-t border-b border-brand-base"
|
||||
>
|
||||
{invitations.map((invitation) => (
|
||||
<SingleInvitation
|
||||
key={invitation.id}
|
||||
invitation={invitation}
|
||||
invitationsRespond={invitationsRespond}
|
||||
handleInvitation={handleInvitation}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<div className="mt-6 flex items-center gap-2">
|
||||
<Link href="/">
|
||||
<a className="w-full">
|
||||
<SecondaryButton className="w-full">Go Home</SecondaryButton>
|
||||
</a>
|
||||
</Link>
|
||||
<PrimaryButton className="w-full" onClick={submitInvitations}>
|
||||
Accept and Continue
|
||||
</PrimaryButton>
|
||||
<div className="flex h-[436px] w-full max-w-xl rounded-[10px] p-7 bg-brand-base shadow-md">
|
||||
{invitations && workspaces ? (
|
||||
invitations.length > 0 ? (
|
||||
<div className="flex w-full flex-col gap-3 justify-between">
|
||||
<div className="flex flex-col gap-2 justify-center ">
|
||||
<h3 className="text-base font-semibold text-brand-base">
|
||||
Workspace Invitations
|
||||
</h3>
|
||||
<p className="text-sm text-brand-secondary">
|
||||
Create or join the workspace to get started with Plane.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul role="list" className="h-[255px] w-full overflow-y-auto">
|
||||
{invitations.map((invitation) => (
|
||||
<SingleInvitation
|
||||
key={invitation.id}
|
||||
invitation={invitation}
|
||||
invitationsRespond={invitationsRespond}
|
||||
handleInvitation={handleInvitation}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Link href="/">
|
||||
<a className="w-full">
|
||||
<SecondaryButton className="w-full">Go Home</SecondaryButton>
|
||||
</a>
|
||||
</Link>
|
||||
<PrimaryButton className="w-full" onClick={submitInvitations}>
|
||||
Accept and Continue
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : workspaces && workspaces.length > 0 ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<h2 className="mb-4 text-xl font-medium">Your workspaces</h2>
|
||||
{workspaces.map((workspace) => (
|
||||
<Link key={workspace.id} href={workspace.slug}>
|
||||
<a>
|
||||
<div className="mb-2 flex items-center justify-between rounded border border-brand-base px-4 py-2">
|
||||
<div className="flex items-center gap-x-2 text-sm">
|
||||
<CubeIcon className="h-5 w-5 text-brand-secondary" />
|
||||
{workspace.name}
|
||||
) : workspaces && workspaces.length > 0 ? (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
<h2 className="mb-4 text-xl font-medium">Your workspaces</h2>
|
||||
{workspaces.map((workspace) => (
|
||||
<Link key={workspace.id} href={workspace.slug}>
|
||||
<a>
|
||||
<div className="mb-2 flex items-center justify-between rounded border border-brand-base px-4 py-2">
|
||||
<div className="flex items-center gap-x-2 text-sm">
|
||||
<CubeIcon className="h-5 w-5 text-brand-secondary" />
|
||||
{workspace.name}
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 text-xs text-brand-secondary">
|
||||
{workspace.owner.first_name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 text-xs text-brand-secondary">
|
||||
{workspace.owner.first_name}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
invitations.length === 0 &&
|
||||
workspaces.length === 0 && (
|
||||
<EmptySpace
|
||||
title="You don't have any workspaces yet"
|
||||
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
|
||||
>
|
||||
<EmptySpaceItem
|
||||
Icon={PlusIcon}
|
||||
title={"Create your Workspace"}
|
||||
action={() => {
|
||||
router.push("/create-workspace");
|
||||
}}
|
||||
/>
|
||||
</EmptySpace>
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
invitations.length === 0 &&
|
||||
workspaces.length === 0 && (
|
||||
<EmptySpace
|
||||
title="You don't have any workspaces yet"
|
||||
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
|
||||
>
|
||||
<EmptySpaceItem
|
||||
Icon={PlusIcon}
|
||||
title={"Create your Workspace"}
|
||||
action={() => {
|
||||
router.push("/create-workspace");
|
||||
}}
|
||||
/>
|
||||
</EmptySpace>
|
||||
)
|
||||
)
|
||||
)
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute flex flex-col gap-1 justify-center items-start left-5 top-5">
|
||||
<span className="text-xs text-brand-secondary">Logged in:</span>
|
||||
<span className="text-sm text-brand-base">{user?.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import Router, { useRouter } from "next/router";
|
||||
|
||||
import { mutate } from "swr";
|
||||
|
||||
// services
|
||||
import userService from "services/user.service";
|
||||
import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useUserAuth from "hooks/use-user-auth";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper";
|
||||
|
|
@ -38,15 +39,17 @@ const Onboarding: NextPage = () => {
|
|||
|
||||
const router = useRouter();
|
||||
|
||||
const { user } = useUser();
|
||||
const { user } = useUserAuth("onboarding");
|
||||
|
||||
console.log("user", user);
|
||||
|
||||
return (
|
||||
<UserAuthorizationLayout>
|
||||
<DefaultLayout>
|
||||
<div className="grid h-full place-items-center p-5">
|
||||
<div className="relative grid h-full place-items-center p-5">
|
||||
{step <= 3 ? (
|
||||
<div className="w-full">
|
||||
<div className="mb-8 flex items-center justify-center text-center">
|
||||
<div className="h-full flex flex-col justify-center w-full py-4">
|
||||
<div className="mb-7 flex items-center justify-center text-center">
|
||||
<OnboardingLogo className="h-12 w-48 fill-current text-brand-base" />
|
||||
</div>
|
||||
{step === 1 ? (
|
||||
|
|
@ -59,7 +62,7 @@ const Onboarding: NextPage = () => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="flex w-full max-w-2xl flex-col gap-12">
|
||||
<div className="flex flex-col items-center justify-center gap-7 rounded-[10px] bg-brand-base pb-10 text-center shadow-md">
|
||||
<div className="flex flex-col items-center justify-center gap-7 rounded-[10px] bg-brand-base pb-7 text-center shadow-md">
|
||||
{step === 4 ? (
|
||||
<OnboardingCard data={ONBOARDING_CARDS.welcome} />
|
||||
) : step === 5 ? (
|
||||
|
|
@ -80,7 +83,7 @@ const Onboarding: NextPage = () => {
|
|||
if (step === 8) {
|
||||
userService
|
||||
.updateUserOnBoard({ userRole })
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
mutate<ICurrentUserResponse>(
|
||||
CURRENT_USER,
|
||||
(prevData) => {
|
||||
|
|
@ -96,7 +99,28 @@ const Onboarding: NextPage = () => {
|
|||
},
|
||||
false
|
||||
);
|
||||
router.push("/");
|
||||
const userWorkspaces = await workspaceService.userWorkspaces();
|
||||
|
||||
const lastActiveWorkspace = userWorkspaces.find(
|
||||
(workspace) => workspace.id === user?.last_workspace_id
|
||||
);
|
||||
|
||||
if (lastActiveWorkspace) {
|
||||
Router.push(`/${lastActiveWorkspace.slug}`);
|
||||
return;
|
||||
} else if (userWorkspaces.length > 0) {
|
||||
Router.push(`/${userWorkspaces[0].slug}`);
|
||||
return;
|
||||
} else {
|
||||
const invitations = await workspaceService.userWorkspaceInvitations();
|
||||
if (invitations.length > 0) {
|
||||
Router.push(`/invitations`);
|
||||
return;
|
||||
} else {
|
||||
Router.push(`/create-workspace`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
|
|
@ -110,6 +134,10 @@ const Onboarding: NextPage = () => {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute flex flex-col gap-1 justify-center items-start left-5 top-5">
|
||||
<span className="text-xs text-brand-secondary">Logged in:</span>
|
||||
<span className="text-sm text-brand-base">{user?.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
</UserAuthorizationLayout>
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
import React, { useCallback, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Image from "next/image";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
import authenticationService from "services/authentication.service";
|
||||
// layouts
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
// social button
|
||||
import {
|
||||
GoogleLoginButton,
|
||||
GithubLoginButton,
|
||||
EmailSignInForm,
|
||||
EmailPasswordForm,
|
||||
} from "components/account";
|
||||
// ui
|
||||
import { Spinner } from "components/ui";
|
||||
// icons
|
||||
import Logo from "public/logo.png";
|
||||
// types
|
||||
import type { NextPage } from "next";
|
||||
|
||||
const SignInPage: NextPage = () => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
// user hook
|
||||
const { mutateUser } = useUser();
|
||||
// states
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const onSignInSuccess = useCallback(async () => {
|
||||
setLoading(true);
|
||||
await mutateUser();
|
||||
const nextLocation = router.asPath.split("?next=")[1];
|
||||
if (nextLocation) await router.push(nextLocation as string);
|
||||
else await router.push("/");
|
||||
}, [mutateUser, router]);
|
||||
|
||||
const handleGoogleSignIn = ({ clientId, credential }: any) => {
|
||||
if (clientId && credential) {
|
||||
setLoading(true);
|
||||
authenticationService
|
||||
.socialAuth({
|
||||
medium: "google",
|
||||
credential,
|
||||
clientId,
|
||||
})
|
||||
.then(async () => {
|
||||
await onSignInSuccess();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleGithubSignIn = useCallback(
|
||||
(credential: string) => {
|
||||
setLoading(true);
|
||||
authenticationService
|
||||
.socialAuth({
|
||||
medium: "github",
|
||||
credential,
|
||||
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
|
||||
})
|
||||
.then(async () => {
|
||||
await onSignInSuccess();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setToastAlert({
|
||||
title: "Error signing in!",
|
||||
type: "error",
|
||||
message: "Something went wrong. Please try again later or contact the support team.",
|
||||
});
|
||||
setLoading(false);
|
||||
});
|
||||
},
|
||||
[onSignInSuccess, setToastAlert]
|
||||
);
|
||||
|
||||
return (
|
||||
<DefaultLayout>
|
||||
{isLoading ? (
|
||||
<div className="absolute top-0 left-0 z-50 flex h-full w-full flex-col items-center justify-center gap-y-3">
|
||||
<h2 className="text-xl text-brand-base">Signing in. Please wait...</h2>
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-screen w-full items-center justify-center overflow-auto">
|
||||
<div className="flex min-h-full w-full flex-col justify-center py-12 px-6 lg:px-8">
|
||||
<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-brand-base">
|
||||
Sign In to your Plane Account
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col rounded-[10px] bg-brand-base shadow-md">
|
||||
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
|
||||
<>
|
||||
<EmailSignInForm handleSuccess={onSignInSuccess} />
|
||||
|
||||
<div className="flex flex-col items-center justify-center gap-3 border-t border-brand-base py-5 px-5 ">
|
||||
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
||||
|
||||
<GithubLoginButton handleSignIn={handleGithubSignIn} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<EmailPasswordForm onSuccess={onSignInSuccess} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DefaultLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInPage;
|
||||
|
|
@ -49,7 +49,7 @@ const WorkspaceInvitation: NextPage = () => {
|
|||
if (email === user?.email) {
|
||||
router.push("/invitations");
|
||||
} else {
|
||||
router.push("/signin");
|
||||
router.push("/");
|
||||
}
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
|
|
@ -108,7 +108,7 @@ const WorkspaceInvitation: NextPage = () => {
|
|||
Icon={UserIcon}
|
||||
title="Sign in to continue"
|
||||
action={() => {
|
||||
router.push("/signin");
|
||||
router.push("/");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue