Skip to content

Commit d473af9

Browse files
google-labs-jules[bot]theMattCode
authored andcommitted
Replace event calender
1 parent a97f89f commit d473af9

File tree

6 files changed

+161
-316
lines changed

6 files changed

+161
-316
lines changed

app/(web)/verein/veranstaltungen/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import { BlockTitle } from "#/components/block-title/BlockTitle";
33
import { PageBase } from "#/components/page/PageBase";
4-
import { EventCalendar } from "#/components/calendar/Calendar";
4+
import { EventList } from "#/components/events/EventList";
55
import { Metadata } from "next";
66
import { getTitle } from "#/lib/page";
77
import { veranstaltungen } from "#/content/sitemap";
@@ -14,8 +14,8 @@ export default function Veranstaltungen() {
1414
return (
1515
<PageBase>
1616
<BlockTitle title={veranstaltungen.name} />
17-
<div className="bg-white shadow-2xl p-2 flex flex-col lg:flex-row gap-2">
18-
<EventCalendar />
17+
<div className="bg-white shadow-2xl p-2">
18+
<EventList />
1919
</div>
2020
</PageBase>
2121
);

components/calendar/Calendar.tsx

Lines changed: 0 additions & 149 deletions
This file was deleted.

components/events/EventCard.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Event } from '#/content/events';
2+
import Link from 'next/link';
3+
import { GrLocation } from 'react-icons/gr'; // Assuming react-icons is available
4+
5+
// Helper function for formatting date and time (can be basic for now)
6+
// It should handle allDay events appropriately.
7+
const formatEventDateTime = (start: Date, end: Date, allDay?: boolean): string => {
8+
const options: Intl.DateTimeFormatOptions = {
9+
weekday: 'short',
10+
year: 'numeric',
11+
month: 'short',
12+
day: 'numeric',
13+
hour: '2-digit',
14+
minute: '2-digit',
15+
hour12: false, // Use 24-hour format or true for 12-hour
16+
};
17+
18+
if (allDay) {
19+
return `${new Date(start).toLocaleDateString('de-DE', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' })} (Ganztägig)`;
20+
}
21+
22+
const startDateStr = new Date(start).toLocaleDateString('de-DE', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' });
23+
const startTimeStr = new Date(start).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', hour12: false });
24+
const endTimeStr = new Date(end).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', hour12: false });
25+
26+
// If start and end are on the same day
27+
if (new Date(start).toDateString() === new Date(end).toDateString()) {
28+
return `${startDateStr}, ${startTimeStr} - ${endTimeStr}`;
29+
} else {
30+
// If events span multiple days (less common for cards, but good to handle)
31+
const endDateStr = new Date(end).toLocaleDateString('de-DE', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' });
32+
return `${startDateStr}, ${startTimeStr} - ${endDateStr}, ${endTimeStr}`;
33+
}
34+
};
35+
36+
type EventCardProps = {
37+
event: Event;
38+
};
39+
40+
export function EventCard({ event }: EventCardProps) {
41+
return (
42+
<div className="bg-white shadow-lg rounded-lg p-4 mb-4 flex flex-col">
43+
<h3 className="text-xl font-semibold text-svw-blue-default mb-2">{event.title}</h3>
44+
<p className="text-sm text-gray-600 mb-1">
45+
{formatEventDateTime(event.start, event.end, event.allDay)}
46+
</p>
47+
{event.place && (
48+
<div className="flex items-center text-sm text-gray-600 mb-2">
49+
<GrLocation className="mr-1 flex-shrink-0" />
50+
<span className="font-medium">{event.place.name}</span>
51+
{event.place.address && <span className="ml-1">, {event.place.address}</span>}
52+
</div>
53+
)}
54+
{event.url && (
55+
<div className="mt-auto pt-2"> {/* Pushes link to the bottom */}
56+
<Link
57+
href={event.url}
58+
className="inline-block bg-svw-orange-default text-white px-4 py-2 rounded hover:bg-svw-orange-darker transition-colors text-sm font-medium"
59+
>
60+
Details ansehen
61+
</Link>
62+
</div>
63+
)}
64+
</div>
65+
);
66+
}

components/events/EventList.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"use client";
2+
3+
import { useEffect, useState } from 'react';
4+
import { EventMatter, Event } from '#/content/events'; // Adjust import if Event is not directly under #/content/events
5+
import { EventCard } from './EventCard'; // Assuming EventCard is in the same directory
6+
import { Spinner } from '#/components/spinner/Spinner'; // Assuming Spinner component path
7+
8+
export function EventList() {
9+
const [eventMatters, setEventMatters] = useState<EventMatter[] | null>(null);
10+
const [isLoading, setIsLoading] = useState(true);
11+
const [error, setError] = useState<string | null>(null);
12+
13+
useEffect(() => {
14+
const fetchEvents = async () => {
15+
try {
16+
const response = await fetch('/api/events');
17+
if (!response.ok) {
18+
throw new Error(`HTTP error! status: ${response.status}`);
19+
}
20+
const data: EventMatter[] = await response.json();
21+
setEventMatters(data);
22+
} catch (e) {
23+
if (e instanceof Error) {
24+
setError(e.message);
25+
} else {
26+
setError('An unknown error occurred.');
27+
}
28+
} finally {
29+
setIsLoading(false);
30+
}
31+
};
32+
33+
fetchEvents();
34+
}, []);
35+
36+
if (isLoading) {
37+
return (
38+
<div className="w-full text-center py-10">
39+
<Spinner />
40+
</div>
41+
);
42+
}
43+
44+
if (error) {
45+
return (
46+
<div className="w-full text-center py-10 text-red-600">
47+
Fehler beim Laden der Veranstaltungen: {error}
48+
</div>
49+
);
50+
}
51+
52+
if (!eventMatters || eventMatters.length === 0) {
53+
return (
54+
<div className="w-full text-center py-10 text-gray-700">
55+
Zurzeit sind keine Veranstaltungen vorhanden.
56+
</div>
57+
);
58+
}
59+
60+
// Flatten all calendar entries from all event matters into a single list of events
61+
// and sort them by start date, earliest first.
62+
const allEvents = eventMatters
63+
.flatMap(em => em.calendarEntries.map(entry => ({
64+
...entry,
65+
// Ensure start and end are Date objects if they aren't already
66+
// The API sends them as strings, EventCard expects Date objects
67+
start: new Date(entry.start),
68+
end: new Date(entry.end),
69+
})))
70+
.sort((a, b) => a.start.getTime() - b.start.getTime());
71+
72+
if (allEvents.length === 0) {
73+
return (
74+
<div className="w-full text-center py-10 text-gray-700">
75+
Zurzeit sind keine Veranstaltungen vorhanden.
76+
</div>
77+
);
78+
}
79+
80+
return (
81+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
82+
{allEvents.map((eventItem, index) => (
83+
// Using slug + index as key if event items within calendarEntries don't have unique IDs
84+
// Ideally, each Event object should have a unique ID for the key.
85+
// For now, we'll use the title and index as a composite key.
86+
// The event object from calendarEntries might not have a slug.
87+
// The Event type itself doesn't have a slug.
88+
<EventCard key={`${eventItem.title}-${index}`} event={eventItem} />
89+
))}
90+
</div>
91+
);
92+
}

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
"node-fetch": "2.7.0",
4242
"nodemailer": "^6.10.0",
4343
"react": "19.0.0",
44-
"react-big-calendar": "^1.17.1",
4544
"react-dom": "19.0.0",
4645
"react-hotkeys-hook": "^4.6.1",
4746
"react-icons": "^5.0.1",
@@ -69,7 +68,6 @@
6968
"@types/node-fetch": "^2.6.12",
7069
"@types/nodemailer": "^6.4.14",
7170
"@types/react": "19.0.10",
72-
"@types/react-big-calendar": "^1.16.1",
7371
"@types/react-dom": "19.0.4",
7472
"@types/ws": "^8.18.1",
7573
"@typescript-eslint/eslint-plugin": "^8.32.1",

0 commit comments

Comments
 (0)