Affiche les coordonnées du référent aux visiteurs connectés

L'annuaire, les pages métier et la fiche adhérent montrent désormais la
personne à joindre chez l'adhérent — nom, prénom, ligne directe — mais
uniquement à un visiteur connecté.

Trois colonnes sur `members` (`contact_first_name`, `contact_last_name`,
`contact_phone`), saisies dans les deux formulaires de fiche via le
composant partagé `MemberContactFields`.

Le filtrage ne repose pas sur du CSS ni sur un rendu conditionnel tardif :
aucune requête publique ne lit ces colonnes. Seules `getMemberContacts()`
et `getMemberContact()` les rapatrient, et les pages ne les appellent
qu'après un `getSession()` positif. Un visiteur anonyme ne reçoit donc
rien — ni dans le HTML, ni dans le JSON-LD, ni dans les props du composant
client de l'annuaire. `tests/security.test.ts` verrouille cette séparation.

`src/lib/member-contact.ts` reste pur (composition du nom, lien `tel:`) et
est couvert par `tests/member-contact.test.ts`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012TAK3c4jAUqxVMzy746wWQ
This commit is contained in:
Claude committed 2026-09-14 11:31:59 +00:00
1 parent fb09bf0e3a
commit 67cd9d6684
20 files changed
+1854 -7

No files matched your search

+7
View File
@@ -9,6 +9,7 @@ import { ImageField } from "@/components/ImageField";
import { ImageConsentForm } from "@/components/ImageConsentForm";
import { HoursEditor } from "@/components/HoursEditor";
import { TagsField } from "@/components/TagsField";
import { MemberContactFields } from "@/components/MemberContactFields";
import { communeOptions } from "@/lib/communes";
import { saveImageConsent, updateOwnProfile } from "../actions";
import { EspaceHeader } from "./EspaceHeader";
@@ -167,6 +168,12 @@ export default async function EspacePage() {
</div>
</div>
<MemberContactFields
firstName={profile.contactFirstName}
lastName={profile.contactLastName}
phone={profile.contactPhone}
/>
<div className="grid grid-2" style={{ gap: 16, marginTop: 16 }}>
<ImageField name="coverUrl" label="Image d'entête (bannière)" defaultValue={profile.coverUrl ?? ""} height={130} />
<ImageField name="logoUrl" label="Logo" defaultValue={profile.logoUrl ?? ""} height={130} fit="contain" />