1- import { formatDistanceToNow } from 'date-fns' ;
2- import { BriefcaseBusiness , ExternalLink , Hourglass , Link2 , Mail , MapPin , Timer , UsersRound } from 'lucide-react' ;
31import type { Metadata } from 'next' ;
4- import Image from 'next/image' ;
5- import Link from 'next/link' ;
62
7- import { Page } from '@/components/page/page' ;
8- import { AspectRatio } from '@/components/ui/aspect-ratio' ;
9- import { Avatar , AvatarImage } from '@/components/ui/avatar' ;
10- import { Button } from '@/components/ui/button' ;
11- import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from '@/components/ui/tooltip' ;
12- import { graphqlRequest } from '@/lib/graphql-request' ;
13- import { ProfileForMetadataDocument , UserDocument } from '@/types/generated/graphql' ;
3+ import { graphqlDirect } from '@/lib/graphql/graphql-direct' ;
4+ import { ProfileForMetadataDocument } from '@/types/generated/graphql' ;
145
15- import { ProfileListItem } from './components/profile-list-item' ;
166import { ProfileTimeline } from './components/profile-timeline' ;
177import { RanksOverview } from './components/ranks-overview' ;
18- import { RefreshButton } from './components/refresh-button' ;
198import { RepositoriesOverview } from './components/repositories-overiview' ;
20- import { getSocialIcon } from './utils/get-social-icon ' ;
9+ import { fetchProfileData } from './utils/fetch-profile-data ' ;
2110
2211type Props = {
2312 params : Promise < { login : string } > ;
2413} ;
2514
2615export async function generateMetadata ( { params } : Props ) : Promise < Metadata > {
2716 const { login } = await params ;
28- const { rankByLogin } = ( await graphqlRequest ( ProfileForMetadataDocument , { login } ) ) ?? { } ;
17+ const { rankByLogin } = ( await graphqlDirect ( ProfileForMetadataDocument , { login } ) ) ?? { } ;
2918
3019 if ( ! rankByLogin ?. user ) {
3120 return { title : 'GitHub Profile Analytics & Rankings · GitRanks' } ;
@@ -41,107 +30,26 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
4130
4231export default async function Profile ( { params } : { params : Promise < { login : string } > } ) {
4332 const { login } = await params ;
44- const { user } = ( await graphqlRequest ( UserDocument , { login } ) ) ?? { } ;
33+ const { user } = await fetchProfileData ( login ) ;
4534
46- const showContact = ! ! user ?. email || ! ! user ?. websiteUrl || ! ! user ?. socialAccounts ?. nodes ?. length ;
35+ if ( ! user ) {
36+ return null ;
37+ }
4738
4839 return (
49- < Page className = "gap-6 flex-col md:flex-row" >
50- < div className = "w-full md:w-3xs xl:w-2xs flex flex-col shrink-0 gap-4" >
51- < div className = "flex flex-row md:flex-col items-center md:items-start gap-4" >
52- < div className = "w-[64] sm:w-[128] md:w-full" >
53- < AspectRatio ratio = { 1 } >
54- < Avatar className = "w-full h-full rounded-full" asChild >
55- < AvatarImage src = { user . avatarUrl ! } />
56- </ Avatar >
57- </ AspectRatio >
58- </ div >
59- < div >
60- < h1 className = "font-semibold text-2xl" > { user . name } </ h1 >
61- < h2 className = "text-muted-foreground" > @{ user . login } </ h2 >
62- </ div >
63- </ div >
64- < div className = "flex flex-row md:flex-col gap-4" >
65- < RefreshButton />
66- < Button size = "sm" variant = "secondary" className = "flex-grow" asChild >
67- < Link href = { `https://github.com/${ user . login } ` } target = "_blank" rel = "noopener noreferrer" >
68- Open GitHub
69- < ExternalLink className = "size-4" />
70- </ Link >
71- </ Button >
72- </ div >
73- < div className = "flex flex-col gap-6" >
74- < div className = "flex flex-col gap-1.5" >
75- < ProfileListItem value = { user . location } Icon = { MapPin } />
76- < ProfileListItem value = { user . company } Icon = { BriefcaseBusiness } />
77- < ProfileListItem value = { 'Profile age: ' + formatDistanceToNow ( user . githubCreatedAt ) } Icon = { Hourglass } />
78- < ProfileListItem
79- value = { 'Updated ' + formatDistanceToNow ( user . githubFetchedAt , { addSuffix : true } ) }
80- Icon = { Timer }
81- />
82- < ProfileListItem
83- value = { `${ user . followersCount ?. toLocaleString ( 'en-US' ) } followers • ${ user . followingCount ?. toLocaleString (
84- 'en-US' ,
85- ) } following`}
86- Icon = { UsersRound }
87- />
88- </ div >
89- { showContact && (
90- < div className = "flex flex-col gap-1.5" >
91- < h4 className = "text-lg font-semibold" > Contacts</ h4 >
92- < ProfileListItem value = { user . email } url = { `mailto:${ user . email } ` } Icon = { Mail } />
93- < ProfileListItem value = { user . websiteUrl } url = { user . websiteUrl ! } Icon = { Link2 } />
94- { user . socialAccounts ?. nodes ?. map ( ( account ) => (
95- < ProfileListItem
96- key = { account . displayName }
97- value = { account . displayName }
98- url = { account . url }
99- Icon = { getSocialIcon ( account . provider , account . url ) }
100- />
101- ) ) }
102- </ div >
103- ) }
104- { ! ! user . organizations ?. length && (
105- < div className = "flex flex-col gap-1.5" >
106- < h4 className = "text-lg font-semibold" > Organizations</ h4 >
107- < div className = "flex gap-1 flex-wrap" >
108- { user . organizations ?. map ( ( org ) => (
109- < TooltipProvider key = { org . login } >
110- < Tooltip >
111- < TooltipTrigger asChild >
112- < Link href = { `https://github.com/${ org . login } ` } target = "_blank" rel = "noopener noreferrer" >
113- < Image
114- src = { org . avatarUrl ! }
115- alt = { org . login }
116- width = { 32 }
117- height = { 32 }
118- className = "rounded-sm bg-secondary"
119- />
120- </ Link >
121- </ TooltipTrigger >
122- < TooltipContent > { org . login } </ TooltipContent >
123- </ Tooltip >
124- </ TooltipProvider >
125- ) ) }
126- </ div >
127- </ div >
128- ) }
129- </ div >
40+ < div className = "flex-grow flex flex-col gap-6" >
41+ < div className = "flex flex-col md:flex-row flex-wrap gap-6" >
42+ < RanksOverview ranksData = { user . rank } />
43+ < RepositoriesOverview
44+ repositories = { user . repositories }
45+ contributions = { user . contributions }
46+ ownedStars = { user . ownedStars }
47+ contributedStars = { user . contributedStars }
48+ />
13049 </ div >
131- < div className = "flex-grow flex flex-col gap-6" >
132- < div className = "flex flex-col md:flex-row flex-wrap gap-6" >
133- < RanksOverview ranksData = { user . rank } />
134- < RepositoriesOverview
135- repositories = { user . repositories }
136- contributions = { user . contributions }
137- ownedStars = { user . ownedStars }
138- contributedStars = { user . contributedStars }
139- />
140- </ div >
141- < div >
142- < ProfileTimeline timeline = { user . timeline } firstSeenAt = { user . firstSeenAt } />
143- </ div >
50+ < div >
51+ < ProfileTimeline timeline = { user . timeline } firstSeenAt = { user . firstSeenAt } />
14452 </ div >
145- </ Page >
53+ </ div >
14654 ) ;
14755}
0 commit comments