[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:
Prateek Shourya 2024-05-23 16:45:23 +05:30 committed by GitHub
parent 073d453752
commit ddc28d37d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 82 additions and 119 deletions

View File

@ -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": {

View File

@ -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}

View File

@ -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";

View File

@ -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}

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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 />