1+ import { formatDistanceToNow } from 'date-fns' ;
2+ import {
3+ BriefcaseBusiness ,
4+ ExternalLink ,
5+ Hourglass ,
6+ Link2 ,
7+ Mail ,
8+ MapPin ,
9+ RefreshCw ,
10+ Timer ,
11+ UsersRound ,
12+ } from 'lucide-react' ;
113import type { Metadata } from 'next' ;
14+ import Image from 'next/image' ;
15+ import Link from 'next/link' ;
216
317import { Page } from '@/components/page/page' ;
18+ import { AspectRatio } from '@/components/ui/aspect-ratio' ;
19+ import { Avatar , AvatarImage } from '@/components/ui/avatar' ;
20+ import { Button } from '@/components/ui/button' ;
21+ import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from '@/components/ui/tooltip' ;
422import { graphqlRequest } from '@/lib/graphql-request' ;
523import { ProfileForMetadataDocument , UserDocument } from '@/types/generated/graphql' ;
624
25+ import { ProfileListItem } from './components/profile-list-item' ;
26+ import { RanksOverview } from './components/ranks-overview' ;
27+ import { RepositoriesOverview } from './components/repositories-overiview' ;
28+ import { getSocialIcon } from './utils/get-social-icon' ;
29+
730type Props = {
831 params : Promise < { login : string } > ;
932} ;
@@ -28,12 +51,102 @@ export default async function Profile({ params }: { params: Promise<{ login: str
2851 const { login } = await params ;
2952 const { user } = ( await graphqlRequest ( UserDocument , { login } ) ) ?? { } ;
3053
31- console . log ( ' user' , user ) ;
54+ const showContact = ! ! user ?. email || ! ! user ?. websiteUrl || ! ! user ?. socialAccounts ?. nodes ?. length ;
3255
3356 return (
34- < Page className = "gap-6" >
35- < div >
36- < h1 className = "text-2xl font-semibold" > { login } Profile</ h1 >
57+ < Page className = "gap-6 flex-row" >
58+ < div className = "w-3xs xl:w-2xs flex flex-col gap-4" >
59+ < div className = "flex flex-col gap-4" >
60+ < AspectRatio ratio = { 1 } >
61+ < Avatar className = "w-full h-full rounded-full" asChild >
62+ < AvatarImage src = { user . avatarUrl ! } />
63+ </ Avatar >
64+ </ AspectRatio >
65+ < div >
66+ < h1 className = "font-semibold text-2xl" > { user . name } </ h1 >
67+ < h2 className = "text-muted-foreground" > @{ user . login } </ h2 >
68+ </ div >
69+ </ div >
70+ < div className = "flex flex-col gap-4" >
71+ < Button className = "w-full" >
72+ Refresh
73+ < RefreshCw className = "size-4" />
74+ </ Button >
75+ < Button variant = "secondary" className = "w-full" asChild >
76+ < Link href = { `https://github.com/${ user . login } ` } target = "_blank" rel = "noopener noreferrer" >
77+ Open GitHub
78+ < ExternalLink className = "size-4" />
79+ </ Link >
80+ </ Button >
81+ </ div >
82+ < div className = "flex flex-col gap-6" >
83+ < div className = "flex flex-col gap-1.5" >
84+ < ProfileListItem value = { user . location } Icon = { MapPin } />
85+ < ProfileListItem value = { user . company } Icon = { BriefcaseBusiness } />
86+ < ProfileListItem value = { 'Profile age: ' + formatDistanceToNow ( user . githubCreatedAt ) } Icon = { Hourglass } />
87+ < ProfileListItem
88+ value = { 'Updated ' + formatDistanceToNow ( user . githubFetchedAt , { addSuffix : true } ) }
89+ Icon = { Timer }
90+ />
91+ < ProfileListItem
92+ value = { `${ user . followersCount ?. toLocaleString ( 'en-US' ) } followers • ${ user . followingCount ?. toLocaleString (
93+ 'en-US' ,
94+ ) } following`}
95+ Icon = { UsersRound }
96+ />
97+ </ div >
98+ { showContact && (
99+ < div className = "flex flex-col gap-1.5" >
100+ < h4 className = "text-lg font-semibold" > Contacts</ h4 >
101+ < ProfileListItem value = { user . email } Icon = { Mail } />
102+ < ProfileListItem value = { user . websiteUrl } Icon = { Link2 } />
103+ { user . socialAccounts ?. nodes ?. map ( ( account ) => (
104+ < ProfileListItem
105+ key = { account . displayName }
106+ value = { account . displayName }
107+ Icon = { getSocialIcon ( account . provider ) }
108+ />
109+ ) ) }
110+ </ div >
111+ ) }
112+ { ! ! user . organizations ?. length && (
113+ < div className = "flex flex-col gap-1.5" >
114+ < h4 className = "text-lg font-semibold" > Organizations</ h4 >
115+ < div className = "flex gap-1 flex-wrap" >
116+ { user . organizations ?. map ( ( org ) => (
117+ < TooltipProvider key = { org . login } >
118+ < Tooltip >
119+ < TooltipTrigger asChild >
120+ < Link href = { `https://github.com/${ org . login } ` } target = "_blank" rel = "noopener noreferrer" >
121+ < Image
122+ src = { org . avatarUrl ! }
123+ alt = { org . login }
124+ width = { 32 }
125+ height = { 32 }
126+ className = "rounded-sm bg-secondary"
127+ />
128+ </ Link >
129+ </ TooltipTrigger >
130+ < TooltipContent > { org . login } </ TooltipContent >
131+ </ Tooltip >
132+ </ TooltipProvider >
133+ ) ) }
134+ </ div >
135+ </ div >
136+ ) }
137+ </ div >
138+ </ div >
139+ < div className = "flex-grow flex flex-col gap-6" >
140+ < div className = "flex flex-wrap gap-6" >
141+ < RanksOverview ranksData = { user . rank } />
142+ < RepositoriesOverview
143+ repositories = { user . repositories }
144+ contributions = { user . contributions }
145+ ownedStars = { user . ownedStars }
146+ contributedStars = { user . contributedStars }
147+ />
148+ </ div >
149+ < div > Feed</ div >
37150 </ div >
38151 </ Page >
39152 ) ;
0 commit comments