diff --git a/packages/types/src/issues/issue.d.ts b/packages/types/src/issues/issue.d.ts index c9cd133ef06..41763ffeb9a 100644 --- a/packages/types/src/issues/issue.d.ts +++ b/packages/types/src/issues/issue.d.ts @@ -1,7 +1,7 @@ -import {TIssuePriorities} from "../issues"; -import {TIssueAttachment} from "./issue_attachment"; -import {TIssueLink} from "./issue_link"; -import {TIssueReaction} from "./issue_reaction"; +import { TIssuePriorities } from "../issues"; +import { TIssueAttachment } from "./issue_attachment"; +import { TIssueLink } from "./issue_link"; +import { TIssueReaction } from "./issue_reaction"; // new issue structure types @@ -42,7 +42,7 @@ export type TBaseIssue = { export type TIssue = TBaseIssue & { description_html?: string; is_subscribed?: boolean; - parent?: Partial; + parent?: partial; issue_reactions?: TIssueReaction[]; issue_attachment?: TIssueAttachment[]; issue_link?: TIssueLink[]; diff --git a/web/app/[workspaceSlug]/(projects)/profile/[userId]/header.tsx b/web/app/[workspaceSlug]/(projects)/profile/[userId]/header.tsx index f39ebfc44f0..743108dd49f 100644 --- a/web/app/[workspaceSlug]/(projects)/profile/[userId]/header.tsx +++ b/web/app/[workspaceSlug]/(projects)/profile/[userId]/header.tsx @@ -6,44 +6,53 @@ import { observer } from "mobx-react"; import Link from "next/link"; import { useParams } from "next/navigation"; import { ChevronDown, PanelRight } from "lucide-react"; +import { IUserProfileProjectSegregation } from "@plane/types"; import { Breadcrumbs, CustomMenu } from "@plane/ui"; import { BreadcrumbLink } from "@/components/common"; // components +import { ProfileIssuesFilter } from "@/components/profile"; import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile"; +import { EUserWorkspaceRoles } from "@/constants/workspace"; import { cn } from "@/helpers/common.helper"; import { useAppTheme, useUser } from "@/hooks/store"; type TUserProfileHeader = { + userProjectsData: IUserProfileProjectSegregation | undefined; type?: string | undefined; + showProfileIssuesFilter?: boolean; }; export const UserProfileHeader: FC = observer((props) => { - const { type = undefined } = props; + const { userProjectsData, type = undefined, showProfileIssuesFilter } = props; // router const { workspaceSlug, userId } = useParams(); // store hooks const { toggleProfileSidebar, profileSidebarCollapsed } = useAppTheme(); const { membership: { currentWorkspaceRole }, + data: currentUser, } = useUser(); // derived values - const AUTHORIZED_ROLES = [20, 15, 10]; + const isAuthorized = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.VIEWER; if (!currentWorkspaceRole) return null; - const isAuthorized = AUTHORIZED_ROLES.includes(currentWorkspaceRole); const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB; + const userName = `${userProjectsData?.user_data?.first_name} ${userProjectsData?.user_data?.last_name}`; + + const isCurrentUser = currentUser?.id === userId; + + const breadcrumbLabel = `${isCurrentUser ? "Your" : userName} Activity`; + return (
- } - /> + } /> +
{showProfileIssuesFilter && }
= observer((props) => { const { membership: { currentWorkspaceRole }, } = useUser(); + + const windowSize = useSize(); + const isSmallerScreen = windowSize[0] >= 768; + + const { data: userProjectsData } = useSWR( + workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null, + workspaceSlug && userId + ? () => userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString()) + : null + ); // derived values const isAuthorized = currentWorkspaceRole && AUTHORIZED_ROLES.includes(currentWorkspaceRole); const isAuthorizedPath = @@ -43,25 +59,36 @@ const UseProfileLayout: React.FC = observer((props) => { <> {/* Passing the type prop from the current route value as we need the header as top most component. TODO: We are depending on the route path to handle the mobile header type. If the path changes, this logic will break. */} - } - mobileHeader={isIssuesTab && } - /> - -
-
- - {isAuthorized || !isAuthorizedPath ? ( -
{children}
- ) : ( -
- You do not have the permission to access this page. +
+
+ + } + mobileHeader={isIssuesTab && } + /> + +
+
+ + {isAuthorized || !isAuthorizedPath ? ( +
{children}
+ ) : ( +
+ You do not have the permission to access this page. +
+ )}
- )} -
- + {!isSmallerScreen && } +
+
- + {isSmallerScreen && } +
); }); diff --git a/web/app/[workspaceSlug]/(projects)/profile/[userId]/navbar.tsx b/web/app/[workspaceSlug]/(projects)/profile/[userId]/navbar.tsx index c54cdfd568e..39fe4c6d58a 100644 --- a/web/app/[workspaceSlug]/(projects)/profile/[userId]/navbar.tsx +++ b/web/app/[workspaceSlug]/(projects)/profile/[userId]/navbar.tsx @@ -4,31 +4,29 @@ import Link from "next/link"; import { useParams, usePathname } from "next/navigation"; // components -import { ProfileIssuesFilter } from "@/components/profile"; // constants import { PROFILE_ADMINS_TAB, PROFILE_VIEWER_TAB } from "@/constants/profile"; type Props = { isAuthorized: boolean; - showProfileIssuesFilter?: boolean; }; export const ProfileNavbar: React.FC = (props) => { - const { isAuthorized, showProfileIssuesFilter } = props; + const { isAuthorized } = props; const { workspaceSlug, userId } = useParams(); -const pathname = usePathname(); + const pathname = usePathname(); const tabsList = isAuthorized ? [...PROFILE_VIEWER_TAB, ...PROFILE_ADMINS_TAB] : PROFILE_VIEWER_TAB; return ( -
+
{tabsList.map((tab) => ( ))}
- {showProfileIssuesFilter && }
); }; diff --git a/web/core/components/profile/sidebar.tsx b/web/core/components/profile/sidebar.tsx index 389060c286a..3d533601ae7 100644 --- a/web/core/components/profile/sidebar.tsx +++ b/web/core/components/profile/sidebar.tsx @@ -1,50 +1,49 @@ "use client"; -import { useEffect, useRef } from "react"; +import { FC, useEffect, useRef } from "react"; import { observer } from "mobx-react"; import Link from "next/link"; import { useParams } from "next/navigation"; -import useSWR from "swr"; // icons import { ChevronDown, Pencil } from "lucide-react"; // headless ui import { Disclosure, Transition } from "@headlessui/react"; +// types +import { IUserProfileProjectSegregation } from "@plane/types"; // plane ui import { Loader, Tooltip } from "@plane/ui"; // components import { Logo } from "@/components/common"; // fetch-keys -import { USER_PROFILE_PROJECT_SEGREGATION } from "@/constants/fetch-keys"; // helpers +import { cn } from "@/helpers/common.helper"; import { renderFormattedDate } from "@/helpers/date-time.helper"; // hooks import { useAppTheme, useProject, useUser } from "@/hooks/store"; import useOutsideClickDetector from "@/hooks/use-outside-click-detector"; import { usePlatformOS } from "@/hooks/use-platform-os"; // services -import { UserService } from "@/services/user.service"; // components import { ProfileSidebarTime } from "./time"; // services -const userService = new UserService(); -export const ProfileSidebar = observer(() => { +type TProfileSidebar = { + userProjectsData: IUserProfileProjectSegregation | undefined; + className?: string; +}; + +export const ProfileSidebar: FC = observer((props) => { + const { userProjectsData, className = "" } = props; // refs const ref = useRef(null); // router - const { workspaceSlug, userId } = useParams(); + const { userId } = useParams(); // store hooks const { data: currentUser } = useUser(); const { profileSidebarCollapsed, toggleProfileSidebar } = useAppTheme(); const { getProjectById } = useProject(); const { isMobile } = usePlatformOS(); - const { data: userProjectsData } = useSWR( - workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null, - workspaceSlug && userId - ? () => userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString()) - : null - ); useOutsideClickDetector(ref, () => { if (profileSidebarCollapsed === false) { @@ -82,12 +81,15 @@ export const ProfileSidebar = observer(() => { return (
{userProjectsData ? ( <> -
+
{currentUser?.id === userId && (
@@ -100,7 +102,7 @@ export const ProfileSidebar = observer(() => { {userProjectsData.user_data?.display_name}
{userProjectsData.user_data?.avatar && userProjectsData.user_data?.avatar !== "" ? ( diff --git a/web/core/components/workspace/sidebar/user-menu.tsx b/web/core/components/workspace/sidebar/user-menu.tsx index 54d67e808cb..dccb50f64d7 100644 --- a/web/core/components/workspace/sidebar/user-menu.tsx +++ b/web/core/components/workspace/sidebar/user-menu.tsx @@ -34,6 +34,9 @@ export const SidebarUserMenu = observer(() => { // computed const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST; + const getHref = (link: any) => + `/${workspaceSlug}${link.href}${link.key === "your-work" ? `/${currentUser?.id}` : ""}`; + const handleLinkClick = (itemKey: string) => { if (window.innerWidth < 768) { toggleSidebar(); @@ -67,14 +70,10 @@ export const SidebarUserMenu = observer(() => { disabled={!sidebarCollapsed} isMobile={isMobile} > - handleLinkClick(link.key)} - > + handleLinkClick(link.key)}>
diff --git a/web/core/constants/dashboard.ts b/web/core/constants/dashboard.ts index 6bf2b1ca6ea..a723a40ba6f 100644 --- a/web/core/constants/dashboard.ts +++ b/web/core/constants/dashboard.ts @@ -293,12 +293,16 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: { }, ]; +type TLinkOptions = { + userId: string | undefined; +}; + export const SIDEBAR_USER_MENU_ITEMS: { key: string; label: string; href: string; access: EUserWorkspaceRoles; - highlight: (pathname: string, baseUrl: string) => boolean; + highlight: (pathname: string, baseUrl: string, options?: TLinkOptions) => boolean; Icon: React.FC; }[] = [ { @@ -314,7 +318,8 @@ export const SIDEBAR_USER_MENU_ITEMS: { label: "Your work", href: "/profile", access: EUserWorkspaceRoles.GUEST, - highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/profile/`), + highlight: (pathname: string, baseUrl: string, options?: TLinkOptions) => + options?.userId ? pathname.includes(`${baseUrl}/profile/${options?.userId}`) : false, Icon: UserActivityIcon, }, { diff --git a/web/core/constants/profile.ts b/web/core/constants/profile.ts index cf5066177e6..479cb825bd6 100644 --- a/web/core/constants/profile.ts +++ b/web/core/constants/profile.ts @@ -31,26 +31,26 @@ export const PROFILE_ACTION_LINKS: { Icon: Activity, }, { - key: "appearance", - label: "Appearance", - href: `/profile/appearance`, - highlight: (pathname: string) => pathname.includes("/profile/appearance"), - Icon: Settings2, - }, - { - key: "notifications", - label: "Notifications", - href: `/profile/notifications`, - highlight: (pathname: string) => pathname === "/profile/notifications/", - Icon: Bell, - }, + key: "appearance", + label: "Appearance", + href: `/profile/appearance`, + highlight: (pathname: string) => pathname.includes("/profile/appearance"), + Icon: Settings2, + }, + { + key: "notifications", + label: "Notifications", + href: `/profile/notifications`, + highlight: (pathname: string) => pathname === "/profile/notifications/", + Icon: Bell, + }, ]; export const PROFILE_VIEWER_TAB = [ { route: "", label: "Summary", - selected: "", + selected: "/", }, ]; @@ -58,21 +58,21 @@ export const PROFILE_ADMINS_TAB = [ { route: "assigned", label: "Assigned", - selected: "/assigned", + selected: "/assigned/", }, { route: "created", label: "Created", - selected: "/created", + selected: "/created/", }, { route: "subscribed", label: "Subscribed", - selected: "/subscribed", + selected: "/subscribed/", }, { route: "activity", label: "Activity", - selected: "/activity", + selected: "/activity/", }, ];