mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-23 23:28:04 +02:00
Logo en remplacement de la vitrine : centre et reduit (contain)
Quand un adherent n'a pas de photo de couverture mais un logo, le logo n'est plus etire en plein cadre : il est centre et contenu (~80%) sur fond blanc. Composant partage VitrineImage applique a l'annuaire, au carrousel d'accueil et a la fiche adherent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
c15213c131
commit
aa63462eff
4 files changed
+85
-55
No files matched your search
@@ -4,6 +4,7 @@ import type { Metadata } from "next";
|
|||||||
import { Sparkle } from "@/components/Sparkle";
|
import { Sparkle } from "@/components/Sparkle";
|
||||||
import { SiteHeader } from "@/components/SiteHeader";
|
import { SiteHeader } from "@/components/SiteHeader";
|
||||||
import { SiteFooter } from "@/components/SiteFooter";
|
import { SiteFooter } from "@/components/SiteFooter";
|
||||||
|
import { VitrineImage } from "@/components/VitrineImage";
|
||||||
import { getMemberLivePromotions, getPublicMember } from "@/lib/queries";
|
import { getMemberLivePromotions, getPublicMember } from "@/lib/queries";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
@@ -96,25 +97,15 @@ export default async function FicheAdherentPage({
|
|||||||
|
|
||||||
{/* cover + identity */}
|
{/* cover + identity */}
|
||||||
<section style={{ position: "relative", borderRadius: 22, overflow: "hidden", border: "1px solid #e6dcc6" }}>
|
<section style={{ position: "relative", borderRadius: 22, overflow: "hidden", border: "1px solid #e6dcc6" }}>
|
||||||
<div
|
<VitrineImage
|
||||||
style={{
|
coverUrl={member.coverUrl}
|
||||||
position: "relative",
|
logoUrl={member.logoUrl}
|
||||||
height: 230,
|
height={230}
|
||||||
background: (member.coverUrl || member.logoUrl)
|
stripe={COVER_STRIPE}
|
||||||
? `url(${member.coverUrl || member.logoUrl})`
|
placeholder="photo de couverture"
|
||||||
: COVER_STRIPE,
|
|
||||||
backgroundSize: "cover",
|
|
||||||
backgroundPosition: "center",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{!(member.coverUrl || member.logoUrl) && (
|
|
||||||
<span style={{ fontSize: 11, letterSpacing: "0.14em", color: "#b3a888", textTransform: "uppercase" }}>photo de couverture</span>
|
|
||||||
)}
|
|
||||||
<Sparkle color={accent} size={20} style={{ top: 20, right: 24 }} duration={3.2} />
|
<Sparkle color={accent} size={20} style={{ top: 20, right: 24 }} duration={3.2} />
|
||||||
</div>
|
</VitrineImage>
|
||||||
<div style={{ background: "#fff", padding: "0 30px 26px", display: "flex", alignItems: "flex-end", gap: 22, flexWrap: "wrap" }}>
|
<div style={{ background: "#fff", padding: "0 30px 26px", display: "flex", alignItems: "flex-end", gap: 22, flexWrap: "wrap" }}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { VitrineImage } from "@/components/VitrineImage";
|
||||||
|
|
||||||
export type DirectoryMember = {
|
export type DirectoryMember = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -190,7 +191,6 @@ export function AnnuaireClient({
|
|||||||
{list.length > 0 ? (
|
{list.length > 0 ? (
|
||||||
<div className="grid grid-3" style={{ gap: 20 }}>
|
<div className="grid grid-3" style={{ gap: 20 }}>
|
||||||
{list.map((m, i) => {
|
{list.map((m, i) => {
|
||||||
const image = m.coverUrl || m.logoUrl;
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={m.id}
|
key={m.id}
|
||||||
@@ -198,23 +198,12 @@ export function AnnuaireClient({
|
|||||||
className="lift-card"
|
className="lift-card"
|
||||||
style={{ textDecoration: "none", color: "inherit", background: "#fff", border: "1px solid #e6dcc6", borderRadius: 18, overflow: "hidden", display: "flex", flexDirection: "column" }}
|
style={{ textDecoration: "none", color: "inherit", background: "#fff", border: "1px solid #e6dcc6", borderRadius: 18, overflow: "hidden", display: "flex", flexDirection: "column" }}
|
||||||
>
|
>
|
||||||
<div
|
<VitrineImage
|
||||||
style={{
|
coverUrl={m.coverUrl}
|
||||||
position: "relative",
|
logoUrl={m.logoUrl}
|
||||||
height: 150,
|
height={150}
|
||||||
background: image
|
stripe={i % 2 === 0 ? STRIPE_WARM : STRIPE_COOL}
|
||||||
? `center/cover no-repeat url(${image})`
|
|
||||||
: i % 2 === 0 ? STRIPE_WARM : STRIPE_COOL,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{!image && (
|
|
||||||
<span style={{ fontSize: 10, letterSpacing: "0.12em", color: "#a99c82", textTransform: "uppercase" }}>
|
|
||||||
photo vitrine
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{m.categoryLabel && (
|
{m.categoryLabel && (
|
||||||
<span style={{ position: "absolute", top: 12, left: 12, background: m.accent ?? "#9a6638", color: "#fff", borderRadius: 999, padding: "5px 12px", fontSize: 11, fontWeight: 700 }}>
|
<span style={{ position: "absolute", top: 12, left: 12, background: m.accent ?? "#9a6638", color: "#fff", borderRadius: 999, padding: "5px 12px", fontSize: 11, fontWeight: 700 }}>
|
||||||
{m.categoryLabel}
|
{m.categoryLabel}
|
||||||
@@ -225,7 +214,7 @@ export function AnnuaireClient({
|
|||||||
{m.promoBadge}
|
{m.promoBadge}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</VitrineImage>
|
||||||
<div style={{ padding: "16px 18px 18px", display: "flex", flexDirection: "column", flex: 1 }}>
|
<div style={{ padding: "16px 18px 18px", display: "flex", flexDirection: "column", flex: 1 }}>
|
||||||
<h3 className="font-display" style={{ fontWeight: 700, fontSize: 18, margin: 0, color: "#26201a" }}>
|
<h3 className="font-display" style={{ fontWeight: 700, fontSize: 18, margin: 0, color: "#26201a" }}>
|
||||||
{m.name}
|
{m.name}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { VitrineImage } from "./VitrineImage";
|
||||||
|
|
||||||
export type CarouselMember = {
|
export type CarouselMember = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -15,16 +16,12 @@ export type CarouselMember = {
|
|||||||
accent: string | null;
|
accent: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const STRIPE_WARM =
|
|
||||||
"repeating-linear-gradient(45deg,#efe9da,#efe9da 12px,#e6ddc9 12px,#e6ddc9 24px)";
|
|
||||||
|
|
||||||
const VISIBLE = 3;
|
const VISIBLE = 3;
|
||||||
const INTERVAL_MS = 20_000;
|
const INTERVAL_MS = 20_000;
|
||||||
const STAGGER_MS = 220; // décalage de disparition/apparition entre cartes
|
const STAGGER_MS = 220; // décalage de disparition/apparition entre cartes
|
||||||
const FADE_MS = 450;
|
const FADE_MS = 450;
|
||||||
|
|
||||||
function Card({ m, visible, index }: { m: CarouselMember; visible: boolean; index: number }) {
|
function Card({ m, visible, index }: { m: CarouselMember; visible: boolean; index: number }) {
|
||||||
const image = m.coverUrl || m.logoUrl;
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/adherents/${m.id}`}
|
href={`/adherents/${m.id}`}
|
||||||
@@ -43,27 +40,13 @@ function Card({ m, visible, index }: { m: CarouselMember; visible: boolean; inde
|
|||||||
transitionDelay: `${index * STAGGER_MS}ms`,
|
transitionDelay: `${index * STAGGER_MS}ms`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<VitrineImage coverUrl={m.coverUrl} logoUrl={m.logoUrl} height={150}>
|
||||||
style={{
|
|
||||||
position: "relative",
|
|
||||||
height: 150,
|
|
||||||
background: image ? `center/cover no-repeat url(${image})` : STRIPE_WARM,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!image && (
|
|
||||||
<span style={{ fontSize: 10.5, letterSpacing: "0.12em", color: "#a99c82", textTransform: "uppercase" }}>
|
|
||||||
photo vitrine
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{m.categoryLabel && (
|
{m.categoryLabel && (
|
||||||
<span style={{ position: "absolute", top: 12, left: 12, background: "#9a6638", color: "#fff", borderRadius: 999, padding: "5px 12px", fontSize: 11, fontWeight: 700 }}>
|
<span style={{ position: "absolute", top: 12, left: 12, background: "#9a6638", color: "#fff", borderRadius: 999, padding: "5px 12px", fontSize: 11, fontWeight: 700 }}>
|
||||||
{m.categoryLabel}
|
{m.categoryLabel}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</VitrineImage>
|
||||||
<div style={{ padding: "16px 18px 18px" }}>
|
<div style={{ padding: "16px 18px 18px" }}>
|
||||||
<h3 className="font-display" style={{ fontWeight: 700, fontSize: 18, margin: "0 0 5px", color: "#26201a" }}>
|
<h3 className="font-display" style={{ fontWeight: 700, fontSize: 18, margin: "0 0 5px", color: "#26201a" }}>
|
||||||
{m.name}
|
{m.name}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import type { CSSProperties, ReactNode } from "react";
|
||||||
|
|
||||||
|
const STRIPE_WARM =
|
||||||
|
"repeating-linear-gradient(45deg,#efe9da,#efe9da 12px,#e6ddc9 12px,#e6ddc9 24px)";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zone d'image "vitrine" d'un adhérent.
|
||||||
|
* - Photo de couverture présente -> affichée en plein cadre (cover).
|
||||||
|
* - Sinon, le logo est affiché centré et réduit (contain ~80%), pas étiré.
|
||||||
|
* - Sinon, fond rayé + texte placeholder.
|
||||||
|
*/
|
||||||
|
export function VitrineImage({
|
||||||
|
coverUrl,
|
||||||
|
logoUrl,
|
||||||
|
height,
|
||||||
|
stripe = STRIPE_WARM,
|
||||||
|
placeholder = "photo vitrine",
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
coverUrl?: string | null;
|
||||||
|
logoUrl?: string | null;
|
||||||
|
height: number;
|
||||||
|
stripe?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
}) {
|
||||||
|
const cover = coverUrl || null;
|
||||||
|
const logoOnly = !cover && logoUrl ? logoUrl : null;
|
||||||
|
|
||||||
|
const base: CSSProperties = {
|
||||||
|
position: "relative",
|
||||||
|
height,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
overflow: "hidden",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cover) {
|
||||||
|
return (
|
||||||
|
<div style={{ ...base, background: `center/cover no-repeat url(${cover})` }}>{children}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logoOnly) {
|
||||||
|
return (
|
||||||
|
<div style={{ ...base, background: "#fff" }}>
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={logoOnly}
|
||||||
|
alt=""
|
||||||
|
style={{ maxWidth: "80%", maxHeight: "80%", objectFit: "contain", display: "block" }}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ ...base, background: stripe }}>
|
||||||
|
<span style={{ fontSize: 10.5, letterSpacing: "0.12em", color: "#a99c82", textTransform: "uppercase" }}>
|
||||||
|
{placeholder}
|
||||||
|
</span>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user