Compare commits

...

15 commits

Author SHA1 Message Date
ebd074b866 Théâtre : section La Pièce (Compagnie AspiRêves, mise en scène Michel Cals) 2026-03-12 00:29:13 +01:00
f88a17b0ee Logos clients dans la mini-timeline /code (FR/EN/AR) 2026-03-12 00:28:06 +01:00
3d67c2a320 Urssaf : Télétravail / Montreuil 2026-03-12 00:03:36 +01:00
677142a7c4 Lieux : télétravail en FR, Remote en EN, Veepee Saint-Denis 2026-03-12 00:01:59 +01:00
663ce9a879 DisMoi : Remote / Bordeaux 2026-03-12 00:00:59 +01:00
973b2faa04 Libeo : Remote / Paris 2026-03-12 00:00:34 +01:00
5dfdbd88ba GoBuild : Remote / Lyon 2026-03-12 00:00:13 +01:00
78a72bfb92 Parcours : dates/lieux plus visibles en mode nuit (opacité 80%, taille sm), ARaymond Remote / Grenoble 2026-03-11 23:59:36 +01:00
6b3ceef903 GoBuild : type employment → freelance 2026-03-11 23:48:58 +01:00
0a4ed28fc0 Correction des dates et lieux des expériences d'après l'export LinkedIn 2026-03-11 23:47:32 +01:00
c8f444993d Ajout logo ESN 81 2026-03-11 23:36:12 +01:00
52aaa0a5aa Ajout logo GoBuild (Go-Decision) 2026-03-11 23:33:56 +01:00
88ed94dca3 Ajout logo Team Logics 2026-03-11 23:29:37 +01:00
f32cd09940 Tri des expériences par date de fin décroissante puis date de début (parcours et /code, FR/EN/AR) 2026-03-11 23:21:54 +01:00
366d18764b Héritage i18n : les fichiers EN/AR ne surchargent que les champs traduits, les métadonnées partagées sont héritées de la base FR
Ajout de getLocalizedCollection/getLocalizedEntry dans content-i18n.ts pour fusionner automatiquement les entrées localisées avec leur base FR. Schémas adaptés (champs partagés optionnels), 78 fichiers de contenu allégés, consommateurs migrés.
2026-03-11 23:17:19 +01:00
120 changed files with 369 additions and 567 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,7 +1,8 @@
---
import { getCollection, render } from "astro:content";
import { render } from "astro:content";
import { Image } from "astro:assets";
import { t, getDateLocale, type Locale } from "../../utils/i18n";
import { getLocalizedCollection } from "../../utils/content-i18n";
import "../../styles/exp-card.css";
interface Props {
@ -53,9 +54,14 @@ function computeDuration(startStr: string, endStr?: string) {
return formatDuration(years, months);
}
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 experiences = (await getLocalizedCollection("experiences", locale))
.filter((e) => !e.data.draft)
.sort((a, b) => {
const endA = a.data.endDate ?? '9999-12';
const endB = b.data.endDate ?? '9999-12';
if (endA !== endB) return endB > endA ? 1 : -1;
return b.data.startDate! > a.data.startDate! ? 1 : -1;
});
const presentLabel = t('career', 'present', locale);
const logoAltPrefix = t('career', 'logoAlt', locale);
@ -107,11 +113,11 @@ const logoAltPrefix = t('career', 'logoAlt', locale);
<span class:list={["inline-block px-2 py-0.5 text-[11px] font-medium rounded-full border", colors.badge]}>
{label}
</span>
<span class="exp-meta text-xs">
<span class="exp-meta text-sm">
{start} — {end}
</span>
<span class="exp-meta text-xs">· {duration}</span>
{exp.data.location && <span class="exp-meta text-xs">· {exp.data.location}</span>}
<span class="exp-meta text-sm">· {duration}</span>
{exp.data.location && <span class="exp-meta text-sm">· {exp.data.location}</span>}
</div>
<h3 class="text-xl font-bold pe-20">{exp.data.role}</h3>

View file

@ -3,7 +3,7 @@ import CategoryNav from './CategoryNav.astro';
import HeroViewport from './HeroViewport.astro';
import Lightbox from './Lightbox.astro';
import { Picture } from 'astro:assets';
import { getEntry } from 'astro:content';
import { getLocalizedEntry } from '../../utils/content-i18n';
import { getCategoryEntryId, type Locale } from '../../utils/i18n';
interface Props {
@ -13,10 +13,9 @@ interface Props {
const { category, lang = 'fr' } = Astro.props;
// Récupérer les métadonnées de la catégorie (localisées avec fallback FR)
// Récupérer les métadonnées de la catégorie (localisées avec fusion FR)
const entryId = getCategoryEntryId(category, lang);
const categoryData = await getEntry('photoCategories', entryId)
?? await getEntry('photoCategories', category);
const categoryData = await getLocalizedEntry('photoCategories', entryId, lang);
// Auto-détection des images du dossier de la catégorie
const allImages = import.meta.glob<{ default: ImageMetadata }>('/src/assets/images/photos/categories/**/*.{jpg,jpeg,png,webp}');

View file

@ -1,5 +1,5 @@
---
import { getCollection } from 'astro:content';
import { getLocalizedCollection } from '../../utils/content-i18n';
import HomeIcon from '../icons/HomeIcon.astro';
import { t, getPhotoBasePath, getPhotoBlogPath, getPhotoAlbumsPath, getHomePath, type Locale } from '../../utils/i18n';
@ -15,10 +15,8 @@ const photoBasePath = getPhotoBasePath(lang);
const homePath = getHomePath(lang);
// Récupérer les catégories depuis la collection, filtrées par langue
const allCategories = await getCollection('photoCategories');
const langCategories = allCategories.filter(c => (c.data.lang ?? 'fr') === lang);
const effectiveCategories = langCategories.length > 0 ? langCategories : allCategories.filter(c => (c.data.lang ?? 'fr') === 'fr');
const sortedCategories = effectiveCategories.sort((a, b) => (a.data.order || 99) - (b.data.order || 99));
const localizedCategories = await getLocalizedCollection('photoCategories', lang);
const sortedCategories = localizedCategories.sort((a, b) => (a.data.order || 99) - (b.data.order || 99));
// Extraire l'id de base (sans suffixe de langue)
const categories = sortedCategories.map(cat => ({

View file

@ -1,5 +1,5 @@
---
import { getCollection } from 'astro:content';
import { getLocalizedCollection } from '../../utils/content-i18n';
import { t, getPhotoBlogPath, getPhotoAlbumsPath, type Locale } from '../../utils/i18n';
interface Props {
@ -9,10 +9,8 @@ interface Props {
const { lang = 'fr' } = Astro.props;
// Récupérer les catégories filtrées par langue
const allCategories = await getCollection('photoCategories');
const langCategories = allCategories.filter(c => (c.data.lang ?? 'fr') === lang);
const effectiveCategories = langCategories.length > 0 ? langCategories : allCategories.filter(c => (c.data.lang ?? 'fr') === 'fr');
const sortedCategories = effectiveCategories.sort((a, b) => (a.data.order || 99) - (b.data.order || 99));
const sortedCategories = (await getLocalizedCollection('photoCategories', lang))
.sort((a, b) => (a.data.order || 99) - (b.data.order || 99));
---
<section id="explore-section" class="explore-section">

View file

@ -1,5 +1,5 @@
---
import { getCollection } from 'astro:content';
import { getLocalizedCollection } from '../../utils/content-i18n';
import { t, type Locale } from '../../utils/i18n';
interface Props {
@ -15,12 +15,10 @@ const { images, albumTitle = '', showCategory = false, category = '', lang = 'fr
const imagesForJS = JSON.stringify(images);
// Construire les labels depuis la collection filtrée par langue
const allCategories = await getCollection('photoCategories');
const langCategories = allCategories.filter(c => (c.data.lang ?? 'fr') === lang);
const effectiveCategories = langCategories.length > 0 ? langCategories : allCategories.filter(c => (c.data.lang ?? 'fr') === 'fr');
const localizedCategories = await getLocalizedCollection('photoCategories', lang);
const categoryLabels: Record<string, string> = {
'blog': t('photo', 'photoFeed', lang),
...Object.fromEntries(effectiveCategories.map(cat => [cat.id.replace(/\.(en|ar)$/, ''), cat.data.title]))
...Object.fromEntries(localizedCategories.map(cat => [cat.id.replace(/\.(en|ar)$/, ''), cat.data.title]))
};
---

View file

@ -2,7 +2,7 @@
import PhotoLayout from '../../../layouts/PhotoLayout.astro';
import CategoryGrid from '../CategoryGrid.astro';
import { getCategoryEntryId, type Locale } from '../../../utils/i18n';
import { getEntry } from 'astro:content';
import { getLocalizedEntry } from '../../../utils/content-i18n';
interface Props {
category: string;
@ -13,8 +13,7 @@ const { category, lang = 'fr' } = Astro.props;
// Récupérer le titre localisé pour le title de la page
const entryId = getCategoryEntryId(category, lang);
const categoryData = await getEntry('photoCategories', entryId)
?? await getEntry('photoCategories', category);
const categoryData = await getLocalizedEntry('photoCategories', entryId, lang);
const categoryLabel = categoryData?.data.title || category;
const categorySubtitle = categoryData?.data.subtitle || "";

View file

@ -1,7 +1,7 @@
---
import PhotoLayout from '../../../layouts/PhotoLayout.astro';
import CategoryNav from '../CategoryNav.astro';
import { getCollection } from 'astro:content';
import { getLocalizedCollection } from '../../../utils/content-i18n';
import { Picture } from 'astro:assets';
import { t, getPhotoBlogPath, getDateLocale, getPostBaseSlug, type Locale } from '../../../utils/i18n';
@ -20,8 +20,7 @@ const allImages = import.meta.glob<{ default: ImageMetadata }>(
);
// Récupération des posts photo filtrés par langue
const allPhotoBlogPosts = (await getCollection('photoBlogPosts'))
.filter(post => (post.data.lang ?? 'fr') === lang);
const allPhotoBlogPosts = await getLocalizedCollection('photoBlogPosts', lang);
// Tri par date (plus récent en premier)
const sortedPosts = allPhotoBlogPosts.sort((a, b) =>

View file

@ -37,19 +37,19 @@ const projectsCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
date: z.date(),
category: z.enum(['dev', 'comedy', 'photo']),
date: z.date().optional(),
category: z.enum(['dev', 'comedy', 'photo']).optional(),
technologies: z.array(z.string()).optional(),
url: z.string().url().optional(),
github: z.string().url().optional(),
image: z.string().optional(),
imageAlt: z.string().optional(),
featured: z.boolean().default(false),
draft: z.boolean().default(false),
featured: z.boolean().optional(),
draft: z.boolean().optional(),
lang: z.enum(['fr', 'en', 'ar']).default('fr'),
}).transform((data) => ({
...data,
dateFormatted: formatDate(data.date, data.lang),
dateFormatted: data.date ? formatDate(data.date, data.lang) : undefined,
})),
});
@ -57,16 +57,16 @@ const experiencesCollection = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/experiences", ...stripExtension('md') }),
schema: z.object({
role: z.string(),
company: z.string(),
company: z.string().optional(),
companyUrl: z.string().url().optional(),
logo: z.string().optional(),
location: z.string().optional(),
startDate: z.string(),
startDate: z.string().optional(),
endDate: z.string().optional(),
technologies: z.array(z.string()).optional(),
type: z.enum(['employment', 'freelance', 'teaching', 'community', 'entrepreneurship']),
featured: z.boolean().default(false),
draft: z.boolean().default(false),
type: z.enum(['employment', 'freelance', 'teaching', 'community', 'entrepreneurship']).optional(),
featured: z.boolean().optional(),
draft: z.boolean().optional(),
lang: z.enum(['fr', 'en', 'ar']).default('fr'),
}),
});
@ -110,11 +110,11 @@ const photoBlogPostsCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
date: z.date(),
coverImage: z.string(),
date: z.date().optional(),
coverImage: z.string().optional(),
tags: z.array(z.string()).optional(),
featured: z.boolean().default(false),
draft: z.boolean().default(false),
featured: z.boolean().optional(),
draft: z.boolean().optional(),
lang: z.enum(['fr', 'en', 'ar']).default('fr'),
}),
});

View file

@ -1,14 +1,6 @@
---
role: "مهندس معماري للواجهات"
company: "ARaymond"
companyUrl: "https://www.araymond.com/"
logo: "araymond.png"
location: "غرونوبل"
startDate: "2022-01"
endDate: "2023-01"
technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"]
type: "freelance"
featured: true
location: "عن بُعد / غرونوبل"
lang: "ar"
---

View file

@ -1,14 +1,6 @@
---
role: "Frontend Architect"
company: "ARaymond"
companyUrl: "https://www.araymond.com/"
logo: "araymond.png"
location: "Grenoble"
startDate: "2022-01"
endDate: "2023-01"
technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"]
type: "freelance"
featured: true
location: "Remote / Grenoble"
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Architecte frontend"
company: "ARaymond"
companyUrl: "https://www.araymond.com/"
logo: "araymond.png"
location: "Grenoble"
startDate: "2022-01"
endDate: "2023-01"
location: "Télétravail / Grenoble"
startDate: "2022-07"
endDate: "2023-07"
technologies: ["TypeScript", "React.js", "Redux", "Nx", "Vite"]
type: "freelance"
featured: true

View file

@ -1,13 +1,6 @@
---
role: "أستاذ هندسة البرمجيات"
company: "جامعة شامبوليون"
companyUrl: "https://www.univ-jfc.fr/"
logo: "champollion.png"
location: "ألبي"
startDate: "2019-09"
technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"]
type: "teaching"
featured: true
lang: "ar"
---

View file

@ -1,13 +1,5 @@
---
role: "Software Engineering Professor"
company: "Université Champollion"
companyUrl: "https://www.univ-jfc.fr/"
logo: "champollion.png"
location: "Albi"
startDate: "2019-09"
technologies: ["TypeScript", "JavaScript", "Node.js", "TDD", "Clean Code"]
type: "teaching"
featured: true
lang: "en"
---

View file

@ -1,13 +1,6 @@
---
role: "حرفي برمجيات / مؤسس مشارك"
company: "DisMoi"
companyUrl: "https://github.com/dis-moi"
logo: "dismoi.png"
location: "عن بُعد"
startDate: "2019-01"
endDate: "2021-06"
technologies: ["TypeScript", "React.js", "Redux", "Web Extension", "Node.js"]
type: "entrepreneurship"
location: "عن بُعد / بوردو"
lang: "ar"
---

View file

@ -1,13 +1,6 @@
---
role: "Software Craftsman / Cofounder"
company: "DisMoi"
companyUrl: "https://github.com/dis-moi"
logo: "dismoi.png"
location: "Remote"
startDate: "2019-01"
endDate: "2021-06"
technologies: ["TypeScript", "React.js", "Redux", "Web Extension", "Node.js"]
type: "entrepreneurship"
location: "Remote / Bordeaux"
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Software Craftsman / Cofondateur"
company: "DisMoi"
companyUrl: "https://github.com/dis-moi"
logo: "dismoi.png"
location: "Remote"
startDate: "2019-01"
endDate: "2021-06"
location: "Télétravail / Bordeaux"
startDate: "2019-02"
endDate: "2021-04"
technologies: ["TypeScript", "React.js", "Redux", "Web Extension", "Node.js"]
type: "entrepreneurship"
lang: "fr"

View file

@ -1,12 +1,6 @@
---
role: "مدرّس تطوير"
company: "ESN 81"
companyUrl: "https://www.esn81.fr/"
location: "كاستر"
startDate: "2020-09"
endDate: "2021-06"
technologies: ["JavaScript", "Node.js"]
type: "teaching"
lang: "ar"
---

View file

@ -1,12 +1,5 @@
---
role: "Development Teacher"
company: "ESN 81"
companyUrl: "https://www.esn81.fr/"
location: "Castres"
startDate: "2020-09"
endDate: "2021-06"
technologies: ["JavaScript", "Node.js"]
type: "teaching"
lang: "en"
---

View file

@ -2,9 +2,10 @@
role: "Enseignant en développement"
company: "ESN 81"
companyUrl: "https://www.esn81.fr/"
logo: "esn81.png"
location: "Castres"
startDate: "2020-09"
endDate: "2021-06"
startDate: "2020-01"
endDate: "2021-07"
technologies: ["JavaScript", "Node.js"]
type: "teaching"
lang: "fr"

View file

@ -1,13 +1,6 @@
---
role: "مطوّر ويب"
company: "e-Themis"
companyUrl: "https://www.e-themis.com/"
logo: "ethemis.png"
location: "فرساي"
startDate: "2002-06"
endDate: "2002-12"
technologies: ["PHP", "JavaScript", "HTML", "MySQL"]
type: "employment"
location: "بور مارلي"
lang: "ar"
---

View file

@ -1,13 +1,5 @@
---
role: "Web Developer"
company: "e-Themis"
companyUrl: "https://www.e-themis.com/"
logo: "ethemis.png"
location: "Versailles"
startDate: "2002-06"
endDate: "2002-12"
technologies: ["PHP", "JavaScript", "HTML", "MySQL"]
type: "employment"
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Développeur web"
company: "e-Themis"
companyUrl: "https://www.e-themis.com/"
logo: "ethemis.png"
location: "Versailles"
startDate: "2002-06"
endDate: "2002-12"
location: "Port-Marly"
startDate: "2002-02"
endDate: "2002-11"
technologies: ["PHP", "JavaScript", "HTML", "MySQL"]
type: "employment"
lang: "fr"

View file

@ -1,13 +1,6 @@
---
role: "مهندس برمجيات أول"
company: "e-Themis"
companyUrl: "https://www.e-themis.com/"
logo: "ethemis.png"
location: "فرساي"
startDate: "2011-01"
endDate: "2015-12"
technologies: ["PHP", "Symfony", "JavaScript", "MySQL", "Linux"]
type: "employment"
location: "سان جيرمان أون لاي"
lang: "ar"
---

View file

@ -1,13 +1,5 @@
---
role: "Senior Software Engineer"
company: "e-Themis"
companyUrl: "https://www.e-themis.com/"
logo: "ethemis.png"
location: "Versailles"
startDate: "2011-01"
endDate: "2015-12"
technologies: ["PHP", "Symfony", "JavaScript", "MySQL", "Linux"]
type: "employment"
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Ingénieur logiciel senior"
company: "e-Themis"
companyUrl: "https://www.e-themis.com/"
logo: "ethemis.png"
location: "Versailles"
startDate: "2011-01"
endDate: "2015-12"
location: "Saint-Germain-en-Laye"
startDate: "2011-12"
endDate: "2015-06"
technologies: ["PHP", "Symfony", "JavaScript", "MySQL", "Linux"]
type: "employment"
lang: "fr"

View file

@ -1,11 +1,6 @@
---
role: "مطوّر ويب وبرمجيات"
company: "مستقل"
location: "إيل دو فرانس"
startDate: "2003-01"
endDate: "2006-12"
technologies: ["PHP", "JavaScript", "HTML", "MySQL", "Linux"]
type: "freelance"
location: "شاتو"
lang: "ar"
---

View file

@ -1,11 +1,5 @@
---
role: "Web & Software Developer"
company: "Freelance"
location: "Île-de-France"
startDate: "2003-01"
endDate: "2006-12"
technologies: ["PHP", "JavaScript", "HTML", "MySQL", "Linux"]
type: "freelance"
lang: "en"
---

View file

@ -1,8 +1,8 @@
---
role: "Développeur web & logiciel"
company: "Indépendant"
location: "Île-de-France"
startDate: "2003-01"
location: "Chatou"
startDate: "2003-08"
endDate: "2006-12"
technologies: ["PHP", "JavaScript", "HTML", "MySQL", "Linux"]
type: "freelance"

View file

@ -1,12 +1,6 @@
---
role: "مدير تقني"
company: "GoBuild (Go-Decision)"
companyUrl: "https://www.gobuild.fr"
location: "ليون"
startDate: "2020-06"
endDate: "2022-01"
technologies: ["TypeScript", "React.js", "Elixir", "PostgreSQL", "Docker"]
type: "employment"
location: "عن بُعد / ليون"
lang: "ar"
---

View file

@ -1,12 +1,6 @@
---
role: "CTO"
company: "GoBuild (Go-Decision)"
companyUrl: "https://www.gobuild.fr"
location: "Lyon"
startDate: "2020-06"
endDate: "2022-01"
technologies: ["TypeScript", "React.js", "Elixir", "PostgreSQL", "Docker"]
type: "employment"
location: "Remote / Lyon"
lang: "en"
---

View file

@ -2,11 +2,12 @@
role: "CTO"
company: "GoBuild (Go-Decision)"
companyUrl: "https://www.gobuild.fr"
location: "Lyon"
startDate: "2020-06"
endDate: "2022-01"
logo: "go-decision.png"
location: "Télétravail / Lyon"
startDate: "2020-02"
endDate: "2022-06"
technologies: ["TypeScript", "React.js", "Elixir", "PostgreSQL", "Docker"]
type: "employment"
type: "freelance"
lang: "fr"
---

View file

@ -1,13 +1,6 @@
---
role: "مهندس برمجيات fullstack"
company: "Libeo"
companyUrl: "https://www.libeo.io/"
logo: "libeo.png"
location: "عن بُعد"
startDate: "2021-01"
endDate: "2021-06"
technologies: ["TypeScript", "React.js", "Node.js", "GraphQL"]
type: "freelance"
location: "عن بُعد / باريس"
lang: "ar"
---

View file

@ -1,13 +1,6 @@
---
role: "Fullstack Software Engineer"
company: "Libeo"
companyUrl: "https://www.libeo.io/"
logo: "libeo.png"
location: "Remote"
startDate: "2021-01"
endDate: "2021-06"
technologies: ["TypeScript", "React.js", "Node.js", "GraphQL"]
type: "freelance"
location: "Remote / Paris"
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Ingénieur logiciel fullstack"
company: "Libeo"
companyUrl: "https://www.libeo.io/"
logo: "libeo.png"
location: "Remote"
startDate: "2021-01"
endDate: "2021-06"
location: "Télétravail / Paris"
startDate: "2021-02"
endDate: "2021-05"
technologies: ["TypeScript", "React.js", "Node.js", "GraphQL"]
type: "freelance"
lang: "fr"

View file

@ -1,13 +1,6 @@
---
role: "مهندس برمجيات أول"
company: "Obat"
companyUrl: "https://www.obat.fr/"
logo: "obat.png"
location: "عن بُعد"
startDate: "2023-02"
endDate: "2024-01"
technologies: ["TypeScript", "React.js", "Node.js", "NestJS", "PostgreSQL"]
type: "freelance"
lang: "ar"
---

View file

@ -1,13 +1,6 @@
---
role: "Senior Software Engineer"
company: "Obat"
companyUrl: "https://www.obat.fr/"
logo: "obat.png"
location: "Remote"
startDate: "2023-02"
endDate: "2024-01"
technologies: ["TypeScript", "React.js", "Node.js", "NestJS", "PostgreSQL"]
type: "freelance"
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Ingénieur logiciel senior"
company: "Obat"
companyUrl: "https://www.obat.fr/"
logo: "obat.png"
location: "Remote"
startDate: "2023-02"
endDate: "2024-01"
location: "Télétravail"
startDate: "2023-12"
endDate: "2024-04"
technologies: ["TypeScript", "React.js", "Node.js", "NestJS", "PostgreSQL"]
type: "freelance"
lang: "fr"

View file

@ -1,12 +1,6 @@
---
role: "مؤسّس ومدير عام"
company: "Team Logics"
location: "فرساي"
startDate: "2007-01"
endDate: "2011-12"
technologies: ["PHP", "CakePHP", "JavaScript", "MySQL", "Linux"]
type: "entrepreneurship"
featured: true
lang: "ar"
---

View file

@ -1,12 +1,5 @@
---
role: "Founder & CEO"
company: "Team Logics"
location: "Versailles"
startDate: "2007-01"
endDate: "2011-12"
technologies: ["PHP", "CakePHP", "JavaScript", "MySQL", "Linux"]
type: "entrepreneurship"
featured: true
lang: "en"
---

View file

@ -1,9 +1,10 @@
---
role: "Fondateur & CEO"
company: "Team Logics"
logo: "team-logics.png"
location: "Versailles"
startDate: "2007-01"
endDate: "2011-12"
endDate: "2011-11"
technologies: ["PHP", "CakePHP", "JavaScript", "MySQL", "Linux"]
type: "entrepreneurship"
featured: true

View file

@ -1,13 +1,6 @@
---
role: "مطوّر رئيسي"
company: "Urssaf Caisse nationale"
companyUrl: "https://www.urssaf.fr/"
logo: "urssaf.png"
location: "عن بُعد / باريس"
startDate: "2024-02"
technologies: ["TypeScript", "React.js", "Publicodes", "Node.js", "GitHub"]
type: "freelance"
featured: true
location: "عن بُعد / مونتروي"
lang: "ar"
---

View file

@ -1,13 +1,6 @@
---
role: "Lead Developer"
company: "Urssaf Caisse nationale"
companyUrl: "https://www.urssaf.fr/"
logo: "urssaf.png"
location: "Remote / Paris"
startDate: "2024-02"
technologies: ["TypeScript", "React.js", "Publicodes", "Node.js", "GitHub"]
type: "freelance"
featured: true
location: "Remote / Montreuil"
lang: "en"
---

View file

@ -3,7 +3,7 @@ role: "Lead Developer"
company: "Urssaf Caisse nationale"
companyUrl: "https://www.urssaf.fr/"
logo: "urssaf.png"
location: "Remote / Paris"
location: "Télétravail / Montreuil"
startDate: "2024-02"
technologies: ["TypeScript", "React.js", "Publicodes", "Node.js", "GitHub"]
type: "freelance"

View file

@ -1,14 +1,6 @@
---
role: "مطوّر رئيسي ← قائد تقني"
company: "Veepee"
companyUrl: "https://www.veepee.com/"
logo: "veepee.png"
location: "باريس"
startDate: "2016-02"
endDate: "2019-06"
technologies: ["TypeScript", "JavaScript", "React.js", "Redux", "redux-saga", "RxJS", "Node.js", "Webpack"]
type: "freelance"
featured: true
location: "سان دوني"
lang: "ar"
---

View file

@ -1,14 +1,5 @@
---
role: "Lead Developer → Tech Lead"
company: "Veepee"
companyUrl: "https://www.veepee.com/"
logo: "veepee.png"
location: "Paris"
startDate: "2016-02"
endDate: "2019-06"
technologies: ["TypeScript", "JavaScript", "React.js", "Redux", "redux-saga", "RxJS", "Node.js", "Webpack"]
type: "freelance"
featured: true
lang: "en"
---

View file

@ -3,9 +3,9 @@ role: "Lead Developer → Tech Lead"
company: "Veepee"
companyUrl: "https://www.veepee.com/"
logo: "veepee.png"
location: "Paris"
startDate: "2016-02"
endDate: "2019-06"
location: "Saint-Denis"
startDate: "2016-01"
endDate: "2019-03"
technologies: ["TypeScript", "JavaScript", "React.js", "Redux", "redux-saga", "RxJS", "Node.js", "Webpack"]
type: "freelance"
featured: true

View file

@ -1,11 +1,6 @@
---
title: "تصوير إيرول"
description: "أراد إيرول صورًا له لإعداد كتاب أعمال. عملنا طوال اليوم لتنويع الأجواء..."
date: 2011-10-02
coverImage: "18-Eroll-Shooting-1-19.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Shooting Eroll"
description: "Eroll wanted some photos of him in order to have a modeling book. We worked all day in order to have some ambiance variations..."
date: 2011-10-02
coverImage: "18-Eroll-Shooting-1-19.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "Inox Park Paris 2011"
description: "بعد نجاحه في 2010، يعود مهرجان Inox Park Paris إلى جزيرة شاتو في نسخته الثانية. ثلاث مسارح، 15 دي جي، 12 ساعة من الحفل في الهواء الطلق: Tiësto، Joachim Garraud، Sven Väth، Steve Aoki..."
date: 2011-09-10
coverImage: "01-Inox-Park-Paris-Chatou-2011.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Inox Park Paris 2011"
description: "After its 2010 success, the Inox Park Paris Electro Festival is back to the island of Chatou for its second edition. Three stages, 15 DJs, 12 hours of outdoor party: Tiësto, Joachim Garraud, Sven Väth, Steve Aoki..."
date: 2011-09-10
coverImage: "01-Inox-Park-Paris-Chatou-2011.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "عملية المحفظة 2012"
description: "توزيع محافظ مدرسية مجانية في مدارس محرومة من طرف جمعية محلية (JCI)، طنجة، المغرب."
date: 2012-09-30
coverImage: "35-Moroccan-Schoolgirls.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Schoolbag Operation 2012"
description: "During a distribution of free schoolbags in poor schools by a local association (JCI), Tangier, Morocco."
date: 2012-09-30
coverImage: "35-Moroccan-Schoolgirls.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "جولة في طنجة"
description: "جولة فوتوغرافية في شوارع طنجة."
date: 2012-05-26
coverImage: "01-Observer-le-changement.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Tangier Walk"
description: "A photographic walk through the streets of Tangier."
date: 2012-05-26
coverImage: "01-Observer-le-changement.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "هلسنكي"
description: "اختفى الثلج من هلسنكي وسرعان ما أفسح المجال للربيع..."
date: 2013-05-15
coverImage: "01-Library-of-University-of-Helsinki.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Helsinki"
description: "The snow has disappeared from Helsinki and quickly gave way to spring..."
date: 2013-05-15
coverImage: "01-Library-of-University-of-Helsinki.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "نزهة في إفران"
description: "نزهة شتوية في جبال الأطلس المتوسط."
date: 2013-01-13
coverImage: "03-3.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Ifrane Hike"
description: "Winter hike in the Middle Atlas mountains."
date: 2013-01-13
coverImage: "03-3.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "London Calling"
description: "عطلة نهاية أسبوع فوتوغرافية في لندن."
date: 2014-07-15
coverImage: "01-The-sky-inside.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "London Calling"
description: "A photographic weekend in London."
date: 2014-07-15
coverImage: "01-The-sky-inside.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "أحد سيكواني"
description: "نزهة يوم أحد على ضفاف نهر السين."
date: 2014-05-18
coverImage: "04-La-Defense-seen-from-Pont-de-Suresnes-2.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Sequanian Sunday"
description: "A sunday walk near the Seine."
date: 2014-05-18
coverImage: "04-La-Defense-seen-from-Pont-de-Suresnes-2.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "تجوال في مدينة طنجة القديمة"
description: "أثناء التجوال في أزقة طنجة القديمة، صادفت ساعاتيًا، ونجّارًا، وقمرًا عملاقًا..."
date: 2014-08-10
coverImage: "01-The-watchmaker.jpg"
tags: []
featured: true
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Wandering Tangier Medina"
description: "Walking in the streets of the old Tangier, met a watchmaker, a carpenter and a super-moon..."
date: 2014-08-10
coverImage: "01-The-watchmaker.jpg"
tags: []
featured: true
draft: false
lang: en
---

View file

@ -4,11 +4,6 @@ description: |
إنيغما كانت مسابقة بحث عن الكنز بين المدارس نظّمها المركز العالي للدراسات CESIM يوم السبت، تحت عنوان «البحث عن كنز ابن بطوطة المنسي». 4 فرق طنجاوية دُعيت لتمثيل مؤسساتها.
تغطية.
date: 2015-04-25
coverImage: "01-Enigma-v1.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -4,11 +4,6 @@ description: |
Enigma was an inter-school treasure hunt organized this saturday by the CESIM, on the theme "In search of forgotten treasure of Ibn Battuta". 4 Tangier teams were invited to represent their respective institutions.
Recap.
date: 2015-04-25
coverImage: "01-Enigma-v1.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "Field of Stones"
description: "كواليس تصوير غلاف ألبوم ماركو وولتر. أراد أن تُلتقط الصورة في سينماتيك طنجة. لا وقت، لا إضاءة، لكن لا خيار. حاولنا تقديم أفضل ما لدينا..."
date: 2015-04-02
coverImage: "01-Marco-Wolter-Field-of-Stones-2.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Field of Stones"
description: "Making of the album cover for Marco Wolter. He wanted it to be shot in the Cinémathèque of Tangier. We had no time, no light, but no choice. We tried to make the best of it..."
date: 2015-04-02
coverImage: "01-Marco-Wolter-Field-of-Stones-2.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "لا رياح في لاس كويفاس"
description: "كان من المفترض أن يكون يومًا مثاليًا لتطيير طائرتنا الورقية: مشمس وعاصف. مشمس كان، لكن الرياح لم تأتِ أبدًا."
date: 2015-01-10
coverImage: "13-No-wind-at-Las-Cuevas.jpg"
tags: []
featured: false
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "No Wind at Las Cuevas"
description: "It was supposed to be a perfect day for flying our kite: sunny and windy. Sunny it was, but the wind never came."
date: 2015-01-10
coverImage: "13-No-wind-at-Las-Cuevas.jpg"
tags: []
featured: false
draft: false
lang: en
---

View file

@ -1,11 +1,6 @@
---
title: "زفاف أورور وتوما"
description: "كان لي شرف ومتعة أن أكون شاهد توما في زفافه الجميل مع أورور. ليس سهلًا التصوير في نفس الوقت، لكن كل الصور مليئة بالحب."
date: 2015-09-26
coverImage: "10-Mariage-Aurore-Thomas-10.jpg"
tags: []
featured: true
draft: false
lang: ar
---

View file

@ -1,11 +1,6 @@
---
title: "Wedding Aurore & Thomas"
description: "I had the honor and pleasure to be Thomas' best man for his beautiful wedding with Aurore. Not easy to shoot pictures though, but all of them are filled with love."
date: 2015-09-26
coverImage: "10-Mariage-Aurore-Thomas-10.jpg"
tags: []
featured: true
draft: false
lang: en
---

View file

@ -1,6 +1,5 @@
{
"title": "ثقافات وتقاليد",
"subtitle": "ثراء التقاليد الإنسانية",
"order": 4,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Cultures & Traditions",
"subtitle": "Richness of human traditions",
"order": 4,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "محرّكات",
"subtitle": "ميكانيكا وقوة في حركة",
"order": 7,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Engines",
"subtitle": "Mechanics and power in motion",
"order": 7,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "يوميات",
"subtitle": "لحظات من الحياة اليومية",
"order": 8,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Everyday Life",
"subtitle": "Moments of everyday life",
"order": 8,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "موسيقى واحتفالات",
"subtitle": "نغمات وأغانٍ واهتزازات تحتفي بلحظات حياتنا الكبيرة والصغيرة",
"order": 5,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Music & Celebrations",
"subtitle": "Notes, songs and vibrations celebrating life's big and small moments",
"order": 5,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "طبيعة",
"subtitle": "سحر العالم الطبيعي",
"order": 3,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Nature",
"subtitle": "The magic of the natural world",
"order": 3,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "مناظر طبيعية",
"subtitle": "جمال المناظر والأماكن",
"order": 2,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Landscapes",
"subtitle": "Beauty of landscapes and places",
"order": 2,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "بورتريهات",
"subtitle": "تعابير ومشاعر ملتقطة",
"order": 1,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Portraits",
"subtitle": "Captured expressions and emotions",
"order": 1,
"lang": "en"
}

View file

@ -1,6 +1,5 @@
{
"title": "رياضة",
"subtitle": "حركة وجهد وتجاوز للذات",
"order": 6,
"lang": "ar"
}

View file

@ -1,6 +1,5 @@
{
"title": "Sports",
"subtitle": "Movement, effort and pushing limits",
"order": 6,
"lang": "en"
}

View file

@ -1,11 +1,6 @@
---
title: "Débats.co"
description: "منصّة تعاونية لتلخيص النقاشات المجتمعية. ويكيبيديا المواقف."
date: 2015-01-01
category: "dev"
technologies: ["Next.js", "React", "TypeScript", "Supabase", "PostgreSQL", "Effect TS"]
url: "https://debats.co"
featured: true
lang: "ar"
---

View file

@ -1,11 +1,6 @@
---
title: "Débats.co"
description: "Collaborative platform for synthesizing public debates. The Wikipedia of public stances."
date: 2015-01-01
category: "dev"
technologies: ["Next.js", "React", "TypeScript", "Supabase", "PostgreSQL", "Effect TS"]
url: "https://debats.co"
featured: true
lang: "en"
---

View file

@ -1,12 +1,6 @@
---
title: "DisMoi"
description: "إضافة متصفّح في مجال التكنولوجيا المدنية تضيف معلومات سياقية على أي صفحة ويب."
date: 2019-01-01
category: "dev"
technologies: ["TypeScript", "React.js", "Redux", "Web Extension", "PHP", "Symfony"]
url: "https://www.dismoi.io/"
github: "https://github.com/dis-moi"
featured: true
lang: "ar"
---

View file

@ -1,12 +1,6 @@
---
title: "DisMoi"
description: "Civic tech browser extension that adds contextual information to any web page."
date: 2019-01-01
category: "dev"
technologies: ["TypeScript", "React.js", "Redux", "Web Extension", "PHP", "Symfony"]
url: "https://www.dismoi.io/"
github: "https://github.com/dis-moi"
featured: true
lang: "en"
---

View file

@ -1,9 +1,6 @@
---
title: "ICU"
description: "منصة مشاركة صور عبر الإنترنت، مشروع شخصي تاريخي."
date: 2005-01-01
category: "dev"
technologies: ["PHP", "JavaScript", "MySQL"]
lang: "ar"
---

View file

@ -1,9 +1,6 @@
---
title: "ICU"
description: "Online photo sharing platform, a historical personal project."
date: 2005-01-01
category: "dev"
technologies: ["PHP", "JavaScript", "MySQL"]
lang: "en"
---

View file

@ -1,12 +1,6 @@
---
title: "mon-entreprise.urssaf.fr"
description: "المساعد الرسمي لروّاد الأعمال في فرنسا. أكثر من 20 محاكيًا اجتماعيًا-ضريبيًا، مليون مستخدم شهريًا."
date: 2024-01-01
category: "dev"
technologies: ["TypeScript", "React.js", "Publicodes", "Redux", "Node.js"]
url: "https://mon-entreprise.urssaf.fr/"
github: "https://github.com/betagouv/mon-entreprise"
featured: false
lang: "ar"
---

View file

@ -1,12 +1,6 @@
---
title: "mon-entreprise.urssaf.fr"
description: "The official assistant for entrepreneurs in France. Over 20 socio-fiscal simulators, one million users per month."
date: 2024-01-01
category: "dev"
technologies: ["TypeScript", "React.js", "Publicodes", "Redux", "Node.js"]
url: "https://mon-entreprise.urssaf.fr/"
github: "https://github.com/betagouv/mon-entreprise"
featured: false
lang: "en"
---

View file

@ -1,9 +1,6 @@
---
title: "N.Gine"
description: "نظام إدارة محتوى خاص بُني من الصفر، استُخدم للعديد من مشاريع العملاء."
date: 2004-01-01
category: "dev"
technologies: ["PHP", "JavaScript", "MySQL"]
lang: "ar"
---

Some files were not shown because too many files have changed in this diff Show more