Make profile formatting hydration-safe

This commit is contained in:
test
2026-08-30 09:28:07 +02:00
parent 5db36cb29f
commit 71972afb87
9 changed files with 44 additions and 60 deletions
@@ -1,14 +1,11 @@
import React from 'react'
import { formatProfileDate, formatProfileNumber } from '../../lib/profileFormatters'
function formatDate(value) {
if (!value) return null
try {
return new Intl.DateTimeFormat('en', {
month: 'short',
day: 'numeric',
year: 'numeric',
}).format(new Date(value))
return formatProfileDate(value, { month: 'short', day: 'numeric', year: 'numeric' })
} catch {
return null
}
@@ -17,7 +14,7 @@ function formatDate(value) {
function formatYear(value) {
if (!value) return null
try {
return new Intl.DateTimeFormat('en', { year: 'numeric' }).format(new Date(value))
return formatProfileDate(value, { year: 'numeric' })
} catch {
return null
}
@@ -268,8 +265,8 @@ function YearlyProductivityChart({ recaps }) {
<div className="absolute inset-y-0 left-0 flex w-full items-center px-3 opacity-0 transition-opacity group-hover:opacity-100">
<span className="text-[11px] font-semibold text-white drop-shadow-sm">
{uploads} upload{uploads !== 1 ? 's' : ''}
{(item.metrics?.views ?? 0) > 0 ? ` · ${Number(item.metrics.views).toLocaleString()} views` : ''}
{(item.metrics?.downloads ?? 0) > 0 ? ` · ${Number(item.metrics.downloads).toLocaleString()} dl` : ''}
{(item.metrics?.views ?? 0) > 0 ? ` · ${formatProfileNumber(item.metrics.views)} views` : ''}
{(item.metrics?.downloads ?? 0) > 0 ? ` · ${formatProfileNumber(item.metrics.downloads)} dl` : ''}
</span>
</div>
</div>
@@ -292,8 +289,8 @@ function YearlyProductivityChart({ recaps }) {
{/* Summary footer */}
<div className="mt-5 flex flex-wrap gap-4 border-t border-white/5 pt-4 text-[12px] text-slate-400">
<span><span className="font-semibold text-white">{sorted.length}</span> active years</span>
<span><span className="font-semibold text-white">{sorted.reduce((s, r) => s + r.metrics.uploads_count, 0).toLocaleString()}</span> total uploads</span>
<span><span className="font-semibold text-white">{Number(sorted.reduce((s, r) => s + (r.metrics.views ?? 0), 0)).toLocaleString()}</span> total views</span>
<span><span className="font-semibold text-white">{formatProfileNumber(sorted.reduce((s, r) => s + r.metrics.uploads_count, 0))}</span> total uploads</span>
<span><span className="font-semibold text-white">{formatProfileNumber(sorted.reduce((s, r) => s + (r.metrics.views ?? 0), 0))}</span> total views</span>
</div>
</div>
)
@@ -550,8 +547,8 @@ export default function CreatorJourneySection({ journey, username }) {
<p className="mt-3 text-sm leading-relaxed text-slate-300">{item.summary}</p>
<div className="mt-4 grid gap-2 sm:grid-cols-2">
<StatPill label="Views" value={(item.metrics?.views ?? 0).toLocaleString()} />
<StatPill label="Downloads" value={(item.metrics?.downloads ?? 0).toLocaleString()} />
<StatPill label="Views" value={formatProfileNumber(item.metrics?.views ?? 0)} />
<StatPill label="Downloads" value={formatProfileNumber(item.metrics?.downloads ?? 0)} />
{(item.metrics?.featured_count ?? 0) > 0 && (
<StatPill label="Featured" value={item.metrics.featured_count} />
)}