From 43ce850ae93a298981026d49a47f47d7099e64f1 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Tue, 30 Apr 2024 19:51:17 +0530 Subject: [PATCH 1/2] style: switch or delete account workflow --- .../switch-delete-account-modal.tsx | 32 ++------ .../switch-or-delete-account-dropdown.tsx | 82 +++++++++---------- 2 files changed, 46 insertions(+), 68 deletions(-) diff --git a/web/components/onboarding/switch-delete-account-modal.tsx b/web/components/onboarding/switch-delete-account-modal.tsx index 22d914903..7b03322a2 100644 --- a/web/components/onboarding/switch-delete-account-modal.tsx +++ b/web/components/onboarding/switch-delete-account-modal.tsx @@ -5,7 +5,7 @@ import { mutate } from "swr"; import { Trash2 } from "lucide-react"; import { Dialog, Transition } from "@headlessui/react"; // hooks -import { TOAST_TYPE, setToast } from "@plane/ui"; +import { Button, TOAST_TYPE, setToast } from "@plane/ui"; import { useUser } from "@/hooks/store"; // ui @@ -24,7 +24,7 @@ export const SwitchOrDeleteAccountModal: React.FC = (props) => { // store hooks const { signOut, deactivateAccount } = useUser(); - const { resolvedTheme, setTheme } = useTheme(); + const { setTheme } = useTheme(); const handleClose = () => { setSwitchingAccount(false); @@ -102,17 +102,11 @@ export const SwitchOrDeleteAccountModal: React.FC = (props) => { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
-
+
@@ -129,22 +123,12 @@ export const SwitchOrDeleteAccountModal: React.FC = (props) => {
- - + +
diff --git a/web/components/onboarding/switch-or-delete-account-dropdown.tsx b/web/components/onboarding/switch-or-delete-account-dropdown.tsx index 0cf9f3387..1f9f8ede9 100644 --- a/web/components/onboarding/switch-or-delete-account-dropdown.tsx +++ b/web/components/onboarding/switch-or-delete-account-dropdown.tsx @@ -4,6 +4,8 @@ import { ChevronDown } from "lucide-react"; import { Menu, Transition } from "@headlessui/react"; // ui import { Avatar } from "@plane/ui"; +// helpers +import { cn } from "@/helpers/common.helper"; // hooks import { useUser } from "@/hooks/store"; // components @@ -20,19 +22,19 @@ export const SwitchOrDeleteAccountDropdown: FC 0 + ? fullName + : user?.email; + return (
setShowDeleteAccountModal(false)} />
{user?.avatar && ( 0 - ? fullName - : user?.email - } + name={displayName} src={user?.avatar} size={24} shape="square" @@ -40,43 +42,35 @@ export const SwitchOrDeleteAccountDropdown: FC )} -
- - - -

- {user?.first_name - ? `${user?.first_name} ${user?.last_name ?? ""}` - : fullName && fullName.trim().length > 0 - ? fullName - : user?.email} -

-
- -
- - - -
{ - setShowDeleteAccountModal(true); - }} - > - Wrong e-mail address? -
-
-
-
-
-
+ + + {displayName} + + + + + + cn("text-red-500 px-1 py-1.5 whitespace-nowrap text-left rounded w-full", { + "bg-custom-background-80": active, + }) + } + onClick={() => setShowDeleteAccountModal(true)} + > + Wrong e-mail address? + + + +
); From 4330f0f0c91100de069bcfe048a876259500828b Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Wed, 1 May 2024 12:06:51 +0530 Subject: [PATCH 2/2] chore: onboarding ui updates and accept invitation workflow updates. --- .../account/auth-forms/password.tsx | 4 +- web/components/account/auth-forms/root.tsx | 12 +- .../account/auth-forms/unique-code.tsx | 20 ++- .../onboarding/create-or-join-workspaces.tsx | 8 +- web/components/onboarding/invitations.tsx | 113 ++++++------- web/components/onboarding/invite-members.tsx | 35 ++-- web/components/onboarding/profile-setup.tsx | 61 +++---- web/constants/fetch-keys.ts | 4 +- web/constants/workspace.ts | 27 ++- web/layouts/auth-layout/user-wrapper.tsx | 3 +- web/pages/invitations/index.tsx | 3 +- web/pages/onboarding/index.tsx | 57 ++++--- .../onboarding/user-personalization-light.svg | 158 ++++++++---------- 13 files changed, 251 insertions(+), 254 deletions(-) diff --git a/web/components/account/auth-forms/password.tsx b/web/components/account/auth-forms/password.tsx index d3d2cfd79..0cc120958 100644 --- a/web/components/account/auth-forms/password.tsx +++ b/web/components/account/auth-forms/password.tsx @@ -58,7 +58,7 @@ export const AuthPasswordForm: React.FC = observer((props: Props) => { authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token)); }, [csrfToken]); - const redirectToUniqueCodeLogin = async () => { + const redirectToUniqueCodeSignIn = async () => { handleStepChange(EAuthSteps.UNIQUE_CODE); }; @@ -194,7 +194,7 @@ export const AuthPasswordForm: React.FC = observer((props: Props) => { {instance && isSmtpConfigured && (
- diff --git a/web/components/onboarding/create-or-join-workspaces.tsx b/web/components/onboarding/create-or-join-workspaces.tsx index ae8fcc450..4ddf6dd02 100644 --- a/web/components/onboarding/create-or-join-workspaces.tsx +++ b/web/components/onboarding/create-or-join-workspaces.tsx @@ -24,10 +24,11 @@ type Props = { invitations: IWorkspaceMemberInvitation[]; totalSteps: number; stepChange: (steps: Partial) => Promise; + finishOnboarding: () => Promise; }; export const CreateOrJoinWorkspaces: React.FC = observer((props) => { - const { invitations, totalSteps, stepChange } = props; + const { invitations, totalSteps, stepChange, finishOnboarding } = props; // states const [currentView, setCurrentView] = useState(null); // store hooks @@ -45,14 +46,15 @@ export const CreateOrJoinWorkspaces: React.FC = observer((props) => { const handleNextStep = async () => { if (!user) return; - await stepChange({ workspace_join: true, workspace_create: true }); + + await finishOnboarding(); }; return (
- +
diff --git a/web/components/onboarding/invitations.tsx b/web/components/onboarding/invitations.tsx index f34cf42ca..a5fd5632f 100644 --- a/web/components/onboarding/invitations.tsx +++ b/web/components/onboarding/invitations.tsx @@ -1,25 +1,23 @@ import React, { useState } from "react"; -import useSWR, { mutate } from "swr"; -// icons -import { CheckCircle2 } from "lucide-react"; +import useSWR from "swr";; // types import { IWorkspaceMemberInvitation } from "@plane/types"; // ui -import { Button } from "@plane/ui"; +import { Button, Checkbox } from "@plane/ui"; // constants import { MEMBER_ACCEPTED } from "@/constants/event-tracker"; -import { USER_WORKSPACES, USER_WORKSPACE_INVITATIONS } from "@/constants/fetch-keys"; +import { USER_WORKSPACE_INVITATIONS } from "@/constants/fetch-keys"; import { ROLE } from "@/constants/workspace"; // helpers import { truncateText } from "@/helpers/string.helper"; import { getUserRole } from "@/helpers/user.helper"; // hooks -import { useEventTracker, useUser, useWorkspace } from "@/hooks/store"; +import { useEventTracker, useWorkspace } from "@/hooks/store"; // services import { WorkspaceService } from "@/services/workspace.service"; type Props = { - handleNextStep: () => void; + handleNextStep: () => Promise; handleCurrentViewChange: () => void; }; const workspaceService = new WorkspaceService(); @@ -31,14 +29,9 @@ export const Invitations: React.FC = (props) => { const [invitationsRespond, setInvitationsRespond] = useState([]); // store hooks const { captureEvent } = useEventTracker(); - const { updateCurrentUser } = useUser(); - const { workspaces, fetchWorkspaces } = useWorkspace(); + const { fetchWorkspaces } = useWorkspace(); - const workspacesList = Object.values(workspaces); - - const { data: invitations, mutate: mutateInvitations } = useSWR(USER_WORKSPACE_INVITATIONS, () => - workspaceService.userWorkspaceInvitations() - ); + const { data: invitations } = useSWR(USER_WORKSPACE_INVITATIONS, () => workspaceService.userWorkspaceInvitations()); const handleInvitation = (workspace_invitation: IWorkspaceMemberInvitation, action: "accepted" | "withdraw") => { if (action === "accepted") { @@ -48,13 +41,6 @@ export const Invitations: React.FC = (props) => { } }; - const updateLastWorkspace = async () => { - if (!workspacesList) return; - await updateCurrentUser({ - last_workspace_id: workspacesList[0]?.id, - }); - }; - const submitInvitations = async () => { const invitation = invitations?.find((invitation) => invitation.id === invitationsRespond[0]); @@ -62,42 +48,37 @@ export const Invitations: React.FC = (props) => { setIsJoiningWorkspaces(true); - await workspaceService - .joinWorkspaces({ invitations: invitationsRespond }) - .then(async () => { - captureEvent(MEMBER_ACCEPTED, { - member_id: invitation?.id, - role: getUserRole(invitation?.role as any), - project_id: undefined, - accepted_from: "App", - state: "SUCCESS", - element: "Workspace invitations page", - }); - await fetchWorkspaces(); - await mutate(USER_WORKSPACES); - await updateLastWorkspace(); - await handleNextStep(); - await mutateInvitations(); - }) - .catch((error) => { - console.error(error); - captureEvent(MEMBER_ACCEPTED, { - member_id: invitation?.id, - role: getUserRole(invitation?.role as any), - project_id: undefined, - accepted_from: "App", - state: "FAILED", - element: "Workspace invitations page", - }); - }) - .finally(() => setIsJoiningWorkspaces(false)); + try { + await workspaceService.joinWorkspaces({ invitations: invitationsRespond }); + captureEvent(MEMBER_ACCEPTED, { + member_id: invitation?.id, + role: getUserRole(invitation?.role as any), + project_id: undefined, + accepted_from: "App", + state: "SUCCESS", + element: "Workspace invitations page", + }); + await fetchWorkspaces(); + await handleNextStep(); + } catch (error) { + console.error(error); + captureEvent(MEMBER_ACCEPTED, { + member_id: invitation?.id, + role: getUserRole(invitation?.role as any), + project_id: undefined, + accepted_from: "App", + state: "FAILED", + element: "Workspace invitations page", + }); + setIsJoiningWorkspaces(false); + } }; return invitations && invitations.length > 0 ? (

You are invited!

-

Accept the invites to collaborate with your team!

+

Accept the invites to collaborate with your team.

{invitations && @@ -108,11 +89,7 @@ export const Invitations: React.FC = (props) => { return (
handleInvitation(invitation, isSelected ? "withdraw" : "accepted")} >
@@ -136,23 +113,35 @@ export const Invitations: React.FC = (props) => {
{truncateText(invitedWorkspace?.name, 30)}

{ROLE[invitation.role]}

- - + +
); })}
-

or


-
) : ( diff --git a/web/components/onboarding/invite-members.tsx b/web/components/onboarding/invite-members.tsx index 44d43443f..aa0a710ff 100644 --- a/web/components/onboarding/invite-members.tsx +++ b/web/components/onboarding/invite-members.tsx @@ -16,12 +16,12 @@ import { import { Check, ChevronDown, Plus, XCircle } from "lucide-react"; import { Listbox, Transition } from "@headlessui/react"; // types -import { IUser, IWorkspace, TOnboardingSteps } from "@plane/types"; +import { IUser, IWorkspace } from "@plane/types"; // ui import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui"; // constants import { MEMBER_INVITED } from "@/constants/event-tracker"; -import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace"; +import { EUserWorkspaceRoles, ROLE, ROLE_DETAILS } from "@/constants/workspace"; // helpers import { getUserRole } from "@/helpers/user.helper"; // hooks @@ -39,7 +39,6 @@ import { SwitchOrDeleteAccountDropdown } from "./switch-or-delete-account-dropdo type Props = { finishOnboarding: () => Promise; totalSteps: number; - stepChange: (steps: Partial) => Promise; user: IUser | undefined; workspace: IWorkspace | undefined; }; @@ -215,10 +214,10 @@ const InviteMemberInput: React.FC = (props) => { >
- {Object.entries(ROLE).map(([key, value]) => ( + {Object.entries(ROLE_DETAILS).map(([key, value]) => ( = (props) => { } > {({ selected }) => ( -
-
{value}
- {selected && } +
+
+
{value.title}
+
{value.description}
+
+ {selected && }
)} @@ -264,7 +266,7 @@ const InviteMemberInput: React.FC = (props) => { }; export const InviteMembers: React.FC = (props) => { - const { finishOnboarding, totalSteps, stepChange, workspace } = props; + const { finishOnboarding, totalSteps, workspace } = props; const [isInvitationDisabled, setIsInvitationDisabled] = useState(true); @@ -287,11 +289,6 @@ export const InviteMembers: React.FC = (props) => { }); const nextStep = async () => { - const payload: Partial = { - workspace_invite: true, - }; - - await stepChange(payload); await finishOnboarding(); }; @@ -371,11 +368,11 @@ export const InviteMembers: React.FC = (props) => {
-
+

Invite your teammates

- Work in plane happens best with your team. Invite them now to use Plane to it’s potential. + Work in plane happens best with your team. Invite them now to use Plane to its potential.

= (props) => {
-
+