fixed a LOT of repetition

This commit is contained in:
Valentijn
2026-05-15 18:35:47 +02:00
parent f6c44128b3
commit 8b40305b22
15 changed files with 356 additions and 467 deletions

61
src/components/Box.astro Normal file
View File

@@ -0,0 +1,61 @@
---
interface Props {
accent?: string;
isTitle?: boolean
}
const { accent, isTitle } = Astro.props as Props;
let opachity = 0.05;
if (isTitle) {
opachity = 0.2;
}
---
<div class="container">
{accent && <div class="accent" style={`background-color: ${accent}; width:4px; border-radius: 999px; flex-shrink: 0;`}></div>}
<div class="content">
<slot />
</div>
</div>
<style define:vars={{color: `rgba(255,255,255,${opachity})`}}>
.container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
display: flex;
align-items: stretch;
gap: 1rem;
min-height: 120px;
padding: 1rem;
background: var(--color);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
box-shadow: 0 8px 20px rgba(0,0,0,0.18);
color: white;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
margin-top: 0.5rem;
}
.container:hover {
transform: translateY(-1px);
box-shadow: 0 14px 28px rgba(0,0,0,0.24);
border-color: rgba(255,255,255,0.14);
}
.content {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
}
@media (max-width: 768px) {
.container {
min-height: 100px;
padding: 0.85rem;
gap: 0.8rem;
}
}
</style>

View File

@@ -1,78 +1,76 @@
---
const { title, description } = Astro.props;
import Box from "../Box.astro";
---
<div class="container">
<div class="content">
<Box>
<form method="POST" action="https://example.com/example">
<input type="text" name="name" id="nameInput" class="name" placeholder="John Doe" autocomplete="name">
<input type="email" name="email" id="emailInput" class="email" placeholder="john@example.com" autocomplete="email">
<textarea name="message" id="messageInput" placeholder="Your message here"></textarea>
<input type="submit" value="Indienen" class="submitBtn">
</form>
</div>
</div>
</Box>
<style>
.container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
display: flex;
align-items: stretch;
gap: 1rem;
min-height: 120px
padding: 1rem 1rem 1rem 0.9rem;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
box-shadow: 0 8px 20px rgba(0,0,0,0.18);
color: white;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
margin-top: 0.5rem;
padding: 1rem;
}
.container:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.24);
border-color: rgba(255,255,255,0.14);
}
.content {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
}
input, textarea {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
border-radius: 10pt;
padding:10px;
font-size: .8rem;
border-radius: 10px;
color: white;
border: 1px solid rgba(255, 255, 255, 0.14);
background: rgba(18, 18, 28, 0.24);
backdrop-filter: blur(2px);
box-shadow: 0 24px 50px rgba(0, 0, 0, 0.18);
transition: all 200ms ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
font-size: 0.75rem;
padding: 0.8rem 1rem;
}
transition: transform var(--transition-time), border-color var(--transition-time), background var(--transition-time), box-shadow var(--transition-time);
padding: 0.95rem 1.2rem;
input::placeholder, textarea::placeholder {
color: rgba(255, 255, 255, 0.5);
}
input:focus, textarea:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.3);
background: rgba(18, 18, 28, 0.4);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3), 0 0 0 3px rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
input:hover, textarea:hover {
border-color: rgba(255, 255, 255, 0.2);
background: rgba(18, 18, 28, 0.35);
}
.submitBtn {
background: linear-gradient(135deg, rgba(140, 90, 20, 0.4), rgba(96, 67, 8, 0.3));
border: 1px solid rgba(255, 255, 255, 0.15);
color: white;
font-weight: 600;
cursor: pointer;
padding: unset !important;
background: rgba(96, 67, 8, 0.24);
transition: all 200ms ease;
}
.submitBtn:hover {
background: linear-gradient(135deg, rgba(160, 110, 30, 0.5), rgba(116, 87, 18, 0.4));
border-color: rgba(255, 255, 255, 0.25);
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
}
.submitBtn:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
form {
display: flex;
flex-direction: column;
justify-content: start;
gap: .5rem;
width: 50%;
justify-content: flex-start;
width: 100%;
gap: 0.6rem;
}
form input {
@@ -87,15 +85,14 @@ const { title, description } = Astro.props;
@media (max-width: 768px) {
.container {
min-height: 100px;
padding: 0.85rem 0.85rem 0.85rem 0.75rem;
gap: 0.8rem;
}
form {
width: 100%;
gap: 0.6rem;
}
input, textarea {
font-size: 0.75rem;
padding: 0.8rem 1rem;
}
}
</style>

View File

@@ -0,0 +1,14 @@
---
import Box from "../Box.astro";
---
<Box>
<div class="image"></div>
</Box>
<style>
.image {
width: 40vw;
height: 400px;
background: white;
}
</style>

View File

@@ -1,48 +1,43 @@
---
import TitleCard from '../TitleCard.astro';
import ContactForm from './ContactForm.astro';
import ContactImage from './ContactImage.astro';
import Section from '../Section.astro';
---
<div id="container">
<section class="contact-section" aria-label="Contact">
<Section className="contact-section" label="Contact">
<TitleCard title="Contact" description="Neem gerust contact met me op!"/>
<ContactForm/>
</section>
</div>
<div class="formContaner">
<ContactForm />
<div class="imgContainer">
<ContactImage />
</div>
</div>
</Section>
<style>
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
margin: 10px;
padding: clamp(1.5rem, 3vw, 2.5rem);
background:
linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02)),
rgb(74, 74, 74);
border-radius: 20px;
box-shadow: 0 24px 60px rgba(16, 14, 12, 0.18);
overflow: hidden;
.formContaner {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
align-items: start;
grid-template-columns: 1fr 1fr;
column-gap: 1rem;
}
.contact-section {
min-width: 0;
.image {
width: 40vw;
height: 400px;
background: white;
}
/* Mobile: collapse to one column,
so "Kennis" ends up fully under contact */
@media (max-width: 768px) {
#container {
margin: 5px;
padding: 1rem;
.imgContainer {
display: none;
}
.formContaner {
grid-template-columns: 1fr;
gap: 1rem;
}
}
</style>

View File

@@ -1,15 +1,12 @@
---
const { experience } = Astro.props;
import Box from "../Box.astro";
const color: string = experience.color
---
<div class="container">
<div class="accent"></div>
<div class="content">
<Box accent={color}>
<div class="titleRow">
<div class="titleContainer">
<h3 class="main-title">{experience.name}</h3>
@@ -30,40 +27,9 @@ const color: string = experience.color
</p>
</div>
</div>
</div>
</div>
</Box>
<style define:vars={{color}}>
.container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
display: flex;
align-items: stretch;
gap: 1rem;
min-height: 120px;
padding: 1rem 1rem 1rem 0.9rem;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
box-shadow: 0 8px 20px rgba(0,0,0,0.18);
color: white;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
margin-top: 0.5rem;
}
.container:hover {
transform: translateY(-2px);
box-shadow: 0 14px 28px rgba(0,0,0,0.24);
border-color: rgba(255,255,255,0.14);
}
.accent {
width: 4px;
border-radius: 999px;
background: var(--color);
flex-shrink: 0;
}
.content {
flex: 1;
min-width: 0;
@@ -109,12 +75,6 @@ const color: string = experience.color
}
@media (max-width: 768px) {
.container {
min-height: 100px;
padding: 0.85rem 0.85rem 0.85rem 0.75rem;
gap: 0.8rem;
}
.titleRow {
flex-direction: column;
gap: 0.55rem;

View File

@@ -2,44 +2,20 @@
import { siteData } from '../../data/site';
import ExperienceBox from './ExperienceBox.astro';
import TitleCard from '../TitleCard.astro';
import Section from '../Section.astro';
const experience = [...siteData.experience];
const sortedExperience = experience.sort((a, b) => b.start.getTime() - a.start.getTime());
---
<div id="container">
<section class="experience-section" aria-label="Experience">
<Section className="experience-section" label="Ervaring">
<div class="experience-list">
<TitleCard title="Ervaringen" description="Hieronder zie je alle werkervaring die ik tot nu toe hebv opgedaan. "/>
{sortedExperience.map((experience) => (
<ExperienceBox experience={experience} />
))}
</div>
</section>
</div>
</Section>
<style>
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
margin: 10px;
padding: clamp(1.5rem, 3vw, 2.5rem);
background:
linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02)),
rgb(74, 74, 74);
border-radius: 20px;
box-shadow: 0 24px 60px rgba(16, 14, 12, 0.18);
overflow: hidden;
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
align-items: start;
}
.experience-section {
min-width: 0;
}
.experience-list {
display: flex;
@@ -51,13 +27,6 @@ const sortedExperience = experience.sort((a, b) => b.start.getTime() - a.start.g
/* Mobile: collapse to one column,
so "Kennis" ends up fully under experience */
@media (max-width: 768px) {
#container {
margin: 5px;
padding: 1rem;
grid-template-columns: 1fr;
gap: 1rem;
}
.experience-section {
margin-top: 2rem;
}

View File

@@ -1,55 +1,32 @@
---
import { siteData } from '../../data/site';
import {age} from "../../utils/getAge"
import {study} from "../../utils/getCurrentStudy"
import hero_img from "../../assets/hero.png"
import Button from "../Button.astro"
import EmailButton from "./EmailButton.astro"
import Section from '../Section.astro';
const hero_img_src = `url(${hero_img.src})`;
const socials = [...siteData.socials];
const occupation = siteData.occupation;
---
<div id="container">
<Section className="hero-section" label="Hero" isHero={true}>
<div class="titleContainer">
<h1 class="main-title">Valentijn van der Jagt</h1>
<h3 class="sub-title">{age} Jaar - {study?.label}</h3>
<h3 class="sub-title">{age} Jaar - {occupation}</h3>
<h3></h3>
</div>
<div class="buttonContainer">
<Button icon="mdi:git" label="Gitea" href="https://git.herpiederpiee.nl/valentijn?tab=repositories"/>
<Button icon="mdi:linkedin" label="LinkedIn" href="https://nl.linkedin.com/in/valentijn-van-der-jagt"/>
{socials.map((link) => (
<Button icon={link.icon} label={link.platform} href={link.url} />
))}
<EmailButton icon="mdi:email-outline" label="E-mail" />
</div>
</Section>
</div>
<style define:vars={{hero_img_src}}>
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
min-height: clamp(420px, 58vh, 680px);
margin: 10px;
padding: clamp(1.5rem, 3vw, 2.5rem);
background: radial-gradient(circle at 15% 18%, rgba(255, 246, 228, 0.32), transparent 24%),
linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(10, 10, 10, 0.24)),
var(--hero_img_src);
background-size: cover;
background-position: center;
border-radius: 20px;
box-shadow: 0 24px 60px rgba(16, 14, 12, 0.18);
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-start;
animation: driftBackground 16s ease-in-out infinite alternate;
}
<style>
.buttonContainer {
display: flex;
flex-wrap: wrap;
@@ -105,10 +82,6 @@ const hero_img_src = `url(${hero_img.src})`;
}
@media (max-width: 900px) {
#container {
padding: 1.5rem 1.2rem;
}
.buttonContainer {
justify-content: center;
}
@@ -122,10 +95,6 @@ const hero_img_src = `url(${hero_img.src})`;
}
@media (max-width: 600px) {
#container {
min-height: 360px;
}
.buttonContainer {
justify-content: center;
gap: 0.8rem;

View File

@@ -2,44 +2,22 @@
import { siteData } from '../../data/site';
import ProjectBox from './ProjectBox.astro';
import TitleCard from '../TitleCard.astro';
import Section from '../Section.astro';
const projects = [...siteData.projects];
---
<div id="container">
<section class="projects-section" aria-label="Projects">
<Section className="projects-section" label="Projecten">
<div class="projects-list">
<TitleCard title="Projecten" description="Hieronder zie je een aantal van mijn favoriete persoonlijke projecten!"/>
{projects.map((project) => (
<ProjectBox project={project} />
))}
</div>
</section>
</div>
</Section>
<style>
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
margin: 10px;
padding: clamp(1.5rem, 3vw, 2.5rem);
background:
linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02)),
rgb(74, 74, 74);
border-radius: 20px;
box-shadow: 0 24px 60px rgba(16, 14, 12, 0.18);
overflow: hidden;
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
align-items: start;
}
.projects-section {
min-width: 0;
}
.projects-list {
display: flex;
flex-direction: column;
@@ -50,13 +28,6 @@ const projects = [...siteData.projects];
/* Mobile: collapse to one column,
so "Kennis" ends up fully under studies */
@media (max-width: 768px) {
#container {
margin: 5px;
padding: 1rem;
grid-template-columns: 1fr;
gap: 1rem;
}
.projects-section {
margin-top: 2rem;
}

View File

@@ -6,10 +6,10 @@ import {techColors} from "../../data/site"
const colors: { [id: string] : string; } = {...techColors}
import Button from "../Button.astro";
import Box from "../Box.astro";
---
<div class="container">
<div class="content">
<Box>
<div class="titleRow">
<div class="titleContainer">
@@ -28,41 +28,9 @@ import Button from "../Button.astro";
</div>
<Button label="Bekijk Project" href={project.link} icon="mdi:link"/>
</div>
</div>
</div>
</Box>
<style>
/* ... Existing Styles Stay Exactly the Same ... */
.container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
display: flex;
align-items: stretch;
gap: 1rem;
min-height: 120px;
padding: 1rem 1rem 1rem 0.9rem;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
box-shadow: 0 8px 20px rgba(0,0,0,0.18);
color: white;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
margin-top:0.5rem;
}
.container:hover {
transform: translateY(-2px);
box-shadow: 0 14px 28px rgba(0,0,0,0.24);
border-color: rgba(255,255,255,0.14);
}
.content {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
}
.titleRow {
width: 100%;
display: flex;
@@ -113,12 +81,6 @@ import Button from "../Button.astro";
/* Keep your existing media queries */
@media (max-width: 768px) {
.container {
min-height: 100px;
padding: 0.85rem 0.85rem 0.85rem 0.75rem;
gap: 0.8rem;
}
.titleRow {
flex-direction: column;
gap: 0.55rem;

View File

@@ -0,0 +1,74 @@
---
interface Props {
className: string;
label: string;
isHero?: boolean;
}
const { className, label, isHero } = Astro.props;
import hero_img from '../assets/hero.png';
let containerBackground;
if (isHero) {
containerBackground = `radial-gradient(circle at 15% 18%, rgba(255, 246, 228, 0.32), transparent 24%),
linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(10, 10, 10, 0.24)),
url("${hero_img.src}")`;
} else {
containerBackground = `linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02)),
rgb(74, 74, 74)`;
}
---
<div id="container">
<section class={className} aria-label={label}>
<slot/>
</section>
</div>
<style define:vars={{containerBackground}}>
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
margin: 10px;
padding: clamp(1.5rem, 3vw, 2.5rem);
background: var(--containerBackground);
background-size: cover;
background-position: center;
border-radius: 20px;
box-shadow: 0 24px 60px rgba(16, 14, 12, 0.18);
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-start;
animation: driftBackground 10s ease-in-out infinite alternate;
}
/* Mobile: collapse to one column,
so "Kennis" ends up fully under studies */
@media (max-width: 768px) {
#container {
margin: 5px;
padding: 1rem;
grid-template-columns: 1fr;
gap: 1rem;
}
section {
margin-top: 2rem;
}
}
@keyframes driftBackground {
0% { background-position: center top; }
100% { background-position: 12% 62%; }
}
</style>

View File

@@ -2,45 +2,22 @@
import { siteData } from '../../data/site';
import StudyBox from './StudyBox.astro';
import TitleCard from '../TitleCard.astro';
import Section from '../Section.astro';
const studies = [...siteData.studies];
const sortedStudies = studies.sort((a, b) => b.start.getTime() - a.start.getTime());
---
<div id="container">
<section class="studies-section" aria-label="Studies">
<Section className="studies-section" label="Studies">
<div class="studies-list">
<TitleCard title="Opleidingen" description="Hieronder zie je alle opleidingen die ik heb gevolgd tot nu toe. "/>
{sortedStudies.map((study) => (
<StudyBox study={study} />
))}
</div>
</section>
</div>
</Section>
<style>
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
margin: 10px;
padding: clamp(1.5rem, 3vw, 2.5rem);
background:
linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02)),
rgb(74, 74, 74);
border-radius: 20px;
box-shadow: 0 24px 60px rgba(16, 14, 12, 0.18);
overflow: hidden;
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
align-items: start;
}
.studies-section {
min-width: 0;
}
.studies-list {
display: flex;
flex-direction: column;
@@ -48,18 +25,5 @@ const sortedStudies = studies.sort((a, b) => b.start.getTime() - a.start.getTime
position: relative;
}
/* Mobile: collapse to one column,
so "Kennis" ends up fully under studies */
@media (max-width: 768px) {
#container {
margin: 5px;
padding: 1rem;
grid-template-columns: 1fr;
gap: 1rem;
}
.studies-section {
margin-top: 2rem;
}
}
</style>

View File

@@ -1,15 +1,12 @@
---
const { study } = Astro.props;
import Box from "../Box.astro";
const color: string = study.color
---
<div class="container">
<div class="accent" aria-hidden="true"></div>
<div class="content">
<Box accent={color}>
<div class="titleRow">
<div class="titleContainer">
<h3 class="main-title">{study.study}</h3>
@@ -26,8 +23,7 @@ const color: string = study.color
</p>
</div>
</div>
</div>
</div>
</Box>
<style define:vars={{color}}>
.container {

View File

@@ -1,49 +1,17 @@
---
const { title, description } = Astro.props;
import Box from "./Box.astro";
---
<div class="container">
<div class="content">
<Box isTitle={true}>
<div class="titleRow">
<div class="titleContainer">
<h2 class="main-title">{title}</h2>
<p class="sub-title">{description}</p>
</div>
</div>
</div>
</div>
</Box>
<style>
.container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
position: relative;
display: flex;
align-items: stretch;
gap: 1rem;
min-height: 80px;
padding: 1rem 1rem 1rem 0.9rem;
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
box-shadow: 0 8px 20px rgba(0,0,0,0.18);
color: white;
transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}
.container:hover {
transform: translateY(-2px);
box-shadow: 0 14px 28px rgba(0,0,0,0.24);
border-color: rgba(255,255,255,0.14);
}
.content {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
}
.titleRow {
width: 100%;
display: flex;
@@ -72,12 +40,6 @@ const { title, description } = Astro.props;
@media (max-width: 768px) {
.container {
min-height: 100px;
padding: 0.85rem 0.85rem 0.85rem 0.75rem;
gap: 0.8rem;
}
.titleRow {
flex-direction: column;
gap: 0.55rem;

View File

@@ -1,7 +1,32 @@
export const siteData = {
/*
Personal Information
*/
name: "Valentijn van der Jagt",
email: "valentijnkijkuit@outlook.com",
about: "Softwareontwikkelaar met passie voor praktische oplossingen. Ervaring met Java, Python, IoT en moderne webtechnologieën. Momenteel student Technische Informatica aan Hogeschool Rotterdam.",
occupation: "Student Technische Informatica",
birthDate: new Date("2006-06-27"),
experience: [
{
name: "Brightpark",
role: "Stagaire Junior Developer",
description: "Bij Brightpark heb ik mijn eerste ervaring opgedaan in het werkveld van Software Ontwikkeling. Ik heb hier gewerkt aan meerdere projecten. Een van de leukere was een iOS app die helpt bij het meten van huizen door gebruik te maken van de Bosch GLM- series meetapperatuur. Deze app heb ik gemaakt in Flutter, met wat kleine stappen Swift in om de meetapperatuur werkend te krijgen.",
start: new Date("2024-09-10"),
end: new Date("2025-05-1"),
tech: ["Flutter", "Swift", "Svelte", "Laravel"],
color: "#30e7ffe9"
}
],
/*
Online Presence
*/
socials: [
{ icon: "mdi:git", platform: "Gitea", url: "https://git.herpiederpiee.nl/valentijn?tab=repositories" },
{ icon: "mdi:linkedin", platform: "LinkedIn", url: "https://nl.linkedin.com/in/valentijn-van-der-jagt" },
],
projects: [
{
title: "Albert Heijn Bonus Scraper",
@@ -10,12 +35,16 @@ export const siteData = {
link: "https://git.herpiederpiee.nl/valentijn/Appie-Bonus-Scraper/"
},
{
title: "Spotify Windows Intergratie ",
title: "Spotify Windows Integratie ",
description: "Een Python-applicatie die via de Spotify Web API real-time playback-data synchroniseert met een custom Windows Taskbar-widget. Maakt gebruik van Tkinter voor een hardware-versnelde, transparante overlay die naadloos integreert met de Windows Shell-omgeving",
tech: ["Python", "Tkinter", "Spotify API"],
link: "https://git.herpiederpiee.nl/valentijn/Windows-Spotify-Taskbar/"
},
],
/*
Academic Path
*/
studies: [
{
school: "Hogeschool Rotterdam",
@@ -35,17 +64,6 @@ export const siteData = {
end: new Date("2025-06-18"),
color: "#8FE508"
}
],
experience: [
{
name: "Brightpark",
role: "Stagaire Junior Developer",
description: "Bij Brightpark heb ik mijn eerste ervaring opgedaan in het werkveld van Software Ontwikkeling. Ik heb hier gewerkt aan meerdere projecten. Een van de leukere was een iOS app die helpt bij het meten van huizen door gebruik te maken van de Bosch GLM- series meetapperatuur. Deze app heb ik gemaakt in Flutter, met wat kleine stappen Swift in om de meetapperatuur werkend te krijgen.",
start: new Date("2024-09-10"),
end: new Date("2025-05-1"),
tech: ["Flutter", "Swift", "Svelte", "Laravel"],
color: "#30e7ffe9"
}
]
}

View File

@@ -1,23 +0,0 @@
import { siteData } from '../data/site';
type Study = {
school: string;
study: string;
label: string;
start: Date;
end: Date | null;
};
function getStudy(studies: Study[]){
if (studies.length === 0) return null;
return studies.reduce((latest, study) => {
if (latest.end === null) return latest;
if (study.end === null) return study;
return study.start.getTime() > latest.start.getTime() ? study : latest;
});
}
export const study = getStudy(siteData.studies)