Fix NaN rendering error in About section from Directus CMS data (#62)

* Initial plan

* Fix NaN error in gaming and custom activities rendering

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

* Add unit tests for NaN handling in ActivityFeed

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

* Refactor: Extract gaming text logic to helper function

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

* Address code review feedback: improve types and tests

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

* Fix NaN error in About component (tech stack and hobbies)

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

* Update TypeScript interfaces to match actual data types

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

* Improve NaN handling with defensive logging and better null checks

Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: denshooter <44590296+denshooter@users.noreply.github.com>
This commit is contained in:
Copilot
2026-01-23 12:49:47 +01:00
committed by GitHub
parent a4fa9b42fa
commit 9266b22fb4
3 changed files with 225 additions and 24 deletions

View File

@@ -37,8 +37,8 @@ interface StatusData {
isPlaying: boolean;
name: string;
image: string | null;
state?: string;
details?: string;
state?: string | number;
details?: string | number;
} | null;
coding: {
isActive: boolean;
@@ -54,6 +54,20 @@ interface StatusData {
customActivities?: Record<string, CustomActivity>; // Dynamisch!
}
/**
* Safely converts gaming activity details/state to a string, filtering out NaN values.
* Priority: string details > string state > numeric details > numeric state > fallback
*/
function getSafeGamingText(details: string | number | undefined, state: string | number | undefined, fallback: string): string {
// Check string values first (maintain original precedence)
if (typeof details === 'string' && details.trim().length > 0) return details;
if (typeof state === 'string' && state.trim().length > 0) return state;
// Check numeric values, but filter out NaN
if (typeof details === 'number' && !isNaN(details)) return String(details);
if (typeof state === 'number' && !isNaN(state)) return String(state);
return fallback;
}
export default function ActivityFeed() {
const [data, setData] = useState<StatusData | null>(null);
const [isExpanded, setIsExpanded] = useState(true);
@@ -1803,12 +1817,10 @@ export default function ActivityFeed() {
</span>
</div>
<p className="font-bold text-sm text-white truncate mb-0.5">
{data.gaming.name}
{String(data.gaming.name || "")}
</p>
<p className="text-xs text-indigo-200/60 truncate">
{data.gaming.details ||
data.gaming.state ||
"Playing..."}
{getSafeGamingText(data.gaming.details, data.gaming.state, "Playing...")}
</p>
</div>
</div>
@@ -1967,7 +1979,7 @@ export default function ActivityFeed() {
)}
{/* Progress Bar wenn vorhanden */}
{activity.progress !== undefined && typeof activity.progress === 'number' && (
{activity.progress !== undefined && typeof activity.progress === 'number' && !isNaN(activity.progress) && (
<div className="mt-1.5">
<div className="h-1 bg-white/10 rounded-full overflow-hidden">
<motion.div
@@ -1990,12 +2002,12 @@ export default function ActivityFeed() {
return null;
}
// Nur einfache Werte rendern
if (typeof value === 'string' || typeof value === 'number') {
// Nur einfache Werte rendern, aber NaN ausfiltern
if (typeof value === 'string' || (typeof value === 'number' && !isNaN(value))) {
return (
<div key={key} className="text-[10px] text-white/50 mt-0.5">
<span className="capitalize">{key.replace(/_/g, ' ')}: </span>
<span className="font-medium">{value}</span>
<span className="font-medium">{String(value)}</span>
</div>
);
}