style: profile page responsiveness added (#1710)

* refactor: folder structure

* style: mobile responsiveness added

* chore: add user profile redirection

* chore: profile page authorization
This commit is contained in:
Aaryan Khandelwal 2023-07-31 16:57:28 +05:30 committed by GitHub
parent 406b323e8e
commit 0586d30a33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 323 additions and 270 deletions

View File

@ -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<Props> = ({ issueId, user }) => {
{activityItem.field === "archived_at" &&
activityItem.new_value !== "restore" ? (
<span className="text-gray font-medium">Plane</span>
) : (
) : activityItem.actor_detail.is_bot ? (
<span className="text-gray font-medium">
{activityItem.actor_detail.first_name}
{activityItem.actor_detail.is_bot
? " Bot"
: " " + activityItem.actor_detail.last_name}
{activityItem.actor_detail.first_name} Bot
</span>
) : (
<Link href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}>
<a className="text-gray font-medium">
{activityItem.actor_detail.first_name}{" "}
{activityItem.actor_detail.last_name}
</a>
</Link>
)}{" "}
{message}{" "}
<span className="whitespace-nowrap">

View File

@ -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<Props> = ({ memberRole }) => {
const router = useRouter();
const { workspaceSlug, userId } = router.query;
const tabsList =
memberRole.isOwner || memberRole.isMember || memberRole.isViewer
? [...viewerTabs, ...adminTabs]
: viewerTabs;
return (
<div className="px-4 sm:px-5 flex items-center justify-between gap-4 border-b border-custom-border-300">
<div className="sticky -top-0.5 z-[1] md:static px-4 sm:px-5 flex items-center justify-between gap-4 bg-custom-background-100 border-b border-custom-border-300">
<div className="flex items-center overflow-x-scroll">
{tabsList.map((tab) => (
<Link key={tab.route} href={`/${workspaceSlug}/profile/${userId}/${tab.route}`}>

View File

@ -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 (
<div className="space-y-2">
<h3 className="text-lg font-medium">Recent Activity</h3>
<div className="border border-custom-border-100 rounded p-6">
{userProfileActivity ? (
<div className="space-y-5">
{userProfileActivity.results.map((activity) => (
<div key={activity.id} className="flex gap-3">
<div className="flex-shrink-0">
{activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
<img
src={activity.actor_detail.avatar}
alt={activity.actor_detail.first_name}
height={24}
width={24}
className="rounded"
/>
) : (
<div className="grid h-6 w-6 place-items-center rounded border-2 bg-gray-700 text-xs text-white">
{activity.actor_detail.first_name.charAt(0)}
</div>
)}
</div>
<div className="-mt-1 w-4/5 break-words">
<p className="text-sm text-custom-text-200">
<span className="font-medium text-custom-text-100">
{activity.actor_detail.first_name} {activity.actor_detail.last_name}{" "}
</span>
{activity.field ? (
activityDetails[activity.field]?.message(activity as any)
) : (
<span>
created this{" "}
<a
href={`/${activity.workspace_detail.slug}/projects/${activity.project}/issues/${activity.issue}`}
target="_blank"
rel="noopener noreferrer"
className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline"
>
Issue
<Icon iconName="launch" className="!text-xs" />
</a>
</span>
)}
</p>
<p className="text-xs text-custom-text-200">{timeAgo(activity.created_at)}</p>
</div>
</div>
))}
</div>
) : (
<Loader className="space-y-5">
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
</Loader>
)}
</div>
</div>
);
};

View File

@ -1,3 +1,4 @@
export * from "./activity";
export * from "./priority-distribution";
export * from "./state-distribution";
export * from "./stats";

View File

@ -10,7 +10,7 @@ type Props = {
export const ProfileWorkload: React.FC<Props> = ({ stateDistribution }) => (
<div className="space-y-2">
<h3 className="text-lg font-medium">Workload</h3>
<div className="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-5 gap-4 justify-stretch">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-4 justify-stretch">
{stateDistribution.map((group) => (
<div key={group.state_group}>
<a className="flex gap-2 p-4 rounded border border-custom-border-100 whitespace-nowrap">
@ -21,7 +21,13 @@ export const ProfileWorkload: React.FC<Props> = ({ stateDistribution }) => (
}}
/>
<div className="space-y-1 -mt-1">
<p className="text-custom-text-400 text-sm capitalize">{group.state_group}</p>
<p className="text-custom-text-400 text-sm capitalize">
{group.state_group === "unstarted"
? "Not Started"
: group.state_group === "started"
? "Working on"
: group.state_group}
</p>
<p className="text-xl font-semibold">{group.state_count}</p>
</div>
</a>

View File

@ -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 (
// <CustomSearchSelect
// value={projects ?? null}
// onChange={(val: string[] | null) => console.log(val)}
// label="Filters"
// options={options}
// position="right"
// multiple
// />
// );
return (
<div className="flex items-center gap-2">

View File

@ -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: (
<span>
{render12HourFormatTime(new Date())}{" "}
<span className="text-custom-text-200">{userProjectsData?.user_data.user_timezone}</span>
</span>
),
},
];
return (
<div
className="flex-shrink-0 h-full w-80 overflow-y-auto"
className="flex-shrink-0 md:h-full w-full md:w-80 overflow-y-auto"
style={{
boxShadow:
theme === "light"
@ -64,10 +68,23 @@ export const ProfileSidebar = () => {
{userProjectsData ? (
<>
<div className="relative h-32">
{user?.id === userId && (
<div className="absolute top-3.5 right-3.5 h-5 w-5 bg-white rounded grid place-items-center">
<Link href={`/${workspaceSlug}/me/profile`}>
<a className="grid place-items-center text-black">
<EditOutlined
sx={{
fontSize: 12,
}}
/>
</a>
</Link>
</div>
)}
<img
src={
userProjectsData.user_data.cover_image ??
"https://images.unsplash.com/photo-1672243775941-10d763d9adef?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80"
"https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"
}
alt={userProjectsData.user_data.first_name}
className="h-32 w-full object-cover"
@ -96,8 +113,8 @@ export const ProfileSidebar = () => {
<div className="mt-6 space-y-5">
{userDetails.map((detail) => (
<div key={detail.label} className="flex items-center gap-4 text-sm">
<div className="text-custom-text-200 w-2/5">{detail.label}</div>
<div className="font-medium">{detail.value}</div>
<div className="flex-shrink-0 text-custom-text-200 w-2/5">{detail.label}</div>
<div className="font-medium w-3/5 break-words">{detail.value}</div>
</div>
))}
</div>
@ -143,6 +160,7 @@ export const ProfileSidebar = () => {
</div>
</div>
<div className="flex-shrink-0 flex items-center gap-2">
<Tooltip tooltipContent="Completion percentage" position="left">
<div
className={`px-1 py-0.5 text-xs font-medium rounded ${
completedIssuePercentage <= 35
@ -154,6 +172,7 @@ export const ProfileSidebar = () => {
>
{completedIssuePercentage}%
</div>
</Tooltip>
<Icon iconName="arrow_drop_down" className="!text-lg" />
</div>
</Disclosure.Button>

View File

@ -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 (

View File

@ -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))

View File

@ -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
);

View File

@ -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 (
<WorkspaceAuthorizationLayout
breadcrumbs={
<Breadcrumbs>
<BreadcrumbItem title="Projects" link={`/${workspaceSlug}/projects`} />
<BreadcrumbItem title={`User Name`} />
</Breadcrumbs>
}
>
<ProfileLayout {...props} />
</WorkspaceAuthorizationLayout>
);
};
const ProfileLayout: React.FC<Props> = ({ children, className }) => {
const { memberRole } = useWorkspaceMyMembership();
return (
<div className="h-full w-full md:flex md:flex-row-reverse md:overflow-hidden">
<ProfileSidebar />
<div className="md:h-full w-full flex flex-col md:overflow-hidden">
<ProfileNavbar memberRole={memberRole} />
{memberRole.isOwner || memberRole.isMember || memberRole.isViewer ? (
<div className={`md:h-full w-full overflow-hidden ${className}`}>{children}</div>
) : (
<div className="h-full w-full grid place-items-center text-custom-text-200">
You do not have the permission to access this page.
</div>
)}
</div>
</div>
);
};

View File

@ -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 = () => (
<ProfileIssuesContextProvider>
<WorkspaceAuthorizationLayout
breadcrumbs={
<Breadcrumbs>
<BreadcrumbItem title="Settings" link={`/${workspaceSlug}/me/profile`} />
<BreadcrumbItem title={`${user?.first_name} ${user?.last_name}`} />
</Breadcrumbs>
}
>
<div className="h-full w-full flex overflow-hidden">
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileNavbar />
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileAuthWrapper>
<ProfileIssuesView />
</div>
</div>
<ProfileSidebar />
</div>
</WorkspaceAuthorizationLayout>
</ProfileAuthWrapper>
</ProfileIssuesContextProvider>
);
};
export default ProfileAssignedIssues;

View File

@ -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 = () => (
<ProfileIssuesContextProvider>
<WorkspaceAuthorizationLayout
breadcrumbs={
<Breadcrumbs>
<BreadcrumbItem title="Settings" link={`/${workspaceSlug}/me/profile`} />
<BreadcrumbItem title={`${user?.first_name} ${user?.last_name}`} />
</Breadcrumbs>
}
>
<div className="h-full w-full flex overflow-hidden">
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileNavbar />
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileAuthWrapper>
<ProfileIssuesView />
</div>
</div>
<ProfileSidebar />
</div>
</WorkspaceAuthorizationLayout>
</ProfileAuthWrapper>
</ProfileIssuesContextProvider>
);
};
export default ProfileCreatedIssues;

View File

@ -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,18 +42,8 @@ const ProfileOverview: NextPage = () => {
});
return (
<WorkspaceAuthorizationLayout
breadcrumbs={
<Breadcrumbs>
<BreadcrumbItem title="Projects" link={`/${workspaceSlug}/projects`} />
<BreadcrumbItem title={`User Name`} />
</Breadcrumbs>
}
>
<div className="h-full w-full flex overflow-hidden">
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileNavbar />
<div className="h-full w-full overflow-y-auto px-9 py-5 space-y-7">
<ProfileAuthWrapper>
<div className="h-full w-full px-5 md:px-9 py-5 space-y-7 overflow-y-auto">
<ProfileStats userProfile={userProfile} />
<ProfileWorkload stateDistribution={stateDistribution} />
<div className="grid grid-cols-1 xl:grid-cols-2 items-stretch gap-5">
@ -80,72 +53,9 @@ const ProfileOverview: NextPage = () => {
userProfile={userProfile}
/>
</div>
<div className="space-y-2">
<h3 className="text-lg font-medium">Recent Activity</h3>
<div className="border border-custom-border-100 rounded p-6">
{userProfileActivity ? (
<div className="space-y-5">
{userProfileActivity.results.map((activity) => (
<div key={activity.id} className="flex gap-3">
<div className="flex-shrink-0">
{activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
<img
src={activity.actor_detail.avatar}
alt={activity.actor_detail.first_name}
height={24}
width={24}
className="rounded"
/>
) : (
<div className="grid h-6 w-6 place-items-center rounded border-2 bg-gray-700 text-xs text-white">
{activity.actor_detail.first_name.charAt(0)}
<ProfileActivity />
</div>
)}
</div>
<div className="-mt-1">
<p className="text-sm text-custom-text-200">
<span className="font-medium text-custom-text-100">
{activity.actor_detail.first_name} {activity.actor_detail.last_name}{" "}
</span>
{activity.field ? (
activityDetails[activity.field]?.message(activity as any)
) : (
<span>
created this{" "}
<Link
href={`/${activity.workspace_detail.slug}/projects/${activity.project}/issues/${activity.issue}`}
>
<a className="font-medium text-custom-text-100 inline-flex items-center gap-1 hover:underline">
Issue
<Icon iconName="launch" className="!text-xs" />
</a>
</Link>
</span>
)}
</p>
<p className="text-xs text-custom-text-200">
{timeAgo(activity.created_at)}
</p>
</div>
</div>
))}
</div>
) : (
<Loader className="space-y-5">
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
</Loader>
)}
</div>
</div>
</div>
</div>
<ProfileSidebar />
</div>
</WorkspaceAuthorizationLayout>
</ProfileAuthWrapper>
);
};

View File

@ -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 = () => (
<ProfileIssuesContextProvider>
<WorkspaceAuthorizationLayout
breadcrumbs={
<Breadcrumbs>
<BreadcrumbItem title="Settings" link={`/${workspaceSlug}/me/profile`} />
<BreadcrumbItem title={`${user?.first_name} ${user?.last_name}`} />
</Breadcrumbs>
}
>
<div className="h-full w-full flex overflow-hidden">
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileNavbar />
<div className="h-full w-full flex flex-col overflow-hidden">
<ProfileAuthWrapper>
<ProfileIssuesView />
</div>
</div>
<ProfileSidebar />
</div>
</WorkspaceAuthorizationLayout>
</ProfileAuthWrapper>
</ProfileIssuesContextProvider>
);
};
export default ProfileSubscribedIssues;

View File

@ -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 = () => {
)}
</div>
<div>
{member.member ? (
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
<a className="text-sm">
{member.first_name} {member.last_name}
</a>
</Link>
) : (
<h4 className="text-sm">
{member.first_name} {member.last_name}
</h4>
)}
<p className="mt-0.5 text-xs text-custom-text-200">{member.email}</p>
</div>
</div>

View File

@ -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";

View File

@ -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 = () => {
)}
</div>
<div>
{member.member ? (
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
<a className="text-sm">
{member.first_name} {member.last_name}
</a>
</Link>
) : (
<h4 className="text-sm">
{member.first_name} {member.last_name}
</h4>
)}
<p className="text-xs text-custom-text-200">{member.email}</p>
</div>
</div>