@@ -11,14 +11,16 @@ interface PlaybackData {
1111 date : string ;
1212 minutes : number ;
1313}
14-
1514const NEXT_PAGE = "/" ;
1615export default function MinutesPlayedPerDayPage ( ) {
1716 const { showBoundary } = useErrorBoundary ( ) ;
18- const navigate = useNavigate ( ) ;
1917 const [ isLoading , setIsLoading ] = useState ( true ) ;
2018 const [ playbackData , setPlaybackData ] = useState < PlaybackData [ ] > ( [ ] ) ;
19+ const containerRef = useRef < HTMLDivElement > ( null ) ;
2120 const svgRef = useRef < SVGSVGElement > ( null ) ;
21+ const navigate = useNavigate ( ) ;
22+
23+ // Format time display helper
2224 const formatTimeDisplay = ( minutes : number ) : string => {
2325 const hours = Math . floor ( minutes / 60 ) ;
2426 const remainingMinutes = minutes % 60 ;
@@ -33,13 +35,13 @@ export default function MinutesPlayedPerDayPage() {
3335
3436 return `${ hours } ${ hours === 1 ? "hour" : "hours" } ${ remainingMinutes } ${ remainingMinutes === 1 ? "minute" : "minutes" } ` ;
3537 } ;
38+
3639 // Fetch data
3740 useEffect ( ( ) => {
3841 const fetchData = async ( ) => {
3942 setIsLoading ( true ) ;
4043 try {
4144 const data = await getMinutesPlayedPerDay ( ) ;
42- // Sort data chronologically for the graph
4345 setPlaybackData (
4446 data . sort (
4547 ( a , b ) => new Date ( a . date ) . getTime ( ) - new Date ( b . date ) . getTime ( ) ,
@@ -55,16 +57,26 @@ export default function MinutesPlayedPerDayPage() {
5557 } , [ ] ) ;
5658
5759 // Create/Update D3 visualization
58- useEffect ( ( ) => {
59- if ( ! playbackData . length || ! svgRef . current ) return ;
60+ const updateChart = ( ) => {
61+ if ( ! playbackData . length || ! svgRef . current || ! containerRef . current )
62+ return ;
6063
6164 // Clear any existing SVG content
6265 d3 . select ( svgRef . current ) . selectAll ( "*" ) . remove ( ) ;
6366
64- // Set up dimensions
65- const margin = { top : 20 , right : 30 , bottom : 40 , left : 60 } ;
66- const width = 800 - margin . left - margin . right ;
67- const height = 400 - margin . top - margin . bottom ;
67+ // Get container width
68+ const containerWidth = containerRef . current . clientWidth ;
69+
70+ // Set responsive dimensions
71+ const margin = {
72+ top : 20 ,
73+ right : 20 ,
74+ bottom : containerWidth < 600 ? 60 : 40 , // More space for labels on mobile
75+ left : containerWidth < 600 ? 50 : 60 ,
76+ } ;
77+ const width = containerWidth - margin . left - margin . right ;
78+ const height =
79+ Math . min ( 400 , containerWidth * 0.6 ) - margin . top - margin . bottom ;
6880
6981 // Create SVG
7082 const svg = d3
@@ -116,13 +128,15 @@ export default function MinutesPlayedPerDayPage() {
116128 . attr ( "stroke-width" , 2 )
117129 . attr ( "d" , line ) ;
118130
119- // Add axes
131+ // Configure axes with responsive settings
120132 const xAxis = d3
121133 . axisBottom ( xScale )
122- . ticks ( d3 . timeMonth . every ( 1 ) )
134+ . ticks ( containerWidth < 600 ? 4 : d3 . timeMonth . every ( 1 ) )
123135 . tickFormat ( ( d ) => {
124136 if ( d instanceof Date ) {
125- return d3 . timeFormat ( "%b %Y" ) ( d ) ;
137+ return containerWidth < 600
138+ ? d3 . timeFormat ( "%b" ) ( d ) // Short month name on mobile
139+ : d3 . timeFormat ( "%b %Y" ) ( d ) ;
126140 }
127141 return "" ;
128142 } ) ;
@@ -132,6 +146,7 @@ export default function MinutesPlayedPerDayPage() {
132146 . ticks ( 5 )
133147 . tickFormat ( ( d : d3 . NumberValue ) => `${ d . valueOf ( ) } m` ) ;
134148
149+ // Add x-axis
135150 svg
136151 . append ( "g" )
137152 . attr ( "transform" , `translate(0,${ height } )` )
@@ -140,8 +155,9 @@ export default function MinutesPlayedPerDayPage() {
140155 . style ( "text-anchor" , "end" )
141156 . attr ( "dx" , "-.8em" )
142157 . attr ( "dy" , ".15em" )
143- . attr ( "transform" , "rotate(-45)" ) ;
158+ . attr ( "transform" , containerWidth < 600 ? "rotate(-45)" : "rotate(-45)" ) ;
144159
160+ // Add y-axis
145161 svg . append ( "g" ) . call ( yAxis ) ;
146162
147163 // Add hover effects
@@ -154,7 +170,10 @@ export default function MinutesPlayedPerDayPage() {
154170 . style ( "padding" , "8px" )
155171 . style ( "border-radius" , "4px" )
156172 . style ( "box-shadow" , "0 2px 4px rgba(0,0,0,0.1)" )
157- . style ( "display" , "none" ) ;
173+ . style ( "display" , "none" )
174+ . style ( "z-index" , "1000" )
175+ . style ( "pointer-events" , "none" )
176+ . style ( "font-size" , containerWidth < 600 ? "12px" : "14px" ) ;
158177
159178 const dots = svg
160179 . selectAll ( ".dot" )
@@ -164,7 +183,7 @@ export default function MinutesPlayedPerDayPage() {
164183 . attr ( "class" , "dot" )
165184 . attr ( "cx" , ( d ) => xScale ( new Date ( d . date ) ) )
166185 . attr ( "cy" , ( d ) => yScale ( d . minutes ) )
167- . attr ( "r" , 4 )
186+ . attr ( "r" , containerWidth < 600 ? 3 : 4 )
168187 . attr ( "fill" , "var(--yellow-9)" )
169188 . style ( "opacity" , 0 ) ;
170189
@@ -175,15 +194,12 @@ export default function MinutesPlayedPerDayPage() {
175194 . style ( "fill" , "none" )
176195 . style ( "pointer-events" , "all" )
177196 . on ( "mousemove" , function ( event : PointerEvent ) {
178- const [ xPos ] = d3 . pointer ( event ) ;
197+ const [ xPos ] = d3 . pointer ( event , this ) ;
179198 const xDate = xScale . invert ( xPos ) ;
180- const bisectByDate = d3 . bisector < PlaybackData , Date > (
199+ const bisectLeft = d3 . bisector < PlaybackData , Date > (
181200 ( d ) => new Date ( d . date ) ,
182- ) ;
183- const bisectLeft = ( data : PlaybackData [ ] , date : Date ) =>
184- bisectByDate . left ( data , date ) ;
201+ ) . left ;
185202 const index = bisectLeft ( playbackData , xDate ) ;
186-
187203 const d = playbackData [ index ] ;
188204
189205 if ( d ) {
@@ -194,20 +210,31 @@ export default function MinutesPlayedPerDayPage() {
194210 . style ( "display" , "block" )
195211 . style ( "left" , `${ event . pageX + 10 } px` )
196212 . style ( "top" , `${ event . pageY - 10 } px` )
197- . html (
198- `Date: ${ d . date } <br>Time: ${ formatTimeDisplay ( d . minutes ) } ` ,
199- ) ;
213+ . html ( `Date: ${ d . date } <br>${ formatTimeDisplay ( d . minutes ) } ` ) ;
200214 }
201215 } )
202216 . on ( "mouseout" , ( ) => {
203217 dots . style ( "opacity" , 0 ) ;
204218 tooltip . style ( "display" , "none" ) ;
205219 } ) ;
206220
207- // Cleanup function
208221 return ( ) => {
209222 tooltip . remove ( ) ;
210223 } ;
224+ } ;
225+
226+ // Update chart on window resize
227+ useEffect ( ( ) => {
228+ const handleResize = ( ) => {
229+ updateChart ( ) ;
230+ } ;
231+
232+ window . addEventListener ( "resize" , handleResize ) ;
233+ updateChart ( ) ;
234+
235+ return ( ) => {
236+ window . removeEventListener ( "resize" , handleResize ) ;
237+ } ;
211238 } , [ playbackData ] ) ;
212239
213240 if ( isLoading ) {
@@ -220,25 +247,7 @@ export default function MinutesPlayedPerDayPage() {
220247 minHeight : "100vh" ,
221248 } }
222249 >
223- < Box
224- style = { {
225- backgroundColor : "var(--green-8)" ,
226- minHeight : "100vh" ,
227- minWidth : "100vw" ,
228- } }
229- className = "min-h-screen"
230- >
231- < Box
232- style = { {
233- backgroundColor : "var(--green-8)" ,
234- minHeight : "100vh" ,
235- minWidth : "100vw" ,
236- } }
237- className = "min-h-screen"
238- >
239- < Spinner size = "3" />
240- </ Box >
241- </ Box >
250+ < Spinner size = "3" />
242251 </ div >
243252 ) ;
244253 }
@@ -247,14 +256,7 @@ export default function MinutesPlayedPerDayPage() {
247256 const averageMinutes = Math . round ( totalMinutes / playbackData . length ) ;
248257
249258 return (
250- < Box
251- style = { {
252- backgroundColor : "var(--green-8)" ,
253- minHeight : "100vh" ,
254- minWidth : "100vw" ,
255- } }
256- className = "min-h-screen"
257- >
259+ < Box className = "min-h-screen" >
258260 < Container size = "4" p = "4" >
259261 < Grid gap = "6" >
260262 < div style = { { textAlign : "center" } } >
@@ -267,11 +269,14 @@ export default function MinutesPlayedPerDayPage() {
267269 </ div >
268270
269271 < div
272+ ref = { containerRef }
270273 style = { {
271274 backgroundColor : "white" ,
272275 padding : "20px" ,
273276 borderRadius : "8px" ,
274277 boxShadow : "0 2px 4px rgba(0,0,0,0.1)" ,
278+ width : "100%" ,
279+ overflowX : "hidden" ,
275280 } }
276281 >
277282 < svg ref = { svgRef } > </ svg >
0 commit comments