diff --git a/apps/app/components/issues/activity.tsx b/apps/app/components/issues/activity.tsx index 2780d3ba1..540d81df9 100644 --- a/apps/app/components/issues/activity.tsx +++ b/apps/app/components/issues/activity.tsx @@ -1,5 +1,6 @@ import React from "react"; +import Link from "next/link"; import { useRouter } from "next/router"; import useSWR from "swr"; @@ -143,13 +144,17 @@ export const IssueActivitySection: React.FC = ({ issueId, user }) => { {activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? ( Plane - ) : ( + ) : activityItem.actor_detail.is_bot ? ( - {activityItem.actor_detail.first_name} - {activityItem.actor_detail.is_bot - ? " Bot" - : " " + activityItem.actor_detail.last_name} + {activityItem.actor_detail.first_name} Bot + ) : ( + + + {activityItem.actor_detail.first_name}{" "} + {activityItem.actor_detail.last_name} + + )}{" "} {message}{" "} diff --git a/apps/app/components/profile/navbar.tsx b/apps/app/components/profile/navbar.tsx index 305ac80d7..e8988d670 100644 --- a/apps/app/components/profile/navbar.tsx +++ b/apps/app/components/profile/navbar.tsx @@ -1,15 +1,26 @@ +import React from "react"; + import { useRouter } from "next/router"; import Link from "next/link"; // components import { ProfileIssuesViewOptions } from "components/profile"; +// types +import { UserAuth } from "types"; -const tabsList = [ +type Props = { + memberRole: UserAuth; +}; + +const viewerTabs = [ { route: "", label: "Overview", selected: "/[workspaceSlug]/profile/[userId]", }, +]; + +const adminTabs = [ { route: "assigned", label: "Assigned", @@ -27,12 +38,17 @@ const tabsList = [ }, ]; -export const ProfileNavbar = () => { +export const ProfileNavbar: React.FC = ({ memberRole }) => { const router = useRouter(); const { workspaceSlug, userId } = router.query; + const tabsList = + memberRole.isOwner || memberRole.isMember || memberRole.isViewer + ? [...viewerTabs, ...adminTabs] + : viewerTabs; + return ( -
+
{tabsList.map((tab) => ( diff --git a/apps/app/components/profile/overview/activity.tsx b/apps/app/components/profile/overview/activity.tsx new file mode 100644 index 000000000..28801ed8f --- /dev/null +++ b/apps/app/components/profile/overview/activity.tsx @@ -0,0 +1,91 @@ +import { useRouter } from "next/router"; +import Link from "next/link"; + +import useSWR from "swr"; + +// services +import userService from "services/user.service"; +// ui +import { Icon, Loader } from "components/ui"; +// helpers +import { activityDetails } from "helpers/activity.helper"; +import { timeAgo } from "helpers/date-time.helper"; +// fetch-keys +import { USER_PROFILE_ACTIVITY } from "constants/fetch-keys"; + +export const ProfileActivity = () => { + const router = useRouter(); + const { workspaceSlug, userId } = router.query; + + const { data: userProfileActivity } = useSWR( + workspaceSlug && userId + ? USER_PROFILE_ACTIVITY(workspaceSlug.toString(), userId.toString()) + : null, + workspaceSlug && userId + ? () => userService.getUserProfileActivity(workspaceSlug.toString(), userId.toString()) + : null + ); + + return ( +
+

Recent Activity

+
+ {userProfileActivity ? ( +
+ {userProfileActivity.results.map((activity) => ( +
+
+ {activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? ( + {activity.actor_detail.first_name} + ) : ( +
+ {activity.actor_detail.first_name.charAt(0)} +
+ )} +
+
+

+ + {activity.actor_detail.first_name} {activity.actor_detail.last_name}{" "} + + {activity.field ? ( + activityDetails[activity.field]?.message(activity as any) + ) : ( + + created this{" "} + + Issue + + + + )} +

+

{timeAgo(activity.created_at)}

+
+
+ ))} +
+ ) : ( + + + + + + + + )} +
+
+ ); +}; diff --git a/apps/app/components/profile/overview/index.ts b/apps/app/components/profile/overview/index.ts index 9aabc6d35..0c69a50de 100644 --- a/apps/app/components/profile/overview/index.ts +++ b/apps/app/components/profile/overview/index.ts @@ -1,3 +1,4 @@ +export * from "./activity"; export * from "./priority-distribution"; export * from "./state-distribution"; export * from "./stats"; diff --git a/apps/app/components/profile/overview/workload.tsx b/apps/app/components/profile/overview/workload.tsx index f1785c0ce..18c902e2d 100644 --- a/apps/app/components/profile/overview/workload.tsx +++ b/apps/app/components/profile/overview/workload.tsx @@ -10,7 +10,7 @@ type Props = { export const ProfileWorkload: React.FC = ({ stateDistribution }) => (

Workload

-
+
{stateDistribution.map((group) => (
@@ -21,7 +21,13 @@ export const ProfileWorkload: React.FC = ({ stateDistribution }) => ( }} />
-

{group.state_group}

+

+ {group.state_group === "unstarted" + ? "Not Started" + : group.state_group === "started" + ? "Working on" + : group.state_group} +

{group.state_count}

diff --git a/apps/app/components/profile/profile-issues-view-options.tsx b/apps/app/components/profile/profile-issues-view-options.tsx index e68a821e9..0441987eb 100644 --- a/apps/app/components/profile/profile-issues-view-options.tsx +++ b/apps/app/components/profile/profile-issues-view-options.tsx @@ -10,7 +10,7 @@ import useEstimateOption from "hooks/use-estimate-option"; // components import { MyIssuesSelectFilters } from "components/issues"; // ui -import { CustomMenu, ToggleSwitch, Tooltip } from "components/ui"; +import { CustomMenu, CustomSearchSelect, ToggleSwitch, Tooltip } from "components/ui"; // icons import { ChevronDownIcon } from "@heroicons/react/24/outline"; import { FormatListBulletedOutlined, GridViewOutlined } from "@mui/icons-material"; @@ -21,6 +21,7 @@ import { checkIfArraysHaveSameElements } from "helpers/array.helper"; import { Properties, TIssueViewOptions } from "types"; // constants import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue"; +import useProjects from "hooks/use-projects"; const issueViewOptions: { type: TIssueViewOptions; Icon: any }[] = [ { @@ -37,6 +38,8 @@ export const ProfileIssuesViewOptions: React.FC = () => { const router = useRouter(); const { workspaceSlug, userId } = router.query; + const { projects } = useProjects(); + const { issueView, setIssueView, @@ -54,12 +57,28 @@ export const ProfileIssuesViewOptions: React.FC = () => { const { isEstimateActive } = useEstimateOption(); + const options = projects?.map((project) => ({ + value: project.id, + query: project.name + " " + project.identifier, + content: project.name, + })); + if ( !router.pathname.includes("assigned") && !router.pathname.includes("created") && !router.pathname.includes("subscribed") ) return null; + // return ( + // console.log(val)} + // label="Filters" + // options={options} + // position="right" + // multiple + // /> + // ); return (
diff --git a/apps/app/components/profile/sidebar.tsx b/apps/app/components/profile/sidebar.tsx index df2cf2642..90945fb89 100644 --- a/apps/app/components/profile/sidebar.tsx +++ b/apps/app/components/profile/sidebar.tsx @@ -1,4 +1,5 @@ import { useRouter } from "next/router"; +import Link from "next/link"; import useSWR from "swr"; @@ -8,10 +9,14 @@ import { useTheme } from "next-themes"; import { Disclosure, Transition } from "@headlessui/react"; // services import userService from "services/user.service"; +// hooks +import useUser from "hooks/use-user"; // ui -import { Icon, Loader } from "components/ui"; +import { Icon, Loader, Tooltip } from "components/ui"; +// icons +import { EditOutlined } from "@mui/icons-material"; // helpers -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; +import { render12HourFormatTime, renderLongDetailDateFormat } from "helpers/date-time.helper"; import { renderEmoji } from "helpers/emoji.helper"; // fetch-keys import { USER_PROFILE_PROJECT_SEGREGATION } from "constants/fetch-keys"; @@ -22,6 +27,8 @@ export const ProfileSidebar = () => { const { theme } = useTheme(); + const { user } = useUser(); + const { data: userProjectsData } = useSWR( workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) @@ -33,27 +40,24 @@ export const ProfileSidebar = () => { ); const userDetails = [ - { - label: "Username", - value: "", - }, { label: "Joined on", value: renderLongDetailDateFormat(userProjectsData?.user_data.date_joined ?? ""), }, { label: "Timezone", - value: userProjectsData?.user_data.user_timezone, - }, - { - label: "Status", - value: "Online", + value: ( + + {render12HourFormatTime(new Date())}{" "} + {userProjectsData?.user_data.user_timezone} + + ), }, ]; return (
{ {userProjectsData ? ( <>
+ {user?.id === userId && ( +
+ + + + + +
+ )} {userProjectsData.user_data.first_name} {
{userDetails.map((detail) => (
-
{detail.label}
-
{detail.value}
+
{detail.label}
+
{detail.value}
))}
@@ -143,17 +160,19 @@ export const ProfileSidebar = () => {
-
- {completedIssuePercentage}% -
+ +
+ {completedIssuePercentage}% +
+
diff --git a/apps/app/components/ui/dropdowns/custom-search-select.tsx b/apps/app/components/ui/dropdowns/custom-search-select.tsx index e40976d13..afbd27e4a 100644 --- a/apps/app/components/ui/dropdowns/custom-search-select.tsx +++ b/apps/app/components/ui/dropdowns/custom-search-select.tsx @@ -22,7 +22,7 @@ export type CustomSearchSelectProps = DropdownProps & { | { multiple?: false; value: any } // if multiple is false, value can be anything | { multiple?: true; - value: any[]; // if multiple is true, value should be an array + value: any[] | null; // if multiple is true, value should be an array } ); @@ -68,7 +68,7 @@ export const CustomSearchSelect = ({ className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`} {...props} > - {({ open }: any) => { + {({ open }: { open: boolean }) => { if (open && onOpen) onOpen(); return ( diff --git a/apps/app/hooks/use-profile-issues.tsx b/apps/app/hooks/use-profile-issues.tsx index 07bff53ba..0a8e9f877 100644 --- a/apps/app/hooks/use-profile-issues.tsx +++ b/apps/app/hooks/use-profile-issues.tsx @@ -73,8 +73,6 @@ const useProfileIssues = (workspaceSlug: string | undefined, userId: string | un useEffect(() => { if (!userId || !filters) return; - console.log("Triggered"); - if ( router.pathname.includes("assigned") && (!filters.assignees || !filters.assignees.includes(userId)) diff --git a/apps/app/hooks/use-projects.tsx b/apps/app/hooks/use-projects.tsx index c537e3a62..3363ff278 100644 --- a/apps/app/hooks/use-projects.tsx +++ b/apps/app/hooks/use-projects.tsx @@ -11,13 +11,17 @@ import { IProject } from "types"; // fetch-keys import { PROJECTS_LIST } from "constants/fetch-keys"; -const useProjects = (type?: "all" | boolean) => { +const useProjects = (type?: "all" | boolean, fetchCondition?: boolean) => { + fetchCondition = fetchCondition ?? true; + const router = useRouter(); const { workspaceSlug } = router.query; const { data: projects, mutate: mutateProjects } = useSWR( - workspaceSlug ? PROJECTS_LIST(workspaceSlug as string, { is_favorite: type ?? "all" }) : null, - workspaceSlug + workspaceSlug && fetchCondition + ? PROJECTS_LIST(workspaceSlug as string, { is_favorite: type ?? "all" }) + : null, + workspaceSlug && fetchCondition ? () => projectService.getProjects(workspaceSlug as string, { is_favorite: type ?? "all" }) : null ); diff --git a/apps/app/layouts/profile-layout.tsx b/apps/app/layouts/profile-layout.tsx new file mode 100644 index 000000000..10c38bb10 --- /dev/null +++ b/apps/app/layouts/profile-layout.tsx @@ -0,0 +1,53 @@ +import { useRouter } from "next/router"; + +// hooks +import { useWorkspaceMyMembership } from "contexts/workspace-member.context"; +// layouts +import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; +// components +import { ProfileNavbar, ProfileSidebar } from "components/profile"; +// ui +import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs"; + +type Props = { + children: React.ReactNode; + className?: string; +}; + +export const ProfileAuthWrapper = (props: Props) => { + const router = useRouter(); + const { workspaceSlug } = router.query; + + return ( + + + + + } + > + + + ); +}; + +const ProfileLayout: React.FC = ({ children, className }) => { + const { memberRole } = useWorkspaceMyMembership(); + + return ( +
+ +
+ + {memberRole.isOwner || memberRole.isMember || memberRole.isViewer ? ( +
{children}
+ ) : ( +
+ You do not have the permission to access this page. +
+ )} +
+
+ ); +}; diff --git a/apps/app/pages/[workspaceSlug]/profile/[userId]/assigned.tsx b/apps/app/pages/[workspaceSlug]/profile/[userId]/assigned.tsx index 0f0c31829..a60116503 100644 --- a/apps/app/pages/[workspaceSlug]/profile/[userId]/assigned.tsx +++ b/apps/app/pages/[workspaceSlug]/profile/[userId]/assigned.tsx @@ -1,48 +1,19 @@ import React from "react"; -import { useRouter } from "next/router"; - // contexts import { ProfileIssuesContextProvider } from "contexts/profile-issues-context"; -// hooks -import useUser from "hooks/use-user"; -// layouts -import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; +import { ProfileAuthWrapper } from "layouts/profile-layout"; // components -import { ProfileIssuesView, ProfileNavbar, ProfileSidebar } from "components/profile"; -// ui -import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; +import { ProfileIssuesView } from "components/profile"; // types import type { NextPage } from "next"; -const ProfileAssignedIssues: NextPage = () => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { user } = useUser(); - - return ( - - - - - - } - > -
-
- -
- -
-
- -
-
-
- ); -}; +const ProfileAssignedIssues: NextPage = () => ( + + + + + +); export default ProfileAssignedIssues; diff --git a/apps/app/pages/[workspaceSlug]/profile/[userId]/created.tsx b/apps/app/pages/[workspaceSlug]/profile/[userId]/created.tsx index 73113eeb2..1cac31e62 100644 --- a/apps/app/pages/[workspaceSlug]/profile/[userId]/created.tsx +++ b/apps/app/pages/[workspaceSlug]/profile/[userId]/created.tsx @@ -1,48 +1,20 @@ import React from "react"; -import { useRouter } from "next/router"; - // contexts import { ProfileIssuesContextProvider } from "contexts/profile-issues-context"; -// hooks -import useUser from "hooks/use-user"; // layouts -import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; +import { ProfileAuthWrapper } from "layouts/profile-layout"; // components -import { ProfileIssuesView, ProfileNavbar, ProfileSidebar } from "components/profile"; -// ui -import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; +import { ProfileIssuesView } from "components/profile"; // types import type { NextPage } from "next"; -const ProfileCreatedIssues: NextPage = () => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { user } = useUser(); - - return ( - - - - - - } - > -
-
- -
- -
-
- -
-
-
- ); -}; +const ProfileCreatedIssues: NextPage = () => ( + + + + + +); export default ProfileCreatedIssues; diff --git a/apps/app/pages/[workspaceSlug]/profile/[userId]/index.tsx b/apps/app/pages/[workspaceSlug]/profile/[userId]/index.tsx index c29c78490..0cf54bb0d 100644 --- a/apps/app/pages/[workspaceSlug]/profile/[userId]/index.tsx +++ b/apps/app/pages/[workspaceSlug]/profile/[userId]/index.tsx @@ -1,34 +1,26 @@ import React from "react"; import { useRouter } from "next/router"; -import Link from "next/link"; import useSWR from "swr"; -// layouts -import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; // services import userService from "services/user.service"; +// layouts +import { ProfileAuthWrapper } from "layouts/profile-layout"; // components import { - ProfileNavbar, + ProfileActivity, ProfilePriorityDistribution, - ProfileSidebar, ProfileStateDistribution, ProfileStats, ProfileWorkload, } from "components/profile"; -// ui -import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; -import { Icon, Loader } from "components/ui"; -// helpers -import { activityDetails } from "helpers/activity.helper"; -import { timeAgo } from "helpers/date-time.helper"; // types import type { NextPage } from "next"; import { IUserStateDistribution, TStateGroups } from "types"; // constants -import { USER_PROFILE_DATA, USER_PROFILE_ACTIVITY } from "constants/fetch-keys"; +import { USER_PROFILE_DATA } from "constants/fetch-keys"; import { GROUP_CHOICES } from "constants/project"; const ProfileOverview: NextPage = () => { @@ -42,15 +34,6 @@ const ProfileOverview: NextPage = () => { : null ); - const { data: userProfileActivity } = useSWR( - workspaceSlug && userId - ? USER_PROFILE_ACTIVITY(workspaceSlug.toString(), userId.toString()) - : null, - workspaceSlug && userId - ? () => userService.getUserProfileActivity(workspaceSlug.toString(), userId.toString()) - : null - ); - const stateDistribution: IUserStateDistribution[] = Object.keys(GROUP_CHOICES).map((key) => { const group = userProfile?.state_distribution.find((g) => g.state_group === key); @@ -59,93 +42,20 @@ const ProfileOverview: NextPage = () => { }); return ( - - - - - } - > -
-
- -
- - -
- - -
-
-

Recent Activity

-
- {userProfileActivity ? ( -
- {userProfileActivity.results.map((activity) => ( -
-
- {activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? ( - {activity.actor_detail.first_name} - ) : ( -
- {activity.actor_detail.first_name.charAt(0)} -
- )} -
-
-

- - {activity.actor_detail.first_name} {activity.actor_detail.last_name}{" "} - - {activity.field ? ( - activityDetails[activity.field]?.message(activity as any) - ) : ( - - created this{" "} - - - Issue - - - - - )} -

-

- {timeAgo(activity.created_at)} -

-
-
- ))} -
- ) : ( - - - - - - - - )} -
-
-
+ +
+ + +
+ +
- +
- +
); }; diff --git a/apps/app/pages/[workspaceSlug]/profile/[userId]/subscribed.tsx b/apps/app/pages/[workspaceSlug]/profile/[userId]/subscribed.tsx index c04681990..3a1ca01ee 100644 --- a/apps/app/pages/[workspaceSlug]/profile/[userId]/subscribed.tsx +++ b/apps/app/pages/[workspaceSlug]/profile/[userId]/subscribed.tsx @@ -1,48 +1,20 @@ import React from "react"; -import { useRouter } from "next/router"; - // contexts import { ProfileIssuesContextProvider } from "contexts/profile-issues-context"; -// hooks -import useUser from "hooks/use-user"; // layouts -import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; +import { ProfileAuthWrapper } from "layouts/profile-layout"; // components -import { ProfileIssuesView, ProfileNavbar, ProfileSidebar } from "components/profile"; -// ui -import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; +import { ProfileIssuesView } from "components/profile"; // types import type { NextPage } from "next"; -const ProfileSubscribedIssues: NextPage = () => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { user } = useUser(); - - return ( - - - - - - } - > -
-
- -
- -
-
- -
-
-
- ); -}; +const ProfileSubscribedIssues: NextPage = () => ( + + + + + +); export default ProfileSubscribedIssues; diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx index 61e285542..0a5897013 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { useRouter } from "next/router"; +import Link from "next/link"; import useSWR from "swr"; @@ -187,9 +188,17 @@ const MembersSettings: NextPage = () => { )}
-

- {member.first_name} {member.last_name} -

+ {member.member ? ( + + + {member.first_name} {member.last_name} + + + ) : ( +

+ {member.first_name} {member.last_name} +

+ )}

{member.email}

diff --git a/apps/app/pages/[workspaceSlug]/settings/index.tsx b/apps/app/pages/[workspaceSlug]/settings/index.tsx index 66284382a..8c76d6e6e 100644 --- a/apps/app/pages/[workspaceSlug]/settings/index.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/index.tsx @@ -14,7 +14,6 @@ import useToast from "hooks/use-toast"; import useUserAuth from "hooks/use-user-auth"; // layouts import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; -import SettingsNavbar from "layouts/settings-navbar"; // components import { ImageUploadModal } from "components/core"; import { DeleteWorkspaceModal, SettingsHeader } from "components/workspace"; diff --git a/apps/app/pages/[workspaceSlug]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/settings/members.tsx index 35d7a03bd..2f6167277 100644 --- a/apps/app/pages/[workspaceSlug]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/members.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import Image from "next/image"; +import Link from "next/link"; import { useRouter } from "next/router"; import useSWR from "swr"; @@ -187,9 +187,17 @@ const MembersSettings: NextPage = () => { )}
-

- {member.first_name} {member.last_name} -

+ {member.member ? ( + + + {member.first_name} {member.last_name} + + + ) : ( +

+ {member.first_name} {member.last_name} +

+ )}

{member.email}