update viewers page

This commit is contained in:
2026-05-16 07:46:37 +01:00
parent a586282b3f
commit 30cc816aa0
+58 -48
View File
@@ -2012,32 +2012,33 @@ const shortDateFormat = new Intl.DateTimeFormat('en-GB', {
})
const timezoneMapPoints = {
'America/Los_Angeles': [18, 42],
'America/Denver': [24, 43],
'America/Chicago': [30, 45],
'America/New_York': [37, 43],
'America/Toronto': [38, 40],
'America/Sao_Paulo': [43, 68],
'America/Mexico_City': [27, 54],
'Europe/London': [48, 36],
'Europe/Paris': [51, 39],
'Europe/Berlin': [53, 37],
'Europe/Madrid': [49, 42],
'Europe/Rome': [53, 43],
'Europe/Amsterdam': [51, 37],
'Europe/Stockholm': [54, 31],
'Europe/Warsaw': [56, 37],
'Europe/Moscow': [63, 34],
'Africa/Cairo': [58, 49],
'Africa/Johannesburg': [58, 75],
'Asia/Dubai': [66, 52],
'Asia/Kolkata': [72, 56],
'Asia/Singapore': [79, 66],
'Asia/Tokyo': [88, 45],
'Asia/Seoul': [85, 44],
'Asia/Shanghai': [81, 48],
'Australia/Sydney': [90, 78],
'Pacific/Auckland': [95, 84],
'America/Los_Angeles': [34.05, -118.24],
'America/Denver': [39.74, -104.99],
'America/Chicago': [41.88, -87.63],
'America/New_York': [40.71, -74.01],
'America/Toronto': [43.65, -79.38],
'America/Phoenix': [33.45, -112.07],
'America/Sao_Paulo': [-23.55, -46.63],
'America/Mexico_City': [19.43, -99.13],
'Europe/London': [51.51, -0.13],
'Europe/Paris': [48.86, 2.35],
'Europe/Berlin': [52.52, 13.41],
'Europe/Madrid': [40.42, -3.7],
'Europe/Rome': [41.9, 12.5],
'Europe/Amsterdam': [52.37, 4.9],
'Europe/Stockholm': [59.33, 18.07],
'Europe/Warsaw': [52.23, 21.01],
'Europe/Moscow': [55.76, 37.62],
'Africa/Cairo': [30.04, 31.24],
'Africa/Johannesburg': [-26.2, 28.04],
'Asia/Dubai': [25.2, 55.27],
'Asia/Kolkata': [22.57, 88.36],
'Asia/Singapore': [1.35, 103.82],
'Asia/Tokyo': [35.68, 139.65],
'Asia/Seoul': [37.57, 126.98],
'Asia/Shanghai': [31.23, 121.47],
'Australia/Sydney': [-33.87, 151.21],
'Pacific/Auckland': [-36.85, 174.76],
}
const countryNames = {
@@ -2092,22 +2093,15 @@ const countryMapPoints = {
ZA: [24, -29],
}
function mapPointFromPercent(x, y) {
return [
85 - (y / 100) * 145,
(x / 100) * 360 - 180,
]
}
function timezonePoint(timezone = '') {
if (timezoneMapPoints[timezone]) return timezoneMapPoints[timezone]
if (timezone.startsWith('America/')) return [30, 48]
if (timezone.startsWith('Europe/')) return [53, 38]
if (timezone.startsWith('Africa/')) return [56, 60]
if (timezone.startsWith('Asia/')) return [75, 52]
if (timezone.startsWith('Australia/')) return [88, 76]
if (timezone.startsWith('Pacific/')) return [92, 70]
return [50, 50]
if (timezone.startsWith('America/')) return [39, -96]
if (timezone.startsWith('Europe/')) return [50, 10]
if (timezone.startsWith('Africa/')) return [0, 20]
if (timezone.startsWith('Asia/')) return [34, 100]
if (timezone.startsWith('Australia/')) return [-25, 134]
if (timezone.startsWith('Pacific/')) return [-15, 170]
return [20, 0]
}
function filledLast30Days(rows) {
@@ -2128,6 +2122,7 @@ function filledLast30Days(rows) {
}
function MiniLineChart({ accent = 'text-fury-cyan', data, label, metric, stroke = '#e82517' }) {
const [hoveredPoint, setHoveredPoint] = useState(null)
const width = 320
const height = 112
const padding = 12
@@ -2143,7 +2138,7 @@ function MiniLineChart({ accent = 'text-fury-cyan', data, label, metric, stroke
const latest = points.at(-1)?.value || 0
return (
<div className="rounded-md border border-border bg-bg p-3">
<div className="relative rounded-md border border-border bg-bg p-3">
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-sm font-semibold">{label}</p>
@@ -2161,15 +2156,31 @@ function MiniLineChart({ accent = 'text-fury-cyan', data, label, metric, stroke
cy={point.y}
fill={stroke}
key={`${metric}-${point.date}`}
onBlur={() => setHoveredPoint(null)}
onFocus={() => setHoveredPoint(point)}
onMouseEnter={() => setHoveredPoint(point)}
onMouseLeave={() => setHoveredPoint(null)}
r="4"
tabIndex="0"
>
<title>
{`${shortDateFormat.format(new Date(point.date))}: ${formatNumber(point.value)} ${label.toLowerCase()}, ${formatNumber(point.visitors || 0)} visitors`}
</title>
</circle>
/>
))}
</svg>
{hoveredPoint ? (
<div
className="pointer-events-none absolute z-20 w-max max-w-52 rounded-md bg-text px-2 py-1 text-xs font-semibold text-bg shadow-lg"
style={{
left: `${(hoveredPoint.x / width) * 100}%`,
top: `${52 + (hoveredPoint.y / height) * 112}px`,
transform: 'translate(-50%, -115%)',
}}
>
{shortDateFormat.format(new Date(hoveredPoint.date))}: {formatNumber(hoveredPoint.value)} {label.toLowerCase()}
<span className="block font-normal text-bg/80">
{formatNumber(hoveredPoint.visitors || 0)} visitors
</span>
</div>
) : null}
</div>
)
}
@@ -2543,8 +2554,7 @@ function LocationSignalMap({ countries, locations }) {
})
fallbackLocations.forEach((location) => {
const [x, y] = timezonePoint(location.timezone)
const [lat, lon] = mapPointFromPercent(x, y)
const [lat, lon] = timezonePoint(location.timezone)
const radius = 7 + (Number(location.visitors || 0) / maxLocationVisitors) * 16
L.circleMarker([lat, lon], {
color: '#000000',