33import { useEffect , useMemo , useState } from 'react' ;
44import CalendarHeatmap from 'react-calendar-heatmap' ;
55import 'react-calendar-heatmap/dist/styles.css' ;
6+ import { Tooltip } from 'react-tooltip' ;
7+ import 'react-tooltip/dist/react-tooltip.css' ;
68import { fetchDailyStats } from '@/services/heatmap/dailyStat.service' ;
79
8- type HeatmapValue = {
9- date : string ;
10- count : number ;
11- } ;
10+ type HeatmapValue = { date : string ; count : number } ;
1211
13- /** 토요일 기준으로 endDate 설정 */
1412function endOfWeek ( date = new Date ( ) ) {
1513 const d = new Date ( date ) ;
16- const diff = 6 - d . getDay ( ) ; // 0=Sun, 6=Sat
17- d . setDate ( d . getDate ( ) + diff ) ;
14+ d . setDate ( d . getDate ( ) + ( 6 - d . getDay ( ) ) ) ;
1815 return d ;
1916}
2017
21- type Props = {
22- uid ?: string ;
23- } ;
18+ type Props = { uid ?: string } ;
2419
25- export const GrassHeatmap = ( { uid } : Props ) => {
20+ export default function GrassHeatmap ( { uid } : Props ) {
2621 const endDate = useMemo ( ( ) => endOfWeek ( new Date ( ) ) , [ ] ) ;
2722 const startDate = useMemo ( ( ) => {
2823 const d = new Date ( endDate ) ;
@@ -31,28 +26,46 @@ export const GrassHeatmap = ({ uid }: Props) => {
3126 return d ;
3227 } , [ endDate ] ) ;
3328
34- const [ values , setValues ] = useState < HeatmapValue [ ] > ( [ ] ) ;
29+ const [ values , setValues ] = useState <
30+ { date : string ; total : number ; tilCount ?: number ; todoDoneCount ?: number } [ ]
31+ > ( [ ] ) ;
32+
3533 useEffect ( ( ) => {
3634 if ( ! uid ) return ;
37-
3835 ( async ( ) => {
3936 const stats = await fetchDailyStats ( uid ) ;
40- const heatmapValues : HeatmapValue [ ] = stats . map ( ( s ) => ( {
41- date : s . date ,
42- count : s . total ,
43- } ) ) ;
44- setValues ( heatmapValues ) ;
37+ setValues ( stats ) ;
4538 } ) ( ) ;
4639 } , [ uid ] ) ;
40+
41+ const byDate = useMemo ( ( ) => {
42+ const m = new Map <
43+ string ,
44+ { tilCount : number ; todoDoneCount : number ; total : number }
45+ > ( ) ;
46+ for ( const s of values ) {
47+ m . set ( s . date , {
48+ tilCount : s . tilCount ?? 0 ,
49+ todoDoneCount : s . todoDoneCount ?? 0 ,
50+ total : s . total ?? 0 ,
51+ } ) ;
52+ }
53+ return m ;
54+ } , [ values ] ) ;
55+
56+ const heatmapValues : HeatmapValue [ ] = useMemo (
57+ ( ) => values . map ( ( s ) => ( { date : s . date , count : s . total ?? 0 } ) ) ,
58+ [ values ]
59+ ) ;
60+
4761 return (
4862 < >
49- { /* 가로 길어질 때 대비 */ }
5063 < div className = "overflow-x-auto" >
5164 < div className = "min-w-max" >
5265 < CalendarHeatmap
5366 startDate = { startDate }
5467 endDate = { endDate }
55- values = { values }
68+ values = { heatmapValues }
5669 gutterSize = { 2 }
5770 showWeekdayLabels
5871 classForValue = { ( value ) => {
@@ -63,13 +76,38 @@ export const GrassHeatmap = ({ uid }: Props) => {
6376 return 'grass-1' ;
6477 } }
6578 tooltipDataAttrs = { ( value ) => {
66- if ( ! value ) return { 'data-tip' : '기록 없음' } ;
67- return { 'data-tip' : `${ value . date } · ${ value . count } 개 완료` } ;
79+ if ( ! value ?. date )
80+ return { 'data-tooltip-id' : '' , 'data-tooltip-html' : '' } ;
81+ const d = byDate . get ( value . date ) ;
82+
83+ const til = d ?. tilCount ?? 0 ;
84+ const todo = d ?. todoDoneCount ?? 0 ;
85+ const total = d ?. total ?? 0 ;
86+
87+ return {
88+ 'data-tooltip-id' : 'grass-tip' ,
89+ // HTML 툴팁 (작은 박스)
90+ 'data-tooltip-html' :
91+ total === 0
92+ ? `<div style="font-size:12px"><b>${ value . date } </b><br/>기록 없음</div>`
93+ : `<div style="font-size:12px">
94+ <b>${ value . date } </b><br/>
95+ 📘 TIL: ${ til } 개<br/>
96+ ✅ Plan: ${ todo } 개<br/>
97+ 🔥 합계: ${ total } 개
98+ </div>` ,
99+ } ;
68100 } }
69101 />
70102 </ div >
71103 </ div >
72104
105+ { /* 툴팁 컴포넌트 */ }
106+ < Tooltip
107+ id = "grass-tip"
108+ place = "top"
109+ className = "!rounded-lg !bg-black/80 !px-3 !py-2 !text-xs !text-white"
110+ />
73111 { /* 범례 */ }
74112 < div className = "mt-4 flex items-center justify-end gap-2 text-xs text-slate-500" >
75113 < span > Less</ span >
@@ -82,5 +120,4 @@ export const GrassHeatmap = ({ uid }: Props) => {
82120 </ div >
83121 </ >
84122 ) ;
85- } ;
86- export default GrassHeatmap ;
123+ }
0 commit comments