@@ -3,23 +3,26 @@ import { useQuery } from '@apollo/client/react'
33import Image from 'next/image'
44import Link from 'next/link'
55import { useParams } from 'next/navigation'
6- import { useTheme } from 'next-themes'
7- import React , { useState , useEffect , useRef } from 'react'
6+ import React , { useState , useEffect } from 'react'
87import { FaCodeMerge , FaFolderOpen , FaPersonWalkingArrowRight , FaUserPlus } from 'react-icons/fa6'
98import { handleAppError , ErrorDisplay } from 'app/global-error'
109
1110import { GetUserDataDocument } from 'types/__generated__/userQueries.generated'
1211import { Badge } from 'types/badge'
1312import { formatDate } from 'utils/dateFormatter'
14- import { drawContributions , fetchHeatmapData , HeatmapData } from 'utils/helpers/githubHeatmap'
13+ import { fetchHeatmapData } from 'utils/helpers/githubHeatmap'
1514import Badges from 'components/Badges'
1615import DetailsCard from 'components/CardDetailsPage'
16+ import ContributionHeatmap from 'components/ContributionHeatmap'
1717import MemberDetailsPageSkeleton from 'components/skeletons/MemberDetailsPageSkeleton'
1818
1919const UserDetailsPage : React . FC = ( ) => {
2020 const { memberKey } = useParams < { memberKey : string } > ( )
21- const [ data , setData ] = useState < HeatmapData > ( { } as HeatmapData )
22- const [ username , setUsername ] = useState ( '' )
21+ const [ contributionData , setContributionData ] = useState < Record < string , number > > ( { } )
22+ const [ dateRange , setDateRange ] = useState < { startDate : string ; endDate : string } > ( {
23+ startDate : '' ,
24+ endDate : '' ,
25+ } )
2326 const [ isPrivateContributor , setIsPrivateContributor ] = useState ( false )
2427
2528 const {
@@ -50,9 +53,20 @@ const UserDetailsPage: React.FC = () => {
5053 setIsPrivateContributor ( true )
5154 return
5255 }
53- if ( result ?. contributions ) {
54- setUsername ( memberKey )
55- setData ( result as HeatmapData )
56+ if ( result ?. contributions && Array . isArray ( result . contributions ) ) {
57+ const transformedData : Record < string , number > = { }
58+ result . contributions . forEach ( ( contribution ) => {
59+ transformedData [ contribution . date ] = contribution . count
60+ } )
61+ setContributionData ( transformedData )
62+
63+ if ( result . contributions . length > 0 ) {
64+ const dates = result . contributions . map ( ( c ) => c . date ) . sort ( ( a , b ) => a . localeCompare ( b ) )
65+ setDateRange ( {
66+ startDate : dates [ 0 ] ,
67+ endDate : dates . at ( - 1 ) ?? '' ,
68+ } )
69+ }
5670 }
5771 }
5872 fetchData ( )
@@ -128,61 +142,6 @@ const UserDetailsPage: React.FC = () => {
128142 { icon : FaCodeMerge , value : user ?. contributionsCount || 0 , unit : 'Contribution' } ,
129143 ]
130144
131- const Heatmap = ( ) => {
132- const canvasRef = useRef < HTMLCanvasElement | null > ( null )
133- const [ imgSrc , setImgSrc ] = useState ( '' )
134- const { resolvedTheme } = useTheme ( )
135- const isDarkMode = ( resolvedTheme ?? 'light' ) === 'dark'
136-
137- useEffect ( ( ) => {
138- if ( canvasRef . current && data ?. years ?. length ) {
139- drawContributions ( canvasRef . current , {
140- data,
141- username,
142- themeName : isDarkMode ? 'dark' : 'light' ,
143- } )
144- const imageURL = canvasRef . current . toDataURL ( )
145- setImgSrc ( imageURL )
146- } else {
147- setImgSrc ( '' )
148- }
149- } , [ isDarkMode ] )
150-
151- return (
152- < div className = "overflow-hidden rounded-lg bg-white dark:bg-gray-800" >
153- < div className = "relative" >
154- < canvas ref = { canvasRef } style = { { display : 'none' } } aria-hidden = "true" > </ canvas >
155- { imgSrc ? (
156- < div className = "h-32" >
157- < Image
158- width = { 100 }
159- height = { 100 }
160- src = { imgSrc }
161- className = "h-full w-full object-cover object-[54%_60%]"
162- alt = "Contribution Heatmap"
163- />
164- </ div >
165- ) : (
166- < div className = "relative h-32 items-center justify-center" >
167- < Image
168- height = { 100 }
169- width = { 100 }
170- src = {
171- isDarkMode
172- ? '/img/heatmap-background-dark.png'
173- : '/img/heatmap-background-light.png'
174- }
175- className = "heatmap-background-loader h-full w-full border-none object-cover object-[54%_60%]"
176- alt = "Heatmap Background"
177- />
178- < div className = "heatmap-loader" > </ div >
179- </ div >
180- ) }
181- </ div >
182- </ div >
183- )
184- }
185-
186145 const UserSummary = ( ) => (
187146 < div className = "mt-4 flex flex-col items-center lg:flex-row" >
188147 < Image
@@ -192,7 +151,7 @@ const UserDetailsPage: React.FC = () => {
192151 src = { user ?. avatarUrl || '/placeholder.svg' }
193152 alt = { user ?. name || user ?. login || 'User Avatar' }
194153 />
195- < div className = "w-full text-center lg:text-left" >
154+ < div className = "w-full overflow-x-auto text-center lg:text-left" >
196155 < div className = "pl-0 lg:pl-4" >
197156 < 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" >
198157 < Link
@@ -217,11 +176,22 @@ const UserDetailsPage: React.FC = () => {
217176 </ div >
218177 < p className = "text-gray-600 dark:text-gray-400" > { formattedBio } </ p >
219178 </ div >
220- { ! isPrivateContributor && (
221- < div className = "hidden w-full lg:block" >
222- < Heatmap />
223- </ div >
224- ) }
179+ { ! isPrivateContributor &&
180+ contributionData &&
181+ Object . keys ( contributionData ) . length > 0 &&
182+ dateRange . startDate &&
183+ dateRange . endDate && (
184+ < div className = "w-full lg:block" >
185+ < div className = "overflow-x-auto rounded-lg bg-white dark:bg-gray-800" >
186+ < ContributionHeatmap
187+ contributionData = { contributionData }
188+ startDate = { dateRange . startDate }
189+ endDate = { dateRange . endDate }
190+ variant = "medium"
191+ />
192+ </ div >
193+ </ div >
194+ ) }
225195 </ div >
226196 </ div >
227197 )
0 commit comments