fix: guard optional objectives/technologies fields at type and render layer

This commit is contained in:
2026-04-19 12:57:03 +02:00
parent 7285dfd74d
commit efadaa6918
4 changed files with 15 additions and 11 deletions

View File

@@ -17,7 +17,11 @@ export const fetchExperiences = createServerFn({ method: 'GET' }).handler(
throw new Error(`Strapi request failed: ${res.status} ${res.url}`) throw new Error(`Strapi request failed: ${res.status} ${res.url}`)
const json = (await res.json()) as { data: TExperience[] } const json = (await res.json()) as { data: TExperience[] }
console.log(`[Strapi] ← GET ${url} (${json.data.length} items)`) console.log(`[Strapi] ← GET ${url} (${json.data.length} items)`)
return json.data return json.data.map((item) => ({
...item,
objectives: item.objectives ?? [],
technologies: item.technologies ?? [],
}))
}, },
) )

View File

@@ -88,9 +88,9 @@ function ExperienceDetailPage() {
</div> </div>
{/* Technologies */} {/* Technologies */}
{exp.technologies.length > 0 && ( {(exp.technologies ?? []).length > 0 && (
<div className="mt-5 flex flex-wrap gap-1.5"> <div className="mt-5 flex flex-wrap gap-1.5">
{exp.technologies.map((tech) => ( {(exp.technologies ?? []).map((tech) => (
<TechChip key={tech.id} label={tech.name} /> <TechChip key={tech.id} label={tech.name} />
))} ))}
</div> </div>
@@ -98,12 +98,12 @@ function ExperienceDetailPage() {
</section> </section>
{/* ── Objectives ────────────────────────────────────── */} {/* ── Objectives ────────────────────────────────────── */}
{exp.objectives.length > 0 && ( {(exp.objectives ?? []).length > 0 && (
<section className="mt-10"> <section className="mt-10">
<h2 className="island-kicker mb-4">Responsibilities</h2> <h2 className="island-kicker mb-4">Responsibilities</h2>
<div className="island-shell feature-card rounded-2xl p-6"> <div className="island-shell feature-card rounded-2xl p-6">
<ul className="ml-0 list-none space-y-3 p-0"> <ul className="ml-0 list-none space-y-3 p-0">
{exp.objectives.map((obj) => ( {(exp.objectives ?? []).map((obj) => (
<li <li
key={obj.id} key={obj.id}
className="flex gap-3 text-sm leading-relaxed text-[var(--sea-ink-soft)]" className="flex gap-3 text-sm leading-relaxed text-[var(--sea-ink-soft)]"

View File

@@ -153,9 +153,9 @@ function ExperienceCard({
<span>{duration}</span> <span>{duration}</span>
</div> </div>
{exp.objectives.length > 0 && ( {(exp.objectives ?? []).length > 0 && (
<ul className="mb-3 ml-0 list-none space-y-1.5 p-0"> <ul className="mb-3 ml-0 list-none space-y-1.5 p-0">
{exp.objectives.map((obj) => ( {(exp.objectives ?? []).map((obj) => (
<li <li
key={obj.id} key={obj.id}
className="flex gap-2 text-sm text-[var(--sea-ink-soft)]" className="flex gap-2 text-sm text-[var(--sea-ink-soft)]"
@@ -167,9 +167,9 @@ function ExperienceCard({
</ul> </ul>
)} )}
{exp.technologies.length > 0 && ( {(exp.technologies ?? []).length > 0 && (
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
{exp.technologies.map((tech) => ( {(exp.technologies ?? []).map((tech) => (
<TechChip key={tech.id} label={tech.name} /> <TechChip key={tech.id} label={tech.name} />
))} ))}
</div> </div>

View File

@@ -132,8 +132,8 @@ export type TExperience = {
readonly endDate: string | null readonly endDate: string | null
readonly type: 'full-time' | 'part-time' | null readonly type: 'full-time' | 'part-time' | null
readonly locale?: string readonly locale?: string
readonly objectives: readonly TObjective[] readonly objectives?: readonly TObjective[]
readonly technologies: readonly TTechnology[] readonly technologies?: readonly TTechnology[]
} }
export type TEducation = { export type TEducation = {