fix: onboarding bugs & improvements (#2839)
* fix: terms & condition alignment * fix: onboarding page scrolling * fix: create workspace name clear * fix: setup profile sidebar workspace name * fix: invite team screen button text * fix: inner div min height * fix: allow single invite also in invite member * fix: UI clipping in invite members * fix: signin screen scroll * fix: sidebar notification icon * fix: sidebar project name & icon * fix: user detail bottom image alignment * fix: step indicator in invite member * fix: try different account modal state * fix: setup profile remove image * fix: workspace slug clear * fix: invite member UI & focus * fix: step indicator size * fix: inner div placement * fix: invite member validation logic * fix: cuurent user data persistency * fix: sidebar animation colors * feat: signup & resend * fix: sign out theme persist from popover * fix: imports * chore: signin responsiveness * fix: sign-in, sign-up top padding
This commit is contained in:
parent
f9590929dc
commit
3c89ef8cc3
21 changed files with 607 additions and 375 deletions
|
|
@ -2,8 +2,6 @@
|
|||
import React, { useState } from "react";
|
||||
// next
|
||||
import { useRouter } from "next/router";
|
||||
// components
|
||||
import { Button } from "@plane/ui";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
|
|
@ -11,8 +9,10 @@ import { AuthService } from "services/auth.service";
|
|||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import { UserService } from "services/user.service";
|
||||
import { useTheme } from "next-themes";
|
||||
import { mutate } from "swr";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
|
|
@ -25,13 +25,17 @@ const userService = new UserService();
|
|||
const DeleteAccountModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose } = props;
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const { setTheme } = useTheme();
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await authService
|
||||
.signOut()
|
||||
.then(() => {
|
||||
mutate("CURRENT_USER_DETAILS", null);
|
||||
setTheme("system");
|
||||
router.push("/");
|
||||
})
|
||||
.catch(() =>
|
||||
|
|
@ -53,6 +57,8 @@ const DeleteAccountModal: React.FC<Props> = (props) => {
|
|||
title: "Success!",
|
||||
message: "Account deleted successfully.",
|
||||
});
|
||||
mutate("CURRENT_USER_DETAILS", null);
|
||||
setTheme("system");
|
||||
router.push("/");
|
||||
})
|
||||
.catch((err) =>
|
||||
|
|
@ -100,7 +106,7 @@ const DeleteAccountModal: React.FC<Props> = (props) => {
|
|||
<div className="">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<AlertTriangle className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
<Trash2 className="h-5 w-5 text-red-600" aria-hidden="true" />
|
||||
</div>
|
||||
<Dialog.Title as="h3" className="text-2xl font-medium leading-6 text-onboarding-text-100">
|
||||
Not the right workspace?
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { XCircle } from "lucide-react";
|
||||
// ui
|
||||
import { Button, Input } from "@plane/ui";
|
||||
// components
|
||||
import { AuthType } from "components/page-views";
|
||||
// services
|
||||
import { AuthService } from "services/auth.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useTimer from "hooks/use-timer";
|
||||
// icons
|
||||
import { XCircle } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
// types
|
||||
type EmailCodeFormValues = {
|
||||
email: string;
|
||||
key?: string;
|
||||
|
|
@ -20,7 +19,14 @@ type EmailCodeFormValues = {
|
|||
|
||||
const authService = new AuthService();
|
||||
|
||||
export const EmailCodeForm = ({ handleSignIn }: any) => {
|
||||
type Props = {
|
||||
handleSignIn: any;
|
||||
authType: AuthType;
|
||||
};
|
||||
|
||||
export const EmailCodeForm: React.FC<Props> = (Props) => {
|
||||
const { handleSignIn, authType } = Props;
|
||||
// states
|
||||
const [codeSent, setCodeSent] = useState(false);
|
||||
const [codeResent, setCodeResent] = useState(false);
|
||||
const [isCodeResending, setIsCodeResending] = useState(false);
|
||||
|
|
@ -37,7 +43,6 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
|
|||
setError,
|
||||
setValue,
|
||||
getValues,
|
||||
watch,
|
||||
formState: { errors, isSubmitting, isValid, isDirty },
|
||||
} = useForm<EmailCodeFormValues>({
|
||||
defaultValues: {
|
||||
|
|
@ -49,14 +54,13 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
|
|||
reValidateMode: "onChange",
|
||||
});
|
||||
|
||||
const isResendDisabled = resendCodeTimer > 0 || isCodeResending || isSubmitting || errorResendingCode;
|
||||
const isResendDisabled = resendCodeTimer > 0 || isCodeResending || isSubmitting;
|
||||
|
||||
const onSubmit = async ({ email }: EmailCodeFormValues) => {
|
||||
setErrorResendingCode(false);
|
||||
await authService
|
||||
.emailCode({ email })
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
setSentEmail(email);
|
||||
setValue("key", res.key);
|
||||
setCodeSent(true);
|
||||
|
|
@ -139,12 +143,20 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
|
|||
) : (
|
||||
<>
|
||||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-onboarding-text-100">
|
||||
Let’s get you prepped!
|
||||
{authType === "sign-in" ? "Get on your flight deck!" : "Let’s get you prepped!"}
|
||||
</h1>
|
||||
<p className="text-center text-sm text-onboarding-text-200 mt-3">
|
||||
This whole thing will take less than two minutes.
|
||||
</p>
|
||||
<p className="text-center text-sm text-onboarding-text-200 mt-1">Promise!</p>
|
||||
{authType == "sign-up" ? (
|
||||
<div>
|
||||
<p className="text-center text-sm text-onboarding-text-200 mt-3">
|
||||
This whole thing will take less than two minutes.
|
||||
</p>
|
||||
<p className="text-center text-sm text-onboarding-text-200 mt-1">Promise!</p>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-center text-sm text-onboarding-text-200 px-20 mt-3">
|
||||
Sign in with the email you used to sign up for Plane
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
@ -216,11 +228,39 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
|
|||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.token)}
|
||||
placeholder="get-set-fly"
|
||||
placeholder="gets-sets-flys"
|
||||
className="border-onboarding-border-100 h-[46px] w-full"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{resendCodeTimer <= 0 && !isResendDisabled && (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex absolute w-fit right-3.5 justify-end text-xs outline-none cursor-pointer text-custom-primary-100`}
|
||||
onClick={() => {
|
||||
setIsCodeResending(true);
|
||||
onSubmit({ email: getValues("email") }).then(() => {
|
||||
setCodeResent(true);
|
||||
setIsCodeResending(false);
|
||||
setResendCodeTimer(30);
|
||||
});
|
||||
}}
|
||||
disabled={isResendDisabled}
|
||||
>
|
||||
<span className="font-medium">Resend</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`flex w-full justify-end text-xs outline-none ${
|
||||
isResendDisabled ? "cursor-default text-custom-text-200" : "cursor-pointer text-custom-primary-100"
|
||||
} `}
|
||||
>
|
||||
{resendCodeTimer > 0 ? (
|
||||
<span className="text-right">Request new code in {resendCodeTimer}s</span>
|
||||
) : isCodeResending ? (
|
||||
"Sending new code..."
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -238,8 +278,8 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
|
|||
>
|
||||
{isLoading ? "Signing in..." : "Next step"}
|
||||
</Button>
|
||||
<div className="w-[70%] my-4 mx-auto">
|
||||
<p className="text-xs text-onboarding-text-300">
|
||||
<div className="w-3/4 my-4 mx-auto">
|
||||
<p className="text-xs text-center text-onboarding-text-300">
|
||||
When you click the button above, you agree with our{" "}
|
||||
<a
|
||||
href="https://plane.so/terms-and-conditions"
|
||||
|
|
|
|||
|
|
@ -6,16 +6,18 @@ import Image from "next/image";
|
|||
import { useRouter } from "next/router";
|
||||
import { useTheme } from "next-themes";
|
||||
// images
|
||||
import githubBlackImage from "/public/logos/github-black.png";
|
||||
import githubWhiteImage from "/public/logos/github-white.png";
|
||||
import githubLightModeImage from "/public/logos/github-black.png";
|
||||
import githubDarkModeImage from "/public/logos/github-dark.svg";
|
||||
import { AuthType } from "components/page-views";
|
||||
|
||||
export interface GithubLoginButtonProps {
|
||||
handleSignIn: React.Dispatch<string>;
|
||||
clientId: string;
|
||||
authType: AuthType;
|
||||
}
|
||||
|
||||
export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
|
||||
const { handleSignIn, clientId } = props;
|
||||
const { handleSignIn, clientId, authType } = props;
|
||||
// states
|
||||
const [loginCallBackURL, setLoginCallBackURL] = useState(undefined);
|
||||
const [gitCode, setGitCode] = useState<null | string>(null);
|
||||
|
|
@ -24,7 +26,7 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
|
|||
query: { code },
|
||||
} = useRouter();
|
||||
// theme
|
||||
const { theme } = useTheme();
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
if (code && !gitCode) {
|
||||
|
|
@ -37,22 +39,23 @@ export const GithubLoginButton: FC<GithubLoginButtonProps> = (props) => {
|
|||
const origin = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
|
||||
setLoginCallBackURL(`${origin}/` as any);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full flex justify-center items-center">
|
||||
<Link
|
||||
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 hover:bg-onboarding-background-300 rounded border border-onboarding-border-200 p-2 text-sm font-medium text-custom-text-100 duration-300 h-[46px]`}
|
||||
className={`flex w-full items-center justify-center gap-2 hover:bg-onboarding-background-300 rounded border px-2 text-sm font-medium text-custom-text-100 duration-300 h-[42px] ${
|
||||
resolvedTheme === "dark" ? "bg-[#2F3135] border-[#43484F]" : "border-[#D9E4FF]"
|
||||
}`}
|
||||
>
|
||||
<Image
|
||||
src={theme === "dark" ? githubWhiteImage : githubBlackImage}
|
||||
src={resolvedTheme === "dark" ? githubDarkModeImage : githubLightModeImage}
|
||||
height={20}
|
||||
width={20}
|
||||
alt="GitHub Logo"
|
||||
/>
|
||||
<span className="text-onboarding-text-200">Sign in with GitHub</span>
|
||||
<span className="text-onboarding-text-200">{authType == "sign-in" ? "Sign-in" : "Sign-up"} with GitHub</span>
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Avatar, DiceIcon, PhotoFilterIcon } from "@plane/ui";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// react-hook-form
|
||||
import { useTheme } from "next-themes";
|
||||
import Image from "next/image";
|
||||
import { Control, Controller, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
||||
// types
|
||||
import { IWorkspace } from "types";
|
||||
// icons
|
||||
import {
|
||||
BarChart2,
|
||||
Briefcase,
|
||||
|
|
@ -19,7 +14,16 @@ import {
|
|||
PenSquare,
|
||||
Search,
|
||||
Settings,
|
||||
Bell,
|
||||
} from "lucide-react";
|
||||
import { Avatar, DiceIcon, PhotoFilterIcon } from "@plane/ui";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
|
||||
// types
|
||||
import { IWorkspace } from "types";
|
||||
// assets
|
||||
import projectEmoji from "public/emoji/project-emoji.svg";
|
||||
|
||||
const workspaceLinks = [
|
||||
{
|
||||
|
|
@ -39,7 +43,7 @@ const workspaceLinks = [
|
|||
name: "All Issues",
|
||||
},
|
||||
{
|
||||
Icon: CheckCircle,
|
||||
Icon: Bell,
|
||||
name: "Notifications",
|
||||
},
|
||||
];
|
||||
|
|
@ -89,22 +93,23 @@ const DummySidebar: React.FC<Props> = (props) => {
|
|||
const { workspace: workspaceStore, user: userStore } = useMobxStore();
|
||||
const workspace = workspaceStore.workspaces ? workspaceStore.workspaces[0] : null;
|
||||
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
const handleZoomWorkspace = (value: string) => {
|
||||
// console.log(lastWorkspaceName,value);
|
||||
if (lastWorkspaceName === value) return;
|
||||
lastWorkspaceName = value;
|
||||
if (timer > 0) {
|
||||
timer += 2;
|
||||
timer = Math.min(timer, 4);
|
||||
timer = Math.min(timer, 2);
|
||||
} else {
|
||||
timer = 2;
|
||||
timer = Math.min(timer, 4);
|
||||
timer = Math.min(timer, 2);
|
||||
const interval = setInterval(() => {
|
||||
if (timer < 0) {
|
||||
setValue!("name", lastWorkspaceName);
|
||||
clearInterval(interval);
|
||||
}
|
||||
console.log("timer", timer);
|
||||
timer--;
|
||||
}, 1000);
|
||||
}
|
||||
|
|
@ -112,7 +117,7 @@ const DummySidebar: React.FC<Props> = (props) => {
|
|||
|
||||
useEffect(() => {
|
||||
if (watch) {
|
||||
watch();
|
||||
watch("name");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -126,22 +131,34 @@ const DummySidebar: React.FC<Props> = (props) => {
|
|||
render={({ field: { value } }) => {
|
||||
if (value.length > 0) {
|
||||
handleZoomWorkspace(value);
|
||||
} else {
|
||||
lastWorkspaceName = "";
|
||||
}
|
||||
return timer > 0 ? (
|
||||
<div className="py-6 pl-4 top-3 mt-4 transition-all bg-onboarding-background-200 w-full max-w-screen-sm flex items-center ml-6 border-8 border-onboarding-background-100 rounded-md">
|
||||
<div className="bg-onboarding-background-100 w-full p-1 flex items-center">
|
||||
<div className="flex flex-shrink-0">
|
||||
<Avatar
|
||||
name={value.length > 0 ? value[0].toLocaleUpperCase() : "N"}
|
||||
src={""}
|
||||
size={30}
|
||||
shape="square"
|
||||
fallbackBackgroundColor="black"
|
||||
className="!text-base"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`top-3 mt-4 transition-all bg-onboarding-background-200 w-full max-w-screen-sm flex items-center ml-6 border-[6px] ${
|
||||
resolvedTheme == "dark" ? "border-onboarding-background-100" : "border-custom-primary-20"
|
||||
} rounded-xl`}
|
||||
>
|
||||
<div className="border rounded-lg py-6 pl-4 w-full border-onboarding-background-400">
|
||||
<div
|
||||
className={`${
|
||||
resolvedTheme == "light" ? "bg-[#F5F5F5]" : "bg-[#363A40]"
|
||||
} w-full p-1 flex items-center`}
|
||||
>
|
||||
<div className="flex flex-shrink-0">
|
||||
<Avatar
|
||||
name={value.length > 0 ? value[0].toLocaleUpperCase() : "N"}
|
||||
src={""}
|
||||
size={30}
|
||||
shape="square"
|
||||
fallbackBackgroundColor="black"
|
||||
className="!text-base"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<span className="text-xl font-medium text-onboarding-text-100 ml-2 truncate">{value}</span>
|
||||
<span className="text-xl font-medium text-onboarding-text-100 ml-2 truncate">{value}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -206,7 +223,7 @@ const DummySidebar: React.FC<Props> = (props) => {
|
|||
<div className={`flex items-center justify-between w-full px-1 mb-3 gap-2 mt-4 `}>
|
||||
<div
|
||||
className={`relative flex items-center justify-between w-full rounded gap-1 group
|
||||
px-3 shadow-custom-sidebar-shadow-2xs border-[0.5px] border-custom-border-200
|
||||
px-3 shadow-custom-shadow-2xs border-onboarding-border-100 border
|
||||
`}
|
||||
>
|
||||
<div className={`relative flex items-center gap-2 flex-grow rounded flex-shrink-0 py-1.5 outline-none`}>
|
||||
|
|
@ -217,7 +234,7 @@ const DummySidebar: React.FC<Props> = (props) => {
|
|||
|
||||
<div
|
||||
className={`flex items-center justify-center rounded flex-shrink-0 p-2 outline-none
|
||||
shadow-custom-sidebar-shadow-2xs border-[0.5px] border-onboarding-border-200
|
||||
shadow-custom-shadow-2xs border border-onboarding-border-100
|
||||
`}
|
||||
>
|
||||
<Search className="h-4 w-4 text-onboarding-text-200" />
|
||||
|
|
@ -244,11 +261,15 @@ const DummySidebar: React.FC<Props> = (props) => {
|
|||
<div className="px-3">
|
||||
{" "}
|
||||
<div className="w-4/5 flex items-center text-base font-medium text-custom-text-200 mb-3 justify-between">
|
||||
<span> Plane web</span>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Image src={projectEmoji} alt="Plane Logo" className="h-4 w-4" />
|
||||
<span> Plane</span>
|
||||
</div>
|
||||
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</div>
|
||||
{projectLinks.map((link) => (
|
||||
<a className="block w-full">
|
||||
<a className="block ml-6 w-full">
|
||||
<div
|
||||
className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-base font-medium outline-none
|
||||
text-custom-sidebar-text-200 focus:bg-custom-sidebar-background-80
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@ import React from "react";
|
|||
|
||||
const OnboardingStepIndicator = ({ step }: { step: number }) => (
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="h-4 w-4 rounded-full bg-custom-primary-100 z-10" />
|
||||
<div className="h-3 w-3 rounded-full bg-custom-primary-100 z-10" />
|
||||
<div className={`h-1 w-14 -ml-1 ${step >= 2 ? "bg-custom-primary-100" : "bg-onboarding-background-100"}`} />
|
||||
<div
|
||||
className={` z-10 -ml-1 rounded-full ${
|
||||
step >= 2 ? "bg-custom-primary-100 h-4 w-4" : " h-3 w-3 bg-onboarding-background-100"
|
||||
step >= 2 ? "bg-custom-primary-100 h-3 w-3" : " h-2 w-2 bg-onboarding-background-100"
|
||||
}`}
|
||||
/>
|
||||
<div className={`h-1 w-14 -ml-1 ${step >= 3 ? "bg-custom-primary-100" : "bg-onboarding-background-100"}`} />
|
||||
<div
|
||||
className={`rounded-full -ml-1 z-10 ${
|
||||
step >= 3 ? "bg-custom-primary-100 h-4 w-4" : "h-3 w-3 bg-onboarding-background-100"
|
||||
step >= 3 ? "bg-custom-primary-100 h-3 w-3" : "h-2 w-2 bg-onboarding-background-100"
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue