diff --git a/src/app/association/page.tsx b/src/app/association/page.tsx index 0fa2142..c176ff3 100644 --- a/src/app/association/page.tsx +++ b/src/app/association/page.tsx @@ -1,11 +1,12 @@ import Link from "next/link"; -import { and, asc, eq, gte, inArray, sql } from "drizzle-orm"; +import { and, asc, desc, eq, gte, inArray, sql } from "drizzle-orm"; import { auth } from "@/auth"; import { db } from "@/db"; -import { meetingRegistrations, meetings } from "@/db/schema"; +import { meetingRegistrations, meetings, pastMeetingPhotos, pastMeetings } from "@/db/schema"; +import { PastMeetingGallery } from "@/components/PastMeetingGallery"; import { SiteFooter } from "@/components/SiteFooter"; import { SiteHeader } from "@/components/SiteHeader"; -import { getSiteSettings, parseBoardMembers } from "@/lib/site-settings"; +import { getSiteSettings, parseBoardMembers, splitLines } from "@/lib/site-settings"; export const dynamic = "force-dynamic"; @@ -45,6 +46,25 @@ export default async function AssociationPage() { .groupBy(meetings.id) .orderBy(asc(meetings.startsAt)); + const past = await db.select().from(pastMeetings).orderBy(desc(pastMeetings.eventDate)).limit(3); + const pastIds = past.map((meeting) => meeting.id); + const linkedMeetingIds = past.map((meeting) => meeting.meetingId).filter((id): id is number => id !== null); + const [pastPhotos, linkedRegistrations] = await Promise.all([ + pastIds.length > 0 + ? db + .select() + .from(pastMeetingPhotos) + .where(inArray(pastMeetingPhotos.pastMeetingId, pastIds)) + .orderBy(asc(pastMeetingPhotos.position), asc(pastMeetingPhotos.id)) + : [], + linkedMeetingIds.length > 0 + ? db + .select({ meetingId: meetingRegistrations.meetingId, id: meetingRegistrations.id }) + .from(meetingRegistrations) + .where(inArray(meetingRegistrations.meetingId, linkedMeetingIds)) + : [], + ]); + const myRegistrations = memberId && upcoming.length > 0 ? await db @@ -61,6 +81,14 @@ export default async function AssociationPage() { ) : []; const myMeetingIds = new Set(myRegistrations.map((row) => row.meetingId)); + const photosByPast = new Map(); + for (const photo of pastPhotos) { + photosByPast.set(photo.pastMeetingId, [...(photosByPast.get(photo.pastMeetingId) ?? []), photo]); + } + const registrationCounts = new Map(); + for (const registration of linkedRegistrations) { + registrationCounts.set(registration.meetingId, (registrationCounts.get(registration.meetingId) ?? 0) + 1); + } const officers = [ ["Vice-présidence", settings.association_vice_president], @@ -133,8 +161,14 @@ export default async function AssociationPage() { const remaining = Math.max(0, meeting.capacity - registered); const isRegistered = myMeetingIds.has(meeting.id); return ( -
-
+
+
+ {isRegistered && ( + + ✓ Inscrit + + )} +
{formatDate(meeting.startsAt)} · {formatTime(meeting.startsAt)} @@ -166,6 +200,50 @@ export default async function AssociationPage() {
+
+
+
+
+
+ Ils y étaient +
+

+ Nos dernières rencontres +

+
+ + Voir toutes les rencontres passées + +
+ {past.length === 0 ? ( +
+ Aucune rencontre passée publiée pour le moment. +
+ ) : ( +
+ {past.map((meeting) => { + const photos = photosByPast.get(meeting.id) ?? []; + const participantCount = meeting.meetingId + ? registrationCounts.get(meeting.meetingId) ?? 0 + : splitLines(meeting.participants ?? "").length; + return ( +
+
{formatDate(meeting.eventDate)}
+

{meeting.title}

+ {meeting.location &&
{meeting.location}
} + {meeting.description &&

{meeting.description}

} +
+ {participantCount} participant{participantCount > 1 ? "s" : ""} · {photos.length} photo{photos.length > 1 ? "s" : ""} +
+ +
+ ); + })} +
+ )} +
+
+
Gouvernance diff --git a/src/app/backend/espace/page.tsx b/src/app/backend/espace/page.tsx index d15554b..7c4f0a1 100644 --- a/src/app/backend/espace/page.tsx +++ b/src/app/backend/espace/page.tsx @@ -218,7 +218,9 @@ export default async function EspacePage() { Droit à l'image
- Vous pouvez modifier votre décision à tout moment. + {latestConsent?.decision === "accepted" + ? "Autorisation signée et enregistrée. Vous pouvez la retirer à tout moment." + : "Vous pouvez autoriser l'utilisation de votre image à tout moment."}
{latestConsent && ( @@ -227,7 +229,12 @@ export default async function EspacePage() { )}
- + )} diff --git a/src/app/backend/rencontres-passees/page.tsx b/src/app/backend/rencontres-passees/page.tsx index 073405d..6489ebf 100644 --- a/src/app/backend/rencontres-passees/page.tsx +++ b/src/app/backend/rencontres-passees/page.tsx @@ -56,7 +56,7 @@ export default async function PastMeetingsAdminPage() {

Nouvelle rencontre passée

-

Les archives publiées apparaissent sur la page publique « Rencontres passées ».

+

Les trois archives les plus récentes apparaissent sur « L'association » et la page complète rassemble toutes les rencontres passées.

diff --git a/src/app/rencontres-passees/page.tsx b/src/app/rencontres-passees/page.tsx index b08dcf9..a497cab 100644 --- a/src/app/rencontres-passees/page.tsx +++ b/src/app/rencontres-passees/page.tsx @@ -26,18 +26,22 @@ export default async function PastMeetingsPage() { : []; const photosByPast = new Map(); - for (const photo of photos) photosByPast.set(photo.pastMeetingId, [...(photosByPast.get(photo.pastMeetingId) ?? []), photo]); + for (const photo of photos) { + photosByPast.set(photo.pastMeetingId, [...(photosByPast.get(photo.pastMeetingId) ?? []), photo]); + } const registrationCounts = new Map(); - for (const registration of linkedRegistrations) registrationCounts.set(registration.meetingId, (registrationCounts.get(registration.meetingId) ?? 0) + 1); + for (const registration of linkedRegistrations) { + registrationCounts.set(registration.meetingId, (registrationCounts.get(registration.meetingId) ?? 0) + 1); + } return (
- +
Ils y étaient
-

Nos rencontres passées

+

Toutes nos rencontres passées

Revivez en images les temps forts de Plein R et les moments partagés par les acteurs économiques du Bassin de Pompey.

@@ -50,6 +54,7 @@ export default async function PastMeetingsPage() { ) : (
{archives.map((archive) => { + const archivePhotos = photosByPast.get(archive.id) ?? []; const participantCount = archive.meetingId ? registrationCounts.get(archive.meetingId) ?? 0 : splitLines(archive.participants ?? "").length; @@ -60,9 +65,9 @@ export default async function PastMeetingsPage() { {archive.location &&
{archive.location}
} {archive.description &&

{archive.description}

}
- {participantCount} participant{participantCount > 1 ? "s" : ""} · {(photosByPast.get(archive.id) ?? []).length} photo{(photosByPast.get(archive.id) ?? []).length > 1 ? "s" : ""} + {participantCount} participant{participantCount > 1 ? "s" : ""} · {archivePhotos.length} photo{archivePhotos.length > 1 ? "s" : ""}
- +
); })} diff --git a/src/components/ImageConsentForm.tsx b/src/components/ImageConsentForm.tsx index e1db35a..ccd42d1 100644 --- a/src/components/ImageConsentForm.tsx +++ b/src/components/ImageConsentForm.tsx @@ -6,10 +6,12 @@ export function ImageConsentForm({ saveAction, defaultName, compact = false, + currentDecision = null, }: { saveAction: (formData: FormData) => Promise; defaultName: string; compact?: boolean; + currentDecision?: "accepted" | "refused" | null; }) { const canvasRef = useRef(null); const [drawing, setDrawing] = useState(false); @@ -65,6 +67,31 @@ export function ImageConsentForm({ } } + if (currentDecision === "accepted") { + return ( + + + + + + ); + } + return (