1- import { useQuery } from '@tanstack/react-query' ;
2- import { getCalendarData , type CalendarData } from '@/lib/playback-reporting-queries' ;
3- import { ResponsiveCalendar } from '@nivo/calendar' ;
4- import { Card } from '@/components/ui/card' ;
5- import { Button , Box , Spinner } from '@radix-ui/themes' ;
6- import { useNavigate } from 'react-router-dom' ;
7- import { subYears , format } from 'date-fns' ;
8- import { Title } from '../ui/styled' ;
9- import { motion } from 'framer-motion' ;
1+ import { useQuery } from "@tanstack/react-query" ;
2+ import {
3+ getCalendarData ,
4+ } from "@/lib/playback-reporting-queries" ;
5+ import { ResponsiveCalendar } from "@nivo/calendar" ;
6+ import { Card } from "@/components/ui/card" ;
7+ import { Button , Box , Spinner } from "@radix-ui/themes" ;
8+ import { useNavigate } from "react-router-dom" ;
9+ import { subYears , format } from "date-fns" ;
10+ import { Title } from "../ui/styled" ;
11+ import { motion } from "framer-motion" ;
12+ import { useEffect , useState } from "react" ;
1013
1114const NEXT_PAGE = "/" ;
1215
16+ // You can adjust this breakpoint based on your needs
17+ const MOBILE_BREAKPOINT = "768px" ;
18+
19+ function useIsMobile ( ) {
20+ const [ isMobile , setIsMobile ] = useState ( false ) ;
21+
22+ useEffect ( ( ) => {
23+ // Check if window is available (for SSR compatibility)
24+ if ( typeof window === "undefined" ) return ;
25+
26+ const mediaQuery = window . matchMedia ( `(max-width: ${ MOBILE_BREAKPOINT } )` ) ;
27+
28+ // Set initial value
29+ setIsMobile ( mediaQuery . matches ) ;
30+
31+ // Create event listener function
32+ const handleResize = ( event : MediaQueryListEvent ) => {
33+ setIsMobile ( event . matches ) ;
34+ } ;
35+
36+ // Add event listener
37+ mediaQuery . addEventListener ( "change" , handleResize ) ;
38+
39+ // Cleanup
40+ return ( ) => mediaQuery . removeEventListener ( "change" , handleResize ) ;
41+ } , [ ] ) ;
42+
43+ return isMobile ;
44+ }
45+
1346export default function ActivityCalendarPage ( ) {
1447 const navigate = useNavigate ( ) ;
48+ const isMobile = useIsMobile ( ) ;
1549 const { data, isLoading, error } = useQuery ( {
16- queryKey : [ ' calendar-data' ] ,
17- queryFn : getCalendarData
50+ queryKey : [ " calendar-data" ] ,
51+ queryFn : getCalendarData ,
1852 } ) ;
1953
20- const from = format ( subYears ( new Date ( ) , 1 ) , ' yyyy-MM-dd' ) ;
21- const to = format ( new Date ( ) , ' yyyy-MM-dd' ) ;
54+ const from = format ( subYears ( new Date ( ) , 1 ) , " yyyy-MM-dd" ) ;
55+ const to = format ( new Date ( ) , " yyyy-MM-dd" ) ;
2256
2357 if ( isLoading ) {
2458 return (
25- < div style = { {
26- display : 'flex' ,
27- alignItems : 'center' ,
28- justifyContent : 'center' ,
29- minHeight : '100vh'
30- } } >
31- < Box style = { {
32- backgroundColor : 'var(--gray-8)' ,
33- minHeight : '100vh' ,
34- minWidth : '100vw' ,
35- display : 'flex' ,
36- alignItems : 'center' ,
37- justifyContent : 'center'
38- } } >
59+ < div
60+ style = { {
61+ display : "flex" ,
62+ alignItems : "center" ,
63+ justifyContent : "center" ,
64+ minHeight : "100vh" ,
65+ } }
66+ >
67+ < Box
68+ style = { {
69+ backgroundColor : "var(--gray-8)" ,
70+ minHeight : "100vh" ,
71+ minWidth : "100vw" ,
72+ display : "flex" ,
73+ alignItems : "center" ,
74+ justifyContent : "center" ,
75+ } }
76+ >
3977 < Spinner size = "3" />
4078 </ Box >
4179 </ div >
4280 ) ;
4381 }
4482
4583 if ( error ) {
46- console . error ( ' Error loading calendar data:' , error ) ;
84+ console . error ( " Error loading calendar data:" , error ) ;
4785 return < div > Error loading viewing patterns</ div > ;
4886 }
4987
5088 if ( ! data ?. length ) {
5189 return (
52- < Box
53- style = { { backgroundColor : "var(--purple-8)" } }
54- className = "min-h-screen"
55- >
56- < div className = "container mx-auto p-4" >
57- < h1 className = "text-2xl font-bold mb-4" > No viewing data available</ h1 >
58- < Button
59- size = { "4" }
60- style = { { width : "100%" } }
61- onClick = { ( ) => {
62- void navigate ( NEXT_PAGE ) ;
63- } }
64- >
65- Next
66- </ Button >
67- </ div > </ Box >
90+ < Box
91+ style = { { backgroundColor : "var(--purple-8)" } }
92+ className = "min-h-screen"
93+ >
94+ < div className = "container mx-auto p-4" >
95+ < h1 className = "text-2xl font-bold mb-4" > No viewing data available</ h1 >
96+ < Button
97+ size = { "4" }
98+ style = { { width : "100%" } }
99+ onClick = { ( ) => {
100+ void navigate ( NEXT_PAGE ) ;
101+ } }
102+ >
103+ Next
104+ </ Button >
105+ </ div >
106+ </ Box >
68107 ) ;
69108 }
70109
71110 return (
72- < Box style = { { backgroundColor : 'var(--purple-12)' , minWidth : "100vw" , minHeight : "100vh" } } className = "min-h-screen p-4" >
111+ < Box
112+ style = { {
113+ backgroundColor : "var(--purple-12)" ,
114+ minWidth : "100vw" ,
115+ minHeight : "100vh" ,
116+ } }
117+ className = "min-h-screen p-4"
118+ >
73119 < div className = "container mx-auto" >
74- < Title as = { motion . h1 } style = { { textAlign : "center" } } > Your Viewing Calendar</ Title >
120+ < Title as = { motion . h1 } style = { { textAlign : "center" } } >
121+ Your Calendar Heatmap
122+ </ Title >
75123 < Card className = "p-4 mb-4" >
76- < div style = { {
77- height : 'calc(min(600px, 80vh))' ,
78- minHeight : '400px' ,
79- width : '100%'
80- } } >
124+ < div
125+ style = { {
126+ height : isMobile
127+ ? "calc(min(800px, 90vh))"
128+ : "calc(min(600px, 80vh))" ,
129+ minHeight : isMobile ? "600px" : "400px" ,
130+ width : "100%" ,
131+ } }
132+ >
81133 < ResponsiveCalendar
82134 data = { data }
83135 from = { from }
84136 to = { to }
85137 emptyColor = "#eeeeee"
86- colors = { [
87- '#a1e4c4' ,
88- '#70c4a4' ,
89- '#4da584' ,
90- '#2a8564' ,
91- '#076544'
92- ] }
138+ direction = { isMobile ? "vertical" : undefined }
139+ colors = { [ "#a1e4c4" , "#70c4a4" , "#4da584" , "#2a8564" , "#076544" ] }
93140 margin = { {
94141 top : 50 ,
95142 right : 20 ,
96143 bottom : 50 ,
97- left : 20
144+ left : 60 , // 20
98145 } }
99146 yearSpacing = { 40 }
100147 monthBorderColor = "#ffffff"
101148 dayBorderWidth = { 2 }
102149 dayBorderColor = "#ffffff"
103150 monthLegend = { ( year , month ) => {
104151 const date = new Date ( year , month , 1 ) ;
105- return format ( date , ' MMM' ) ;
152+ return format ( date , " MMM" ) ;
106153 } }
107154 tooltip = { ( { day, value } ) => (
108155 < div
109156 style = { {
110- background : ' rgba(0, 0, 0, 0.9)' ,
111- color : ' white' ,
112- padding : ' 12px 16px' ,
113- borderRadius : ' 4px' ,
114- boxShadow : ' 0 4px 6px rgba(0, 0, 0, 0.3)' ,
115- fontSize : ' 14px' ,
116- lineHeight : ' 1.4'
157+ background : " rgba(0, 0, 0, 0.9)" ,
158+ color : " white" ,
159+ padding : " 12px 16px" ,
160+ borderRadius : " 4px" ,
161+ boxShadow : " 0 4px 6px rgba(0, 0, 0, 0.3)" ,
162+ fontSize : " 14px" ,
163+ lineHeight : " 1.4" ,
117164 } }
118165 >
119- < strong style = { { fontSize : '16px' , display : 'block' , marginBottom : '4px' } } >
120- { format ( new Date ( day ) , 'MMMM d, yyyy' ) }
166+ < strong
167+ style = { {
168+ fontSize : "16px" ,
169+ display : "block" ,
170+ marginBottom : "4px" ,
171+ } }
172+ >
173+ { format ( new Date ( day ) , "MMMM d, yyyy" ) }
121174 </ strong >
122- { value } { value === 1 ? ' item' : ' items' } watched
175+ { value } { value === "1" ? " item" : " items" } watched
123176 </ div >
124177 ) }
125178 theme = { {
126179 text : {
127180 fontSize : 14 ,
128- fill : ' #ffffff' ,
129- fontWeight : 600
181+ fill : " #ffffff" ,
182+ fontWeight : 600 ,
130183 } ,
131184 legends : {
132185 text : {
133186 fontSize : 16 ,
134- fill : ' #ffffff' ,
135- fontWeight : 500
136- }
187+ fill : " #ffffff" ,
188+ fontWeight : 500 ,
189+ } ,
137190 } ,
138191 tooltip : {
139192 container : {
140- background : ' #000000' ,
141- color : ' #ffffff' ,
142- fontSize : ' 14px'
143- }
144- }
193+ background : " #000000" ,
194+ color : " #ffffff" ,
195+ fontSize : " 14px" ,
196+ } ,
197+ } ,
145198 } }
146199 legends = { [
147200 {
148- anchor : ' bottom' ,
149- direction : ' row' ,
201+ anchor : " bottom" ,
202+ direction : " row" ,
150203 translateY : 50 ,
151204 itemCount : 5 ,
152205 itemWidth : 60 ,
153206 itemHeight : 36 ,
154207 itemsSpacing : 20 ,
155- itemDirection : ' right-to-left' ,
156- itemTextColor : ' #ffffff' ,
208+ itemDirection : " right-to-left" ,
209+ itemTextColor : " #ffffff" ,
157210 symbolSize : 20 ,
158211 effects : [
159212 {
160- on : ' hover' ,
213+ on : " hover" ,
161214 style : {
162- itemTextColor : ' #a1e4c4'
163- }
164- }
165- ]
166- }
215+ itemTextColor : " #a1e4c4" ,
216+ } ,
217+ } ,
218+ ] ,
219+ } ,
167220 ] }
168221 />
169222 </ div >
@@ -180,4 +233,4 @@ export default function ActivityCalendarPage() {
180233 </ div >
181234 </ Box >
182235 ) ;
183- }
236+ }
0 commit comments