Promotions : logo de l'adhérent dans l'angle des cartes

Le logo du commerce identifie l'offre avant même qu'on lise le nom. Il est
posé en pastille ronde dans l'angle haut-gauche de la carte, la catégorie se
décalant juste à côté sur la même ligne.

- `src/components/MemberAvatar.tsx` : logo en `contain` sur fond blanc (les
  logos d'adhérents sont des marques sur fond clair, un `cover` les rognerait),
  repli sur les initiales colorées quand la fiche n'a pas de logo.
- Appliqué à la carte d'accueil, à la carte de la fiche adhérent, à l'aperçu
  en direct de l'espace adhérent et à la carte de modération : même composition
  partout, l'aperçu montre donc bien ce que verra le visiteur.
- `getLivePromotions` remonte `members.logo_url` ; la page espace le passe au
  formulaire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
This commit is contained in:
Claude committed 2026-09-06 14:02:53 +00:00
1 parent e980ab6e04
commit d99ee7ec07
7 files changed
+126 -25

No files matched your search

+11 -3
View File
@@ -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<SocialNetwork, string> = {
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 `<select>`. */
categoryGroups: PromoCategoryGroup[];
/** Pré-sélection déduite du métier de l'adhérent. */
@@ -353,9 +357,13 @@ export function MemberSpaceForm({
{badge}
</span>
)}
<span style={{ position: "absolute", top: 12, left: 12, background: "#9a6638", color: "#fff", borderRadius: 999, padding: "5px 12px", fontSize: 11, fontWeight: 700 }}>
{cat}
</span>
{/* Même composition que sur le site : logo puis catégorie. */}
<div style={{ position: "absolute", top: 12, left: 12, display: "flex", alignItems: "center", gap: 8 }}>
<MemberAvatar name={memberName} logoUrl={memberLogoUrl} size={34} />
<span style={{ background: "#9a6638", color: "#fff", borderRadius: 999, padding: "5px 12px", fontSize: 11, fontWeight: 700 }}>
{cat}
</span>
</div>
</div>
<div style={{ padding: "17px 18px 18px" }}>
<h3 className="font-display" style={{ fontWeight: 700, fontSize: 18, margin: "0 0 5px", color: "#26201a" }}>
@@ -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() {
<MemberSpaceForm
memberName={memberName}
memberLogoUrl={memberLogoUrl}
categoryGroups={PROMO_CATEGORY_GROUPS}
defaultCategory={defaultPromoCategory(categorySlug)}
networks={networks}