Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 30 additions & 14 deletions src/app/dashboard/events/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
getEventWithDetails,
getMeetingAttendance,
nomPresent,
telephonePresent,
} from "@/lib/data";
import { formatDateTime, toDatetimeLocal } from "@/lib/dates";
import { formatEurosAbsolute } from "@/lib/money";
Expand Down Expand Up @@ -285,20 +286,35 @@ export default async function EventDetailPage({
{gens.length === 0 ? (
<p className="mt-3 text-sm text-slate-400">Personne pour l’instant.</p>
) : (
<ul className="mt-3 space-y-1.5">
{gens.map((r) => (
<li key={r.id} className="text-sm font-medium text-slate-700">
{nomPresent(r)}
{/* Un parent sans compte a répondu par le lien public :
le dire évite de le chercher dans l'annuaire des
membres, et rappelle que la réunion est ouverte. */}
{!r.userId && (
<span className="ml-1.5 rounded bg-slate-100 px-1.5 py-0.5 align-middle text-[11px] font-bold uppercase tracking-[0.08em] text-slate-500">
invité
</span>
)}
</li>
))}
<ul className="mt-3 space-y-2">
{gens.map((r) => {
const telephone = telephonePresent(r);
return (
<li key={r.id} className="text-sm font-medium text-slate-700">
{nomPresent(r)}
{/* Un parent sans compte a répondu par le lien
public : le dire évite de le chercher dans
l'annuaire des membres, et rappelle que la
réunion est ouverte. */}
{!r.userId && (
<span className="ml-1.5 rounded bg-slate-100 px-1.5 py-0.5 align-middle text-[11px] font-bold uppercase tracking-[0.08em] text-slate-500">
invité
</span>
)}
{/* Cliquable : celui qui consulte cette liste pour
relancer quelqu'un est le plus souvent sur son
téléphone, la veille de la réunion. */}
{telephone && (
<a
href={`tel:${telephone.replace(/[^+0-9]/g, "")}`}
className="mt-0.5 block rounded text-xs font-semibold text-slate-500 underline-offset-2 hover:text-brand-700 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500"
>
{telephone}
</a>
)}
</li>
);
})}
</ul>
)}
</Card>
Expand Down
23 changes: 22 additions & 1 deletion src/lib/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,15 @@ export async function getMeetingAttendance(eventId: string) {
return db.query.meetingAttendance.findMany({
where: eq(meetingAttendance.eventId, eventId),
orderBy: [asc(meetingAttendance.createdAt)],
with: { user: { columns: { id: true, name: true } } },
with: {
user: {
columns: { id: true, name: true },
// Un compte n'a pas de téléphone ; sa fiche d'adhérent, si elle est
// rattachée, en porte un. C'est le seul numéro d'un membre que
// l'association détient — le chercher ailleurs serait le deviner.
with: { associationMember: { columns: { phone: true } } },
},
},
});
}

Expand All @@ -166,6 +174,19 @@ export function nomPresent(reponse: {
return reponse.user?.name ?? reponse.name ?? "Sans nom";
}

/**
* Le téléphone auquel joindre quelqu'un qui a répondu : celui qu'il a saisi
* s'il est venu par le lien public, sinon celui de sa fiche d'adhérent.
* `null` quand l'association n'en connaît pas — la plupart des membres.
*/
export function telephonePresent(reponse: {
phone: string | null;
user: { associationMember: { phone: string | null } | null } | null;
}): string | null {
const numero = reponse.phone ?? reponse.user?.associationMember?.phone ?? null;
return numero?.trim() || null;
}

/** Une présence annoncée publiquement, via son jeton de retrait. */
export async function getMeetingAttendanceByCancelToken(token: string) {
return db.query.meetingAttendance.findFirst({
Expand Down
Loading