Compare commits

...

5 commits

12 changed files with 87 additions and 59 deletions

View file

@ -14,28 +14,35 @@ const { title, description, technologies, featured = false, detailUrl, lang = 'f
const isRtl = lang === 'ar'; const isRtl = lang === 'ar';
const readMoreLabel = t('projects', 'readMore', lang); const readMoreLabel = t('projects', 'readMore', lang);
const primaryTechs = technologies?.slice(0, 3) ?? [];
const secondaryTechs = technologies?.slice(3) ?? [];
--- ---
<a href={detailUrl} class:list={[ <a href={detailUrl} class:list={[
"facet-card rounded-2xl border p-6 flex flex-col h-full transition-all duration-300 hover:scale-[1.02] no-underline", "project-card group relative rounded-2xl border p-6 flex flex-col h-full transition-all duration-300 hover:scale-[1.02] no-underline overflow-hidden",
featured featured
? "bg-white/[0.12] border-purple-300/20 hover:bg-white/[0.18] hover:border-purple-300/30" ? "bg-white/[0.10] border-purple-300/20"
: "bg-white/[0.06] border-white/[0.1] hover:bg-white/[0.12] hover:border-white/[0.2]", : "bg-white/[0.05] border-white/[0.08]",
]}> ]}>
<div class="mb-2"> <div class="mb-3">
<h3 class="text-lg font-bold text-white"> <h3 class="text-xl font-bold text-white">
{title} {title}
</h3> </h3>
</div> </div>
<p class="text-sm text-white/60 leading-relaxed flex-1 mb-4"> <p class="text-white/60 leading-relaxed flex-1 mb-5">
{description} {description}
</p> </p>
{technologies && technologies.length > 0 && ( {(primaryTechs.length > 0 || secondaryTechs.length > 0) && (
<div class="flex flex-wrap gap-1.5 mb-4"> <div class="flex flex-wrap gap-1.5 mb-5">
{technologies.map((tech) => ( {primaryTechs.map((tech) => (
<span class="inline-block px-2 py-0.5 text-xs rounded-full bg-white/[0.08] text-white/60 border border-white/[0.08]"> <span class="inline-block px-2.5 py-1 text-xs font-medium rounded-full bg-purple-400/15 text-purple-200 border border-purple-300/15">
{tech}
</span>
))}
{secondaryTechs.map((tech) => (
<span class="inline-block px-2.5 py-1 text-xs rounded-full bg-white/[0.05] text-white/40 border border-white/[0.06]">
{tech} {tech}
</span> </span>
))} ))}
@ -50,3 +57,11 @@ const readMoreLabel = t('projects', 'readMore', lang);
)} )}
</span> </span>
</a> </a>
<style>
.project-card:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(168, 85, 247, 0.25);
box-shadow: 0 0 24px rgba(168, 85, 247, 0.08), 0 4px 16px rgba(0, 0, 0, 0.2);
}
</style>

View file

@ -0,0 +1,7 @@
---
---
<li class="flex items-start gap-4">
<span class="w-4 h-0.5 bg-purple-400/60 mt-3 flex-shrink-0"></span>
<span><slot /></span>
</li>

View file

@ -7,6 +7,7 @@ startDate: "2022-01"
endDate: "2023-01" endDate: "2023-01"
technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"] technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"]
type: "freelance" type: "freelance"
featured: true
lang: "ar" lang: "ar"
--- ---

View file

@ -7,6 +7,7 @@ startDate: "2022-01"
endDate: "2023-01" endDate: "2023-01"
technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"] technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"]
type: "freelance" type: "freelance"
featured: true
lang: "en" lang: "en"
--- ---

View file

@ -7,6 +7,7 @@ startDate: "2022-01"
endDate: "2023-01" endDate: "2023-01"
technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"] technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"]
type: "freelance" type: "freelance"
featured: true
lang: "fr" lang: "fr"
--- ---

View file

@ -6,6 +6,7 @@ location: "ألبي"
startDate: "2019-09" startDate: "2019-09"
technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"] technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"]
type: "teaching" type: "teaching"
featured: true
lang: "ar" lang: "ar"
--- ---

View file

@ -6,6 +6,7 @@ location: "Albi"
startDate: "2019-09" startDate: "2019-09"
technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"] technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"]
type: "teaching" type: "teaching"
featured: true
lang: "en" lang: "en"
--- ---

View file

@ -6,6 +6,7 @@ location: "Albi"
startDate: "2019-09" startDate: "2019-09"
technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"] technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"]
type: "teaching" type: "teaching"
featured: true
lang: "fr" lang: "fr"
--- ---

View file

@ -17,8 +17,8 @@ const recommendationCount = (await getCollection("recommendations")).length;
--- ---
<Layout <Layout
title="جليل عرفاوي - حِرَفي برمجة • ممثل ارتجالي • مصوّر" title="جليل عرفاوي - مطوّر • مصوّر • ممثل ارتجالي"
description="جليل عرفاوي: مطوّر مستقل (Software Craftsmanship، TDD، DDD)، ممثل ارتجالي ومصوّر مقيم في ألبي، فرنسا." description="جليل عرفاوي: مطوّر مستقل (Software Craftsmanship، TDD، DDD)، مصوّر وممثل مقيم في ألبي، فرنسا."
> >
<!-- Hero Section --> <!-- Hero Section -->
<div dir="rtl" lang="ar" class="relative z-20 w-full max-w-6xl mx-auto mt-16 px-7 md:mt-24 lg:mt-32 xl:px-0"> <div dir="rtl" lang="ar" class="relative z-20 w-full max-w-6xl mx-auto mt-16 px-7 md:mt-24 lg:mt-32 xl:px-0">
@ -28,7 +28,7 @@ const recommendationCount = (await getCollection("recommendations")).length;
جليل عرفاوي جليل عرفاوي
</h1> </h1>
<h2 class="mb-6 text-xl font-medium text-neutral-600 dark:text-neutral-300 md:text-2xl"> <h2 class="mb-6 text-xl font-medium text-neutral-600 dark:text-neutral-300 md:text-2xl">
حِرَفي برمجة • ممثل ارتجالي • مصوّر مطوّر • مصوّر • ممثل
</h2> </h2>
<p class="mb-8 text-lg text-neutral-600 dark:text-neutral-400 leading-relaxed"> <p class="mb-8 text-lg text-neutral-600 dark:text-neutral-400 leading-relaxed">
أكتب الكود من أجل عالم أكثر عدلًا، وأصعد على الخشبة لأجل الباقي. أو العكس. أكتب الكود من أجل عالم أكثر عدلًا، وأصعد على الخشبة لأجل الباقي. أو العكس.

View file

@ -5,6 +5,7 @@ import Layout from "../../layouts/main.astro";
import Link from "../../components/Link.astro"; import Link from "../../components/Link.astro";
import FeaturedRecommendation from "../../components/code/FeaturedRecommendation.astro"; import FeaturedRecommendation from "../../components/code/FeaturedRecommendation.astro";
import ProjectCard from "../../components/code/ProjectCard.astro"; import ProjectCard from "../../components/code/ProjectCard.astro";
import ValueItem from "../../components/code/ValueItem.astro";
import { getProjectBaseSlug, getProjectsBasePath } from "../../utils/i18n"; import { getProjectBaseSlug, getProjectsBasePath } from "../../utils/i18n";
import logoTiqa from "../../assets/images/logo-tiqa-blanc.png"; import logoTiqa from "../../assets/images/logo-tiqa-blanc.png";
@ -15,7 +16,7 @@ const experiences = (await getCollection("experiences"))
.filter((e) => e.data.lang === locale && !e.data.draft) .filter((e) => e.data.lang === locale && !e.data.draft)
.sort((a, b) => (b.data.startDate > a.data.startDate ? 1 : -1)); .sort((a, b) => (b.data.startDate > a.data.startDate ? 1 : -1));
const recentExperiences = experiences.slice(0, 4); const recentExperiences = experiences.filter((e) => e.data.featured).slice(0, 4);
const projects = (await getCollection("projects")) const projects = (await getCollection("projects"))
.filter((p) => p.data.lang === locale && !p.data.draft && p.data.category === "dev" && p.data.featured) .filter((p) => p.data.lang === locale && !p.data.draft && p.data.category === "dev" && p.data.featured)
@ -44,8 +45,10 @@ function formatMonth(dateStr: string) {
> >
<section class="relative z-20 max-w-3xl mx-auto my-16 px-7 lg:px-0"> <section class="relative z-20 max-w-3xl mx-auto my-16 px-7 lg:px-0">
<div class="mb-16"> <div class="mb-16">
<h1 class="text-4xl sm:text-5xl font-semibold text-white font-display tracking-wide">Artisan du logiciel</h1> <span class="inline-block px-3 py-1 text-sm text-purple-200/80 bg-white/[0.06] border border-white/[0.08] rounded-full mb-5">Développeur freelance · Albi</span>
<div class="mt-6 space-y-4 text-lg text-white/60 leading-relaxed max-w-2xl"> <h1 class="text-5xl sm:text-6xl font-semibold text-white font-display tracking-wide">Artisan du logiciel</h1>
<div class="mt-6 mb-6 w-16 h-0.5 bg-gradient-to-r from-purple-400 to-transparent rounded-full"></div>
<div class="space-y-4 text-lg text-white/60 leading-relaxed max-w-2xl">
<p> <p>
TDD, Clean Code, Domain-Driven Design : c'est ma façon de construire du logiciel. J'accompagne les équipes comme développeur senior, tech lead ou coach technique. Ma stack : TypeScript/JavaScript, mais aussi PHP et Elixir. TDD, Clean Code, Domain-Driven Design : c'est ma façon de construire du logiciel. J'accompagne les équipes comme développeur senior, tech lead ou coach technique. Ma stack : TypeScript/JavaScript, mais aussi PHP et Elixir.
</p> </p>
@ -63,30 +66,42 @@ function formatMonth(dateStr: string) {
<h2 class="text-2xl font-bold text-white ">Parcours</h2> <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> <a href="/code/parcours" class="text-sm text-purple-200 hover:text-white transition-colors">Voir tout &rarr;</a>
</div> </div>
<div class="divide-y divide-white/[0.08]"> <div class="relative ml-3">
<div class="absolute left-0 top-1.5 bottom-1.5 w-px bg-purple-300/20"></div>
{recentExperiences.map((exp) => { {recentExperiences.map((exp) => {
const isOngoing = !exp.data.endDate; const isOngoing = !exp.data.endDate;
const start = formatMonth(exp.data.startDate); const start = formatMonth(exp.data.startDate);
const end = exp.data.endDate ? formatMonth(exp.data.endDate) : 'Présent'; const end = exp.data.endDate ? formatMonth(exp.data.endDate) : 'Présent';
return ( return (
<div class="py-4 first:pt-0 last:pb-0"> <div class="relative pl-7 pb-6 last:pb-0">
<div class="flex items-start justify-between gap-4"> <div class:list={[
<div class="min-w-0"> "absolute left-0 top-1.5 w-2.5 h-2.5 rounded-full -translate-x-1/2 border-2",
<p class="font-semibold text-white text-sm">{exp.data.role}</p> isOngoing
<p class="text-sm text-white/45 mt-0.5"> ? "bg-purple-400 border-purple-400/50 shadow-[0_0_8px_rgba(168,85,247,0.4)]"
{exp.data.companyUrl ? ( : "bg-transparent border-purple-300/30"
<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} {isOngoing && (
{exp.data.location && ` · ${exp.data.location}`} <div class="absolute left-0 top-1.5 w-2.5 h-2.5 rounded-full -translate-x-1/2 bg-purple-400/50 animate-ping" />
</p> )}
</div> <span class:list={[isOngoing ? "text-purple-200" : "text-white/55"]}>
<span class:list={["text-sm whitespace-nowrap flex-shrink-0", isOngoing ? "text-purple-200" : "text-white/35"]}> {start} — {end}
{start} — {end} </span>
</span> <p class="font-semibold text-white text-lg mt-0.5">{exp.data.role}</p>
</div> <p class="text-white/55 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> </div>
); );
})} })}
<div class="relative pl-7 pt-2 flex items-center gap-3">
<span class="text-purple-300/40 tracking-[0.3em]">...</span>
<a href="/code/parcours" class="text-purple-200 hover:text-white transition-colors">
Voir le parcours complet &rarr;
</a>
</div>
</div> </div>
</div> </div>
@ -132,27 +147,12 @@ function formatMonth(dateStr: string) {
<div class="mb-16"> <div class="mb-16">
<h2 class="text-2xl font-bold text-white mb-6">Valeurs & Approche</h2> <h2 class="text-2xl font-bold text-white mb-6">Valeurs & Approche</h2>
<ul class="space-y-3 text-white/60"> <ul class="space-y-4 text-white/80">
<li class="flex items-start gap-3"> <ValueItem>Le mouvement <Link href="http://manifesto.softwarecraftsmanship.org/#/fr-fr" external>Software Craftsmanship</Link></ValueItem>
<span class="w-1 h-1 rounded-full bg-purple-300/60 mt-2.5 flex-shrink-0"></span> <ValueItem>L'utilité sociale du développeur</ValueItem>
<span>Le mouvement <Link href="http://manifesto.softwarecraftsmanship.org/#/fr-fr" external>Software Craftsmanship</Link></span> <ValueItem>Être fier de son travail, mais sans égo</ValueItem>
</li> <ValueItem>Approche <strong class="text-white">Domain Driven Design</strong></ValueItem>
<li class="flex items-start gap-3"> <ValueItem>Organisation <Link href="https://agilemanifesto.org/iso/fr/manifesto.html" external>agile</Link> : itération et amélioration continue</ValueItem>
<span class="w-1 h-1 rounded-full bg-purple-300/60 mt-2.5 flex-shrink-0"></span>
<span>L'utilité sociale du développeur</span>
</li>
<li class="flex items-start gap-3">
<span class="w-1 h-1 rounded-full bg-purple-300/60 mt-2.5 flex-shrink-0"></span>
<span>Être fier de son travail, mais sans égo</span>
</li>
<li class="flex items-start gap-3">
<span class="w-1 h-1 rounded-full bg-purple-300/60 mt-2.5 flex-shrink-0"></span>
<span>Approche <strong class="text-white">Domain Driven Design</strong></span>
</li>
<li class="flex items-start gap-3">
<span class="w-1 h-1 rounded-full bg-purple-300/60 mt-2.5 flex-shrink-0"></span>
<span>Organisation <Link href="https://agilemanifesto.org/iso/fr/manifesto.html" external>agile</Link> : itération et amélioration continue</span>
</li>
</ul> </ul>
</div> </div>

View file

@ -17,8 +17,8 @@ const recommendationCount = (await getCollection("recommendations")).length;
--- ---
<Layout <Layout
title="Jalil Arfaoui - Software Craftsman • Improv Actor • Photographer" title="Jalil Arfaoui - Software Craftsman • Photographer • Improv Actor"
description="Jalil Arfaoui: freelance developer (Software Craftsmanship, TDD, DDD), improv actor and photographer based in Albi, France." description="Jalil Arfaoui: freelance developer (Software Craftsmanship, TDD, DDD), photographer and actor based in Albi, France."
> >
<!-- Hero Section --> <!-- Hero Section -->
<div class="relative z-20 w-full max-w-6xl mx-auto mt-16 px-7 md:mt-24 lg:mt-32 xl:px-0"> <div class="relative z-20 w-full max-w-6xl mx-auto mt-16 px-7 md:mt-24 lg:mt-32 xl:px-0">
@ -28,7 +28,7 @@ const recommendationCount = (await getCollection("recommendations")).length;
Jalil Arfaoui Jalil Arfaoui
</h1> </h1>
<h2 class="mb-6 text-xl font-medium text-neutral-600 dark:text-neutral-300 md:text-2xl"> <h2 class="mb-6 text-xl font-medium text-neutral-600 dark:text-neutral-300 md:text-2xl">
Software Craftsman • Improv Actor • Photographer Software Craftsman • Photographer • Actor
</h2> </h2>
<p class="mb-8 text-lg text-neutral-600 dark:text-neutral-400 leading-relaxed"> <p class="mb-8 text-lg text-neutral-600 dark:text-neutral-400 leading-relaxed">
I write code for a fairer world and get on stage for the rest. Or the other way around. I write code for a fairer world and get on stage for the rest. Or the other way around.

View file

@ -17,8 +17,8 @@ const recommendationCount = (await getCollection("recommendations")).length;
--- ---
<Layout <Layout
title="Jalil Arfaoui - Développeur artisan • Comédien improvisateur • Photographe" title="Jalil Arfaoui - Développeur artisan • Photographe • Comédien improvisateur"
description="Jalil Arfaoui : développeur freelance (Software Craftsmanship, TDD, DDD), comédien improvisateur et photographe basé à Albi." description="Jalil Arfaoui : développeur freelance, photographe et comédien improvisateur basé à Albi."
> >
<!-- Hero Section --> <!-- Hero Section -->
<div class="relative z-20 w-full max-w-6xl mx-auto mt-16 px-7 md:mt-24 lg:mt-32 xl:px-0"> <div class="relative z-20 w-full max-w-6xl mx-auto mt-16 px-7 md:mt-24 lg:mt-32 xl:px-0">
@ -28,7 +28,7 @@ const recommendationCount = (await getCollection("recommendations")).length;
Jalil Arfaoui Jalil Arfaoui
</h1> </h1>
<h2 class="mb-6 text-xl font-medium text-neutral-600 dark:text-neutral-300 md:text-2xl"> <h2 class="mb-6 text-xl font-medium text-neutral-600 dark:text-neutral-300 md:text-2xl">
Développeur artisan • Comédien improvisateur • Photographe Développeur • Photographe • Comédien
</h2> </h2>
<p class="mb-8 text-lg text-neutral-600 dark:text-neutral-400 leading-relaxed"> <p class="mb-8 text-lg text-neutral-600 dark:text-neutral-400 leading-relaxed">
Je code pour un monde plus juste et je monte sur scène pour le reste. Ou l'inverse. Je code pour un monde plus juste et je monte sur scène pour le reste. Ou l'inverse.