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

@@ -11,7 +11,7 @@ import CurrentlyReading from "./CurrentlyReading";
// Type definitions for CMS data
interface TechStackItem {
id: string;
name: string;
name: string | number | null | undefined;
url?: string;
icon_url?: string;
sort: number;
@@ -30,7 +30,7 @@ interface Hobby {
id: string;
key: string;
icon: string;
title: string;
title: string | number | null | undefined;
description?: string;
}
@@ -172,20 +172,59 @@ const About = () => {
// Use CMS Hobbies if available, otherwise fallback
const hobbies = hobbiesFromCMS
? hobbiesFromCMS.map((hobby: Hobby) => ({
icon: iconMap[hobby.icon] || Code,
text: hobby.title
}))
? hobbiesFromCMS
.map((hobby: Hobby) => {
// Convert to string, handling NaN/null/undefined
const text = hobby.title == null || (typeof hobby.title === 'number' && isNaN(hobby.title))
? ''
: String(hobby.title);
return {
icon: iconMap[hobby.icon] || Code,
text
};
})
.filter(h => {
const isValid = h.text.trim().length > 0;
if (!isValid && process.env.NODE_ENV === 'development') {
console.log('[About] Filtered out invalid hobby:', h);
}
return isValid;
})
: hobbiesFallback;
// Use CMS Tech Stack if available, otherwise fallback
const techStack = techStackFromCMS
? techStackFromCMS.map((cat: TechStackCategory) => ({
key: cat.key,
category: cat.name,
icon: iconMap[cat.icon] || Code,
items: cat.items.map((item: TechStackItem) => item.name)
}))
? techStackFromCMS.map((cat: TechStackCategory) => {
const items = cat.items
.map((item: TechStackItem) => {
// Convert to string, handling NaN/null/undefined
if (item.name == null || (typeof item.name === 'number' && isNaN(item.name))) {
if (process.env.NODE_ENV === 'development') {
console.log('[About] Invalid item.name in category', cat.key, ':', item);
}
return '';
}
return String(item.name);
})
.filter(name => {
const isValid = name.trim().length > 0;
if (!isValid && process.env.NODE_ENV === 'development') {
console.log('[About] Filtered out empty item name in category', cat.key);
}
return isValid;
});
if (items.length === 0 && process.env.NODE_ENV === 'development') {
console.warn('[About] Category has no valid items after filtering:', cat.key);
}
return {
key: cat.key,
category: cat.name,
icon: iconMap[cat.icon] || Code,
items
};
})
: techStackFallback;
return (
@@ -297,7 +336,7 @@ const About = () => {
: "bg-liquid-sky/10 border-liquid-sky/30 hover:bg-liquid-sky/20 hover:border-liquid-sky/50"
}`}
>
{item}
{String(item)}
</span>
))}
</div>
@@ -336,7 +375,7 @@ const About = () => {
>
<hobby.icon size={20} className="text-stone-600" />
<span className="text-stone-700 font-medium">
{hobby.text}
{String(hobby.text)}
</span>
</motion.div>
))}