Compare commits
7 Commits
9012debcfe
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba8bb0b023 | ||
|
|
badbfd7a74 | ||
|
|
8b40305b22 | ||
|
|
f6c44128b3 | ||
|
|
72137466dc | ||
|
|
ec2f7704e7 | ||
|
|
408afdfd1f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ dist/
|
|||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
|||||||
5349
package-lock.json
generated
5349
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-json/mdi": "^1.2.3",
|
"@iconify-json/mdi": "^1.2.3",
|
||||||
"astro": "^6.1.8",
|
"astro": "^6.1.8",
|
||||||
"astro-icon": "^1.1.5"
|
"astro-icon": "^1.1.5",
|
||||||
|
"sweetalert2": "^11.26.24"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/CV.pdf
Normal file
BIN
public/CV.pdf
Normal file
Binary file not shown.
61
src/components/Box.astro
Normal file
61
src/components/Box.astro
Normal 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>
|
||||||
@@ -13,7 +13,7 @@ const navigationHandler = `window.open('${href}', '_blank')`;
|
|||||||
|
|
||||||
<div class="button" onclick={navigationHandler}>
|
<div class="button" onclick={navigationHandler}>
|
||||||
<Icon name={icon} class="icon" />
|
<Icon name={icon} class="icon" />
|
||||||
<a href={href} target="_blank" rel="noreferrer">
|
<a rel="noreferrer">
|
||||||
{label}
|
{label}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
169
src/components/Contact/ContactForm.astro
Normal file
169
src/components/Contact/ContactForm.astro
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
---
|
||||||
|
const { title, description } = Astro.props;
|
||||||
|
|
||||||
|
import Box from "../Box.astro";
|
||||||
|
---
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<form id="contactForm">
|
||||||
|
<input type="text" name="_honey" id="honey" style="display:none;" tabindex="-1" autocomplete="off" />
|
||||||
|
<input type="text" name="name" id="nameInput" class="name"placeholder="John Doe" autocomplete="name" required>
|
||||||
|
<input type="email" name="email" id="emailInput" class="email" placeholder="john@example.com" autocomplete="email" required>
|
||||||
|
<textarea name="message" id="messageInput" placeholder="Your message here" required></textarea>
|
||||||
|
<input type="submit" value="Indienen" class="submitBtn">
|
||||||
|
</form>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
const form = document.getElementById('contactForm') as HTMLFormElement | null;
|
||||||
|
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const nameInput = document.getElementById('nameInput') as HTMLInputElement | null;
|
||||||
|
const emailInput = document.getElementById('emailInput') as HTMLInputElement | null;
|
||||||
|
const messageInput = document.getElementById('messageInput') as HTMLTextAreaElement | null;
|
||||||
|
const honeyInput = document.getElementById('honey') as HTMLInputElement | null;
|
||||||
|
|
||||||
|
if (!nameInput || !emailInput || !messageInput || !honeyInput) {
|
||||||
|
console.error("One or more form fields were not found in the DOM.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('name', nameInput.value);
|
||||||
|
formData.append('email', emailInput.value);
|
||||||
|
formData.append('message', messageInput.value);
|
||||||
|
formData.append('_honey', honeyInput.value);
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Sending...',
|
||||||
|
didOpen: () => Swal.showLoading()
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://contact.herpiederpiee.nl/submit', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.status === 201) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'success',
|
||||||
|
title: 'Success!',
|
||||||
|
text: 'Your message has been received!',
|
||||||
|
});
|
||||||
|
form.reset();
|
||||||
|
} else if (response.status === 429) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Slow down!',
|
||||||
|
text: result.detail,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Oops...',
|
||||||
|
text: result.detail || 'Something went wrong.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Error',
|
||||||
|
text: 'Could not connect to the contact server.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
input, textarea {
|
||||||
|
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
|
||||||
|
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);
|
||||||
|
transition: all 200ms ease;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
height: 3rem !important;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
form textarea {
|
||||||
|
height: 8rem;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, textarea {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
14
src/components/Contact/ContactImage.astro
Normal file
14
src/components/Contact/ContactImage.astro
Normal 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>
|
||||||
43
src/components/Contact/Page.astro
Normal file
43
src/components/Contact/Page.astro
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
import TitleCard from '../TitleCard.astro';
|
||||||
|
import ContactForm from './ContactForm.astro';
|
||||||
|
import ContactImage from './ContactImage.astro';
|
||||||
|
import Section from '../Section.astro';
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<Section className="contact-section" label="Contact">
|
||||||
|
<TitleCard title="Contact" description="Neem gerust contact met me op!"/>
|
||||||
|
<div class="formContaner">
|
||||||
|
<ContactForm />
|
||||||
|
<div class="imgContainer">
|
||||||
|
<ContactImage />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.formContaner {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
column-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 40vw;
|
||||||
|
height: 400px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: collapse to one column,
|
||||||
|
so "Kennis" ends up fully under contact */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.imgContainer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formContaner {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
96
src/components/Experience/ExperienceBox.astro
Normal file
96
src/components/Experience/ExperienceBox.astro
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
const { experience } = Astro.props;
|
||||||
|
import Box from "../Box.astro";
|
||||||
|
const color: string = experience.color
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Box accent={color}>
|
||||||
|
<div class="titleRow">
|
||||||
|
<div class="titleContainer">
|
||||||
|
<h3 class="main-title">{experience.name}</h3>
|
||||||
|
<p class="sub-title">{experience.role}</p>
|
||||||
|
|
||||||
|
<p class="description">
|
||||||
|
{experience.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dateContainer">
|
||||||
|
<p>
|
||||||
|
{experience.start.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })}
|
||||||
|
-
|
||||||
|
{experience.end
|
||||||
|
? experience.end.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })
|
||||||
|
: 'Nu'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<style define:vars={{color}}>
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleRow {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0 0 0.3rem 0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dateContainer {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dateContainer p {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.titleRow {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dateContainer p {
|
||||||
|
font-size: 0.74rem;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
34
src/components/Experience/Page.astro
Normal file
34
src/components/Experience/Page.astro
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
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());
|
||||||
|
---
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.experience-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.3rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: collapse to one column,
|
||||||
|
so "Kennis" ends up fully under experience */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.experience-section {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -56,6 +56,7 @@ const clipboardHandler = `navigator.clipboard.writeText('${safeEmail}').then(()=
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 24px 65px rgba(0, 0, 0, 0.22);
|
box-shadow: 0 24px 65px rgba(0, 0, 0, 0.22);
|
||||||
width: min(15rem, 100%);
|
width: min(15rem, 100%);
|
||||||
|
transition: all .3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.email-button:hover .label {
|
.email-button:hover .label {
|
||||||
|
|||||||
@@ -1,55 +1,32 @@
|
|||||||
---
|
---
|
||||||
|
import { siteData } from '../../data/site';
|
||||||
import {age} from "../../utils/getAge"
|
import {age} from "../../utils/getAge"
|
||||||
import {study} from "../../utils/getCurrentStudy"
|
|
||||||
|
|
||||||
import hero_img from "../../assets/hero.png"
|
import Button from "../Button.astro"
|
||||||
|
|
||||||
import Button from "./Button.astro"
|
|
||||||
import EmailButton from "./EmailButton.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">
|
<div class="titleContainer">
|
||||||
<h1 class="main-title">Valentijn van der Jagt</h1>
|
<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>
|
||||||
|
|
||||||
<div class="buttonContainer">
|
<div class="buttonContainer">
|
||||||
<Button icon="mdi:git" label="Gitea" href="https://git.herpiederpiee.nl/valentijn?tab=repositories"/>
|
{socials.map((link) => (
|
||||||
<Button icon="mdi:linkedin" label="LinkedIn" href="https://nl.linkedin.com/in/valentijn-van-der-jagt"/>
|
<Button icon={link.icon} label={link.platform} href={link.url} />
|
||||||
|
))}
|
||||||
<EmailButton icon="mdi:email-outline" label="E-mail" />
|
<EmailButton icon="mdi:email-outline" label="E-mail" />
|
||||||
</div>
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<style>
|
||||||
</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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -105,10 +82,6 @@ const hero_img_src = `url(${hero_img.src})`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
#container {
|
|
||||||
padding: 1.5rem 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
@@ -122,10 +95,6 @@ const hero_img_src = `url(${hero_img.src})`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
#container {
|
|
||||||
min-height: 360px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.8rem;
|
gap: 0.8rem;
|
||||||
|
|||||||
35
src/components/Projects/Page.astro
Normal file
35
src/components/Projects/Page.astro
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
import { siteData } from '../../data/site';
|
||||||
|
import ProjectBox from './ProjectBox.astro';
|
||||||
|
import TitleCard from '../TitleCard.astro';
|
||||||
|
import Section from '../Section.astro';
|
||||||
|
|
||||||
|
const projects = [...siteData.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>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.projects-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.3rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: collapse to one column,
|
||||||
|
so "Kennis" ends up fully under studies */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.projects-section {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
102
src/components/Projects/ProjectBox.astro
Normal file
102
src/components/Projects/ProjectBox.astro
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
---
|
||||||
|
const { project } = Astro.props;
|
||||||
|
import {techColors} from "../../data/site"
|
||||||
|
|
||||||
|
|
||||||
|
const colors: { [id: string] : string; } = {...techColors}
|
||||||
|
|
||||||
|
import Button from "../Button.astro";
|
||||||
|
import Box from "../Box.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<div class="titleRow">
|
||||||
|
<div class="titleContainer">
|
||||||
|
|
||||||
|
<h3 class="main-title">{project.title}</h3>
|
||||||
|
<p class="sub-title">{project.description}</p>
|
||||||
|
<div class="tech-stack">
|
||||||
|
{project.tech.map((item: string) => {
|
||||||
|
const color = colors[item] || colors["Default"];
|
||||||
|
return (
|
||||||
|
<span class="tech-badge" style={`--brand-color: ${color}`}>
|
||||||
|
{item}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button label="Bekijk Project" href={project.link} icon="mdi:link"/>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.titleRow {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0 0 0.3rem 0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.6rem;
|
||||||
|
margin-top: 1rem; /* Space between tags and title */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-badge {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
padding: 0.2rem 0.6rem;
|
||||||
|
|
||||||
|
/* The Magic: Using the variable from the style prop */
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border: 1.5px solid var(--brand-color);
|
||||||
|
color: var(--brand-color);
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep your existing media queries */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.titleRow {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure badges don't get too small on mobile */
|
||||||
|
.tech-stack {
|
||||||
|
margin-top: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
74
src/components/Section.astro
Normal file
74
src/components/Section.astro
Normal 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>
|
||||||
@@ -1,90 +1,23 @@
|
|||||||
---
|
---
|
||||||
import { siteData } from '../../data/site';
|
import { siteData } from '../../data/site';
|
||||||
import StudyBox from './StudyBox.astro';
|
import StudyBox from './StudyBox.astro';
|
||||||
import StudyBoxTitle from './StudyBoxTitle.astro';
|
import TitleCard from '../TitleCard.astro';
|
||||||
import SkillChart from './SkillChart.astro';
|
import Section from '../Section.astro';
|
||||||
|
|
||||||
const studies = [...siteData.studies];
|
const studies = [...siteData.studies];
|
||||||
const sortedStudies = studies.sort((a, b) => b.start.getTime() - a.start.getTime());
|
const sortedStudies = studies.sort((a, b) => b.start.getTime() - a.start.getTime());
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id="container">
|
<Section className="studies-section" label="Studies">
|
||||||
<div class="left-column">
|
|
||||||
<aside class="sidebar-card">
|
|
||||||
<h2>Kennis</h2>
|
|
||||||
<div class="skillChart">
|
|
||||||
<SkillChart skills={siteData.skills} />
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="studies-section" aria-label="Studies">
|
|
||||||
<div class="studies-list">
|
<div class="studies-list">
|
||||||
<StudyBoxTitle />
|
<TitleCard title="Opleidingen" description="Hieronder zie je alle opleidingen die ik heb gevolgd tot nu toe. "/>
|
||||||
{sortedStudies.map((study) => (
|
{sortedStudies.map((study) => (
|
||||||
<StudyBox study={study} />
|
<StudyBox study={study} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</Section>
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
<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: minmax(220px, 280px) 1fr;
|
|
||||||
gap: 1.5rem;
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-column {
|
|
||||||
position: sticky;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-card {
|
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 1.25rem;
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,0.18);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-card h2 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: clamp(1.4rem, 2vw, 1.9rem);
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-copy {
|
|
||||||
margin: 0.8rem 0 1.2rem 0;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
line-height: 1.55;
|
|
||||||
opacity: 0.82;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skillChart {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.studies-section {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.studies-list {
|
.studies-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -92,22 +25,5 @@ const sortedStudies = studies.sort((a, b) => b.start.getTime() - a.start.getTime
|
|||||||
position: relative;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left-column {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
|
|
||||||
.studies-section {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
---
|
|
||||||
const { skills } = Astro.props;
|
|
||||||
|
|
||||||
interface Skill {
|
|
||||||
name: string;
|
|
||||||
level: number;
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="skills-chart">
|
|
||||||
{skills.map((skill: Skill) => (
|
|
||||||
<div class="skill">
|
|
||||||
<span class="skill-name">{skill.name}</span>
|
|
||||||
<div class="skill-bar">
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
width: `${(skill.level / 10) * 100}%`
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.skills-chart {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill {
|
|
||||||
display: grid;
|
|
||||||
gap: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill-name {
|
|
||||||
font-size: 0.88rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill-bar {
|
|
||||||
height: 8px;
|
|
||||||
background: rgba(255, 255, 255, 0.08);
|
|
||||||
border-radius: 999px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill-bar span {
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: inherit;
|
|
||||||
background: linear-gradient(90deg, #7dd3fc, #38bdf8);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
---
|
---
|
||||||
const { study } = Astro.props;
|
const { study } = Astro.props;
|
||||||
|
import Box from "../Box.astro";
|
||||||
const color: string = study.color
|
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="titleRow">
|
||||||
<div class="titleContainer">
|
<div class="titleContainer">
|
||||||
<h3 class="main-title">{study.study}</h3>
|
<h3 class="main-title">{study.study}</h3>
|
||||||
@@ -24,8 +23,7 @@ const color: string = study.color
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Box>
|
||||||
</div>
|
|
||||||
|
|
||||||
<style define:vars={{color}}>
|
<style define:vars={{color}}>
|
||||||
.container {
|
.container {
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="content">
|
|
||||||
<div class="titleRow">
|
|
||||||
<div class="titleContainer">
|
|
||||||
<h2 class="main-title">Opleidingen</h2>
|
|
||||||
<p class="sub-title">Lorem ipsum dolor, sit amet consectetur adipisicing elit. Consequuntur amet, quos </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.titleContainer {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-title {
|
|
||||||
font-size: clamp(1.4rem,2vw,1.9rem);
|
|
||||||
line-height: 1.2;
|
|
||||||
margin: 0 0 0.3rem 0;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-title {
|
|
||||||
font-size: 0.92rem;
|
|
||||||
line-height: 1.45;
|
|
||||||
margin: 0;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-title {
|
|
||||||
font-size: 0.84rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
53
src/components/TitleCard.astro
Normal file
53
src/components/TitleCard.astro
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
const { title, description } = Astro.props;
|
||||||
|
import Box from "./Box.astro";
|
||||||
|
---
|
||||||
|
<Box isTitle={true}>
|
||||||
|
<div class="titleRow">
|
||||||
|
<div class="titleContainer">
|
||||||
|
<h2 class="main-title">{title}</h2>
|
||||||
|
<p class="sub-title">{description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.titleRow {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: clamp(1.4rem,2vw,1.9rem);
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0 0 0.3rem 0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.titleRow {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,25 +1,51 @@
|
|||||||
export const siteData = {
|
export const siteData = {
|
||||||
|
/*
|
||||||
|
Personal Information
|
||||||
|
*/
|
||||||
name: "Valentijn van der Jagt",
|
name: "Valentijn van der Jagt",
|
||||||
email: "valentijnkijkuit@outlook.com",
|
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"),
|
birthDate: new Date("2006-06-27"),
|
||||||
skills: [
|
|
||||||
|
experience: [
|
||||||
{
|
{
|
||||||
name: "C++ / Arduino",
|
name: "Brightpark",
|
||||||
level: 7
|
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"),
|
||||||
name: "Java",
|
end: new Date("2025-05-1"),
|
||||||
level: 6
|
tech: ["Flutter", "Swift", "Svelte", "Laravel"],
|
||||||
},
|
color: "#30e7ffe9"
|
||||||
{
|
|
||||||
name: "Python",
|
|
||||||
level: 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Web Development",
|
|
||||||
level: 5
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
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" },
|
||||||
|
{ icon: "mdi:document", platform: "CV", url: "/CV.pdf"}
|
||||||
|
],
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
title: "Albert Heijn Bonus Scraper",
|
||||||
|
description: "Een Java-applicatie die via Puppeteer real-time bonusacties scrapt en visualiseert. Bevat een krachtige zoekfunctionaliteit ondersteund door een zelfgeïmplementeerd fuzzy-search algoritme, waardoor er snel en flexibel door de weekelijkse aanbiedingen genavigeerd kan worden.",
|
||||||
|
tech: ["Java", "Puppeteer", "HTML/CSS"],
|
||||||
|
link: "https://git.herpiederpiee.nl/valentijn/Appie-Bonus-Scraper/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: [
|
studies: [
|
||||||
{
|
{
|
||||||
school: "Hogeschool Rotterdam",
|
school: "Hogeschool Rotterdam",
|
||||||
@@ -41,3 +67,18 @@ export const siteData = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const techColors = {
|
||||||
|
"Default": "#888888",
|
||||||
|
"Java": "#ED8B00",
|
||||||
|
"Python": "#3776AB",
|
||||||
|
"HTML/CSS": "#E34F26",
|
||||||
|
"Puppeteer": "#40B5A4",
|
||||||
|
"Tkinter": "#3e759e",
|
||||||
|
"Spotify API": "#1DB954",
|
||||||
|
"C++": "#00599C",
|
||||||
|
"Arduino": "#00979D",
|
||||||
|
"Flutter": "#02569B",
|
||||||
|
"Svelte": "#FF3E00",
|
||||||
|
"Laravel": "#FF2D20",
|
||||||
|
};
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -2,10 +2,16 @@
|
|||||||
import Layout from '../layouts/Layout.astro';
|
import Layout from '../layouts/Layout.astro';
|
||||||
import Hero from '../components/Hero/Page.astro';
|
import Hero from '../components/Hero/Page.astro';
|
||||||
import Studies from "../components/Studies/Page.astro"
|
import Studies from "../components/Studies/Page.astro"
|
||||||
|
import Projects from "../components/Projects/Page.astro"
|
||||||
|
import Experience from "../components/Experience/Page.astro"
|
||||||
|
import Contact from "../components/Contact/Page.astro"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Hero />
|
<Hero />
|
||||||
<Studies/>
|
<Projects/>
|
||||||
|
<Experience/>
|
||||||
|
<!-- <Studies/> -->
|
||||||
|
<Contact/>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -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)
|
|
||||||
Reference in New Issue
Block a user