Skip to content

Commit 46d04cb

Browse files
nikoshellrodrigogiraoserraopre-commit-ci[bot]egeakman
authored
Sprints page with filter (#1383)
![image](https://github.com/user-attachments/assets/c52a5949-8c40-4591-a5cf-ff6e28fd566a) --------- Co-authored-by: Rodrigo Girão Serrão <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ege Akman <[email protected]>
1 parent df2ec2c commit 46d04cb

File tree

11 files changed

+271
-1
lines changed

11 files changed

+271
-1
lines changed

public/draft2.png

14 KB
Loading

src/components/SprintCard.astro

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
import { getEntry } from 'astro:content';
3+
import Markdown from "@ui/Markdown.astro";
4+
5+
const { slug } = Astro.props;
6+
7+
8+
const sprint = await getEntry("sprints", slug);
9+
10+
if (!sprint) {
11+
throw new Error(`Sprint entry "${slug}" not found`);
12+
}
13+
14+
const { data, body } = sprint;
15+
16+
// <a href={`/sprints/${slug}`} id={slug} class="text-blue-600 hover:text-blue-800 hover:underline"></a>
17+
---
18+
19+
<article class=`${data.draft?"draft2":""} sprint-card bg-white border border-gray-200 rounded-lg p-6 mb-8 shadow-sm hover:shadow-md transition-shadow duration-200` data-python-level={data.pythonLevel} data-room={data.room}>
20+
<header class="mb-4">
21+
<h3 class="text-2xl font-semibold mb-2">
22+
{data.title}
23+
</h3>
24+
<div class="flex flex-wrap gap-4 text-lg text-gray-600">
25+
<span class="flex items-center gap-1">
26+
<span>👥</span>
27+
<span>{data.numberOfPeople}</span>
28+
</span>
29+
<span class="flex items-center gap-1">
30+
<span>🐍</span>
31+
<span>{data.pythonLevel}</span>
32+
</span>
33+
</div>
34+
</header>
35+
36+
<div class="space-y-4">
37+
<div class="text-lg">
38+
<span class="text-gray-900">Contact:</span>
39+
<span class="text-gray-700">{data.contactPerson.name}</span>
40+
{data.contactPerson.email && (
41+
<span class="text-gray-600">
42+
{' '}(<a href={`mailto:${data.contactPerson.email}`} class="text-primary hover:text-primary-hover underline">{data.contactPerson.email}</a>)
43+
</span>
44+
)}
45+
{data.contactPerson.github && (
46+
<span class="text-gray-600">
47+
{' '}(<a href={`https://github.com/${data.contactPerson.github}`} target="_blank" rel="noopener" class="text-primary hover:text-primary-hover underline">@{data.contactPerson.github}</a>)
48+
</span>
49+
)}
50+
{data.contactPerson.twitter && (
51+
<span class="text-gray-600">
52+
{' '}(<a href={`https://x.com/${data.contactPerson.twitter}`} target="_blank" rel="noopener" class="text-primary hover:text-primary-hover underline">@{data.contactPerson.twitter}</a>)
53+
</span>
54+
)}
55+
</div>
56+
57+
{(data.links) && (
58+
<div class="text-lg">
59+
<span class="font-medium text-gray-900">Links:</span>
60+
<ul class="mt-2 ml-6 space-y-1 list-disc">
61+
{data.links?.map((link) => (
62+
<li>
63+
<a href={link.url} target="_blank" rel="noopener" class="text-primary hover:text-primary-hover underline ">{link.title}</a>
64+
</li>
65+
))}
66+
</ul>
67+
</div>
68+
)}
69+
70+
71+
{body &&
72+
<Markdown content={body} />
73+
}
74+
75+
</div>
76+
77+
</article>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
import Markdown from "@ui/Markdown.astro";
3+
4+
const content = await Astro.slots.render('default')
5+
6+
---
7+
8+
<Markdown content={content} />

src/content/config.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,13 +256,39 @@ const jobs = defineCollection({
256256
}),
257257
});
258258

259+
const sprints = defineCollection({
260+
type: "content",
261+
schema: z.object({
262+
title: z.string(),
263+
slug: z.string().optional(), // Auto-generated from filename if not provided
264+
numberOfPeople: z.string().or(z.number()),
265+
pythonLevel: z.enum(["Any", "Beginner", "Intermediate", "Advanced"]),
266+
contactPerson: z.object({
267+
name: z.string(),
268+
email: z.string().email().optional().nullable(),
269+
github: z.string().optional().nullable(),
270+
twitter: z.string().optional().nullable(),
271+
}),
272+
links: z
273+
.array(
274+
z.object({
275+
title: z.string(),
276+
url: z.string().url(),
277+
})
278+
)
279+
.optional(),
280+
draft: z.boolean().optional().default(false),
281+
}),
282+
});
283+
259284
export const collections = {
260285
days,
261286
pages,
262287
deadlines,
263288
week,
264289
sessions,
265290
speakers,
291+
sprints,
266292
keynoters,
267293
sponsors,
268294
jobs,

src/content/pages/sprints.md renamed to src/content/pages/sprints_info.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ together.
3131

3232
## Open-Source Projects
3333

34-
More info soon!
34+
Anyone can submit a project for the sprints, although sprints are typically
35+
submitted by project maintainers or frequet contributors.
36+
37+
To submit a project for the sprints, head to
38+
[the EuroPython website repository](https://github.com/EuroPython/website) and
39+
add your project as a markdown file in the folder `src/content/sprints`. Use the
40+
file `_sprints_template.md` as the template for your project file.
3541

3642
<!-- ## Location of the sprints venue
3743
The Rajska Building (RB), [Žižkov Campus, VŠE](https://maps.app.goo.gl/azRTAczu8B5ma1XFA) (Prague University of Economics and Business) is the home of our sprints again this year.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "Project name"
3+
numberOfPeople: "" # How many people you expect to be able to accommodate.
4+
pythonLevel: "Any" # Any, Beginner, Intermediate, or Advanced.
5+
contactPerson: # The main person to reach out to regarding the sprint.
6+
name: ""
7+
email:
8+
github:
9+
twitter:
10+
links: # Add as many links as relevant.
11+
- title: "Project xyz GitHub repo"
12+
url: "https://github.com/xyz"
13+
---
14+
15+
Your sprint/project description goes here.

src/content/sprints/beeware.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
title: "BeeWare"
3+
numberOfPeople: "TBA"
4+
pythonLevel: "Beginner"
5+
contactPerson:
6+
name: "Russell Keith-Magee"
7+
email:
8+
links:
9+
- title: "BeeWare GitHub"
10+
url: "https://github.com/beeware"
11+
---
12+
13+
Come sprint on the BeeWare project!

src/content/sprints/cpython.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: "CPython Core"
3+
numberOfPeople: "TBA"
4+
pythonLevel: "Intermediate"
5+
contactPerson:
6+
name: "Petr Viktorin"
7+
email:
8+
links:
9+
- title: "CPython's Developer Guide"
10+
url: "https://devguide.python.org/"
11+
- title: "Code of Conduct"
12+
url: "https://www.python.org/psf/conduct/"
13+
- title: "Easy Issues"
14+
url: "https://github.com/python/cpython/issues?q=is%3Aissue+is%3Aopen+label%3Aeasy"
15+
---
16+
17+
Come sprint on CPython and work on Python 3.15!
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
title: "Secret project"
3+
numberOfPeople: "5"
4+
pythonLevel: "Any"
5+
contactPerson:
6+
name: "Nikoś Hell"
7+
email:
8+
github: "nikoshell"
9+
twitter: "nikoshell20"
10+
links:
11+
- title: "Secret GitHub repo"
12+
url: "https://github.com/nikoshell/secret"
13+
draft: true
14+
---
15+
16+
Your sprint/project description goes here.

src/pages/sprints.astro

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
import { getCollection } from 'astro:content';
3+
import { getEntry, render } from 'astro:content';
4+
import Layout from '@layouts/MarkdownLayout.astro';
5+
import SprintCard from '@components/SprintCard.astro';
6+
import Markdown from "@ui/Markdown.astro";
7+
8+
const entry = await getEntry('pages', 'sprints_info');
9+
10+
if (!entry) {
11+
throw new Error('Could not find page entry.');
12+
}
13+
14+
const sprints = await getCollection("sprints", ({ data }) => {
15+
return import.meta.env.MODE === "production" ? data.draft !== true : true;
16+
});
17+
18+
---
19+
20+
<Layout title=`${entry.title}` description=`${entry.description}` toc=true>
21+
22+
<Markdown content={entry.body} />
23+
<div class="flex flex-wrap gap-8 justify-center my-8">
24+
<div class="flex flex-col gap-2">
25+
<label for="level-filter" class="font-semibold text-gray-700 text-lg">Python Level:</label>
26+
<select id="level-filter" class="px-4 py-2 border border-gray-300 rounded-lg bg-white text-lg min-w-[150px] focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
27+
<option value="">All Levels</option>
28+
<option value="Any">Any</option>
29+
<option value="Beginner">Beginner</option>
30+
<option value="Intermediate">Intermediate</option>
31+
<option value="Advanced">Advanced</option>
32+
</select>
33+
</div>
34+
</div>
35+
36+
<div class="text-center mb-8">
37+
<p class="text-gray-600 font-medium">{sprints.length} active {sprints.length === 1 ? 'sprint' : 'sprints'}</p>
38+
</div>
39+
40+
<section class="max-w-4xl mx-auto" id="sprints-list">
41+
{sprints.map((sprint) => (
42+
<SprintCard slug={sprint.slug} />
43+
))}
44+
</section>
45+
46+
{sprints.length === 0 && (
47+
<div class="text-center py-12">
48+
<p class="text-gray-500 text-lg">No sprints are currently available. Check back soon!</p>
49+
</div>
50+
)}
51+
</Layout>
52+
53+
<script>
54+
document.addEventListener('DOMContentLoaded', () => {
55+
const levelFilter = document.getElementById('level-filter') as HTMLSelectElement;
56+
const sprintsList = document.getElementById('sprints-list');
57+
const sprintsCount = document.querySelector('.text-center.mb-8 p');
58+
59+
function filterSprints() {
60+
const selectedLevel = levelFilter.value;
61+
const sprintCards = sprintsList?.querySelectorAll('.sprint-card');
62+
63+
let visibleCount = 0;
64+
65+
sprintCards?.forEach((card) => {
66+
const cardElement = card as HTMLElement;
67+
const pythonLevel = cardElement.getAttribute('data-python-level');
68+
69+
const levelMatch = !selectedLevel || pythonLevel === selectedLevel;
70+
71+
if (levelMatch) {
72+
cardElement.style.display = 'block';
73+
visibleCount++;
74+
} else {
75+
cardElement.style.display = 'none';
76+
}
77+
});
78+
79+
if (sprintsCount) {
80+
sprintsCount.textContent = `${visibleCount} active ${visibleCount === 1 ? 'sprint' : 'sprints'}`;
81+
}
82+
}
83+
84+
levelFilter?.addEventListener('change', filterSprints);
85+
});
86+
</script>

0 commit comments

Comments
 (0)