@@ -18,6 +18,7 @@ import SignOutButton from "@/components/SignOutButton";
1818import ThemeToggle from "@/components/ThemeToggle" ;
1919import UserAvatar from "@/components/UserAvatar" ;
2020import KeyboardShortcuts from "@/components/KeyboardShortcuts" ;
21+ import SyncDataButton from "@/components/SyncDataButton" ;
2122import OnboardingTour from "@/components/OnboardingTour" ;
2223import { Moon , Sun } from "lucide-react" ;
2324import { toast } from "sonner" ;
@@ -51,7 +52,7 @@ const STALE_TIMES: Record<string, number> = {
5152 "/api/metrics/repos" : 5 * 60 * 1000 ,
5253 "/api/metrics/languages" : 5 * 60 * 1000 ,
5354 "/api/notifications" : 1 * 60 * 1000 ,
54- " default" : 2 * 60 * 1000 ,
55+ default : 2 * 60 * 1000 ,
5556} ;
5657
5758function getStaleTime ( url : string ) : number {
@@ -101,7 +102,9 @@ export function DashboardSyncProvider({ children }: { children: ReactNode }) {
101102 useEffect ( ( ) => {
102103 if ( process . env . NODE_ENV === "test" ) return ;
103104 const handleSync = ( ) => {
104- console . log ( "[Client Cache] Invalidating dashboard metrics cache due to sync event." ) ;
105+ console . log (
106+ "[Client Cache] Invalidating dashboard metrics cache due to sync event."
107+ ) ;
105108 clientCache . clear ( ) ;
106109 } ;
107110
@@ -116,13 +119,22 @@ export function DashboardSyncProvider({ children }: { children: ReactNode }) {
116119 const originalFetch = window . fetch ;
117120
118121 window . fetch = async ( input , init ) => {
119- const url = typeof input === "string" ? input : input instanceof URL ? input . toString ( ) : input . url ;
120- const isGet = ! init || ! init . method || init . method . toUpperCase ( ) === "GET" ;
122+ const url =
123+ typeof input === "string"
124+ ? input
125+ : input instanceof URL
126+ ? input . toString ( )
127+ : input . url ;
128+ const isGet =
129+ ! init || ! init . method || init . method . toUpperCase ( ) === "GET" ;
121130
122131 if ( isDashboardDataRequest ( input ) ) {
123132 if ( ! isGet ) {
124133 // Clear cache on write requests (POST, PUT, DELETE, PATCH)
125- console . log ( "[Client Cache] Invalidate cache due to mutation request:" , url ) ;
134+ console . log (
135+ "[Client Cache] Invalidate cache due to mutation request:" ,
136+ url
137+ ) ;
126138 clientCache . clear ( ) ;
127139 return originalFetch ( input , init ) ;
128140 }
@@ -150,7 +162,10 @@ export function DashboardSyncProvider({ children }: { children: ReactNode }) {
150162
151163 const nowTime = new Date ( ) ;
152164 setLastSynced ( nowTime ) ;
153- localStorage . setItem ( "devtrack-last-synced" , nowTime . toISOString ( ) ) ;
165+ localStorage . setItem (
166+ "devtrack-last-synced" ,
167+ nowTime . toISOString ( )
168+ ) ;
154169 }
155170 return response ;
156171 } catch ( error ) {
@@ -258,7 +273,7 @@ export default function DashboardHeader() {
258273 const { isLive : isHeaderLive } = useRealtimeSync (
259274 "users" ,
260275 [ "UPDATE" ] ,
261- loadSettings ,
276+ loadSettings
262277 ) ;
263278 useEffect ( ( ) => {
264279 if ( ! session ?. githubLogin ) return ;
@@ -285,7 +300,10 @@ export default function DashboardHeader() {
285300 if ( nightOwlCommitsCount >= 1 ) setIsNightOwl ( true ) ;
286301 if ( earlyBirdCommitsCount >= 1 ) setIsEarlyBird ( true ) ;
287302 } catch ( err ) {
288- console . error ( "Failed to compile milestone hour distribution profiles:" , err ) ;
303+ console . error (
304+ "Failed to compile milestone hour distribution profiles:" ,
305+ err
306+ ) ;
289307 }
290308 }
291309
@@ -298,7 +316,8 @@ export default function DashboardHeader() {
298316 const [ now , setNow ] = useState ( ( ) => Date . now ( ) ) ;
299317
300318 // Extract a fallback username parameter from active session data strings
301- const displayName = session ?. user ?. name || session ?. githubLogin || "Developer" ;
319+ const displayName =
320+ session ?. user ?. name || session ?. githubLogin || "Developer" ;
302321 useEffect ( ( ) => {
303322 if ( ! lastSynced ) return ;
304323
@@ -318,7 +337,6 @@ export default function DashboardHeader() {
318337 < div className = "pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-[var(--accent)]/40 to-transparent" />
319338 < div className = "pointer-events-none absolute -right-10 -top-12 h-32 w-32 rounded-full bg-[var(--accent)]/10 blur-3xl" />
320339 < div className = "relative z-10 flex min-w-0 flex-col gap-5 lg:flex-row lg:items-end lg:justify-between" >
321-
322340 { /* Left Section */ }
323341 < div className = "min-w-0 pr-0" >
324342 < div className = "mb-1 flex min-w-0 flex-wrap items-center gap-2" >
@@ -327,7 +345,9 @@ export default function DashboardHeader() {
327345 < span className = "animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--accent)] opacity-75" > </ span >
328346 < span className = "relative inline-flex rounded-full h-1.5 w-1.5 bg-[var(--accent)]" > </ span >
329347 </ span >
330- < span className = "truncate" > { greeting } , { displayName } !</ span >
348+ < span className = "truncate" >
349+ { greeting } , { displayName } !
350+ </ span >
331351 </ div >
332352 { isNightOwl && (
333353 < div
@@ -351,7 +371,9 @@ export default function DashboardHeader() {
351371 < div className = "min-w-0" >
352372 < p
353373 className = "text-[11px] font-semibold uppercase tracking-[0.24em] text-[var(--muted-foreground)]"
354- style = { { fontFamily : "var(--font-jetbrains, ui-monospace, monospace)" } }
374+ style = { {
375+ fontFamily : "var(--font-jetbrains, ui-monospace, monospace)" ,
376+ } }
355377 >
356378 Dashboard overview
357379 </ p >
@@ -360,7 +382,10 @@ export default function DashboardHeader() {
360382 </ h1 >
361383 < p
362384 className = "mt-2 max-w-xl text-sm leading-6 text-[var(--muted-foreground)]"
363- style = { { fontFamily : "var(--font-jetbrains, ui-monospace, monospace)" , letterSpacing : "0.06em" } }
385+ style = { {
386+ fontFamily : "var(--font-jetbrains, ui-monospace, monospace)" ,
387+ letterSpacing : "0.06em" ,
388+ } }
364389 >
365390 coding activity at a glance
366391 </ p >
@@ -370,7 +395,9 @@ export default function DashboardHeader() {
370395 aria-atomic = "true"
371396 className = "mt-1 flex items-center gap-1.5 text-xs text-[var(--muted-foreground)]"
372397 >
373- { minutesAgo <= 0 ? "Synced just now" : `Synced ${ minutesAgo } min ago` }
398+ { minutesAgo <= 0
399+ ? "Synced just now"
400+ : `Synced ${ minutesAgo } min ago` }
374401 { isHeaderLive && (
375402 < span
376403 title = "Live — connected to Supabase Realtime"
@@ -395,8 +422,10 @@ export default function DashboardHeader() {
395422 { isPublic === true && session ?. githubLogin && (
396423 < ShareProfileButton githubLogin = { session . githubLogin } />
397424 ) }
398-
399425 < div className = "flex shrink-0 items-center gap-2 rounded-2xl border border-[var(--border)] bg-[var(--card-muted)]/50 p-2 shadow-sm backdrop-blur-sm" >
426+ < div className = "transition-transform duration-200 hover:scale-[1.05]" >
427+ < SyncDataButton />
428+ </ div >
400429 < div className = "transition-transform duration-200 hover:scale-[1.05]" >
401430 < KeyboardShortcuts />
402431 </ div >
@@ -468,6 +497,9 @@ export default function DashboardHeader() {
468497 { menuOpen && (
469498 < div className = "mt-4 space-y-3 rounded-2xl border border-[var(--border)] bg-[var(--card-muted)]/70 p-4 shadow-sm backdrop-blur-sm sm:hidden" >
470499 < div className = "flex flex-wrap items-center gap-2" >
500+ < div className = "transition-transform duration-200 hover:scale-[1.05]" >
501+ < SyncDataButton />
502+ </ div >
471503 < div className = "transition-transform duration-200 hover:scale-[1.05]" >
472504 < KeyboardShortcuts />
473505 </ div >
@@ -503,4 +535,4 @@ export default function DashboardHeader() {
503535 { ! seenOnboarding && < OnboardingTour /> }
504536 </ header >
505537 ) ;
506- }
538+ }
0 commit comments