11'use client'
22
3- import { useMemo , useState } from 'react'
3+ import { useEffect , useMemo , useState } from 'react'
44import Image from 'next/image'
55import Link from 'next/link'
66import {
@@ -64,6 +64,16 @@ function formatActivityDate(value: string | undefined, dateLocale: string, unkno
6464 return date . toLocaleDateString ( dateLocale , { month : 'short' , day : 'numeric' , year : 'numeric' } )
6565}
6666
67+ function formatLedgerDate ( value : string | undefined , dateLocale : string , unknownLabel : string ) {
68+ if ( ! value ) return unknownLabel
69+ const date = new Date ( value )
70+ if ( Number . isNaN ( date . getTime ( ) ) ) return unknownLabel
71+ const isCjkLocale = dateLocale . startsWith ( 'zh' ) || dateLocale . startsWith ( 'ja' )
72+ return date . toLocaleDateString ( dateLocale , isCjkLocale
73+ ? { month : 'short' , day : 'numeric' }
74+ : { month : 'short' , day : 'numeric' , year : 'numeric' } )
75+ }
76+
6777function formatWeekday ( value : string | undefined , dateLocale : string ) {
6878 if ( ! value ) return '—'
6979 const date = new Date ( value )
@@ -148,6 +158,33 @@ function TotalMetric({ label, value, icon: Icon }: { label: string; value: strin
148158 )
149159}
150160
161+ function DesktopRunnerMuse ( ) {
162+ const [ shouldRender , setShouldRender ] = useState ( false )
163+
164+ useEffect ( ( ) => {
165+ const desktopQuery = window . matchMedia ( '(min-width: 1280px)' )
166+ const updateVisibility = ( ) => setShouldRender ( desktopQuery . matches )
167+ updateVisibility ( )
168+ desktopQuery . addEventListener ( 'change' , updateVisibility )
169+ return ( ) => desktopQuery . removeEventListener ( 'change' , updateVisibility )
170+ } , [ ] )
171+
172+ if ( ! shouldRender ) return null
173+
174+ return (
175+ < Image
176+ src = { runnerMuseCameos . dashboardHero . src }
177+ alt = ""
178+ width = { 410 }
179+ height = { 615 }
180+ sizes = "205px"
181+ loading = "eager"
182+ fetchPriority = "high"
183+ className = "hero-runner-float pointer-events-none absolute z-20 hidden object-contain drop-shadow-[0_22px_32px_rgba(0,0,0,0.34)] xl:-bottom-[230px] xl:left-0 xl:block xl:h-[290px] xl:w-[205px]"
184+ />
185+ )
186+ }
187+
151188export function CyberDashboard ( ) {
152189 const { t, dateLocale } = useI18n ( )
153190 const currentYear = new Date ( ) . getFullYear ( )
@@ -214,7 +251,7 @@ export function CyberDashboard() {
214251 < div className = "relative min-w-0 px-1 pb-10 pt-3 sm:px-2 lg:px-5 lg:pt-7 xl:px-8" >
215252 < div className = "grid items-start gap-4 lg:grid-cols-[minmax(450px,1.15fr)_minmax(330px,0.85fr)]" >
216253 < div >
217- < h1 className = "max-w-[620px] text-[2.35rem] font-black leading-[1.02] tracking-[-0.055em] text-[ var(--text-strong)] sm:text-[clamp(2.6rem,3vw,2.85rem )]" >
254+ < h1 className = "dashboard-display-title max-w-[620px] text-[var(--text-strong)]" >
218255 { t ( 'dashboard.headline' ) }
219256 </ h1 >
220257 < p className = "mt-4 max-w-[590px] text-sm leading-6 text-[var(--text-muted)] sm:mt-5 sm:text-base sm:leading-7" >
@@ -272,6 +309,7 @@ export function CyberDashboard() {
272309 padding = { 64 }
273310 maxPoints = { 420 }
274311 strokeWidth = { 6 }
312+ loop
275313 label = { `${ selectedActivity . name || selectedLocation } ${ t ( 'dashboard.routeShape' ) } ` }
276314 />
277315 </ div >
@@ -292,14 +330,7 @@ export function CyberDashboard() {
292330 </ div >
293331 ) }
294332
295- < Image
296- src = { runnerMuseCameos . dashboardHero . src }
297- alt = ""
298- width = { 410 }
299- height = { 615 }
300- sizes = "205px"
301- className = "hero-runner-float pointer-events-none absolute z-20 hidden object-contain drop-shadow-[0_22px_32px_rgba(0,0,0,0.34)] xl:-bottom-[230px] xl:left-0 xl:block xl:h-[290px] xl:w-[205px]"
302- />
333+ < DesktopRunnerMuse />
303334 </ div >
304335 </ div >
305336
@@ -342,7 +373,7 @@ export function CyberDashboard() {
342373 </ select >
343374 </ div >
344375
345- < div className = "mt-8 hidden grid-cols-[88px_minmax(144px,1fr)_62px_62px_58px ] gap-1.5 px-3 text-[10px] font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)] md:grid 2xl:grid-cols-[88px_minmax(180px,1fr)_78px_76px_70px] 2xl:gap-3" >
376+ < div className = "mt-8 hidden grid-cols-[88px_minmax(144px,1fr)_62px_62px_68px ] gap-1.5 px-3 text-[10px] font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)] md:grid 2xl:grid-cols-[88px_minmax(180px,1fr)_78px_76px_70px] 2xl:gap-3" >
346377 < span > { t ( 'common.date' ) } </ span >
347378 < span > { t ( 'dashboard.routeColumn' ) } </ span >
348379 < span className = "text-right" > { t ( 'common.distance' ) } </ span >
@@ -371,7 +402,7 @@ export function CyberDashboard() {
371402 aria-pressed = { isSelected }
372403 >
373404 < span className = "min-w-0" >
374- < span className = "block truncate text-xs text-[var(--text-strong)]" > { formatActivityDate ( activityDate ( activity ) , dateLocale , t ( 'common.unknownDate' ) ) } </ span >
405+ < span className = "block truncate text-xs text-[var(--text-strong)]" > { formatLedgerDate ( activityDate ( activity ) , dateLocale , t ( 'common.unknownDate' ) ) } </ span >
375406 < span className = "mt-1 block text-[11px] text-[var(--text-muted)]" > { formatWeekday ( activityDate ( activity ) , dateLocale ) } </ span >
376407 </ span >
377408 < span className = "grid min-w-0 grid-cols-[60px_1fr] items-center gap-3" >
0 commit comments