Skip to content

Commit eb0d7a3

Browse files
committed
Update speakers page look.
1 parent 13e1bcc commit eb0d7a3

File tree

1 file changed

+108
-65
lines changed

1 file changed

+108
-65
lines changed

src/pages/speakers.astro

Lines changed: 108 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,66 @@
11
---
2-
import { getCollection } from "astro:content";
2+
import { getCollection, type CollectionEntry } from "astro:content";
33
import Layout from "../layouts/Layout.astro";
44
import Prose from "../components/prose/prose.astro";
55
import { Separator } from "../components/separator/separator";
6+
import { Image } from "astro:assets";
7+
8+
type Speaker = CollectionEntry<"speakers">;
69
7-
// Fetch all speaker entries
810
const speakersCollection = await getCollection("speakers");
911
10-
// Define the type for the groups object
11-
type Speaker = {
12-
id: string;
13-
data: {
14-
name: string;
15-
};
16-
};
17-
18-
type Groups = {
19-
[key: string]: Speaker[];
20-
};
21-
22-
// Group speakers by the first letter of their name
23-
const groups: Groups = speakersCollection
24-
.filter((speaker: Speaker) => !!speaker.data.name)
25-
.reduce((acc: Groups, speaker: Speaker) => {
26-
const letter = speaker.data.name[0].toUpperCase();
27-
if (!acc[letter]) {
28-
acc[letter] = [];
12+
const speakersWithNames = speakersCollection.filter((speaker) =>
13+
!!speaker.data?.name
14+
);
15+
16+
const getFirstLetter = (name: string): string => name.charAt(0).toUpperCase();
17+
18+
function createOptimalGroups(speakers: Speaker[]): Record<string, Speaker[]> {
19+
const sortedSpeakers = [...speakers].sort((a, b) =>
20+
a.data.name.localeCompare(b.data.name)
21+
);
22+
23+
const groups: Record<string, Speaker[]> = {};
24+
let currentGroup: Speaker[] = [];
25+
let currentLetter = '';
26+
let groupKey = '';
27+
28+
sortedSpeakers.forEach((speaker) => {
29+
const letter = getFirstLetter(speaker.data.name);
30+
31+
if (currentGroup.length === 0) {
32+
currentLetter = letter;
33+
groupKey = letter;
34+
currentGroup.push(speaker);
35+
}
36+
else if (letter === currentLetter && currentGroup.length < 15) {
37+
currentGroup.push(speaker);
2938
}
30-
acc[letter].push(speaker);
31-
return acc;
32-
}, {} as Groups);
39+
else {
40+
if (currentGroup.length < 10 && currentGroup.length + 1 <= 15) {
41+
currentGroup.push(speaker);
42+
groupKey = `${currentLetter}-${letter}`;
43+
} else {
44+
groups[groupKey] = currentGroup;
45+
currentLetter = letter;
46+
groupKey = letter;
47+
currentGroup = [speaker];
48+
}
49+
}
50+
});
3351
34-
const letters = Object.keys(groups).sort((a, b) => a.localeCompare(b));
52+
if (currentGroup.length > 0) {
53+
groups[groupKey] = currentGroup;
54+
}
3555
36-
const title = "Speakers";
56+
return groups;
57+
}
3758
38-
const description =
39-
"Alphabetical list of all confirmed speakers for the conference";
59+
const speakerGroups = createOptimalGroups(speakersWithNames);
60+
const groupKeys = Object.keys(speakerGroups);
61+
62+
const title = "Speakers";
63+
const description = "Our conference speakers organized alphabetically";
4064
---
4165

4266
<Layout title={title} description={description}>
@@ -45,49 +69,68 @@ const description =
4569
<h1>Speakers</h1>
4670
</Prose>
4771

48-
<div class="flex text-3xl font-bold flex-wrap my-10">
72+
<div class="flex text-xl md:text-2xl font-bold flex-wrap my-8 justify-center">
4973
{
50-
letters.map((letter) => (
51-
<h3 class="mr-2">
52-
<a href={`#letter-${letter}`}>{letter}</a>
53-
</h3>
74+
groupKeys.map((key) => (
75+
<a
76+
href={`#group-${key}`}
77+
class="mx-2 my-1 px-2 py-1 hover:text-primary hover:bg-gray-100 rounded transition-colors"
78+
>
79+
{key}
80+
</a>
5481
))
5582
}
5683
</div>
5784

58-
<ol class="speakers">
59-
{
60-
letters.map((letter, index) => (
61-
<>
62-
<div id={`letter-${letter}`}>
63-
<h2 class="relative font-title text-primary font-bold mb-[0.6em] [&>a]:border-0 [&>a]:text-inherit text-4xl">
64-
{letter}
65-
</h2>
66-
67-
<ul class="pl-4">
68-
{groups[letter]
69-
.sort((a, b) => a.data.name.localeCompare(b.data.name))
70-
.map((speaker) => (
71-
<li {...{ key: speaker.id }} class="mb-1">
72-
<a
73-
class="underline hover:text-primary-hover"
74-
href={`/speaker/${speaker.id}`}
75-
>
76-
{speaker.data.name}
77-
</a>
78-
</li>
79-
))}
80-
</ul>
85+
{
86+
groupKeys.map((key, index) => (
87+
<>
88+
<div id={`group-${key}`} class="pt-6">
89+
<h2 class="text-3xl md:text-4xl font-bold text-primary mb-6">{key}</h2>
90+
91+
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-6 mb-10">
92+
{speakerGroups[key].map((speaker) => (
93+
<a
94+
href={`/speaker/${speaker.id}`}
95+
class="flex flex-col items-center group"
96+
>
97+
<div class="w-full aspect-square rounded-full overflow-hidden mb-3 border-2 border-transparent group-hover:border-primary transition-all">
98+
{speaker.data.avatar ? (
99+
100+
<Image
101+
src={speaker.data.avatar}
102+
alt={`${speaker.data.name}'s profile picture`}
103+
height={200}
104+
width={200}
105+
class="w-full h-full object-cover"
106+
/>
107+
) : (
108+
<div class="w-full h-full bg-[#f7efe6] flex items-center justify-center text-gray-500 text-xl">
109+
{speaker.data.name.charAt(0)}
110+
</div>
111+
)}
112+
</div>
113+
<h3 class="text-center font-medium group-hover:text-primary text-sm md:text-base">
114+
{speaker.data.name}
115+
</h3>
116+
</a>
117+
))}
81118
</div>
119+
</div>
82120

83-
{index !== letters.length - 1 ? (
84-
<Separator />
85-
) : (
86-
<div class="mb-20" />
87-
)}
88-
</>
89-
))
90-
}
91-
</ol>
121+
{index !== groupKeys.length - 1 && <Separator />}
122+
</>
123+
))
124+
}
92125
</div>
93126
</Layout>
127+
128+
<style>
129+
a {
130+
transition: transform 0.2s ease;
131+
}
132+
133+
a.group:hover {
134+
transform: translateY(-3px);
135+
}
136+
</style>

0 commit comments

Comments
 (0)