feat: Add event collection and event page structure
All checks were successful
SteamWarCI Build successful
All checks were successful
SteamWarCI Build successful
- Introduced a new events collection in config.ts with schema validation. - Created a new event markdown file for the WarGear event. - Updated German translations to include new event-related strings. - Modified PageLayout to support a wide layout option. - Enhanced announcements page to improve tag filtering and post rendering. - Implemented dynamic event pages with detailed event information and fight plans. - Added an index page for events to list all upcoming events.
This commit is contained in:
@@ -1,43 +1,48 @@
|
||||
---
|
||||
import {getCollection} from "astro:content";
|
||||
import { getCollection } from "astro:content";
|
||||
import PageLayout from "../../layouts/PageLayout.astro";
|
||||
import {astroI18n, createGetStaticPaths, t} from "astro-i18n";
|
||||
import { astroI18n, createGetStaticPaths, t } from "astro-i18n";
|
||||
import PostComponent from "../../components/PostComponent.astro";
|
||||
import dayjs from "dayjs";
|
||||
import TagComponent from "../../components/TagComponent.astro";
|
||||
import SWPaginator from "@components/styled/SWPaginator.svelte";
|
||||
|
||||
export const getStaticPaths = createGetStaticPaths(async (props) => {
|
||||
const posts = await getCollection("announcements", entry => entry.id.split("/")[0] === astroI18n.locale);
|
||||
const posts = await getCollection("announcements", (entry) => entry.id.split("/")[0] === astroI18n.locale);
|
||||
|
||||
const germanPosts = await getCollection("announcements", entry => entry.id.split("/")[0] === astroI18n.fallbackLocale);
|
||||
const germanPosts = await getCollection("announcements", (entry) => entry.id.split("/")[0] === astroI18n.fallbackLocale);
|
||||
|
||||
germanPosts.forEach(value => {
|
||||
if (posts.find(post => post.data.key === value.data.key)) {
|
||||
germanPosts.forEach((value) => {
|
||||
if (posts.find((post) => post.data.key === value.data.key)) {
|
||||
return;
|
||||
} else {
|
||||
posts.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
return props.paginate(posts.sort((a, b) => dayjs(b.data.created).unix() - dayjs(a.data.created).unix()), {
|
||||
pageSize: 5,
|
||||
});
|
||||
return props.paginate(
|
||||
posts.sort((a, b) => dayjs(b.data.created).unix() - dayjs(a.data.created).unix()),
|
||||
{
|
||||
pageSize: 5,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
async function getTags() {
|
||||
const posts = await getCollection("announcements");
|
||||
const tags = new Map<string, number>();
|
||||
posts.forEach(post => {
|
||||
post.data.tags.forEach(tag => {
|
||||
if (tags.has(tag)) {
|
||||
tags.set(tag, tags.get(tag) + 1);
|
||||
posts.forEach((post) => {
|
||||
post.data.tags.forEach((tag) => {
|
||||
if (tags.has(tag.toLowerCase())) {
|
||||
tags.set(tag.toLowerCase(), tags.get(tag) + 1);
|
||||
} else {
|
||||
tags.set(tag, 1);
|
||||
tags.set(tag.toLowerCase(), 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
return Array.from(tags).sort((a, b) => b[1] - a[1]).map(value => value[0]);
|
||||
return Array.from(tags)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map((value) => value[0]);
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
@@ -46,15 +51,15 @@ const tags = await getTags();
|
||||
|
||||
<PageLayout title={t("blog.title")}>
|
||||
<div class="py-2">
|
||||
{tags.map(tag => (
|
||||
<TagComponent tag={tag} transition:name={`${tag}-tag-filter`} />
|
||||
))}
|
||||
{tags.map((tag) => <TagComponent tag={tag} transition:name={`${tag}-tag-filter`} />)}
|
||||
</div>
|
||||
{page.data.map((post) => (
|
||||
<div>
|
||||
<PostComponent post={post}/>
|
||||
</div>
|
||||
))}
|
||||
{
|
||||
page.data.map((post) => (
|
||||
<div>
|
||||
<PostComponent post={post} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
<SWPaginator
|
||||
maxPage={page.lastPage}
|
||||
page={page.currentPage - 1}
|
||||
@@ -62,6 +67,6 @@ const tags = await getTags();
|
||||
previousUrl={page.url.prev}
|
||||
firstUrl={page.url.first}
|
||||
lastUrl={page.url.last}
|
||||
pagesUrl={(i) => i == 0 ? page.url.first : page.currentPage === page.lastPage ? page.url.current.replace(page.lastPage, i + 1) : page.url.last.replace(page.lastPage, i + 1)}
|
||||
pagesUrl={(i) => (i == 0 ? page.url.first : page.currentPage === page.lastPage ? page.url.current.replace(page.lastPage, i + 1) : page.url.last.replace(page.lastPage, i + 1))}
|
||||
/>
|
||||
</PageLayout>
|
||||
</PageLayout>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
---
|
||||
import {CollectionEntry} from "astro:content";
|
||||
import {astroI18n, createGetStaticPaths, t} from "astro-i18n";
|
||||
import {getCollection} from "astro:content";
|
||||
import { CollectionEntry } from "astro:content";
|
||||
import { astroI18n, createGetStaticPaths, t } from "astro-i18n";
|
||||
import { getCollection } from "astro:content";
|
||||
import PageLayout from "../../../layouts/PageLayout.astro";
|
||||
import {capitalize} from "../../../components/admin/util";
|
||||
import { capitalize } from "../../../components/admin/util";
|
||||
import PostComponent from "../../../components/PostComponent.astro";
|
||||
import dayjs from "dayjs";
|
||||
import { ArrowLeftOutline } from "flowbite-svelte-icons";
|
||||
import {l} from "../../../util/util";
|
||||
import { l } from "../../../util/util";
|
||||
import TagComponent from "../../../components/TagComponent.astro";
|
||||
|
||||
export const getStaticPaths = createGetStaticPaths(async () => {
|
||||
let posts = (await getCollection("announcements", entry => entry.id.split("/")[0] === astroI18n.locale));
|
||||
let posts = await getCollection("announcements", (entry) => entry.id.split("/")[0] === astroI18n.locale);
|
||||
|
||||
const germanPosts = await getCollection("announcements", entry => entry.id.split("/")[0] === "de");
|
||||
const germanPosts = await getCollection("announcements", (entry) => entry.id.split("/")[0] === "de");
|
||||
|
||||
posts.sort((a, b) => dayjs(b.data.created).unix() - dayjs(a.data.created).unix());
|
||||
|
||||
germanPosts.forEach(value => {
|
||||
if (posts.find(post => post.data.key === value.data.key)) {
|
||||
germanPosts.forEach((value) => {
|
||||
if (posts.find((post) => post.data.key === value.data.key)) {
|
||||
return;
|
||||
} else {
|
||||
posts.push(value);
|
||||
@@ -28,16 +28,16 @@ export const getStaticPaths = createGetStaticPaths(async () => {
|
||||
posts = posts.filter((value, index) => index < 20);
|
||||
|
||||
let groupedByTags: Record<string, CollectionEntry<"announcements">[]> = {};
|
||||
posts.forEach(post => {
|
||||
post.data.tags.forEach(tag => {
|
||||
if (!groupedByTags[tag]) {
|
||||
groupedByTags[tag] = [];
|
||||
posts.forEach((post) => {
|
||||
post.data.tags.forEach((tag) => {
|
||||
if (!groupedByTags[tag.toLowerCase()]) {
|
||||
groupedByTags[tag.toLowerCase()] = [];
|
||||
}
|
||||
groupedByTags[tag].push(post);
|
||||
groupedByTags[tag.toLowerCase()].push(post);
|
||||
});
|
||||
});
|
||||
|
||||
return Object.keys(groupedByTags).map(tag => ({
|
||||
return Object.keys(groupedByTags).map((tag) => ({
|
||||
params: {
|
||||
tag: tag,
|
||||
},
|
||||
@@ -53,19 +53,21 @@ interface Props {
|
||||
tag: string;
|
||||
}
|
||||
|
||||
const {posts, tag} = Astro.props;
|
||||
const { posts, tag } = Astro.props;
|
||||
---
|
||||
|
||||
<PageLayout title={t("tag.title", {tag: capitalize(tag)})}>
|
||||
<PageLayout title={t("tag.title", { tag: capitalize(tag) })}>
|
||||
<div class="pb-2">
|
||||
<a class="flex gap-2 items-center" href={l("/ankuendigungen")}>
|
||||
<ArrowLeftOutline />
|
||||
<TagComponent tag={tag} noLink="true" transition:name={`${tag}-tag-filter`}/>
|
||||
<TagComponent tag={tag} noLink="true" transition:name={`${tag}-tag-filter`} />
|
||||
</a>
|
||||
</div>
|
||||
{posts.map((post, index) => (
|
||||
<div>
|
||||
<PostComponent post={post}/>
|
||||
</div>
|
||||
))}
|
||||
</PageLayout>
|
||||
{
|
||||
posts.map((post, index) => (
|
||||
<div>
|
||||
<PostComponent post={post} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</PageLayout>
|
||||
|
||||
85
src/pages/events/[slug].astro
Normal file
85
src/pages/events/[slug].astro
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
import type { ExtendedEvent } from "@components/types/event";
|
||||
import PageLayout from "@layouts/PageLayout.astro";
|
||||
import { astroI18n, createGetStaticPaths } from "astro-i18n";
|
||||
import { getCollection, type CollectionEntry } from "astro:content";
|
||||
import EventFights from "@components/event/EventFights.svelte";
|
||||
|
||||
export const getStaticPaths = createGetStaticPaths(async () => {
|
||||
const events = await Promise.all(
|
||||
(await getCollection("events")).map(async (event) => ({
|
||||
event: (await fetch(import.meta.env.PUBLIC_API_SERVER + "/events/" + event.data.eventId).then((value) => value.json())) as ExtendedEvent,
|
||||
page: event,
|
||||
}))
|
||||
);
|
||||
|
||||
return events.map((event) => ({
|
||||
props: {
|
||||
event: event.event,
|
||||
page: event.page,
|
||||
},
|
||||
params: {
|
||||
slug: event.page.slug,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
const { event, page } = Astro.props as { event: ExtendedEvent; page: CollectionEntry<"events"> };
|
||||
|
||||
const { Content } = await page.render();
|
||||
---
|
||||
|
||||
<PageLayout title={event.event.name} wide={true}>
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">{event.event.name}</h1>
|
||||
<h2 class="text-md text-gray-300 mb-4">
|
||||
{
|
||||
new Date(event.event.start).toLocaleDateString(astroI18n.locale, {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
{
|
||||
new Date(event.event.start).toDateString() !== new Date(event.event.end).toDateString()
|
||||
? ` - ${new Date(event.event.end).toLocaleDateString(astroI18n.locale, {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
})}`
|
||||
: ""
|
||||
}
|
||||
</h2>
|
||||
</div>
|
||||
<article>
|
||||
<Content />
|
||||
</article>
|
||||
{
|
||||
page.data.viewConfig && (
|
||||
<div class="py-2 border-t border-t-gray-600">
|
||||
<h1 class="text-2xl font-bold mb-4">Kampfplan</h1>
|
||||
<EventFights viewConfig={page.data.viewConfig} event={event} client:load />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</PageLayout>
|
||||
|
||||
<style is:global>
|
||||
article {
|
||||
> * {
|
||||
all: revert;
|
||||
}
|
||||
|
||||
code {
|
||||
@apply dark:text-neutral-400 text-neutral-800;
|
||||
}
|
||||
|
||||
pre.astro-code {
|
||||
@apply w-fit p-4 rounded-md border-2 border-gray-600 my-4;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-neutral-800 dark:text-neutral-400 hover:underline;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
36
src/pages/events/index.astro
Normal file
36
src/pages/events/index.astro
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
import type { ExtendedEvent } from "@components/types/event";
|
||||
import PageLayout from "@layouts/PageLayout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
const events = await Promise.all(
|
||||
(await getCollection("events")).map(async (event) => ({
|
||||
...event,
|
||||
data: {
|
||||
...event.data,
|
||||
event: (await fetch(import.meta.env.PUBLIC_API_SERVER + "/events/" + event.data.eventId).then((value) => value.json())) as ExtendedEvent,
|
||||
},
|
||||
}))
|
||||
);
|
||||
---
|
||||
|
||||
<PageLayout title="Events">
|
||||
{
|
||||
events.map((event) => (
|
||||
<article class="mb-8">
|
||||
<h2 class="text-2xl font-bold mb-2">
|
||||
<a href={`/events/${event.slug}/`} class="text-blue-600 hover:underline">
|
||||
{event.data.event.event.name ?? "Hello, World!"}
|
||||
</a>
|
||||
</h2>
|
||||
<p class="text-gray-600 mb-1">
|
||||
{new Date(event.data.event.event.start).toLocaleDateString(undefined, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</p>
|
||||
</article>
|
||||
))
|
||||
}
|
||||
</PageLayout>
|
||||
Reference in New Issue
Block a user