diff --git a/packages/tailwind-config-custom/tailwind.config.js b/packages/tailwind-config-custom/tailwind.config.js index 3807fe43d..4c7ebf963 100644 --- a/packages/tailwind-config-custom/tailwind.config.js +++ b/packages/tailwind-config-custom/tailwind.config.js @@ -372,8 +372,8 @@ module.exports = { 96: "21.6rem", }, backgroundImage: { - "gradient-primary": "var( --gradient-onboarding-primary)", - "gradient-secondary": "var( --gradient-onboarding-secondary)", + "onboarding-gradient-primary": "var( --gradient-onboarding-primary)", + "onboarding-gradient-secondary": "var( --gradient-onboarding-secondary)", }, }, fontFamily: { diff --git a/web/components/account/delete-account-modal.tsx b/web/components/account/delete-account-modal.tsx index 4663a1004..844c3837e 100644 --- a/web/components/account/delete-account-modal.tsx +++ b/web/components/account/delete-account-modal.tsx @@ -12,6 +12,7 @@ import { AuthService } from "services/auth.service"; import { Dialog, Transition } from "@headlessui/react"; // icons import { AlertTriangle } from "lucide-react"; +import { UserService } from "services/user.service"; type Props = { isOpen: boolean; @@ -19,6 +20,7 @@ type Props = { }; const authService = new AuthService(); +const userService = new UserService(); const DeleteAccountModal: React.FC = (props) => { const { isOpen, onClose } = props; @@ -41,6 +43,28 @@ const DeleteAccountModal: React.FC = (props) => { ); }; + const handleDeleteAccount = async () => { + setIsDeleteLoading(true); + await userService + .deleteAccount() + .then(() => { + setToastAlert({ + type: "success", + title: "Success!", + message: "Account deleted successfully.", + }); + router.push("/"); + }) + .catch((err) => + setToastAlert({ + type: "error", + title: "Error!", + message: err?.data?.error, + }) + ); + setIsDeleteLoading(false); + }; + const handleClose = () => { onClose(); }; @@ -84,10 +108,10 @@ const DeleteAccountModal: React.FC = (props) => {
-

+

  • Delete this account if you have another and won’t use this account.
  • Switch to another account if you’d like to come back to this account another time.
  • -

    +
@@ -95,7 +119,10 @@ const DeleteAccountModal: React.FC = (props) => { Switch account - diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index 2b0f7b4e6..6bddae940 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -84,7 +84,7 @@ const LabelPill = ({ labelId }: { labelId: string }) => { return ( l.id === labelId)?.color ?? "#000000", }} @@ -266,12 +266,12 @@ const activityDetails: { if (activity.verb === "created") return ( <> - added this issue to the cycle{" "} + added this issue to the cycle {activity.new_value} @@ -281,12 +281,12 @@ const activityDetails: { else if (activity.verb === "updated") return ( <> - set the cycle to{" "} + set the cycle to {activity.new_value} @@ -301,7 +301,7 @@ const activityDetails: { href={`/${workspaceSlug}/projects/${activity.project}/cycles/${activity.old_identifier}`} target="_blank" rel="noopener noreferrer" - className="w-full font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline" + className="font-medium text-custom-text-100 inline-flex items-center truncate gap-1 hover:underline" > {activity.old_value} @@ -370,15 +370,15 @@ const activityDetails: { return ( <> added a new label{" "} - + - {activity.new_value} + {activity.new_value} {showIssue && ( - <> + {" "} to - + )} ); @@ -386,15 +386,15 @@ const activityDetails: { return ( <> removed the label{" "} - + - {activity.old_value} + {activity.old_value} {showIssue && ( - <> + {" "} from - + )} ); @@ -482,7 +482,7 @@ const activityDetails: { href={`/${workspaceSlug}/projects/${activity.project}/modules/${activity.new_identifier}`} target="_blank" rel="noopener noreferrer" - className="w-full font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline" + className="font-medium text-custom-text-100 inline-flex items-center truncate gap-1 hover:underline" > {activity.new_value} @@ -497,7 +497,7 @@ const activityDetails: { href={`/${workspaceSlug}/projects/${activity.project}/modules/${activity.new_identifier}`} target="_blank" rel="noopener noreferrer" - className="w-full font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline" + className="font-medium text-custom-text-100 inline-flex items-center truncate gap-1 hover:underline" > {activity.new_value} @@ -512,7 +512,7 @@ const activityDetails: { href={`/${workspaceSlug}/projects/${activity.project}/modules/${activity.old_identifier}`} target="_blank" rel="noopener noreferrer" - className="w-full font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline" + className="font-medium text-custom-text-100 inline-flex items-center truncate gap-1 hover:underline" > {activity.old_value} @@ -525,7 +525,7 @@ const activityDetails: { name: { message: (activity, showIssue) => ( <> - set the name to {activity.new_value} + set the name to {activity.new_value} {showIssue && ( <> {" "} diff --git a/web/components/labels/create-update-label-inline.tsx b/web/components/labels/create-update-label-inline.tsx index 91a65f94b..760e0241b 100644 --- a/web/components/labels/create-update-label-inline.tsx +++ b/web/components/labels/create-update-label-inline.tsx @@ -186,6 +186,7 @@ export const CreateUpdateLabelInline = observer( id="labelName" name="name" type="text" + autofocus value={value} onChange={onChange} ref={ref} diff --git a/web/components/onboarding/invitations.tsx b/web/components/onboarding/invitations.tsx index 2919645d8..c5a733ae4 100644 --- a/web/components/onboarding/invitations.tsx +++ b/web/components/onboarding/invitations.tsx @@ -18,6 +18,7 @@ import { ROLE } from "constants/workspace"; import { IWorkspaceMemberInvitation } from "types"; // icons import { CheckCircle2, Search } from "lucide-react"; +import { trackEvent } from "helpers/event-tracker.helper"; type Props = { handleNextStep: () => void; @@ -32,7 +33,7 @@ const Invitations: React.FC = (props) => { const { workspace: workspaceStore, - user: { updateCurrentUser }, + user: { currentUser, updateCurrentUser }, } = useMobxStore(); const { @@ -63,7 +64,8 @@ const Invitations: React.FC = (props) => { await workspaceService .joinWorkspaces({ invitations: invitationsRespond }) - .then(async () => { + .then(async (res) => { + trackEvent("WORKSPACE_USER_INVITE_ACCEPT", res); await mutateInvitations(); await workspaceStore.fetchWorkspaces(); await mutate(USER_WORKSPACES); @@ -145,15 +147,15 @@ const Invitations: React.FC = (props) => { ) : ( - + ); }; -const EmptyInvitation = () => ( +const EmptyInvitation = ({ email }: { email: string }) => (
-

Is your Team already on Plane?

+

Is your team already on Plane?

- We couldn’t find any existing workspaces for the email address bhavesh@caravel.ai + We couldn’t find any existing workspaces for the email address {email}

= (props) => { return (

Members

diff --git a/web/components/onboarding/join-workspaces.tsx b/web/components/onboarding/join-workspaces.tsx index 2dd53ef7b..fa2262ded 100644 --- a/web/components/onboarding/join-workspaces.tsx +++ b/web/components/onboarding/join-workspaces.tsx @@ -7,12 +7,9 @@ import DummySidebar from "components/account/sidebar"; import OnboardingStepIndicator from "components/account/step-indicator"; import { Workspace } from "./workspace"; // types -import { IWorkspaceMemberInvitation, TOnboardingSteps } from "types"; -// fetch-keys -import { USER_WORKSPACES, USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys"; -// constants -import { ROLE } from "constants/workspace"; -import { trackEvent } from "helpers/event-tracker.helper"; +import { IWorkspace, TOnboardingSteps } from "types"; +// react-hook-form +import { Controller, useForm } from "react-hook-form"; type Props = { finishOnboarding: () => Promise; @@ -38,81 +35,30 @@ export const JoinWorkspaces: React.FC = ({ stepChange, setTryDiffAccount const handleNextStep = async () => { if (!user) return; - - await stepChange({ workspace_join: true }); - - if (user.onboarding_step.workspace_create && user.onboarding_step.workspace_invite) await finishOnboarding(); - }; - - const submitInvitations = async () => { - if (invitationsRespond.length <= 0) return; - - setIsJoiningWorkspaces(true); - - await workspaceService - .joinWorkspaces({ invitations: invitationsRespond }) - .then(async (res) => { - trackEvent( - 'WORKSPACE_USER_INVITE_ACCEPT', - res - ) - await mutateInvitations(); - await mutate(USER_WORKSPACES); - await updateLastWorkspace(); - - await handleNextStep(); - }) - .finally(() => setIsJoiningWorkspaces(false)); + await stepChange({ workspace_join: true, workspace_create: true }); }; return ( -
-
We see that someone has invited you to
-

Join a workspace

-
- {invitations && - invitations.map((invitation) => { - const isSelected = invitationsRespond.includes(invitation.id); - - return ( -
handleInvitation(invitation, isSelected ? "withdraw" : "accepted")} - > -
-
- {invitation.workspace.logo && invitation.workspace.logo !== "" ? ( - {invitation.workspace.name} - ) : ( - - {invitation.workspace.name[0]} - - )} -
-
-
-
{truncateText(invitation.workspace.name, 30)}
-

{ROLE[invitation.role]}

-
- - - -
- ); - })} +
+
+ ( + 0 ? value : "New Workspace"} + /> + )} + />
-

What will your workspace

+

What will your workspace be?

= { first_name: "", avatar: "", + use_case: undefined, }; type Props = { @@ -59,6 +60,7 @@ export const UserDetails: React.FC = observer((props) => { ...formData, first_name: formData.first_name.split(" ")[0], last_name: formData.first_name.split(" ")[1], + use_case: formData.use_case, onboarding_step: { ...user.onboarding_step, profile_complete: true, @@ -91,7 +93,6 @@ export const UserDetails: React.FC = observer((props) => { handleDelete={() => {}} onSuccess={(url) => { setValue("avatar", url); - // handleSubmit(onSubmit)(); setIsImageUploadModalOpen(false); }} value={watch("avatar") !== "" ? watch("avatar") : undefined} @@ -158,25 +159,31 @@ export const UserDetails: React.FC = observer((props) => { name="first_name" render={({ field: { value } }) => (

- And how will you use Plane, {value} ? + And how will you use Plane{value.length>0?", ":""}{value}?

)} />

Choose just one

-
- {useCases.map((useCase, index) => ( -
setSelectedUsecase(index)} - > - {useCase} + ( +
+ {useCases.map((useCase) => ( +
onChange(useCase)} + > + {useCase} +
+ ))}
- ))} -
+ )} + />
) : ( -
+
@@ -196,7 +196,7 @@ export const SignInView = observer(() => {
-
+
{!envConfig ? (
diff --git a/web/components/pages/pages-list/recent-pages-list.tsx b/web/components/pages/pages-list/recent-pages-list.tsx index 59bc8c842..67f1fe7fc 100644 --- a/web/components/pages/pages-list/recent-pages-list.tsx +++ b/web/components/pages/pages-list/recent-pages-list.tsx @@ -49,7 +49,9 @@ export const RecentPagesList: React.FC = observer((props) => { return (
-

{replaceUnderscoreIfSnakeCase(key)}

+

+ {replaceUnderscoreIfSnakeCase(key)} +

); diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index 6659dca87..5cf6c1bcd 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -55,7 +55,7 @@ export interface ICreateProjectForm { description: string; emoji_and_icon: string; network: number; - project_lead_member: IWorkspaceMember; + project_lead_member: string; project_lead: string; cover_image: string; icon_prop: any; @@ -126,7 +126,7 @@ export const CreateProjectModal: FC = observer((props) => { if (typeof formData.emoji_and_icon === "object") payload.icon_prop = formData.emoji_and_icon; else payload.emoji = formData.emoji_and_icon; - payload.project_lead = formData.project_lead_member?.member.id; + payload.project_lead = formData.project_lead_member; return projectStore .createProject(workspaceSlug.toString(), payload) @@ -380,7 +380,7 @@ export const CreateProjectModal: FC = observer((props) => { control={control} render={({ field: { value, onChange } }) => ( member.member.id ===value)[0]} onChange={onChange} options={workspaceMembers || []} placeholder="Select Lead" diff --git a/web/components/workspace/member-select.tsx b/web/components/workspace/member-select.tsx index 26b76845d..9cc142699 100644 --- a/web/components/workspace/member-select.tsx +++ b/web/components/workspace/member-select.tsx @@ -11,7 +11,7 @@ import { IWorkspaceMember } from "types"; export interface IWorkspaceMemberSelect { value: IWorkspaceMember | undefined; - onChange: (value: IWorkspaceMember) => void; + onChange: (value: string) => void; options: IWorkspaceMember[]; placeholder?: string; disabled?: boolean; @@ -48,7 +48,7 @@ export const WorkspaceMemberSelect: FC = (props) => { : options?.filter((option) => option.member.display_name.toLowerCase().includes(query.toLowerCase())); const label = ( -
+
{value ? ( <> @@ -64,7 +64,13 @@ export const WorkspaceMemberSelect: FC = (props) => { ); return ( - +
-
+
{activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? ( Plane ) : activityItem.actor_detail.is_bot ? ( @@ -166,7 +166,12 @@ const ProfileActivityPage: NextPageWithLayout = () => { {activityItem.actor_detail.display_name} )}{" "} - {message} {timeAgo(activityItem.created_at)} +
+ {message}{" "} + + {timeAgo(activityItem.created_at)} + +
diff --git a/web/pages/onboarding/index.tsx b/web/pages/onboarding/index.tsx index 6b59ba0f5..f6c8db3e0 100644 --- a/web/pages/onboarding/index.tsx +++ b/web/pages/onboarding/index.tsx @@ -108,7 +108,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => { }} /> {user && step !== null ? ( -
+
@@ -165,8 +165,8 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
-
-
+
+
{step === 1 ? ( { diff --git a/web/services/user.service.ts b/web/services/user.service.ts index 1d9d1ce94..bfa97c1f1 100644 --- a/web/services/user.service.ts +++ b/web/services/user.service.ts @@ -184,4 +184,13 @@ export class UserService extends APIService { throw error?.response?.data; }); } + + async deleteAccount(): Promise { + return this.delete("/api/users/me/") + .then((response) => response?.data) + .catch((error) => { + throw error?.response; + }); + } + } diff --git a/web/services/workspace.service.ts b/web/services/workspace.service.ts index 1e5b18f5a..e64be236d 100644 --- a/web/services/workspace.service.ts +++ b/web/services/workspace.service.ts @@ -64,7 +64,7 @@ export class WorkspaceService extends APIService { } async inviteWorkspace(workspaceSlug: string, data: IWorkspaceBulkInviteFormData): Promise { - return this.post(`/api/workspaces/${workspaceSlug}/invite/`, data) + return this.post(`/api/workspaces/${workspaceSlug}/invitations/`, data) .then((response) => response?.data) .catch((error) => { throw error?.response?.data; diff --git a/web/types/users.d.ts b/web/types/users.d.ts index c9dbd6cbd..b82daa75f 100644 --- a/web/types/users.d.ts +++ b/web/types/users.d.ts @@ -27,6 +27,7 @@ export interface IUser { user_timezone: string; username: string; theme: IUserTheme; + use_case? :string; } export interface IInstanceAdminStatus {