Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/components/OrganizersList/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
import { Image } from "astro:assets";
import defaultPerson from "@/assets/images/people-placeholder.jpeg";
import { lunalink } from "@bearstudio/lunalink";
import { ROUTES } from "@/routes.gen";
import type { CollectionEntry } from "astro:content";

interface Props {
organizers: CollectionEntry<"people">[];
title?: string;
}

const { organizers, title = "Organized by" } = Astro.props;

const sortedOrganizers = organizers.sort((a, b) =>
a.data.name.localeCompare(b.data.name),
);
---

{
sortedOrganizers.length > 0 && (
<div class="flex flex-col gap-3 md:max-w-xs">
<h3 class="text-xs font-medium uppercase tracking-widest opacity-60">
{title}
</h3>
<div class="flex flex-wrap gap-2">
{sortedOrganizers.map((organizer) => (
<a
href={lunalink(ROUTES.people[":id"].__path, {
id: organizer.id,
})}
class="flex items-center gap-2 rounded-full bg-white/10 px-3 py-2 backdrop-blur-sm transition-all hover:bg-white/20"
>
<Image
class="h-8 w-8 rounded-full object-cover"
src={organizer.data.avatar ?? defaultPerson}
alt={organizer.data.name}
width={32}
height={32}
/>
<span class="text-sm font-medium">{organizer.data.name}</span>
</a>
))}
</div>
</div>
)
}
77 changes: 56 additions & 21 deletions src/pages/events/locations/[countryId]/[cityId]/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ImageBackgroundHero from "@/components/ImageBackgroundHero/index.astro";
import { BackButton } from "@/components/BackButton";
import { lunalink } from "@bearstudio/lunalink";
import { ROUTES } from "@/routes.gen";
import OrganizersList from "@/components/OrganizersList/index.astro";

export async function getStaticPaths() {
const events = await getEventsCollection();
Expand All @@ -33,6 +34,39 @@ if (!city || !country) {
}

const eventsInCity = await getEventByCity(cityId);

// Get unique organizers from all events in the city
const allOrganizers = new Set<string>();
eventsInCity.forEach((event) => {
event.data.organizers?.forEach((organizerId) => {
if (typeof organizerId === "string") {
allOrganizers.add(organizerId);
} else if (
organizerId &&
typeof organizerId === "object" &&
"id" in organizerId
) {
allOrganizers.add(organizerId.id);
}
});
});

// Fetch organizer data
const organizers = await Promise.all(
Array.from(allOrganizers).map(async (organizerId) => {
try {
const organizer = await getEntry("people", organizerId);
return organizer;
} catch {
return null;
}
}),
);

const validOrganizers = organizers.filter(
(org): org is NonNullable<typeof org> => org !== null && org !== undefined,
);

const ogImage = new URL(
Astro.url.pathname + "/assets/og-image.jpg",
Astro.site,
Expand All @@ -56,28 +90,29 @@ const ogImage = new URL(
client:load
contextLabel={`Events in ${country.data.name}`}
/>
<div
class="flex w-full flex-col justify-center gap-4 py-12 md:min-h-[40svh]"
>
<div class="flex w-full flex-col gap-2">
<h1
class="w-full text-balance text-left font-heading text-4xl font-medium uppercase tracking-wider max-md:flex-1 md:text-5xl"
>
{city.data.name}
</h1>
<h2
class="w-full text-balance text-left text-base tracking-widest md:text-lg"
>
All the Fork it! tech events in {city.data.name}
</h2>
<div class="flex w-full flex-col justify-between gap-8 py-12 lg:flex-row">
<div class="flex flex-1 flex-col gap-4">
<div class="flex w-full flex-col gap-2">
<h1
class="w-full text-balance text-left font-heading text-4xl font-medium uppercase tracking-wider md:text-5xl"
>
{city.data.name}
</h1>
<h2
class="w-full text-balance text-left text-base tracking-widest md:text-lg"
>
All the Fork it! tech events in {city.data.name}
</h2>
</div>
{
!!city.data.description && (
<p class="max-w-[60ch] text-sm tracking-wide [text-shadow:0_2px_30px_rgba(0,0,0,0.4)]">
{city.data.description}
</p>
)
}
</div>
{
!!city.data.description && (
<p class="max-w-[60ch] text-sm tracking-wide [text-shadow:0_2px_30px_rgba(0,0,0,0.4)]">
{city.data.description}
</p>
)
}
<OrganizersList organizers={validOrganizers} />
</div>
</div>

Expand Down
72 changes: 53 additions & 19 deletions src/pages/events/locations/[countryId]/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BackButton } from "@/components/BackButton";
import { lunalink } from "@bearstudio/lunalink";
import { ROUTES } from "@/routes.gen";
import dayjs from "dayjs";
import OrganizersList from "@/components/OrganizersList/index.astro";

export async function getStaticPaths() {
const countries = await getCollection("countries");
Expand Down Expand Up @@ -41,6 +42,36 @@ const citiesLastEvent = allCities.map((city) => {

const cities = citiesLastEvent.sort((a, b) => b.lastDate.diff(a.lastDate));

const allOrganizers = new Set<string>();
eventsInCountry.forEach((event) => {
event.data.organizers?.forEach((organizerId) => {
if (typeof organizerId === "string") {
allOrganizers.add(organizerId);
} else if (
organizerId &&
typeof organizerId === "object" &&
"id" in organizerId
) {
allOrganizers.add(organizerId.id);
}
});
});

const organizers = await Promise.all(
Array.from(allOrganizers).map(async (organizerId) => {
try {
const organizer = await getEntry("people", organizerId);
return organizer;
} catch {
return null;
}
}),
);

const validOrganizers = organizers.filter(
(org): org is NonNullable<typeof org> => org !== null && org !== undefined,
);

const ogImage = new URL(
Astro.url.pathname + "/assets/og-image.jpg",
Astro.site,
Expand Down Expand Up @@ -73,27 +104,30 @@ if (!country) {
contextLabel="Fork It! Events"
/>
<div
class="flex w-full flex-col justify-center gap-4 py-12 md:min-h-[40svh]"
class="flex w-full flex-col justify-between gap-8 py-12 md:min-h-[40svh] md:flex-row"
>
<div class="flex w-full flex-col gap-2">
<h1
class="w-full text-balance text-left font-heading text-4xl font-medium uppercase tracking-wider max-md:flex-1 md:text-5xl"
>
{country.data.name}
</h1>
<h2
class="w-full text-balance text-left text-base tracking-widest md:text-lg"
>
All the Fork it! tech events in {country.data.name}
</h2>
<div class="flex flex-1 flex-col gap-4">
<div class="flex w-full flex-col gap-2">
<h1
class="w-full text-balance text-left font-heading text-4xl font-medium uppercase tracking-wider md:text-5xl"
>
{country.data.name}
</h1>
<h2
class="w-full text-balance text-left text-base tracking-widest md:text-lg"
>
All the Fork it! tech events in {country.data.name}
</h2>
</div>
{
!!country.data.description && (
<p class="max-w-[60ch] text-sm tracking-wide [text-shadow:0_2px_30px_rgba(0,0,0,0.4)]">
{country.data.description}
</p>
)
}
</div>
{
!!country.data.description && (
<p class="max-w-[60ch] text-sm tracking-wide [text-shadow:0_2px_30px_rgba(0,0,0,0.4)]">
{country.data.description}
</p>
)
}
<OrganizersList organizers={validOrganizers} />
</div>
</div>

Expand Down