diff options
| author | Leo Goetz <dev@leogtz.de> | 2026-01-22 09:10:15 +0100 |
|---|---|---|
| committer | Leo Goetz <dev@leogtz.de> | 2026-01-22 09:10:15 +0100 |
| commit | 01c0f792484f8f52606eae0e58abe528acef3086 (patch) | |
| tree | e30894caf65b39aab1e050035f63450b5032123c /frontend/src/components/Events.ts | |
| parent | b6d422d33c3b647ab249a8cf3520bc986fa2c549 (diff) | |
Diffstat (limited to 'frontend/src/components/Events.ts')
| -rw-r--r-- | frontend/src/components/Events.ts | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/frontend/src/components/Events.ts b/frontend/src/components/Events.ts new file mode 100644 index 0000000..a92cf83 --- /dev/null +++ b/frontend/src/components/Events.ts @@ -0,0 +1,129 @@ +import { Calendar } from "./Icons"; + +const API_URL = import.meta.env.VITE_API_URL; + +interface Event { + id: number; + title: string; + description?: string; + date: Date; + host_id: number; + image_url?: string; + host: { + id: number; + name: string; + email: string; + }; + rsvps: { + id: number; + name: string; + email: string; + }[]; +} + +const loadEventsData = async (): Promise<Event[]> => { + try { + const response = await fetch(`${API_URL}/events`); + return response.json(); + } catch (e) { + console.error(e); + return []; + } +}; + +export const EventModal = (event: Event) => { + const formId = `rsvp-form-${event.id}`; + const modalId = `modal-event-${event.id}`; + return `<dialog id="${modalId}"> + <article> + <header> + <button + aria-label="Close" + rel="prev" + data-target="${modalId}" + class="toggle-modal" + ></button> + <h3>RSVP to ${event.title}</h3> + </header> + <form id="${formId}" data-modal="${modalId}" + action="${API_URL}/events/${event.id}/rsvp" + method="POST" + > + <label for="rsvp-name-${event.id}">Name: + <input type="text" id="rsvp-name-${event.id}" class="rsvp-name" name="name" required /> + </label> + <label for="rsvp-email-${event.id}">Email: + <input type="email" id="rsvp-email-${event.id}" class="rsvp-email" name="email" required /> + </label> + + </form> + <footer> + <button + role="button" + class="toggle-modal cancel" + data-target="${modalId}" + >Cancel</button> + + <button id="submit-${formId}" role="button" form="${formId}" type="submit">Submit RSVP</button> + + </footer> + </article> + </dialog>`; +}; + +export const EventCard = (e: Event) => { + const eventDate = new Date(e.date); + const isPast = eventDate < new Date(); + return ` +<article class="event" > +<header> + ${e.image_url && `<img src=${e.image_url} alt="${e.title} thumbnail" />`} +</header> + <main> + <h4>${e.title}</h4> + <p>${Calendar} ${eventDate.toLocaleDateString()}</p> + <p>Host: ${e.host?.name || `User ${e.host_id}`}</p> + + ${e.description ? `<p>${e.description}</p>` : ""} + </main> + <footer> + <span> + ${e.rsvps?.length || 0} ${isPast ? "went" : "going"} + </span> + ${ + !isPast + ? ` + <button role="button" data-target="modal-event-${e.id}" class="toggle-modal" + title="RSVP to ${e.title}" + > + RSVP + </button>` + : "" + } + </footer> + ${EventModal(e)} +</article> + `; +}; + +export const EventsSection = (title: string, events: Event[]) => { + return ` + <section class='events'> + <h2>${title} events </h2> + <div role = "group"> + ${events.map((e) => EventCard(e)).join("") || "No events"} + </div> + </section>`; +}; + +// IIFE to asynchronously load the Event data before exporting the component +// https://developer.mozilla.org/en-US/docs/Glossary/IIFE +export const Events = await (async () => { + const all = await loadEventsData(); + const past = all.filter((e) => new Date(e.date) < new Date()); + const upcoming = all.filter((e) => new Date(e.date) > new Date()); + return ` + ${EventsSection("Upcoming", upcoming)} + ${EventsSection("Past", past)} +`; +})(); |
