Refonte des pages hub /code en aperçus de contenu

Remplace les NavigationCards génériques par des aperçus réels :
4 expériences récentes, 3 projets featured, 3 catégories de
compétences et 3 recommandations, chacun avec lien "Voir tout".
Supprime le composant NavigationCard devenu inutile.
This commit is contained in:
Jalil Arfaoui 2026-02-22 14:39:47 +01:00
parent bf26caded3
commit e48a551dcb
7 changed files with 260 additions and 144 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -1,23 +0,0 @@
---
interface Props {
title: string;
description: string;
href: string;
icon: string;
}
const { title, description, href, icon } = Astro.props;
---
<a
href={href}
class="facet-card group block rounded-2xl bg-white/[0.06] border border-white/[0.1] p-6 hover:bg-white/[0.12] hover:border-white/[0.2] hover:scale-[1.02] transition-all duration-300 no-underline"
>
<div class="text-3xl mb-3 opacity-80 group-hover:opacity-100 group-hover:scale-110 transition-all duration-300 inline-block">{icon}</div>
<h3 class="text-lg font-bold text-white mb-2 group-hover:text-purple-200 transition-colors">
{title}
</h3>
<p class="text-sm text-white/55 leading-relaxed group-hover:text-white/70 transition-colors">
{description}
</p>
</a>

View file

@ -2,6 +2,7 @@
author: "Antoine Wolff"
authorRole: "Développeur, graphiste et chef de projet"
company: "LeCollectif"
avatar: antoine-wolff.png
url: https://www.linkedin.com/in/wolffantoine
date: 2020-12-07
lang: "fr"

View file

@ -1,6 +1,6 @@
---
author: "Benoit Sarda"
authorRole: "Sr Solution Architect, Manuf"
authorRole: "Sr Solution Architect"
company: "Amazon Web Services (AWS)"
avatar: benoit-sarda.jpg
url: https://www.linkedin.com/in/benoitsarda

View file

@ -3,9 +3,11 @@ import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import Layout from "../../../layouts/main.astro";
import Link from "../../../components/Link.astro";
import NavigationCard from "../../../components/code/NavigationCard.astro";
import FeaturedRecommendation from "../../../components/code/FeaturedRecommendation.astro";
import ProjectCard from "../../../components/code/ProjectCard.astro";
import SkillBadge from "../../../components/code/SkillBadge.astro";
import logoTiqa from "../../../assets/images/logo-tiqa-blanc.png";
import skillsData from "../../../data/skills.json";
const locale = "ar";
@ -13,7 +15,15 @@ const experiences = (await getCollection("experiences"))
.filter((e) => e.data.lang === locale && !e.data.draft)
.sort((a, b) => (b.data.startDate > a.data.startDate ? 1 : -1));
const currentPosition = experiences.find((e) => !e.data.endDate);
const recentExperiences = experiences.slice(0, 4);
const projects = (await getCollection("projects"))
.filter((p) => p.data.lang === locale && !p.data.draft && p.data.category === "dev")
.sort((a, b) => {
if (a.data.featured !== b.data.featured) return a.data.featured ? -1 : 1;
return b.data.date.getTime() - a.data.date.getTime();
})
.slice(0, 3);
const recommendations = (await getCollection("recommendations"))
.sort((a, b) => b.data.date.getTime() - a.data.date.getTime())
@ -23,6 +33,14 @@ const recommendationTexts = recommendations.map((rec) => ({
...rec,
text: rec.body || '',
}));
const topSkills = skillsData.categories.slice(0, 3);
function formatMonth(dateStr: string) {
const [year, month] = dateStr.split('-');
return new Date(parseInt(year), parseInt(month) - 1)
.toLocaleDateString('ar-SA', { year: 'numeric', month: 'short' });
}
---
<Layout
@ -44,47 +62,56 @@ const recommendationTexts = recommendations.map((rec) => ({
</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-12">
<NavigationCard
title="المسار"
description="الجدول الزمني لخبراتي المهنية منذ 2002."
href="/ar/برمجة/مسار"
icon="📋"
/>
<NavigationCard
title="المشاريع"
description="برمجيات مفتوحة المصدر ومشاريع شخصية."
href="/ar/برمجة/مشاريع"
icon="💻"
/>
<NavigationCard
title="المهارات"
description="لغات، أطر عمل، ممارسات وأدوات."
href="/ar/برمجة/مهارات"
icon="🛠"
/>
<NavigationCard
title="التوصيات"
description="ما يقوله الأشخاص الذين عملت معهم."
href="/ar/برمجة/توصيات"
icon="💬"
/>
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">المسار</h2>
<a href="/ar/برمجة/مسار" class="text-sm text-purple-200 hover:text-white transition-colors">&larr; عرض الكل</a>
</div>
<div class="space-y-3">
{recentExperiences.map((exp) => {
const isOngoing = !exp.data.endDate;
const start = formatMonth(exp.data.startDate);
const end = exp.data.endDate ? formatMonth(exp.data.endDate) : 'الحالي';
return (
<div class="facet-card rounded-xl bg-white/[0.06] border border-white/[0.1] p-4 hover:bg-white/[0.1] transition-colors">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<p class="font-semibold text-white text-sm truncate">{exp.data.role}</p>
<p class="text-xs text-white/50 mt-0.5">
{exp.data.companyUrl ? (
<a href={exp.data.companyUrl} target="_blank" rel="noopener noreferrer" class="text-purple-200 hover:text-white transition-colors">{exp.data.company}</a>
) : exp.data.company}
{exp.data.location && ` · ${exp.data.location}`}
</p>
</div>
<span class:list={["text-xs whitespace-nowrap flex-shrink-0", isOngoing ? "text-purple-200 font-semibold" : "text-white/40"]}>
{start} — {end}
</span>
</div>
</div>
);
})}
</div>
</div>
{currentPosition && (
<div class="facet-card rounded-2xl bg-gradient-to-r from-purple-500/20 to-indigo-500/20 border border-purple-300/15 p-6 mb-10">
<p class="text-xs font-semibold text-purple-200 uppercase tracking-wider mb-2">المنصب الحالي</p>
<p class="text-xl font-bold text-white">{currentPosition.data.role}</p>
<p class="text-sm text-white/60 mt-1">
{currentPosition.data.companyUrl ? (
<a href={currentPosition.data.companyUrl} target="_blank" rel="noopener noreferrer" class="text-purple-200 hover:text-white transition-colors">{currentPosition.data.company}</a>
) : (
currentPosition.data.company
)}
{currentPosition.data.location && ` · ${currentPosition.data.location}`}
</p>
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">المشاريع</h2>
<a href="/ar/برمجة/مشاريع" class="text-sm text-purple-200 hover:text-white transition-colors">&larr; عرض الكل</a>
</div>
)}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
{projects.map((project) => (
<ProjectCard
title={project.data.title}
description={project.data.description}
technologies={project.data.technologies}
url={project.data.url}
github={project.data.github}
featured={project.data.featured}
/>
))}
</div>
</div>
{recommendationTexts.length > 0 && (
<div class="mb-12">
@ -107,6 +134,25 @@ const recommendationTexts = recommendations.map((rec) => ({
</div>
)}
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">المهارات</h2>
<a href="/ar/برمجة/مهارات" class="text-sm text-purple-200 hover:text-white transition-colors">&larr; عرض الكل</a>
</div>
<div class="space-y-4">
{topSkills.map((category) => (
<div>
<p class="text-xs font-semibold text-white/40 uppercase tracking-wider mb-2">{category.name[locale as keyof typeof category.name]}</p>
<div class="flex flex-wrap gap-1.5">
{category.skills.map((skill) => (
<SkillBadge name={skill} />
))}
</div>
</div>
))}
</div>
</div>
<h2 class="text-2xl font-bold text-white mb-5">القيم والمنهج</h2>
<div class="facet-card rounded-2xl bg-white/[0.04] border border-white/[0.08] p-6 mb-10">
<ul class="space-y-3 text-white/70">

View file

@ -3,9 +3,11 @@ import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import Layout from "../../layouts/main.astro";
import Link from "../../components/Link.astro";
import NavigationCard from "../../components/code/NavigationCard.astro";
import FeaturedRecommendation from "../../components/code/FeaturedRecommendation.astro";
import ProjectCard from "../../components/code/ProjectCard.astro";
import SkillBadge from "../../components/code/SkillBadge.astro";
import logoTiqa from "../../assets/images/logo-tiqa-blanc.png";
import skillsData from "../../data/skills.json";
const locale = "fr";
@ -13,7 +15,15 @@ const experiences = (await getCollection("experiences"))
.filter((e) => e.data.lang === locale && !e.data.draft)
.sort((a, b) => (b.data.startDate > a.data.startDate ? 1 : -1));
const currentPosition = experiences.find((e) => !e.data.endDate);
const recentExperiences = experiences.slice(0, 4);
const projects = (await getCollection("projects"))
.filter((p) => p.data.lang === locale && !p.data.draft && p.data.category === "dev")
.sort((a, b) => {
if (a.data.featured !== b.data.featured) return a.data.featured ? -1 : 1;
return b.data.date.getTime() - a.data.date.getTime();
})
.slice(0, 3);
const recommendations = (await getCollection("recommendations"))
.sort((a, b) => b.data.date.getTime() - a.data.date.getTime())
@ -23,6 +33,14 @@ const recommendationTexts = recommendations.map((rec) => ({
...rec,
text: rec.body || '',
}));
const topSkills = skillsData.categories.slice(0, 3);
function formatMonth(dateStr: string) {
const [year, month] = dateStr.split('-');
return new Date(parseInt(year), parseInt(month) - 1)
.toLocaleDateString('fr-FR', { year: 'numeric', month: 'short' });
}
---
<Layout
@ -44,47 +62,56 @@ const recommendationTexts = recommendations.map((rec) => ({
</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-12">
<NavigationCard
title="Parcours"
description="Timeline de mes expériences professionnelles depuis 2002."
href="/code/parcours"
icon="📋"
/>
<NavigationCard
title="Projets"
description="Logiciels open source et projets personnels."
href="/code/projets"
icon="💻"
/>
<NavigationCard
title="Compétences"
description="Langages, frameworks, pratiques et outils."
href="/code/competences"
icon="🛠"
/>
<NavigationCard
title="Recommandations"
description="Ce que disent les gens avec qui j'ai travaillé."
href="/code/recommandations"
icon="💬"
/>
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">Parcours</h2>
<a href="/code/parcours" class="text-sm text-purple-200 hover:text-white transition-colors">Voir tout &rarr;</a>
</div>
<div class="space-y-3">
{recentExperiences.map((exp) => {
const isOngoing = !exp.data.endDate;
const start = formatMonth(exp.data.startDate);
const end = exp.data.endDate ? formatMonth(exp.data.endDate) : 'Présent';
return (
<div class="facet-card rounded-xl bg-white/[0.06] border border-white/[0.1] p-4 hover:bg-white/[0.1] transition-colors">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<p class="font-semibold text-white text-sm truncate">{exp.data.role}</p>
<p class="text-xs text-white/50 mt-0.5">
{exp.data.companyUrl ? (
<a href={exp.data.companyUrl} target="_blank" rel="noopener noreferrer" class="text-purple-200 hover:text-white transition-colors">{exp.data.company}</a>
) : exp.data.company}
{exp.data.location && ` · ${exp.data.location}`}
</p>
</div>
<span class:list={["text-xs whitespace-nowrap flex-shrink-0", isOngoing ? "text-purple-200 font-semibold" : "text-white/40"]}>
{start} — {end}
</span>
</div>
</div>
);
})}
</div>
</div>
{currentPosition && (
<div class="facet-card rounded-2xl bg-gradient-to-r from-purple-500/20 to-indigo-500/20 border border-purple-300/15 p-6 mb-10">
<p class="text-xs font-semibold text-purple-200 uppercase tracking-wider mb-2">Poste actuel</p>
<p class="text-xl font-bold text-white">{currentPosition.data.role}</p>
<p class="text-sm text-white/60 mt-1">
{currentPosition.data.companyUrl ? (
<a href={currentPosition.data.companyUrl} target="_blank" rel="noopener noreferrer" class="text-purple-200 hover:text-white transition-colors">{currentPosition.data.company}</a>
) : (
currentPosition.data.company
)}
{currentPosition.data.location && ` · ${currentPosition.data.location}`}
</p>
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">Projets</h2>
<a href="/code/projets" class="text-sm text-purple-200 hover:text-white transition-colors">Voir tous &rarr;</a>
</div>
)}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
{projects.map((project) => (
<ProjectCard
title={project.data.title}
description={project.data.description}
technologies={project.data.technologies}
url={project.data.url}
github={project.data.github}
featured={project.data.featured}
/>
))}
</div>
</div>
{recommendationTexts.length > 0 && (
<div class="mb-12">
@ -107,6 +134,25 @@ const recommendationTexts = recommendations.map((rec) => ({
</div>
)}
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">Compétences</h2>
<a href="/code/competences" class="text-sm text-purple-200 hover:text-white transition-colors">Voir toutes &rarr;</a>
</div>
<div class="space-y-4">
{topSkills.map((category) => (
<div>
<p class="text-xs font-semibold text-white/40 uppercase tracking-wider mb-2">{category.name[locale as keyof typeof category.name]}</p>
<div class="flex flex-wrap gap-1.5">
{category.skills.map((skill) => (
<SkillBadge name={skill} />
))}
</div>
</div>
))}
</div>
</div>
<h2 class="text-2xl font-bold text-white mb-5">Valeurs & Approche</h2>
<div class="facet-card rounded-2xl bg-white/[0.04] border border-white/[0.08] p-6 mb-10">
<ul class="space-y-3 text-white/70">

View file

@ -3,9 +3,11 @@ import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import Layout from "../../../layouts/main.astro";
import Link from "../../../components/Link.astro";
import NavigationCard from "../../../components/code/NavigationCard.astro";
import FeaturedRecommendation from "../../../components/code/FeaturedRecommendation.astro";
import ProjectCard from "../../../components/code/ProjectCard.astro";
import SkillBadge from "../../../components/code/SkillBadge.astro";
import logoTiqa from "../../../assets/images/logo-tiqa-blanc.png";
import skillsData from "../../../data/skills.json";
const locale = "en";
@ -13,7 +15,15 @@ const experiences = (await getCollection("experiences"))
.filter((e) => e.data.lang === locale && !e.data.draft)
.sort((a, b) => (b.data.startDate > a.data.startDate ? 1 : -1));
const currentPosition = experiences.find((e) => !e.data.endDate);
const recentExperiences = experiences.slice(0, 4);
const projects = (await getCollection("projects"))
.filter((p) => p.data.lang === locale && !p.data.draft && p.data.category === "dev")
.sort((a, b) => {
if (a.data.featured !== b.data.featured) return a.data.featured ? -1 : 1;
return b.data.date.getTime() - a.data.date.getTime();
})
.slice(0, 3);
const recommendations = (await getCollection("recommendations"))
.sort((a, b) => b.data.date.getTime() - a.data.date.getTime())
@ -23,6 +33,14 @@ const recommendationTexts = recommendations.map((rec) => ({
...rec,
text: rec.body || '',
}));
const topSkills = skillsData.categories.slice(0, 3);
function formatMonth(dateStr: string) {
const [year, month] = dateStr.split('-');
return new Date(parseInt(year), parseInt(month) - 1)
.toLocaleDateString('en-US', { year: 'numeric', month: 'short' });
}
---
<Layout
@ -44,47 +62,56 @@ const recommendationTexts = recommendations.map((rec) => ({
</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-12">
<NavigationCard
title="Career"
description="Timeline of my professional experiences since 2002."
href="/en/code/career"
icon="📋"
/>
<NavigationCard
title="Projects"
description="Open source software and personal projects."
href="/en/code/projects"
icon="💻"
/>
<NavigationCard
title="Skills"
description="Languages, frameworks, practices and tools."
href="/en/code/skills"
icon="🛠"
/>
<NavigationCard
title="Recommendations"
description="What people I've worked with say about me."
href="/en/code/recommendations"
icon="💬"
/>
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">Career</h2>
<a href="/en/code/career" class="text-sm text-purple-200 hover:text-white transition-colors">See all &rarr;</a>
</div>
<div class="space-y-3">
{recentExperiences.map((exp) => {
const isOngoing = !exp.data.endDate;
const start = formatMonth(exp.data.startDate);
const end = exp.data.endDate ? formatMonth(exp.data.endDate) : 'Present';
return (
<div class="facet-card rounded-xl bg-white/[0.06] border border-white/[0.1] p-4 hover:bg-white/[0.1] transition-colors">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<p class="font-semibold text-white text-sm truncate">{exp.data.role}</p>
<p class="text-xs text-white/50 mt-0.5">
{exp.data.companyUrl ? (
<a href={exp.data.companyUrl} target="_blank" rel="noopener noreferrer" class="text-purple-200 hover:text-white transition-colors">{exp.data.company}</a>
) : exp.data.company}
{exp.data.location && ` · ${exp.data.location}`}
</p>
</div>
<span class:list={["text-xs whitespace-nowrap flex-shrink-0", isOngoing ? "text-purple-200 font-semibold" : "text-white/40"]}>
{start} — {end}
</span>
</div>
</div>
);
})}
</div>
</div>
{currentPosition && (
<div class="facet-card rounded-2xl bg-gradient-to-r from-purple-500/20 to-indigo-500/20 border border-purple-300/15 p-6 mb-10">
<p class="text-xs font-semibold text-purple-200 uppercase tracking-wider mb-2">Current position</p>
<p class="text-xl font-bold text-white">{currentPosition.data.role}</p>
<p class="text-sm text-white/60 mt-1">
{currentPosition.data.companyUrl ? (
<a href={currentPosition.data.companyUrl} target="_blank" rel="noopener noreferrer" class="text-purple-200 hover:text-white transition-colors">{currentPosition.data.company}</a>
) : (
currentPosition.data.company
)}
{currentPosition.data.location && ` · ${currentPosition.data.location}`}
</p>
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">Projects</h2>
<a href="/en/code/projects" class="text-sm text-purple-200 hover:text-white transition-colors">See all &rarr;</a>
</div>
)}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
{projects.map((project) => (
<ProjectCard
title={project.data.title}
description={project.data.description}
technologies={project.data.technologies}
url={project.data.url}
github={project.data.github}
featured={project.data.featured}
/>
))}
</div>
</div>
{recommendationTexts.length > 0 && (
<div class="mb-12">
@ -107,6 +134,25 @@ const recommendationTexts = recommendations.map((rec) => ({
</div>
)}
<div class="mb-12">
<div class="flex items-center justify-between mb-5">
<h2 class="text-2xl font-bold text-white">Skills</h2>
<a href="/en/code/skills" class="text-sm text-purple-200 hover:text-white transition-colors">See all &rarr;</a>
</div>
<div class="space-y-4">
{topSkills.map((category) => (
<div>
<p class="text-xs font-semibold text-white/40 uppercase tracking-wider mb-2">{category.name[locale as keyof typeof category.name]}</p>
<div class="flex flex-wrap gap-1.5">
{category.skills.map((skill) => (
<SkillBadge name={skill} />
))}
</div>
</div>
))}
</div>
</div>
<h2 class="text-2xl font-bold text-white mb-5">Values & Approach</h2>
<div class="facet-card rounded-2xl bg-white/[0.04] border border-white/[0.08] p-6 mb-10">
<ul class="space-y-3 text-white/70">