Skip to content

Commit cb7c19e

Browse files
committed
Merge branch 'main' into issue-95-committee_documentation
2 parents 1599c51 + ac23a32 commit cb7c19e

5 files changed

Lines changed: 72 additions & 82 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
export type EventDateParts = {
2+
weekday: string;
3+
day: string;
4+
month: string;
5+
time: string;
6+
};
7+
8+
/** Parses a date string and returns parts for separate styling. Returns null on invalid input. */
9+
export function getEventDateParts(dateString: string): EventDateParts | null {
10+
try {
11+
const date = new Date(dateString);
12+
const weekday = new Intl.DateTimeFormat("en-US", {
13+
weekday: "long",
14+
}).format(date);
15+
const day = new Intl.DateTimeFormat("en-US", { day: "numeric" }).format(
16+
date,
17+
);
18+
const month = new Intl.DateTimeFormat("en-US", { month: "short" }).format(
19+
date,
20+
);
21+
const time = new Intl.DateTimeFormat("en-US", {
22+
hour: "2-digit",
23+
minute: "2-digit",
24+
hour12: true,
25+
})
26+
.format(date)
27+
.replace("AM", "am")
28+
.replace("PM", "pm");
29+
return { weekday, day, month, time };
30+
} catch {
31+
return null;
32+
}
33+
}
34+
35+
type EventDateDisplayProps = { date: string };
36+
37+
/** Renders event date as: weekday・ day month・ time. */
38+
export function EventDateDisplay({ date }: EventDateDisplayProps) {
39+
const parts = getEventDateParts(date);
40+
if (!parts) return null;
41+
return (
42+
<div className="flex flex-wrap items-baseline gap-x-1">
43+
<span className="whitespace-nowrap text-primary">
44+
{parts.weekday}
45+
{"・"}
46+
</span>
47+
<span className="whitespace-nowrap text-primary">
48+
{parts.day} {parts.month}
49+
{"・"}
50+
</span>
51+
<span className="text-secondary">{parts.time}</span>
52+
</div>
53+
);
54+
}

client/src/components/ui/eventCarousel.tsx

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,14 @@ import { useEffect, useRef, useState } from "react";
66
import { UiEvent as EventType } from "@/hooks/useEvents";
77

88
import { Button } from "./button";
9+
import { EventDateDisplay } from "./EventDateDisplay";
910

1011
type EventCarouselProps = {
1112
items: EventType[];
1213
};
1314

1415
const GAP = 40;
1516

16-
function formatEventDateDisplay(dateString: string): string {
17-
try {
18-
const date = new Date(dateString);
19-
const weekday = new Intl.DateTimeFormat("en-US", {
20-
weekday: "long",
21-
}).format(date);
22-
const day = new Intl.DateTimeFormat("en-US", { day: "numeric" }).format(
23-
date,
24-
);
25-
const month = new Intl.DateTimeFormat("en-US", { month: "short" }).format(
26-
date,
27-
);
28-
const time = new Intl.DateTimeFormat("en-US", {
29-
hour: "2-digit",
30-
minute: "2-digit",
31-
hour12: true,
32-
})
33-
.format(date)
34-
.replace("AM", "am")
35-
.replace("PM", "pm");
36-
return `${weekday} ${day} ${month} ${time}`;
37-
} catch {
38-
return "";
39-
}
40-
}
41-
4217
export default function EventCarousel({ items }: EventCarouselProps) {
4318
const viewportRef = useRef<HTMLDivElement>(null);
4419
const firstItemRef = useRef<HTMLAnchorElement>(null);
@@ -76,16 +51,15 @@ export default function EventCarousel({ items }: EventCarouselProps) {
7651

7752
useEffect(() => {
7853
const updateVisibleCount = () => {
79-
if (window.innerWidth < 768) {
80-
setVisibleCount(1);
81-
} else {
82-
setVisibleCount(3);
83-
}
54+
const newVisibleCount = window.innerWidth < 768 ? 1 : 3;
55+
const newMaxIndex = Math.max(0, items.length - newVisibleCount);
56+
setVisibleCount(newVisibleCount);
57+
setCurrentIndex((prev) => Math.min(prev, newMaxIndex));
8458
};
8559
updateVisibleCount();
8660
window.addEventListener("resize", updateVisibleCount);
8761
return () => window.removeEventListener("resize", updateVisibleCount);
88-
}, []);
62+
}, [items.length]);
8963

9064
return (
9165
<div className="container mx-auto rounded-lg bg-primary-foreground px-4 py-8 lg:px-12">
@@ -124,7 +98,7 @@ export default function EventCarousel({ items }: EventCarouselProps) {
12498
)}
12599

126100
<div className="mt-10 px-10">
127-
<div ref={viewportRef} className="overflow-hidden">
101+
<div ref={viewportRef} className="overflow-hidden px-2 md:px-4">
128102
<div
129103
className="flex transition-transform duration-300 ease-out"
130104
style={{
@@ -137,7 +111,7 @@ export default function EventCarousel({ items }: EventCarouselProps) {
137111
href={`/events/${event.id}`}
138112
key={event.id}
139113
ref={index === 0 ? firstItemRef : undefined}
140-
className={`block w-full flex-shrink-0 rounded-xl transition-transform duration-200 ease-in-out hover:scale-110 md:w-[calc((100%-80px)/3)] ${index === 0 ? "origin-left" : ""}`}
114+
className={`block w-full flex-shrink-0 rounded-xl transition-transform duration-200 ease-in-out hover:scale-110 md:w-[calc((100%-80px)/3)] ${index === currentIndex ? "origin-left" : ""}`}
141115
>
142116
<div className="relative aspect-[16/9] w-full overflow-hidden rounded-lg">
143117
<Image
@@ -152,8 +126,8 @@ export default function EventCarousel({ items }: EventCarouselProps) {
152126
{event.name}
153127
</h3>
154128

155-
<p className="mb-4 text-sm text-primary">
156-
{formatEventDateDisplay(event.date)}
129+
<p className="mb-4 text-base text-primary">
130+
<EventDateDisplay date={event.date} />
157131
</p>
158132

159133
<div className="mt-3 w-full border-b border-white/20" />

client/src/hooks/useEvents.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ type ApiEvent = {
99
description: string;
1010
publicationDate: string;
1111
date: string;
12-
startTime: string | null;
1312
location: string;
1413
cover_image: string | null;
1514
};

client/src/pages/events/[id].tsx

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
import Image from "next/image";
22
import { useRouter } from "next/router";
33

4+
import { EventDateDisplay } from "@/components/ui/EventDateDisplay";
45
import { useEvent } from "@/hooks/useEvent";
56

6-
function formatDateTime(dateString: string): string {
7-
try {
8-
const date = new Date(dateString);
9-
return new Intl.DateTimeFormat("en-AU", {
10-
year: "numeric",
11-
month: "long",
12-
day: "numeric",
13-
hour: "2-digit",
14-
minute: "2-digit",
15-
}).format(date);
16-
} catch {
17-
return dateString; // Fallback to original string if parsing fails
18-
}
19-
}
20-
217
export default function EventPage() {
228
const router = useRouter();
239
const { id } = router.query;
24-
// Wait for router to be ready before fetching (router.query is empty on initial SSR)
10+
2511
const {
2612
data: event,
2713
isPending,
@@ -70,8 +56,9 @@ export default function EventPage() {
7056
aria-hidden="true"
7157
/>
7258
<p className="mt-6 text-lg">
73-
{formatDateTime(event.date)} · {event.location}
59+
<EventDateDisplay date={event.date} />
7460
</p>
61+
<div className="text-primary">{event.location} </div>
7562
<p className="mt-4 max-w-lg text-base leading-relaxed">
7663
{event.description}
7764
</p>

client/src/pages/events/index.tsx

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,9 @@ import Link from "next/link";
33
import { useRouter } from "next/router";
44
import { useEffect, useMemo, useState } from "react";
55

6+
import { EventDateDisplay } from "@/components/ui/EventDateDisplay";
67
import { EventTypeFilter, UiEvent, useEvents } from "@/hooks/useEvents";
78

8-
function formatDateTimeLine(dateString: string): string {
9-
try {
10-
const date = new Date(dateString);
11-
12-
const d = new Intl.DateTimeFormat("en-US", {
13-
month: "long",
14-
day: "numeric",
15-
year: "numeric",
16-
}).format(date);
17-
18-
const t = new Intl.DateTimeFormat("en-US", {
19-
hour: "2-digit",
20-
minute: "2-digit",
21-
hour12: true,
22-
})
23-
.format(date)
24-
.replace("AM", "am")
25-
.replace("PM", "pm");
26-
27-
return `${d}${t}`;
28-
} catch {
29-
return "";
30-
}
31-
}
32-
339
type EventsByYear<T> = Record<number, T[]>;
3410

3511
function groupEventsByYear<T extends { date: string }>(
@@ -216,9 +192,9 @@ export default function EventsPage() {
216192
</h3>
217193

218194
<div className="space-y-1 text-sm md:text-base">
219-
<div className="text-primary">
220-
{formatDateTimeLine(event.date)}
221-
</div>
195+
<p className="text-base text-primary">
196+
<EventDateDisplay date={event.date} />
197+
</p>
222198
<div className="text-primary">{event.location}</div>
223199
</div>
224200

0 commit comments

Comments
 (0)