forked from github/plane
[WEB-1404] chore: auth and onboarding improvements (#4564)
* chore: `switch account` modal revamp. * chore: workspace invitation page message display logic update. * chore: update `showDeleteAccountModal` state to `showSwitchAccountModal`.
This commit is contained in:
parent
073d453752
commit
ddc28d37d3
@ -44,9 +44,9 @@ export const buttonStyling: IButtonStyling = {
|
||||
disabled: `cursor-not-allowed !bg-custom-primary-60 hover:bg-custom-primary-60`,
|
||||
},
|
||||
"accent-primary": {
|
||||
default: `bg-custom-primary-10 text-custom-primary-100`,
|
||||
hover: `hover:bg-custom-primary-20 hover:text-custom-primary-200`,
|
||||
pressed: `focus:bg-custom-primary-20`,
|
||||
default: `bg-custom-primary-100/20 text-custom-primary-100`,
|
||||
hover: `hover:bg-custom-primary-100/10 hover:text-custom-primary-200`,
|
||||
pressed: `focus:bg-custom-primary-100/10`,
|
||||
disabled: `cursor-not-allowed !text-custom-primary-60`,
|
||||
},
|
||||
"outline-primary": {
|
||||
|
@ -6,7 +6,7 @@ import { useTheme } from "next-themes";
|
||||
// types
|
||||
import { IWorkspaceMemberInvitation, TOnboardingSteps } from "@plane/types";
|
||||
// components
|
||||
import { Invitations, OnboardingHeader, SwitchOrDeleteAccountDropdown, CreateWorkspace } from "@/components/onboarding";
|
||||
import { Invitations, OnboardingHeader, SwitchAccountDropdown, CreateWorkspace } from "@/components/onboarding";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
// assets
|
||||
@ -55,7 +55,7 @@ export const CreateOrJoinWorkspaces: React.FC<Props> = observer((props) => {
|
||||
<div className="flex items-center justify-between">
|
||||
<OnboardingHeader currentStep={totalSteps - 1} totalSteps={totalSteps} />
|
||||
<div className="shrink-0 lg:hidden">
|
||||
<SwitchOrDeleteAccountDropdown />
|
||||
<SwitchAccountDropdown />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full items-center justify-center p-8 mt-6">
|
||||
@ -79,7 +79,7 @@ export const CreateOrJoinWorkspaces: React.FC<Props> = observer((props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:block relative w-2/5 h-screen overflow-hidden px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28">
|
||||
<SwitchOrDeleteAccountDropdown />
|
||||
<SwitchAccountDropdown />
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src={resolvedTheme === "dark" ? CreateJoinWorkspaceDark : CreateJoinWorkspace}
|
||||
|
@ -5,6 +5,6 @@ export * from "./profile-setup";
|
||||
export * from "./create-workspace";
|
||||
export * from "./invitations";
|
||||
export * from "./step-indicator";
|
||||
export * from "./switch-or-delete-account-dropdown";
|
||||
export * from "./switch-delete-account-modal";
|
||||
export * from "./switch-account-dropdown";
|
||||
export * from "./switch-account-modal";
|
||||
export * from "./header";
|
||||
|
@ -34,7 +34,7 @@ import InviteMembersDark from "public/onboarding/invite-members-dark.svg";
|
||||
import InviteMembersLight from "public/onboarding/invite-members-light.svg";
|
||||
// components
|
||||
import { OnboardingHeader } from "./header";
|
||||
import { SwitchOrDeleteAccountDropdown } from "./switch-or-delete-account-dropdown";
|
||||
import { SwitchAccountDropdown } from "./switch-account-dropdown";
|
||||
|
||||
type Props = {
|
||||
finishOnboarding: () => Promise<void>;
|
||||
@ -366,7 +366,7 @@ export const InviteMembers: React.FC<Props> = (props) => {
|
||||
{/* Since this will always be the last step */}
|
||||
<OnboardingHeader currentStep={totalSteps} totalSteps={totalSteps} />
|
||||
<div className="shrink-0 lg:hidden">
|
||||
<SwitchOrDeleteAccountDropdown />
|
||||
<SwitchAccountDropdown />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full items-center justify-center p-8 mt-6 md:w-4/5 mx-auto">
|
||||
@ -433,7 +433,7 @@ export const InviteMembers: React.FC<Props> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:block relative w-2/5 h-screen overflow-hidden px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28">
|
||||
<SwitchOrDeleteAccountDropdown />
|
||||
<SwitchAccountDropdown />
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image
|
||||
src={resolvedTheme === "dark" ? InviteMembersDark : InviteMembersLight}
|
||||
|
@ -11,7 +11,7 @@ import { Button, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { PasswordStrengthMeter } from "@/components/account";
|
||||
import { UserImageUploadModal } from "@/components/core";
|
||||
import { OnboardingHeader, SwitchOrDeleteAccountDropdown } from "@/components/onboarding";
|
||||
import { OnboardingHeader, SwitchAccountDropdown } from "@/components/onboarding";
|
||||
// constants
|
||||
import { USER_DETAILS } from "@/constants/event-tracker";
|
||||
// helpers
|
||||
@ -276,7 +276,7 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
||||
<div className="flex items-center justify-between">
|
||||
<OnboardingHeader currentStep={isCurrentStepUserPersonalization ? 2 : 1} totalSteps={totalSteps} />
|
||||
<div className="shrink-0 lg:hidden">
|
||||
<SwitchOrDeleteAccountDropdown fullName={`${watch("first_name")} ${watch("last_name")}`} />
|
||||
<SwitchAccountDropdown fullName={`${watch("first_name")} ${watch("last_name")}`} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full items-center justify-center p-8 mt-6">
|
||||
@ -567,7 +567,7 @@ export const ProfileSetup: React.FC<Props> = observer((props) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:block relative w-2/5 h-screen overflow-hidden px-6 py-10 sm:px-7 sm:py-14 md:px-14 lg:px-28">
|
||||
<SwitchOrDeleteAccountDropdown fullName={`${watch("first_name")} ${watch("last_name")}`} />
|
||||
<SwitchAccountDropdown fullName={`${watch("first_name")} ${watch("last_name")}`} />
|
||||
<div className="absolute inset-0 z-0">
|
||||
{profileSetupStep === EProfileSetupSteps.USER_PERSONALIZATION ? (
|
||||
<Image
|
||||
|
@ -9,16 +9,16 @@ import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
// components
|
||||
import { SwitchOrDeleteAccountModal } from "./switch-delete-account-modal";
|
||||
import { SwitchAccountModal } from "./switch-account-modal";
|
||||
|
||||
type TSwithOrDeleteAccountDropdownProps = {
|
||||
type TSwitchAccountDropdownProps = {
|
||||
fullName?: string;
|
||||
};
|
||||
|
||||
export const SwitchOrDeleteAccountDropdown: FC<TSwithOrDeleteAccountDropdownProps> = observer((props) => {
|
||||
export const SwitchAccountDropdown: FC<TSwitchAccountDropdownProps> = observer((props) => {
|
||||
const { fullName } = props;
|
||||
// states
|
||||
const [showDeleteAccountModal, setShowDeleteAccountModal] = useState(false);
|
||||
const [showSwitchAccountModal, setShowSwitchAccountModal] = useState(false);
|
||||
// store hooks
|
||||
const { data: user } = useUser();
|
||||
|
||||
@ -30,7 +30,7 @@ export const SwitchOrDeleteAccountDropdown: FC<TSwithOrDeleteAccountDropdownProp
|
||||
|
||||
return (
|
||||
<div className="flex w-full shrink-0 justify-end">
|
||||
<SwitchOrDeleteAccountModal isOpen={showDeleteAccountModal} onClose={() => setShowDeleteAccountModal(false)} />
|
||||
<SwitchAccountModal isOpen={showSwitchAccountModal} onClose={() => setShowSwitchAccountModal(false)} />
|
||||
<div className="flex items-center gap-x-2 pr-4 z-10">
|
||||
{user?.avatar && (
|
||||
<Avatar
|
||||
@ -64,7 +64,7 @@ export const SwitchOrDeleteAccountDropdown: FC<TSwithOrDeleteAccountDropdownProp
|
||||
"bg-custom-background-80": active,
|
||||
})
|
||||
}
|
||||
onClick={() => setShowDeleteAccountModal(true)}
|
||||
onClick={() => setShowSwitchAccountModal(true)}
|
||||
>
|
||||
Wrong e-mail address?
|
||||
</Menu.Item>
|
@ -1,34 +1,31 @@
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTheme } from "next-themes";
|
||||
import { mutate } from "swr";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import { ArrowRightLeft } from "lucide-react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// hooks
|
||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { useUser } from "@/hooks/store";
|
||||
// ui
|
||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const SwitchOrDeleteAccountModal: React.FC<Props> = (props) => {
|
||||
export const SwitchAccountModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose } = props;
|
||||
// states
|
||||
const [switchingAccount, setSwitchingAccount] = useState(false);
|
||||
const [isDeactivating, setIsDeactivating] = useState(false);
|
||||
// router
|
||||
const router = useRouter();
|
||||
// store hooks
|
||||
const { signOut, deactivateAccount } = useUser();
|
||||
const { data: userData, signOut } = useUser();
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const handleClose = () => {
|
||||
setSwitchingAccount(false);
|
||||
setIsDeactivating(false);
|
||||
onClose();
|
||||
};
|
||||
|
||||
@ -51,32 +48,6 @@ export const SwitchOrDeleteAccountModal: React.FC<Props> = (props) => {
|
||||
.finally(() => setSwitchingAccount(false));
|
||||
};
|
||||
|
||||
const handleDeactivateAccount = async () => {
|
||||
setIsDeactivating(true);
|
||||
|
||||
await deactivateAccount()
|
||||
.then(() => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: "Account deleted successfully.",
|
||||
});
|
||||
mutate("CURRENT_USER_DETAILS", null);
|
||||
signOut();
|
||||
setTheme("system");
|
||||
router.push("/");
|
||||
handleClose();
|
||||
})
|
||||
.catch((err: any) =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: err?.error,
|
||||
})
|
||||
)
|
||||
.finally(() => setIsDeactivating(false));
|
||||
};
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||
@ -104,32 +75,30 @@ export const SwitchOrDeleteAccountModal: React.FC<Props> = (props) => {
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-100 text-left shadow-custom-shadow-md transition-all sm:my-8 sm:w-[40rem]">
|
||||
<div className="px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<div className="grid place-items-center rounded-full bg-red-500/20 p-4">
|
||||
<Trash2 className="h-6 w-6 text-red-600" aria-hidden="true" />
|
||||
<div className="p-6 pb-1">
|
||||
<div className="flex gap-x-4">
|
||||
<div className="flex items-start">
|
||||
<div className="grid place-items-center rounded-full bg-custom-primary-100/20 p-4">
|
||||
<ArrowRightLeft className="h-5 w-5 text-custom-primary-100" aria-hidden="true" />
|
||||
</div>
|
||||
<Dialog.Title as="h3" className="text-2xl font-medium leading-6 text-onboarding-text-100">
|
||||
Not the right workspace?
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 px-4">
|
||||
<ul className="list-disc text-base font-normal text-onboarding-text-300">
|
||||
<li>Delete this account if you have another and won{"'"}t use this account.</li>
|
||||
<li>Switch to another account if you{"'"}d like to come back to this account another time.</li>
|
||||
</ul>
|
||||
<div className="flex flex-col py-3 gap-y-6">
|
||||
<Dialog.Title as="h3" className="text-2xl font-medium leading-6 text-onboarding-text-100">
|
||||
Switch account
|
||||
</Dialog.Title>
|
||||
{userData?.email && (
|
||||
<div className="text-base font-normal text-onboarding-text-200">
|
||||
If you have signed up via <span className="text-custom-primary-100">{userData.email}</span>{" "}
|
||||
un-intentionally, you can switch your account to a different one from here.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2 flex items-center justify-end gap-3 p-4 sm:px-6">
|
||||
<Button variant="neutral-primary" onClick={handleSwitchAccount} disabled={switchingAccount}>
|
||||
<Button variant="accent-primary" onClick={handleSwitchAccount} disabled={switchingAccount}>
|
||||
{switchingAccount ? "Switching..." : "Switch account"}
|
||||
</Button>
|
||||
<Button variant="outline-danger" onClick={handleDeactivateAccount} loading={isDeactivating}>
|
||||
{isDeactivating ? "Deleting..." : "Delete account"}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
@ -70,53 +70,47 @@ const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center px-3">
|
||||
{invitationDetail ? (
|
||||
<>
|
||||
{error ? (
|
||||
<div className="flex w-full flex-col space-y-4 rounded border border-custom-border-200 bg-custom-background-100 px-4 py-8 text-center shadow-2xl md:w-1/3">
|
||||
<h2 className="text-xl uppercase">INVITATION NOT FOUND</h2>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{invitationDetail.accepted ? (
|
||||
<>
|
||||
<EmptySpace
|
||||
title={`You are already a member of ${invitationDetail.workspace.name}`}
|
||||
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={Boxes} title="Continue to home" href="/" />
|
||||
</EmptySpace>
|
||||
</>
|
||||
) : (
|
||||
<EmptySpace
|
||||
title={`You have been invited to ${invitationDetail.workspace.name}`}
|
||||
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={Check} title="Accept" action={handleAccept} />
|
||||
<EmptySpaceItem Icon={X} title="Ignore" action={handleReject} />
|
||||
</EmptySpace>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : error ? (
|
||||
<EmptySpace
|
||||
title="This invitation link is not active anymore."
|
||||
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
|
||||
link={{ text: "Or start from an empty project", href: "/" }}
|
||||
>
|
||||
{!currentUser ? (
|
||||
<EmptySpaceItem Icon={User2} title="Sign in to continue" href="/" />
|
||||
) : (
|
||||
{invitationDetail && !invitationDetail.responded_at ? (
|
||||
error ? (
|
||||
<div className="flex w-full flex-col space-y-4 rounded border border-custom-border-200 bg-custom-background-100 px-4 py-8 text-center shadow-2xl md:w-1/3">
|
||||
<h2 className="text-xl uppercase">INVITATION NOT FOUND</h2>
|
||||
</div>
|
||||
) : (
|
||||
<EmptySpace
|
||||
title={`You have been invited to ${invitationDetail.workspace.name}`}
|
||||
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={Check} title="Accept" action={handleAccept} />
|
||||
<EmptySpaceItem Icon={X} title="Ignore" action={handleReject} />
|
||||
</EmptySpace>
|
||||
)
|
||||
) : error || invitationDetail?.responded_at ? (
|
||||
invitationDetail?.accepted ? (
|
||||
<EmptySpace
|
||||
title={`You are already a member of ${invitationDetail.workspace.name}`}
|
||||
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={Boxes} title="Continue to home" href="/" />
|
||||
)}
|
||||
<EmptySpaceItem Icon={Star} title="Star us on GitHub" href="https://github.com/makeplane" />
|
||||
<EmptySpaceItem
|
||||
Icon={Share2}
|
||||
title="Join our community of active creators"
|
||||
href="https://discord.com/invite/8SR2N9PAcJ"
|
||||
/>
|
||||
</EmptySpace>
|
||||
</EmptySpace>
|
||||
) : (
|
||||
<EmptySpace
|
||||
title="This invitation link is not active anymore."
|
||||
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
|
||||
link={{ text: "Or start from an empty project", href: "/" }}
|
||||
>
|
||||
{!currentUser ? (
|
||||
<EmptySpaceItem Icon={User2} title="Sign in to continue" href="/" />
|
||||
) : (
|
||||
<EmptySpaceItem Icon={Boxes} title="Continue to home" href="/" />
|
||||
)}
|
||||
<EmptySpaceItem Icon={Star} title="Star us on GitHub" href="https://github.com/makeplane" />
|
||||
<EmptySpaceItem
|
||||
Icon={Share2}
|
||||
title="Join our community of active creators"
|
||||
href="https://discord.com/invite/A92xrEGCge"
|
||||
/>
|
||||
</EmptySpace>
|
||||
)
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<LogoSpinner />
|
||||
|
Loading…
Reference in New Issue
Block a user