chore: deactivate user option added (#2841)
* dev: deactivate user option added * chore: new layout for profile settings * fix: build errors * fix: user profile activity
This commit is contained in:
parent
3c89ef8cc3
commit
db75eced0a
53 changed files with 799 additions and 625 deletions
|
|
@ -4,7 +4,7 @@ import useSWR from "swr";
|
|||
import { observer } from "mobx-react-lite";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/profile-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||
// components
|
||||
import { UserProfileHeader } from "components/headers";
|
||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
||||
|
|
@ -65,7 +65,7 @@ const ProfileAssignedIssuesPage: NextPageWithLayout = observer(() => {
|
|||
|
||||
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<UserProfileHeader title="Assigned" />}>
|
||||
<AppLayout header={<UserProfileHeader />}>
|
||||
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
||||
</AppLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { observer } from "mobx-react-lite";
|
|||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/profile-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||
// components
|
||||
import { UserProfileHeader } from "components/headers";
|
||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
||||
|
|
@ -61,7 +61,7 @@ const ProfileCreatedIssuesPage: NextPageWithLayout = () => {
|
|||
|
||||
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<UserProfileHeader title="Created" />}>
|
||||
<AppLayout header={<UserProfileHeader />}>
|
||||
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
||||
</AppLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import useSWR from "swr";
|
|||
import { UserService } from "services/user.service";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/profile-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||
// components
|
||||
import { UserProfileHeader } from "components/headers";
|
||||
import {
|
||||
|
|
@ -56,7 +56,7 @@ const ProfileOverviewPage: NextPageWithLayout = () => {
|
|||
|
||||
ProfileOverviewPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<UserProfileHeader title="Summary" />}>
|
||||
<AppLayout header={<UserProfileHeader />}>
|
||||
<ProfileAuthWrapper>{page}</ProfileAuthWrapper>
|
||||
</AppLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { observer } from "mobx-react-lite";
|
|||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/profile-layout";
|
||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||
// components
|
||||
import { UserProfileHeader } from "components/headers";
|
||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
||||
|
|
@ -61,7 +61,7 @@ const ProfileSubscribedIssuesPage: NextPageWithLayout = () => {
|
|||
|
||||
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<UserProfileHeader title="Subscribed" />}>
|
||||
<AppLayout header={<UserProfileHeader />}>
|
||||
<ProfileAuthWrapper showProfileIssuesFilter>{page}</ProfileAuthWrapper>
|
||||
</AppLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ import { SendWorkspaceInvitationModal, WorkspaceMembersList } from "components/w
|
|||
import { Button } from "@plane/ui";
|
||||
// icons
|
||||
import { Search } from "lucide-react";
|
||||
// helpers
|
||||
import { trackEvent } from "helpers/event-tracker.helper";
|
||||
// types
|
||||
import { NextPageWithLayout } from "types/app";
|
||||
import { IWorkspaceBulkInviteFormData } from "types";
|
||||
import { trackEvent } from "helpers/event-tracker.helper";
|
||||
|
||||
const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
|
||||
const router = useRouter();
|
||||
|
|
@ -36,7 +37,7 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
|
|||
if (!workspaceSlug) return;
|
||||
|
||||
return inviteMembersToWorkspace(workspaceSlug.toString(), data)
|
||||
.then(async (res) => {
|
||||
.then(async () => {
|
||||
setInviteModal(false);
|
||||
trackEvent("WORKSPACE_USER_INVITE");
|
||||
setToastAlert({
|
||||
|
|
@ -67,14 +68,14 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
|
|||
<section className="pr-9 py-8 w-full overflow-y-auto">
|
||||
<div className="flex items-center justify-between gap-4 py-3.5 border-b border-custom-border-100">
|
||||
<h4 className="text-xl font-medium">Members</h4>
|
||||
<div className="flex gap-1 items-center justify-start ml-auto text-custom-text-400 rounded-md px-2.5 py-1.5 border border-custom-border-200 bg-custom-background-100">
|
||||
<Search className="h-3.5 w-3.5" />
|
||||
<div className="flex items-center gap-1.5 ml-auto rounded-md px-2.5 py-1.5 border border-custom-border-200 bg-custom-background-100">
|
||||
<Search className="h-3.5 w-3.5 text-custom-text-400" />
|
||||
<input
|
||||
className="max-w-[234px] w-full border-none bg-transparent text-sm focus:outline-none"
|
||||
placeholder="Search"
|
||||
className="max-w-[234px] w-full border-none bg-transparent text-sm outline-none placeholder:text-custom-text-400"
|
||||
placeholder="Search..."
|
||||
value={searchQuery}
|
||||
autoFocus={true}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<Button variant="primary" size="sm" onClick={() => setInviteModal(true)}>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@ import Link from "next/link";
|
|||
// services
|
||||
import { UserService } from "services/user.service";
|
||||
// layouts
|
||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
||||
// components
|
||||
import { ActivityIcon, ActivityMessage } from "components/core";
|
||||
import { RichReadOnlyEditor } from "@plane/rich-text-editor";
|
||||
import { WorkspaceSettingHeader } from "components/headers";
|
||||
import { ProfileSettingsHeader } from "components/headers";
|
||||
// icons
|
||||
import { History, MessageSquare } from "lucide-react";
|
||||
// ui
|
||||
|
|
@ -28,10 +27,7 @@ const ProfileActivityPage: NextPageWithLayout = () => {
|
|||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { data: userActivity } = useSWR(
|
||||
workspaceSlug ? USER_ACTIVITY : null,
|
||||
workspaceSlug ? () => userService.getUserWorkspaceActivity(workspaceSlug.toString()) : null
|
||||
);
|
||||
const { data: userActivity } = useSWR(USER_ACTIVITY, () => userService.getUserActivity());
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -197,11 +193,7 @@ const ProfileActivityPage: NextPageWithLayout = () => {
|
|||
};
|
||||
|
||||
ProfileActivityPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<WorkspaceSettingHeader title="My Profile Activity" />}>
|
||||
<WorkspaceSettingLayout>{page}</WorkspaceSettingLayout>
|
||||
</AppLayout>
|
||||
);
|
||||
return <ProfileSettingsLayout header={<ProfileSettingsHeader title="Activity" />}>{page}</ProfileSettingsLayout>;
|
||||
};
|
||||
|
||||
export default ProfileActivityPage;
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useEffect, useState, ReactElement } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import { FileService } from "services/file.service";
|
||||
import { UserService } from "services/user.service";
|
||||
|
|
@ -9,15 +8,15 @@ import { UserService } from "services/user.service";
|
|||
import useUserAuth from "hooks/use-user-auth";
|
||||
import useToast from "hooks/use-toast";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
||||
// components
|
||||
import { ImagePickerPopover, ImageUploadModal } from "components/core";
|
||||
import { WorkspaceSettingHeader } from "components/headers";
|
||||
import { ProfileSettingsHeader } from "components/headers";
|
||||
import { DeactivateAccountModal } from "components/account";
|
||||
// ui
|
||||
import { Button, CustomSelect, CustomSearchSelect, Input, Spinner } from "@plane/ui";
|
||||
// icons
|
||||
import { User2, UserCircle2 } from "lucide-react";
|
||||
import { ChevronDown, User2 } from "lucide-react";
|
||||
// types
|
||||
import type { IUser } from "types";
|
||||
import type { NextPageWithLayout } from "types/app";
|
||||
|
|
@ -30,6 +29,7 @@ const defaultValues: Partial<IUser> = {
|
|||
cover_image: "",
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
display_name: "",
|
||||
email: "",
|
||||
role: "Product / Project Manager",
|
||||
user_timezone: "Asia/Kolkata",
|
||||
|
|
@ -38,12 +38,11 @@ const defaultValues: Partial<IUser> = {
|
|||
const fileService = new FileService();
|
||||
const userService = new UserService();
|
||||
|
||||
const ProfilePage: NextPageWithLayout = () => {
|
||||
const ProfileSettingsPage: NextPageWithLayout = () => {
|
||||
const [isRemoving, setIsRemoving] = useState(false);
|
||||
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
const [deactivateAccountModal, setDeactivateAccountModal] = useState(false);
|
||||
|
||||
// form info
|
||||
const {
|
||||
handleSubmit,
|
||||
|
|
@ -143,6 +142,13 @@ const ProfilePage: NextPageWithLayout = () => {
|
|||
content: timeZone.label,
|
||||
}));
|
||||
|
||||
if (!myProfile)
|
||||
return (
|
||||
<div className="grid h-full w-full place-items-center px-4 sm:px-0">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ImageUploadModal
|
||||
|
|
@ -158,9 +164,10 @@ const ProfilePage: NextPageWithLayout = () => {
|
|||
value={watch("avatar") !== "" ? watch("avatar") : undefined}
|
||||
userImage
|
||||
/>
|
||||
{myProfile ? (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="h-full w-full">
|
||||
<div className={`flex flex-col gap-8 pr-9 py-9 w-full h-full overflow-y-auto`}>
|
||||
<DeactivateAccountModal isOpen={deactivateAccountModal} onClose={() => setDeactivateAccountModal(false)} />
|
||||
<div className="h-full w-full flex flex-col py-9 pr-9 space-y-10 overflow-y-auto">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<div className="relative h-44 w-full mt-6">
|
||||
<img
|
||||
src={watch("cover_image") ?? "https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"}
|
||||
|
|
@ -194,14 +201,12 @@ const ProfilePage: NextPageWithLayout = () => {
|
|||
<Controller
|
||||
control={control}
|
||||
name="cover_image"
|
||||
render={() => (
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ImagePickerPopover
|
||||
label={"Change cover"}
|
||||
onChange={(imageUrl) => {
|
||||
setValue("cover_image", imageUrl);
|
||||
}}
|
||||
onChange={(imageUrl) => onChange(imageUrl)}
|
||||
control={control}
|
||||
value={watch("cover_image") ?? "https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"}
|
||||
value={value ?? "https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -216,14 +221,12 @@ const ProfilePage: NextPageWithLayout = () => {
|
|||
<span className="text-sm tracking-tight">{watch("email")}</span>
|
||||
</div>
|
||||
|
||||
<Link href={`/${workspaceSlug}/profile/${myProfile.id}`}>
|
||||
<a className="flex item-center cursor-pointer gap-2 h-4 leading-4 text-sm text-custom-primary-100">
|
||||
<span className="h-4 w-4">
|
||||
<UserCircle2 className="h-4 w-4" />
|
||||
</span>
|
||||
View Profile
|
||||
{/* <Link href={`/profile/${myProfile.id}`}>
|
||||
<a className="flex item-center gap-1 text-sm text-custom-primary-100 underline font-medium">
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
Activity Overview
|
||||
</a>
|
||||
</Link>
|
||||
</Link> */}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-6 px-8">
|
||||
|
|
@ -387,21 +390,49 @@ const ProfilePage: NextPageWithLayout = () => {
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
<div className="grid h-full w-full place-items-center px-4 sm:px-0">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
<Disclosure as="div" className="border-t border-custom-border-100 px-8">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Disclosure.Button as="button" type="button" className="flex items-center justify-between w-full py-4">
|
||||
<span className="text-lg tracking-tight">Deactivate Account</span>
|
||||
{/* <Icon iconName={open ? "expand_less" : "expand_more"} className="!text-2xl" /> */}
|
||||
<ChevronDown className={`h-5 w-5 transition-all ${open ? "rotate-180" : ""}`} />
|
||||
</Disclosure.Button>
|
||||
|
||||
<Transition
|
||||
show={open}
|
||||
enter="transition duration-100 ease-out"
|
||||
enterFrom="transform opacity-0"
|
||||
enterTo="transform opacity-100"
|
||||
leave="transition duration-75 ease-out"
|
||||
leaveFrom="transform opacity-100"
|
||||
leaveTo="transform opacity-0"
|
||||
>
|
||||
<Disclosure.Panel>
|
||||
<div className="flex flex-col gap-8">
|
||||
<span className="text-sm tracking-tight">
|
||||
The danger zone of the profile page is a critical area that requires careful consideration and
|
||||
attention. When deactivating an account, all of the data and resources within that account will be
|
||||
permanently removed and cannot be recovered.
|
||||
</span>
|
||||
<div>
|
||||
<Button variant="danger" onClick={() => setDeactivateAccountModal(true)}>
|
||||
Deactivate account
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ProfilePage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<WorkspaceSettingHeader title="My Profile" />}>
|
||||
<WorkspaceSettingLayout>{page}</WorkspaceSettingLayout>
|
||||
</AppLayout>
|
||||
);
|
||||
ProfileSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <ProfileSettingsLayout header={<ProfileSettingsHeader title="Settings" />}>{page}</ProfileSettingsLayout>;
|
||||
};
|
||||
|
||||
export default ProfilePage;
|
||||
export default ProfileSettingsPage;
|
||||
|
|
@ -5,11 +5,10 @@ import { useTheme } from "next-themes";
|
|||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import useToast from "hooks/use-toast";
|
||||
// layouts
|
||||
import { AppLayout } from "layouts/app-layout";
|
||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
||||
// components
|
||||
import { CustomThemeSelector, ThemeSwitch } from "components/core";
|
||||
import { WorkspaceSettingHeader } from "components/headers";
|
||||
import { ProfileSettingsHeader } from "components/headers";
|
||||
// ui
|
||||
import { Spinner } from "@plane/ui";
|
||||
// constants
|
||||
|
|
@ -76,11 +75,7 @@ const ProfilePreferencesPage: NextPageWithLayout = observer(() => {
|
|||
});
|
||||
|
||||
ProfilePreferencesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<AppLayout header={<WorkspaceSettingHeader title="My Profile Preferences" />}>
|
||||
<WorkspaceSettingLayout>{page}</WorkspaceSettingLayout>
|
||||
</AppLayout>
|
||||
);
|
||||
return <ProfileSettingsLayout header={<ProfileSettingsHeader title="Preferences" />}>{page}</ProfileSettingsLayout>;
|
||||
};
|
||||
|
||||
export default ProfilePreferencesPage;
|
||||
|
|
@ -22,8 +22,8 @@ import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
|||
import { IUser, TOnboardingSteps } from "types";
|
||||
import { NextPageWithLayout } from "types/app";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { Menu, Popover, Transition } from "@headlessui/react";
|
||||
import DeleteAccountModal from "components/account/delete-account-modal";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { DeactivateAccountModal } from "components/account";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// services
|
||||
|
|
@ -101,12 +101,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DeleteAccountModal
|
||||
isOpen={showDeleteModal}
|
||||
onClose={() => {
|
||||
setShowDeleteModal(false);
|
||||
}}
|
||||
/>
|
||||
<DeactivateAccountModal isOpen={showDeleteModal} onClose={() => setShowDeleteModal(false)} />
|
||||
{user && step !== null ? (
|
||||
<div className={`bg-onboarding-gradient-100 h-full flex flex-col fixed w-full`}>
|
||||
<div className="sm:pt-14 sm:pb-8 py-10 px-4 sm:px-7 md:px-14 lg:pl-28 lg:pr-24 flex items-center">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue