diff --git a/src/app/adherents/[id]/page.tsx b/src/app/adherents/[id]/page.tsx
index 8cebdc5..6d5875c 100644
--- a/src/app/adherents/[id]/page.tsx
+++ b/src/app/adherents/[id]/page.tsx
@@ -6,6 +6,7 @@ import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter";
import { VitrineImage } from "@/components/VitrineImage";
import { PromoImage } from "@/components/PromoImage";
+import { MemberAvatar } from "@/components/MemberAvatar";
import { JsonLd } from "@/components/JsonLd";
import { getMemberLivePromotions, getPublicMember } from "@/lib/queries";
import {
@@ -431,6 +432,11 @@ export default async function FicheAdherentPage({
{p.imageUrl && }
{!p.imageUrl && (photo de l'offre)}
+ {/* Même repère que sur l'accueil : le logo du commerce
+ dans l'angle haut-gauche de la carte. */}
+
+
+
{p.badge && (
{p.badge}
)}
diff --git a/src/app/backend/espace/MemberSpaceForm.tsx b/src/app/backend/espace/MemberSpaceForm.tsx
index efa81bf..01862f3 100644
--- a/src/app/backend/espace/MemberSpaceForm.tsx
+++ b/src/app/backend/espace/MemberSpaceForm.tsx
@@ -4,6 +4,7 @@ import { useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { publishPromo } from "../actions";
import { PromoImage } from "@/components/PromoImage";
+import { MemberAvatar } from "@/components/MemberAvatar";
import { SOCIAL_BRAND, SocialIcon } from "@/components/SocialIcons";
import type { SocialNetwork } from "@/lib/social";
import type { PromoCategoryGroup } from "@/lib/promo-categories";
@@ -20,11 +21,14 @@ const NETWORK_LABELS: Record = {
export function MemberSpaceForm({
memberName,
+ memberLogoUrl,
categoryGroups,
defaultCategory,
networks = [],
}: {
memberName: string;
+ /** Logo de l'adhérent, affiché dans l'angle de l'aperçu comme sur le site. */
+ memberLogoUrl?: string | null;
/** Types de produits / services, groupés pour le `
diff --git a/src/app/backend/espace/promotions/page.tsx b/src/app/backend/espace/promotions/page.tsx
index 88c03b7..b407020 100644
--- a/src/app/backend/espace/promotions/page.tsx
+++ b/src/app/backend/espace/promotions/page.tsx
@@ -52,6 +52,7 @@ export default async function EspacePromotionsPage() {
const session = await getSession();
const memberId = session?.user.memberId ?? null;
let memberName = session?.user.name ?? "Adhérent";
+ let memberLogoUrl: string | null = null;
let subtitle = "Espace adhérent · Promotions";
let publicPath: string | null = null;
let categorySlug: string | null = null;
@@ -65,6 +66,7 @@ export default async function EspacePromotionsPage() {
name: members.name,
city: members.city,
status: members.status,
+ logoUrl: members.logoUrl,
categorySlug: categories.slug,
categoryLabel: categories.label,
})
@@ -73,6 +75,7 @@ export default async function EspacePromotionsPage() {
.where(eq(members.id, memberId));
if (m) {
memberName = m.name;
+ memberLogoUrl = m.logoUrl;
categorySlug = m.categorySlug;
subtitle = ["Espace adhérent", m.categoryLabel, m.city].filter(Boolean).join(" · ");
publicPath = m.status === "active" ? memberPath(m) : null;
@@ -160,6 +163,7 @@ export default async function EspacePromotionsPage() {
{p.imageUrl && }
- {p.category && (
-
- {p.category}
-
- )}
+ {/* Comme sur le site : logo de l'adhérent puis catégorie. */}
+
+
+ {p.category && (
+
+ {p.category}
+
+ )}
+
)}
- {p.category && (
-
- {p.category}
-
- )}
+ {/* Angle haut-gauche : le logo de l'adhérent d'abord, la
+ catégorie ensuite — on identifie le commerce avant l'offre. */}
+
+
+ {p.category && (
+
+ {p.category}
+
+ )}
+
diff --git a/src/components/MemberAvatar.tsx b/src/components/MemberAvatar.tsx
new file mode 100644
index 0000000..7d26581
--- /dev/null
+++ b/src/components/MemberAvatar.tsx
@@ -0,0 +1,73 @@
+/**
+ * Pastille ronde d'un adhérent : son logo, sinon ses initiales.
+ *
+ * Sert à identifier le commerce d'un coup d'œil devant son nom, notamment sur
+ * les cartes de promotion. Le logo est rendu en `contain` sur fond blanc : la
+ * plupart des logos d'adhérents sont des marques sur fond clair, un `cover` les
+ * rognerait.
+ */
+
+export function memberInitials(name: string): string {
+ return name
+ .split(/\s+/)
+ .filter(Boolean)
+ .map((w) => w[0])
+ .join("")
+ .slice(0, 2)
+ .toUpperCase();
+}
+
+export function MemberAvatar({
+ name,
+ logoUrl,
+ size = 26,
+ accent = "#E0A63C",
+}: {
+ name: string;
+ logoUrl?: string | null;
+ size?: number;
+ accent?: string;
+}) {
+ const common: React.CSSProperties = {
+ width: size,
+ height: size,
+ borderRadius: "50%",
+ flexShrink: 0,
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ overflow: "hidden",
+ };
+
+ if (logoUrl) {
+ return (
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+ );
+ }
+
+ return (
+
+ {memberInitials(name)}
+
+ );
+}
diff --git a/src/lib/queries.ts b/src/lib/queries.ts
index a2f4101..580cc06 100644
--- a/src/lib/queries.ts
+++ b/src/lib/queries.ts
@@ -87,6 +87,7 @@ export async function getLivePromotions(limit = 6) {
memberId: promotions.memberId,
memberName: members.name,
memberCity: members.city,
+ memberLogoUrl: members.logoUrl,
})
.from(promotions)
.leftJoin(members, eq(promotions.memberId, members.id))