Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 49 additions & 112 deletions frontend/src/app/members/[memberKey]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,30 @@
'use client'
import { useQuery } from '@apollo/client/react'
import Image from 'next/image'
import Link from 'next/link'
import { useParams } from 'next/navigation'
import React, { useEffect, useMemo } from 'react'
import { FaCodeMerge, FaFolderOpen, FaPersonWalkingArrowRight, FaUserPlus } from 'react-icons/fa6'
import { handleAppError, ErrorDisplay } from 'app/global-error'

import { GetUserDataDocument } from 'types/__generated__/userQueries.generated'
import { Badge } from 'types/badge'
import { User } from 'types/user'
import { formatDate } from 'utils/dateFormatter'
import Badges from 'components/Badges'
import Contributions from 'components/cards/Contributions'
import Contributors from 'components/cards/Contributors'
import Header from 'components/cards/Header'
import IssuesMilestones from 'components/cards/IssuesMilestones'
import Metadata from 'components/cards/Metadata'
import MemberDetailSidebar from 'components/cards/MemberDetailSidebar'
import PageWrapper from 'components/cards/PageWrapper'
import RepositoriesModules from 'components/cards/RepositoriesModules'
import Summary from 'components/cards/Summary'
import ContributionHeatmap from 'components/ContributionHeatmap'
import Milestones from 'components/Milestones'
import RecentIssues from 'components/RecentIssues'
import RecentPullRequests from 'components/RecentPullRequests'
import RecentReleases from 'components/RecentReleases'
import SecondaryCard from 'components/SecondaryCard'
import MemberDetailsPageSkeleton from 'components/skeletons/MemberDetailsPageSkeleton'

type DateRange = { startDate: string; endDate: string }

interface UserSummaryProps {
user: User | null
contributionData: Record<string, number>
dateRange: DateRange
hasContributionData: boolean
formattedBio: React.ReactNode
}

export const UserSummary: React.FC<UserSummaryProps> = ({
user,
contributionData,
dateRange,
hasContributionData,
formattedBio,
}) => (
<div className="mt-4 flex flex-col items-center lg:flex-row">
<Image
width={200}
height={200}
className="mr-4 h-[200px] w-[200px] rounded-full border-2 border-white bg-white object-cover shadow-md dark:border-gray-800 dark:bg-gray-600/60"
src={user?.avatarUrl || '/placeholder.svg'}
alt={user?.name || user?.login || 'User Avatar'}
/>
<div className="w-full overflow-x-auto text-center lg:text-left">
<div className="pl-0 lg:pl-4">
<div className="flex items-center justify-center gap-3 text-center text-sm text-gray-500 lg:justify-start lg:text-left dark:text-gray-400">
<Link href={user?.url || '#'} className="text-xl font-bold text-blue-400 hover:underline">
@{user?.login}
</Link>
{user?.badges && user.badges.length > 0 && (
<div className="flex flex-wrap gap-2">
{user.badges.slice().map((badge: Badge) => (
<React.Fragment key={badge.id}>
<Badges
name={badge.name}
cssClass={badge.cssClass || 'medal'}
showTooltip={true}
/>
</React.Fragment>
))}
</div>
)}
</div>
<p className="text-gray-600 dark:text-gray-400">{formattedBio}</p>
</div>
{hasContributionData && dateRange.startDate && dateRange.endDate && (
<div className="w-full lg:block">
<div className="overflow-x-auto rounded-lg bg-gray-100 dark:bg-gray-800">
<ContributionHeatmap
contributionData={contributionData}
startDate={dateRange.startDate}
endDate={dateRange.endDate}
variant="medium"
/>
</div>
</div>
)}
</div>
</div>
export const UserSummary: React.FC<UserSummaryProps> = ({ user, formattedBio }) => (
<MemberDetailSidebar user={user as User} formattedBio={formattedBio} />
)
Comment thread
Saurabh-2607 marked this conversation as resolved.

const UserDetailsPage: React.FC = () => {
Expand Down Expand Up @@ -181,56 +122,52 @@ const UserDetailsPage: React.FC = () => {
)
}

const userDetails = [
{ label: 'Joined', value: user?.createdAt ? formatDate(user.createdAt) : 'Not available' },
{ label: 'Email', value: user?.email || 'N/A' },
{ label: 'Company', value: user?.company || 'N/A' },
{ label: 'Location', value: user?.location || 'N/A' },
]

const userStats = [
{ icon: FaPersonWalkingArrowRight, value: user?.followersCount || 0, unit: 'Follower' },
{ icon: FaUserPlus, value: user?.followingCount || 0, unit: 'Following' },
{
icon: FaFolderOpen,
pluralizedName: 'Repositories',
unit: 'Repository',
value: user?.publicRepositoriesCount ?? 0,
},
{ icon: FaCodeMerge, value: user?.contributionsCount || 0, unit: 'Contribution' },
]

return (
<PageWrapper>
<Header title={user?.name || user?.login} isActive={true} isArchived={false} />

<Summary
userSummary={
<UserSummary
user={user}
contributionData={contributionData}
dateRange={dateRange}
hasContributionData={hasContributionData}
formattedBio={formattedBio}
/>
}
/>

<Metadata details={userDetails} stats={userStats} detailsTitle="User Details" />

<Contributions hasContributions={false} />
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-6 xl:flex-row xl:items-start">
<aside className="w-full shrink-0 xl:w-[18rem]">
<UserSummary user={user} formattedBio={formattedBio} />
Comment thread
Saurabh-2607 marked this conversation as resolved.
</aside>

<div className="flex min-w-0 flex-1 flex-col gap-6">
{hasContributionData && dateRange.startDate && dateRange.endDate && (
<SecondaryCard className="mb-0!">
<div className="overflow-x-auto rounded-lg bg-gray-100 dark:bg-gray-800">
<ContributionHeatmap
Comment thread
Saurabh-2607 marked this conversation as resolved.
contributionData={contributionData}
startDate={dateRange.startDate}
endDate={dateRange.endDate}
variant="medium"
/>
</div>
</SecondaryCard>
)}

<Contributors />
<div className="flex flex-col [&>div]:mb-0! [&>div]:grow">
<RepositoriesModules repositories={topRepositories} />
</div>

<IssuesMilestones
recentIssues={issues}
recentMilestones={milestones}
pullRequests={pullRequests}
recentReleases={releases}
showAvatar={false}
/>
<div className="grid grow grid-cols-1 gap-6 md:grid-cols-2">
<div className="flex flex-col [&>div]:mb-0! [&>div]:grow">
<RecentIssues data={issues} showAvatar={false} />
</div>
<div className="flex flex-col [&>div]:mb-0! [&>div]:grow">
<RecentPullRequests data={pullRequests} showAvatar={false} />
</div>
</div>

<RepositoriesModules repositories={topRepositories} />
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="flex flex-col [&>div]:mb-0! [&>div]:grow">
<Milestones data={milestones} showAvatar={false} />
</div>
<div className="flex flex-col [&>div]:mb-0! [&>div]:grow">
<RecentReleases data={releases} showAvatar={false} showSingleColumn={true} />
</div>
Comment thread
Saurabh-2607 marked this conversation as resolved.
Outdated
</div>
</div>
</div>
</div>
</PageWrapper>
)
}
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/components/ContributionHeatmap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ const ContributionHeatmap: React.FC<ContributionHeatmapProps> = ({
)}

{/* scroll wrapper for small screens */}
<div className="scrollbar-default w-full overflow-x-auto overflow-y-hidden">
<div className="scrollbar-hidden w-full overflow-x-auto overflow-y-hidden">
<style>
{`
.apexcharts-tooltip {
Expand All @@ -327,10 +327,13 @@ const ContributionHeatmap: React.FC<ContributionHeatmapProps> = ({
.apexcharts-tooltip * {
border: none !important;
}
.scrollbar-default {
scrollbar-width: thin;
.scrollbar-hidden {
scrollbar-width: none;
-ms-overflow-style: none;
}
.scrollbar-hidden::-webkit-scrollbar {
display: none;
}

`}
</style>

Expand Down
18 changes: 7 additions & 11 deletions frontend/src/components/InfoBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
const InfoBlock = ({
className = '',
icon,
label = '',
pluralizedName,
precision = 1,
unit = '',
value = 0,
}: {
className?: string
icon: IconType
icon?: IconType
label?: string

Check warning on line 17 in frontend/src/components/InfoBlock.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'label' PropType is defined but prop is never used

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ539NdwxhoxqMM99hOw&open=AZ539NdwxhoxqMM99hOw&pullRequest=4797
pluralizedName?: string
precision?: number
unit?: string
Expand All @@ -26,15 +25,12 @@
const tooltipValue = value ? `${value.toLocaleString()} ${name}` : `No ${name}`

return (
<div className={`flex ${className}`}>
<IconWrapper icon={icon} className="mt-1 mr-3 w-5" />
<div>
<div className="text-sm md:text-base">
{label && <div className="text-sm font-medium">{label}</div>}
<Tooltip content={tooltipValue} delay={100} closeDelay={100} showArrow placement="top">
{formattedValue}
</Tooltip>
</div>
<div className={`flex items-center gap-2 ${className}`}>
{icon && <IconWrapper icon={icon} className="w-5 text-gray-500" />}
<div className="text-base text-gray-600 dark:text-gray-300">
<Tooltip content={tooltipValue} delay={100} closeDelay={100} showArrow placement="top">
{formattedValue}
</Tooltip>
</div>
</div>
)
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/ItemCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const ItemCardList = ({
<SecondaryCard icon={icon} title={title}>
{data && data.length > 0 ? (
<div
className={`grid ${showSingleColumn ? 'grid-cols-1' : 'gap-4 gap-y-0 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3'}`}
className={`grid gap-4 ${showSingleColumn ? 'grid-cols-1' : 'sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3'}`}
>
{data.map((item, index) => {
const getItemKey = (i: ItemCardData, idx: number): string => {
Expand All @@ -107,7 +107,7 @@ const ItemCardList = ({
return (
<div
key={getItemKey(item, index)}
className="mb-4 w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700"
className="w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700"
>
<div className="flex w-full flex-col justify-between">
<div className="flex w-full items-center">
Expand Down
12 changes: 1 addition & 11 deletions frontend/src/components/Milestones.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { useRouter } from 'next/navigation'
import React from 'react'
import {
FaCalendar,
FaFolderOpen,
FaSignsPost,
FaCircleCheck,
FaCircleExclamation,
} from 'react-icons/fa6'
import { FaCalendar, FaFolderOpen, FaSignsPost, FaCircleExclamation } from 'react-icons/fa6'
import type { Milestone } from 'types/milestone'
import { formatDate } from 'utils/dateFormatter'
import AnchorTitle from 'components/AnchorTitle'
Expand Down Expand Up @@ -43,10 +37,6 @@ const Milestones: React.FC<ProjectMilestonesProps> = ({
<FaCalendar className="mr-2 h-4 w-4" />
<span>{item.createdAt ? formatDate(item.createdAt) : 'N/A'}</span>
</div>
<div className="mr-4 flex items-center">
<FaCircleCheck className="mr-2 h-4 w-4" />
<span>{item.closedIssuesCount} closed</span>
</div>
<div className="mr-4 flex items-center">
<FaCircleExclamation className="mr-2 h-4 w-4" />
<span>{item.openIssuesCount} open</span>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/RecentReleases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const RecentReleases: React.FC<RecentReleasesProps> = ({
>
{data && data.length > 0 ? (
<div
className={`grid ${showSingleColumn ? 'grid-cols-1' : 'gap-4 gap-y-0 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3'}`}
className={`grid gap-4 ${showSingleColumn ? 'grid-cols-1' : 'sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3'}`}
>
{data.map((item) => (
<Release
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Release.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const Release: React.FC<ReleaseProps> = ({
}

return (
<div className={`mb-4 w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700 ${className}`}>
<div className={`w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700 ${className}`}>
<div className="flex w-full flex-col justify-between">
<div className="flex w-full items-center">
{showAvatar && release?.author && (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/RepositoryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import StatusBadge from 'components/StatusBadge'
import { TruncatedText } from 'components/TruncatedText'

const RepositoryCard: React.FC<RepositoryCardListProps> = ({
maxInitialDisplay = 4,
maxInitialDisplay = 6,
repositories,
}) => {
const [showAllRepositories, setShowAllRepositories] = useState(false)
Expand All @@ -25,7 +25,7 @@ const RepositoryCard: React.FC<RepositoryCardListProps> = ({
: repositories.slice(0, maxInitialDisplay)
return (
<div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
{displayedRepositories.map((repository) => {
return (
<RepositoryItem
Expand Down
Loading