jalil.arfaoui.net/src/pages/code/parcours.astro
Jalil Arfaoui a3732887f5 Restructuration section Code en sous-pages avec collections de contenu
Remplacement de la page unique /code par un hub avec 4 sous-pages :
parcours, projets, compétences, recommandations (FR/EN/AR).

Les données statiques (experiences.json, projects.json) sont remplacées
par des collections Astro (experiences, projects, recommendations) avec
support trilingue. Les recommandations sont les vrais textes LinkedIn.

Le design utilise du glassmorphism sur fond violet avec des composants
dédiés (NavigationCard, ProjectCard, RecommendationCard, SkillBadge...).
Le CSS facet est scopé proprement pour ne plus casser les composants.
2026-02-22 01:30:06 +01:00

94 lines
4 KiB
Text

---
import { getCollection, render } from "astro:content";
import Layout from "../../layouts/main.astro";
const locale = "fr";
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 typeIcons: Record<string, string> = {
employment: '🏢', freelance: '💼', teaching: '🎓',
community: '🤝', entrepreneurship: '🚀',
};
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
title="Parcours - Jalil Arfaoui"
facet="code"
description="Parcours professionnel de Jalil Arfaoui : plus de 20 ans d'expérience en développement logiciel, de développeur junior à lead developer et CTO."
>
<section class="relative z-20 max-w-3xl mx-auto my-12 px-7 lg:px-0">
<div class="mb-10">
<a href="/code" class="inline-flex items-center gap-1.5 text-sm text-white/50 hover:text-white/80 transition-colors mb-6 group">
<svg class="w-4 h-4 group-hover:-translate-x-0.5 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
Code
</a>
<h1 class="text-3xl sm:text-4xl font-bold text-white">Parcours</h1>
<p class="mt-3 text-white/60 text-lg">Plus de 20 ans d'expérience en développement logiciel.</p>
</div>
<div class="mt-10">
{experiences.map(async (exp) => {
const { Content } = await render(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="relative pl-8 pb-10 border-l-2 border-white/[0.1] last:pb-0">
<div
class:list={[
"absolute -left-[9px] top-1 w-4 h-4 rounded-full border-2",
isOngoing
? "border-purple-300 bg-purple-400"
: "border-white/20 bg-white/10"
]}
>
{isOngoing && <div class="absolute inset-0.5 rounded-full bg-purple-300 animate-pulse" />}
</div>
<div class="facet-card rounded-2xl bg-white/[0.04] border border-white/[0.08] p-5 hover:bg-white/[0.08] transition-colors duration-200">
<div class="mb-2 flex items-center gap-2 flex-wrap">
<span class="text-xs text-white/40">
{typeIcons[exp.data.type] || '💼'} {start} — {end}
</span>
{exp.data.location && <span class="text-xs text-white/30">· {exp.data.location}</span>}
</div>
<h3 class="text-lg font-bold text-white">{exp.data.role}</h3>
<p class="text-sm font-medium text-purple-200 mb-3">
{exp.data.companyUrl ? (
<a href={exp.data.companyUrl} target="_blank" rel="noopener noreferrer" class="hover:text-white transition-colors">{exp.data.company}</a>
) : exp.data.company}
</p>
<div class="text-sm text-white/60 leading-relaxed mb-3 prose prose-sm prose-invert max-w-none [&_p]:text-white/60 [&_a]:text-purple-200 [&_a:hover]:text-white">
<Content />
</div>
{exp.data.technologies && exp.data.technologies.length > 0 && (
<div class="flex flex-wrap gap-1.5">
{exp.data.technologies.map((tech: string) => (
<span class="inline-block px-2 py-0.5 text-xs rounded-full bg-white/[0.06] text-white/50 border border-white/[0.06]">
{tech}
</span>
))}
</div>
)}
</div>
</div>
);
})}
</div>
</section>
</Layout>