change work -> qualifications (add education)

This commit is contained in:
Moritz Hölting 2026-03-18 15:38:07 +01:00
parent 90fea1f2f6
commit 6ca99de358
10 changed files with 179 additions and 83 deletions

View File

@ -32,7 +32,7 @@ const { Content, headings } = await render(entry);
<!-- Main content -->
<Container size="md">
<article class="animate content">
<article class="animate content prose dark:prose-invert">
<Content />
</article>
</Container>

View File

@ -13,10 +13,10 @@ const subpath = pathname.match(/[^/]+/g);
<header id="header" class="fixed top-0 w-full h-16 z-50">
<Container size="md">
<div class="relative h-full w-full">
<div
class="absolute left-0 top-1/2 -translate-y-1/2 flex gap-1 font-semibold"
>
<div
class="relative h-full w-full flex flex-row justify-between items-center"
>
<div class="flex gap-1 font-semibold">
<a
href="/"
class="flex gap-1 text-current hover:text-black dark:hover:text-white transition-colors duration-300 ease-in-out"
@ -34,9 +34,7 @@ const subpath = pathname.match(/[^/]+/g);
</a>
</div>
<div
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
>
<div>
<nav
class="hidden md:flex items-center justify-center text-sm gap-1"
>
@ -61,9 +59,7 @@ const subpath = pathname.match(/[^/]+/g);
</nav>
</div>
<div
class="buttons absolute right-0 top-1/2 -translate-y-1/2 flex gap-1"
>
<div class="buttons flex gap-1">
<a
href="/search/"
aria-label="Search blog posts and projects"

View File

@ -7,10 +7,10 @@ export const SITE: Site = {
AUTHOR: "Moritz Hölting",
};
// Work Page
export const WORK: Page = {
TITLE: "Work",
DESCRIPTION: "Places I have worked.",
// Qualifications Page
export const QUALIFICATIONS: Page = {
TITLE: "Work & Education",
DESCRIPTION: "My work and education history.",
};
// Blog Page
@ -38,8 +38,8 @@ export const LINKS: Links = [
HREF: "/",
},
{
TEXT: "Work",
HREF: "/work/",
TEXT: "Qualifications",
HREF: "/qualifications/",
},
{
TEXT: "Blog",

View File

@ -12,6 +12,19 @@ const work = defineCollection({
}),
});
const education = defineCollection({
loader: glob({ base: "./src/content/education", pattern: "**/*.{md,mdx}" }),
schema: z.object({
institution: z.string(),
degree: z.string(),
field: z.string().optional(),
location: z.string().optional(),
finalGrade: z.union([z.string(), z.number()]).optional(),
dateStart: z.date(),
dateEnd: z.union([z.date(), z.string()]),
}),
});
const blog = defineCollection({
loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }),
schema: z.object({
@ -36,4 +49,4 @@ const projects = defineCollection({
}),
});
export const collections = { work, blog, projects };
export const collections = { work, education, blog, projects };

View File

@ -0,0 +1,6 @@
---
institution: "Brede Gymnasium"
degree: "Abitur"
dateStart: 2014-08-01
dateEnd: 2022-06-30
---

View File

@ -0,0 +1,15 @@
---
institution: "Paderborn University"
degree: "Bachelor of Science"
field: "Computer Science"
dateStart: 2022-10-01
dateEnd: 2026-11-06
---
Bachelor's thesis: "[Limitations of the Random Oracle Model](/blog/limitations-of-the-random-oracle-model/)" at the "Codes and Cryptography" research group.
Elective modules:
- Einführung in Kryptographie (Introduction to Cryptography)
- 3D Rendering
- Algorithmische Geometrie (Algorithmic Geometry)
- Gestaltung von Nutzungsschnittstellen (User Interface Design)

View File

@ -0,0 +1,7 @@
---
institution: "Paderborn University"
degree: "Master of Science"
field: "Computer Science"
dateStart: 2025-10-01
dateEnd: "expected sep 2027"
---

View File

@ -2,7 +2,7 @@
company: "Diebold Nixdorf"
role: "Working Student - Infrastructure & Operations"
dateStart: 2024-05-01
dateEnd: 2025-04-30
dateEnd: 2026-10-31
---
At at Diebold Nixdorf, I am responsible for conducting "experiments" with new

View File

@ -0,0 +1,123 @@
---
import { getCollection, render } from "astro:content";
import PageLayout from "@/layouts/PageLayout.astro";
import TopLayout from "@/layouts/TopLayout.astro";
import BottomLayout from "@/layouts/BottomLayout.astro";
import { QUALIFICATIONS } from "@/consts";
const workCollection = await getCollection("work");
const educationCollection = await getCollection("education");
workCollection.sort(
(a, b) =>
new Date(b.data.dateStart).getTime() -
new Date(a.data.dateStart).getTime(),
);
educationCollection.sort(
(a, b) =>
new Date(b.data.dateStart).getTime() -
new Date(a.data.dateStart).getTime(),
);
const work = await Promise.all(
workCollection.map(async (item) => {
const { Content } = await render(item);
return { ...item, Content };
}),
);
const education = await Promise.all(
educationCollection.map(async (item) => {
const { Content } = await render(item);
return { ...item, Content };
}),
);
function formatShortDate(input: Date | string) {
if (typeof input === "string") return input;
const month = input.toLocaleDateString("en-US", {
month: "short",
});
const year = new Date(input).getFullYear();
return `${month} ${year}`;
}
---
<PageLayout
title={QUALIFICATIONS.TITLE}
description={QUALIFICATIONS.DESCRIPTION}
>
<!-- Work -->
<TopLayout>
<div class="animate page-heading font-departure text-2xl">Work</div>
</TopLayout>
<BottomLayout>
<ul class="border-b border-black/10 dark:border-white/25">
{
work.map((entry) => (
<li class="animate border-b border-black/10 dark:border-white/25 mt-4 py-8 first-of-type:mt-0 first-of-type:pt-0 last-of-type:border-none">
<div class="font-departure text-sm uppercase mb-4">
{formatShortDate(entry.data.dateStart)} -{" "}
{formatShortDate(entry.data.dateEnd)}
</div>
<div class="font-departure text-black dark:text-white font-semibold">
{entry.data.company}
</div>
<div class="font-departure text-sm font-semibold">
{entry.data.role}
</div>
{entry.body && (
<article class="prose dark:prose-invert mt-2 pb-8">
<entry.Content />
</article>
)}
</li>
))
}
</ul>
</BottomLayout>
<!-- Education -->
<TopLayout>
<div class="animate page-heading font-departure text-2xl">
Education
</div>
</TopLayout>
<BottomLayout>
<ul>
{
education.map((entry) => (
<li class="animate border-b border-black/10 dark:border-white/25 mt-4 py-8 first-of-type:mt-0 first-of-type:pt-0 last-of-type:border-none">
<div class="font-departure text-sm uppercase mb-4">
{formatShortDate(entry.data.dateStart)} -{" "}
{formatShortDate(entry.data.dateEnd)}
</div>
<div class="font-departure text-black dark:text-white font-semibold">
{entry.data.institution}
</div>
<div class="font-departure text-sm font-semibold">
{entry.data.degree}
{entry.data.field && ` - ${entry.data.field}`}
</div>
{(entry.data.finalGrade || entry.data.location) && (
<div class="text-sm">
{entry.data.finalGrade &&
`Grade: ${
entry.data.finalGrade +
(entry.data.location ? ", " : "")
}`}
{entry.data.location}
</div>
)}
{entry.body && (
<article class="prose dark:prose-invert mt-2 pb-8">
<entry.Content />
</article>
)}
</li>
))
}
</ul>
</BottomLayout>
</PageLayout>

View File

@ -1,64 +0,0 @@
---
import { getCollection, render } from "astro:content";
import PageLayout from "@/layouts/PageLayout.astro";
import TopLayout from "@/layouts/TopLayout.astro";
import BottomLayout from "@/layouts/BottomLayout.astro";
import { WORK } from "@/consts";
const collection = await getCollection("work");
collection.sort(
(a, b) =>
new Date(b.data.dateStart).getTime() -
new Date(a.data.dateStart).getTime(),
);
const work = await Promise.all(
collection.map(async (item) => {
const { Content } = await render(item);
return { ...item, Content };
}),
);
function formatWorkDate(input: Date | string) {
if (typeof input === "string") return input;
const month = input.toLocaleDateString("en-US", {
month: "short",
});
const year = new Date(input).getFullYear();
return `${month} ${year}`;
}
---
<PageLayout title={WORK.TITLE} description={WORK.DESCRIPTION}>
<TopLayout>
<div class="animate page-heading">
{WORK.TITLE}
</div>
</TopLayout>
<BottomLayout>
<ul>
{
work.map((entry) => (
<li class="animate border-b border-black/10 dark:border-white/25 mt-4 py-8 first-of-type:mt-0 first-of-type:pt-0 last-of-type:border-none">
<div class="font-departure text-sm uppercase mb-4">
{formatWorkDate(entry.data.dateStart)} -{" "}
{formatWorkDate(entry.data.dateEnd)}
</div>
<div class="font-departure text-black dark:text-white font-semibold">
{entry.data.company}
</div>
<div class="font-departure text-sm font-semibold">
{entry.data.role}
</div>
<article class="prose dark:prose-invert">
<entry.Content />
</article>
</li>
))
}
</ul>
</BottomLayout>
</PageLayout>