Event Detail page

This commit is contained in:
2026-09-25 10:57:10 +05:30
parent 1e5fb9031c
commit 8a75cff501
12 changed files with 550 additions and 35 deletions
+6
View File
@@ -21,6 +21,8 @@ export type MeetingItem = {
venue: string;
landmark: string;
poster: string;
/** Long description shown on the meeting's own page. */
details: string;
};
export type PastEvent = {
@@ -29,6 +31,8 @@ export type PastEvent = {
date: string;
title: string;
text: string;
/** Long description shown on the event's own page. */
details: string;
};
export type SiteContent = {
@@ -105,6 +109,7 @@ export function sanitizeContent(input: unknown): SiteContent {
venue: str(x.venue),
landmark: str(x.landmark),
poster: img(x.poster),
details: str(x.details, 10000),
}))
.filter((x) => x.date)
.sort((a, b) => a.date.localeCompare(b.date));
@@ -116,6 +121,7 @@ export function sanitizeContent(input: unknown): SiteContent {
date: str(e.date, 60),
title: str(e.title, 150),
text: str(e.text, 600),
details: str(e.details, 10000),
}))
.filter((e) => e.title);
return {
+72
View File
@@ -22,3 +22,75 @@ export function formatMeetingDate(date: string) {
timeZone: "UTC",
});
}
/** A card in the website's "Past Events" list: a manual event or a completed meeting. */
export type PastEventCard = {
id: string;
href: string;
image: string;
date: string;
title: string;
text: string;
kind: "event" | "meeting";
};
/** Best-effort sortable timestamp from free-text dates like "12 Aug 2026" or "Aug 2026". */
function toTime(date: string): number | null {
if (!date) return null;
const t = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(date) ? `${date}T00:00:00Z` : date);
return Number.isNaN(t) ? null : t;
}
/**
* Website "Past Events" = events added by the admin + meetings whose date has passed.
* Newest first. Admin events without a readable date keep their admin order at the top.
*/
export function buildPastEvents(
events: { id: string; image: string; date: string; title: string; text: string }[],
meetings: (MeetingItem & { details?: string })[],
): PastEventCard[] {
const today = todayIST();
const cards: (PastEventCard & { t: number | null; i: number })[] = [
...events.map((e, i) => ({
id: e.id,
href: `/events/${e.id}`,
image: e.image,
date: e.date,
title: e.title,
text: e.text,
kind: "event" as const,
t: toTime(e.date),
i,
})),
...meetings
.filter((m) => m.date < today)
.map((m, i) => ({
id: `m-${m.id}`,
href: `/meetings/${m.id}`,
image: m.poster,
date: formatMeetingDate(m.date),
title: m.topic || "Club meeting",
text:
(m.details ?? "").replace(/\s+/g, " ").trim().slice(0, 160) ||
[m.venue && `Club meeting at ${m.venue}`, m.time].filter(Boolean).join(" · "),
kind: "meeting" as const,
t: toTime(m.date),
i: events.length + i,
})),
];
cards.sort((a, b) => {
if (a.t === null && b.t === null) return a.i - b.i;
if (a.t === null) return -1;
if (b.t === null) return 1;
return b.t - a.t || a.i - b.i;
});
return cards.map((c) => ({
id: c.id,
href: c.href,
image: c.image,
date: c.date,
title: c.title,
text: c.text,
kind: c.kind,
}));
}