Recommandations cliquables avec avatars LinkedIn

- Ajout de liens vers les profils des auteurs de recommandations (prop url)
- Ajout d'avatars pour 7 recommandeurs (Maxime Boudier, Matthieu Diouron, Benoit Sarda, Pascal Gentil, Benoit Talbot, Anne Marchadier, Laurent Perez)
- Simplification du champ avatar : juste le nom de fichier au lieu du chemin complet, résolution automatique via import.meta.glob
- Ajout des URLs de profil LinkedIn dans les 14 fichiers de recommandation
This commit is contained in:
Jalil Arfaoui 2026-02-22 14:07:09 +01:00
parent a3732887f5
commit bf26caded3
30 changed files with 74 additions and 12 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

View file

@ -2,15 +2,25 @@
import { Image } from "astro:assets";
import type { ImageMetadata } from "astro";
const avatarImages = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/images/recommendations/*.{jpg,jpeg,png,webp}',
{ eager: true }
);
interface Props {
author: string;
authorRole: string;
company: string;
text: string;
avatar?: ImageMetadata;
avatar?: string;
url?: string;
}
const { author, authorRole, company, text, avatar } = Astro.props;
const { author, authorRole, company, text, avatar, url } = Astro.props;
const avatarImage = avatar
? avatarImages[`/src/assets/images/recommendations/${avatar}`]?.default
: undefined;
const truncated = text.length > 200 ? text.slice(0, 200).replace(/\s+\S*$/, '') + '...' : text;
@ -27,9 +37,9 @@ const initials = author
"{truncated}"
</p>
<div class="mt-3 flex items-center gap-2.5">
{avatar ? (
{avatarImage ? (
<Image
src={avatar}
src={avatarImage}
alt={author}
width={28}
height={28}
@ -41,7 +51,14 @@ const initials = author
</div>
)}
<cite class="not-italic text-xs">
<span class="font-semibold text-white/90">{author}</span>
{url ? (
<a href={url} target="_blank" rel="noopener noreferrer" class="font-semibold text-white/90 hover:text-purple-200 transition-colors">
{author}
<svg class="inline-block w-2.5 h-2.5 ml-0.5 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>
</a>
) : (
<span class="font-semibold text-white/90">{author}</span>
)}
<span class="text-white/40"> · {authorRole}, {company}</span>
</cite>
</div>

View file

@ -2,17 +2,27 @@
import { Image } from "astro:assets";
import type { ImageMetadata } from "astro";
const avatarImages = import.meta.glob<{ default: ImageMetadata }>(
'/src/assets/images/recommendations/*.{jpg,jpeg,png,webp}',
{ eager: true }
);
interface Props {
author: string;
authorRole: string;
company: string;
text: string;
date: Date;
avatar?: ImageMetadata;
avatar?: string;
url?: string;
lang?: string;
}
const { author, authorRole, company, text, date, avatar, lang = 'fr' } = Astro.props;
const { author, authorRole, company, text, date, avatar, url, lang = 'fr' } = Astro.props;
const avatarImage = avatar
? avatarImages[`/src/assets/images/recommendations/${avatar}`]?.default
: undefined;
const dateLocales: Record<string, string> = { fr: 'fr-FR', en: 'en-US', ar: 'ar-SA' };
const formattedDate = date.toLocaleDateString(dateLocales[lang] || 'fr-FR', {
@ -44,9 +54,9 @@ const avatarGradient = colors[colorIndex];
<p class="text-white/80 leading-relaxed flex-1 text-[0.9rem]" set:html={text} />
<div class="mt-5 pt-4 border-t border-white/[0.08] flex items-center gap-3">
{avatar ? (
{avatarImage ? (
<Image
src={avatar}
src={avatarImage}
alt={author}
width={40}
height={40}
@ -58,7 +68,14 @@ const avatarGradient = colors[colorIndex];
</div>
)}
<cite class="not-italic min-w-0">
<span class="block font-semibold text-white text-sm truncate">{author}</span>
{url ? (
<a href={url} target="_blank" rel="noopener noreferrer" class="block font-semibold text-white text-sm truncate hover:text-purple-200 transition-colors">
{author}
<svg class="inline-block w-3 h-3 ml-1 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>
</a>
) : (
<span class="block font-semibold text-white text-sm truncate">{author}</span>
)}
<span class="block text-xs text-white/50 truncate">{authorRole} · {company}</span>
<span class="block text-xs text-white/30 mt-0.5">{formattedDate}</span>
</cite>

View file

@ -72,11 +72,12 @@ const experiencesCollection = defineCollection({
const recommendationsCollection = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/recommendations" }),
schema: ({ image }) => z.object({
schema: z.object({
author: z.string(),
authorRole: z.string(),
company: z.string(),
avatar: image().optional(),
avatar: z.string().optional(),
url: z.string().url().optional(),
date: z.date(),
relationship: z.string().optional(),
lang: z.enum(['fr', 'en']).default('fr'),

View file

@ -2,6 +2,8 @@
author: "Anne Marchadier Valmont"
authorRole: "Responsable administratif"
company: "4CAD Group"
avatar: anne-marchadier.jpg
url: https://www.linkedin.com/in/anne-marchadier-valmont-50421a12
date: 2009-08-19
lang: "fr"
---

View file

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

View file

@ -2,6 +2,8 @@
author: "Benoit Sarda"
authorRole: "Sr Solution Architect, Manuf"
company: "Amazon Web Services (AWS)"
avatar: benoit-sarda.jpg
url: https://www.linkedin.com/in/benoitsarda
date: 2011-12-07
lang: "fr"
---

View file

@ -2,6 +2,8 @@
author: "Benoit Talbot"
authorRole: "Consultant technico-fonctionel Sage ERP X3"
company: "Concept ERP"
avatar: benoit-talbot.jpg
url: https://www.linkedin.com/in/benoit-talbot-65610a81
date: 2015-01-29
lang: "fr"
---

View file

@ -2,6 +2,7 @@
author: "Bouchra Ghaoui"
authorRole: "Senior Engagement Manager"
company: "Capgemini"
url: https://www.linkedin.com/in/bouchra-ghaoui-46509a10
date: 2011-12-09
lang: "fr"
---

View file

@ -2,6 +2,7 @@
author: "Daniel Gall"
authorRole: "Consultant"
company: "Taos Conseil"
url: https://www.linkedin.com/in/daniel-g-385524
date: 2011-12-07
lang: "fr"
---

View file

@ -2,6 +2,7 @@
author: "Grégoire Lacoste"
authorRole: "Chief Product Officer"
company: "CertifiCall"
url: https://www.linkedin.com/in/gregoirelacoste
date: 2020-12-08
lang: "fr"
---

View file

@ -2,6 +2,7 @@
author: "Guillaume Gendrillon"
authorRole: "Lead designer Information Voyageur et signalétique"
company: "RATPgroup"
url: https://www.linkedin.com/in/guillaumegendrillon
date: 2011-12-05
lang: "fr"
---

View file

@ -2,6 +2,8 @@
author: "Laurent Perez"
authorRole: "Senior Developer"
company: "itk"
avatar: laurent-perez.jpg
url: https://www.linkedin.com/in/laurentperez
date: 2009-09-07
lang: "en"
---

View file

@ -2,6 +2,8 @@
author: "Matthieu Diouron"
authorRole: "Director of Business Development"
company: "T-Systems France"
avatar: matthieu-diouron.jpg
url: https://www.linkedin.com/in/mdiouron
date: 2009-09-09
lang: "fr"
---

View file

@ -2,6 +2,8 @@
author: "Maxime Boudier"
authorRole: "Staff Web Engineer"
company: "SNCF Connect & Tech"
avatar: maxime-boudier.jpg
url: https://www.linkedin.com/in/maximeboudier
date: 2020-12-12
lang: "fr"
---

View file

@ -2,6 +2,7 @@
author: "Olivier Cornudet"
authorRole: "Consultant manager"
company: "e-THEMIS"
url: https://www.linkedin.com/in/olivier-cornudet-9a398738
date: 2015-02-11
lang: "fr"
---

View file

@ -2,6 +2,8 @@
author: "Pascal Gentil"
authorRole: "Chef de projet Sage X3"
company: "YOUR PARTNER"
avatar: pascal-gentil.jpg
url: https://www.linkedin.com/in/pascalgentil
date: 2015-02-08
lang: "fr"
---

View file

@ -2,6 +2,7 @@
author: "Vanessa Boissard"
authorRole: "Psychologue sociale"
company: "AlterAlliance"
url: https://www.linkedin.com/in/vanessaboissard
date: 2011-12-05
lang: "fr"
---

View file

@ -100,6 +100,7 @@ const recommendationTexts = recommendations.map((rec) => ({
company={rec.data.company}
text={rec.text}
avatar={rec.data.avatar}
url={rec.data.url}
/>
))}
</div>

View file

@ -34,6 +34,7 @@ const recommendations = (await getCollection("recommendations"))
text={rec.body || ''}
date={rec.data.date}
avatar={rec.data.avatar}
url={rec.data.url}
lang={rec.data.lang}
/>
</div>

View file

@ -100,6 +100,7 @@ const recommendationTexts = recommendations.map((rec) => ({
company={rec.data.company}
text={rec.text}
avatar={rec.data.avatar}
url={rec.data.url}
/>
))}
</div>

View file

@ -34,6 +34,7 @@ const recommendations = (await getCollection("recommendations"))
text={rec.body || ''}
date={rec.data.date}
avatar={rec.data.avatar}
url={rec.data.url}
lang={rec.data.lang}
/>
</div>

View file

@ -100,6 +100,7 @@ const recommendationTexts = recommendations.map((rec) => ({
company={rec.data.company}
text={rec.text}
avatar={rec.data.avatar}
url={rec.data.url}
/>
))}
</div>

View file

@ -34,6 +34,7 @@ const recommendations = (await getCollection("recommendations"))
text={rec.body || ''}
date={rec.data.date}
avatar={rec.data.avatar}
url={rec.data.url}
lang={rec.data.lang}
/>
</div>