Complete english version of site
This commit is contained in:
77
README.md
77
README.md
@@ -1,75 +1,2 @@
|
|||||||
# React + TypeScript + Vite
|
# Hi
|
||||||
|
Nice of you to check out the source of the website. Hopefully you enjoy what you see!
|
||||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
|
||||||
|
|
||||||
Currently, two official plugins are available:
|
|
||||||
|
|
||||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
|
|
||||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
|
||||||
|
|
||||||
## React Compiler
|
|
||||||
|
|
||||||
The React Compiler is enabled on this template. See [this documentation](https://react.dev/learn/react-compiler) for more information.
|
|
||||||
|
|
||||||
Note: This will impact Vite dev & build performances.
|
|
||||||
|
|
||||||
## Expanding the ESLint configuration
|
|
||||||
|
|
||||||
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default defineConfig([
|
|
||||||
globalIgnores(["dist"]),
|
|
||||||
{
|
|
||||||
files: ["**/*.{ts,tsx}"],
|
|
||||||
extends: [
|
|
||||||
// Other configs...
|
|
||||||
|
|
||||||
// Remove tseslint.configs.recommended and replace with this
|
|
||||||
tseslint.configs.recommendedTypeChecked,
|
|
||||||
// Alternatively, use this for stricter rules
|
|
||||||
tseslint.configs.strictTypeChecked,
|
|
||||||
// Optionally, add this for stylistic rules
|
|
||||||
tseslint.configs.stylisticTypeChecked,
|
|
||||||
|
|
||||||
// Other configs...
|
|
||||||
],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
project: ["./tsconfig.node.json", "./tsconfig.app.json"],
|
|
||||||
tsconfigRootDir: import.meta.dirname,
|
|
||||||
},
|
|
||||||
// other options...
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// eslint.config.ts
|
|
||||||
import reactX from "eslint-plugin-react-x";
|
|
||||||
import reactDom from "eslint-plugin-react-dom";
|
|
||||||
|
|
||||||
export default defineConfig([
|
|
||||||
globalIgnores(["dist"]),
|
|
||||||
{
|
|
||||||
files: ["**/*.{ts,tsx}"],
|
|
||||||
extends: [
|
|
||||||
// Other configs...
|
|
||||||
// Enable lint rules for React
|
|
||||||
reactX.configs["recommended-typescript"],
|
|
||||||
// Enable lint rules for React DOM
|
|
||||||
reactDom.configs.recommended,
|
|
||||||
],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
project: ["./tsconfig.node.json", "./tsconfig.app.json"],
|
|
||||||
tsconfigRootDir: import.meta.dirname,
|
|
||||||
},
|
|
||||||
// other options...
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
BIN
assets/Squagon.pdf
Normal file
BIN
assets/Squagon.pdf
Normal file
Binary file not shown.
@@ -1,18 +1,18 @@
|
|||||||
import js from "@eslint/js";
|
import js from '@eslint/js';
|
||||||
import globals from "globals";
|
import globals from 'globals';
|
||||||
import reactHooks from "eslint-plugin-react-hooks";
|
import reactHooks from 'eslint-plugin-react-hooks';
|
||||||
import reactRefresh from "eslint-plugin-react-refresh";
|
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||||
import tseslint from "typescript-eslint";
|
import tseslint from 'typescript-eslint';
|
||||||
import { defineConfig, globalIgnores } from "eslint/config";
|
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||||
|
|
||||||
export default defineConfig([
|
export default defineConfig([
|
||||||
globalIgnores(["dist"]),
|
globalIgnores(['dist', 'node_modules']),
|
||||||
{
|
{
|
||||||
files: ["**/*.{ts,tsx}"],
|
files: ['**/*.{ts,tsx}'],
|
||||||
extends: [
|
extends: [
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
reactHooks.configs["recommended-latest"],
|
reactHooks.configs['recommended-latest'],
|
||||||
reactRefresh.configs.vite,
|
reactRefresh.configs.vite,
|
||||||
],
|
],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<link rel="stylesheet" href="/src/App.css" />
|
<link rel="stylesheet" href="/src/App.css" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>cblt-website</title>
|
<title>Wouter van Veelen</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="background"></div>
|
<div id="background"></div>
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -2412,7 +2412,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "cblt-website",
|
"name": "webhome",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -81,3 +81,10 @@ body {
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
z-index: -100;
|
z-index: -100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
+ p {
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import { Content } from './layout/Content.tsx';
|
|||||||
import { Home } from './pages/Home.tsx';
|
import { Home } from './pages/Home.tsx';
|
||||||
import { About } from './pages/About.tsx';
|
import { About } from './pages/About.tsx';
|
||||||
import { Career } from './pages/Career.tsx';
|
import { Career } from './pages/Career.tsx';
|
||||||
|
import { Storytelling } from './pages/Storytelling.tsx';
|
||||||
|
import { Puzzles } from './pages/Puzzles.tsx';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
@@ -15,6 +17,8 @@ function App() {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route index path='home' element={<Home />} />
|
<Route index path='home' element={<Home />} />
|
||||||
<Route path='career' element={<Career />} />
|
<Route path='career' element={<Career />} />
|
||||||
|
<Route path='storytelling' element={<Storytelling />} />
|
||||||
|
<Route path='puzzles' element={<Puzzles />} />
|
||||||
<Route path='about' element={<About />} />
|
<Route path='about' element={<About />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Content>
|
</Content>
|
||||||
|
|||||||
35
src/components/CareerEntry.tsx
Normal file
35
src/components/CareerEntry.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { type ReactElement, type ReactNode } from 'react';
|
||||||
|
import { CollapsableCard, type CollapsableCardProps } from './CollapsableCard.tsx';
|
||||||
|
import {
|
||||||
|
TimelineConnector,
|
||||||
|
TimelineContent,
|
||||||
|
TimelineDot,
|
||||||
|
TimelineItem,
|
||||||
|
TimelineOppositeContent,
|
||||||
|
TimelineSeparator,
|
||||||
|
} from '@mui/lab';
|
||||||
|
|
||||||
|
export type CareerEntryProps = CollapsableCardProps & {
|
||||||
|
start: ReactNode;
|
||||||
|
finalEntry?: boolean;
|
||||||
|
};
|
||||||
|
export function CareerEntry({
|
||||||
|
finalEntry,
|
||||||
|
startCollapsed = true,
|
||||||
|
variant = 'outlined',
|
||||||
|
start,
|
||||||
|
...props
|
||||||
|
}: CareerEntryProps): ReactElement {
|
||||||
|
return (
|
||||||
|
<TimelineItem>
|
||||||
|
<TimelineOppositeContent color='textPrimary'>{start}</TimelineOppositeContent>
|
||||||
|
<TimelineSeparator>
|
||||||
|
<TimelineDot />
|
||||||
|
{!finalEntry && <TimelineConnector />}
|
||||||
|
</TimelineSeparator>
|
||||||
|
<TimelineContent color='textPrimary'>
|
||||||
|
<CollapsableCard startCollapsed={startCollapsed} variant={variant} {...props} />
|
||||||
|
</TimelineContent>
|
||||||
|
</TimelineItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ export function CollapsableCard({
|
|||||||
startCollapsed,
|
startCollapsed,
|
||||||
...props
|
...props
|
||||||
}: CollapsableCardProps): ReactElement {
|
}: CollapsableCardProps): ReactElement {
|
||||||
|
const hasContent = children != null;
|
||||||
const [collapsed, setCollapsed] = useState(startCollapsed ?? false);
|
const [collapsed, setCollapsed] = useState(startCollapsed ?? false);
|
||||||
const toggleCollapse = () => setCollapsed((oldState) => !oldState);
|
const toggleCollapse = () => setCollapsed((oldState) => !oldState);
|
||||||
|
|
||||||
@@ -26,16 +27,22 @@ export function CollapsableCard({
|
|||||||
title={header}
|
title={header}
|
||||||
subheader={subheader}
|
subheader={subheader}
|
||||||
action={
|
action={
|
||||||
<IconButton onClick={toggleCollapse}>
|
hasContent ?
|
||||||
{collapsed ?
|
<IconButton onClick={toggleCollapse}>
|
||||||
<ArrowDownward />
|
{collapsed ?
|
||||||
: <ArrowUpward />}
|
<ArrowDownward />
|
||||||
</IconButton>
|
: <ArrowUpward />}
|
||||||
|
</IconButton>
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Collapse in={!collapsed}>
|
<Collapse in={!collapsed}>
|
||||||
<CardContent>{children}</CardContent>
|
{hasContent && (
|
||||||
|
<CardContent sx={{ fontSize: '14px', textAlign: 'left' }}>
|
||||||
|
{children}
|
||||||
|
</CardContent>
|
||||||
|
)}
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
12
src/components/OutsideLink.tsx
Normal file
12
src/components/OutsideLink.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { OpenInNewOutlined } from '@mui/icons-material';
|
||||||
|
import { Link, type LinkProps } from '@mui/material';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
|
||||||
|
export function OutsideLink({ children, target = '_blank', ...props }: LinkProps): ReactElement {
|
||||||
|
return (
|
||||||
|
<Link {...props} target={target}>
|
||||||
|
{children}
|
||||||
|
<OpenInNewOutlined fontSize='inherit' />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,7 +6,14 @@ export type ContentProps = PropsWithChildren;
|
|||||||
|
|
||||||
export function Content({ children }: ContentProps): ReactElement {
|
export function Content({ children }: ContentProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ width: { xs: '100%', md: '80%' }, margin: 'auto' }}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: { xs: '100%', md: '80%' },
|
||||||
|
margin: 'auto',
|
||||||
|
height: 'calc(100vh - 25px)',
|
||||||
|
color: (theme) => theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TopBar />
|
<TopBar />
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ import React, { type ReactElement } from 'react';
|
|||||||
import { Box, Tab, Tabs } from '@mui/material';
|
import { Box, Tab, Tabs } from '@mui/material';
|
||||||
import { useLocation, useNavigate } from 'react-router';
|
import { useLocation, useNavigate } from 'react-router';
|
||||||
|
|
||||||
const ValidTabs = ['home', 'career', 'about'] as const satisfies string[];
|
const ValidTabs = [
|
||||||
|
'home',
|
||||||
|
'career',
|
||||||
|
'storytelling',
|
||||||
|
'puzzles',
|
||||||
|
'about',
|
||||||
|
] as const satisfies string[];
|
||||||
|
|
||||||
export function TopBar(): ReactElement {
|
export function TopBar(): ReactElement {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
import { Fragment, type ReactElement } from 'react';
|
import { Fragment, type ReactElement } from 'react';
|
||||||
|
|
||||||
export function About(): ReactElement {
|
export function About(): ReactElement {
|
||||||
return <Fragment>Hello, About.</Fragment>;
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<p />
|
||||||
|
<p>
|
||||||
|
This website is by and about me, Wouter van Veelen. The reason for having this
|
||||||
|
website is three-fold. First, I want to have a place where I can experiment with web
|
||||||
|
techniques and frameworks. Secondly, I want a place where I can publicly share
|
||||||
|
creative works. Third, I wanted to delete my LinkedIn, without becoming completely
|
||||||
|
unfindable for potential employers or collaborators.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
About the technical specs, this website built using Vite + React (compiler) and
|
||||||
|
served by an Apache2 web server running on a self-built Arch-linux server. The
|
||||||
|
storage on the server runs on zfs in a 3+1 HDD configuration with an NVME boot
|
||||||
|
drive.
|
||||||
|
</p>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,153 @@
|
|||||||
import {
|
import { Timeline } from '@mui/lab';
|
||||||
Timeline,
|
import { Fragment, type ReactElement } from 'react';
|
||||||
TimelineConnector,
|
import { Switch, Typography } from '@mui/material';
|
||||||
TimelineContent,
|
import { useToggle } from '../utils.ts';
|
||||||
TimelineDot,
|
import { OutsideLink } from '../components/OutsideLink.tsx';
|
||||||
TimelineItem,
|
import { CareerEntry } from '../components/CareerEntry.tsx';
|
||||||
TimelineOppositeContent,
|
|
||||||
TimelineSeparator,
|
|
||||||
} from '@mui/lab';
|
|
||||||
import type { ReactElement } from 'react';
|
|
||||||
import { CollapsableCard } from '../components/CollapsableCard.tsx';
|
|
||||||
|
|
||||||
export function Career(): ReactElement {
|
export function Career(): ReactElement {
|
||||||
|
const [full, toggleFull] = useToggle(true);
|
||||||
return (
|
return (
|
||||||
<Timeline position='alternate'>
|
<Fragment>
|
||||||
<TimelineItem>
|
<div
|
||||||
<TimelineOppositeContent color='textPrimary'>2014</TimelineOppositeContent>
|
style={{
|
||||||
<TimelineSeparator>
|
display: 'flex',
|
||||||
<TimelineDot />
|
flexDirection: 'row',
|
||||||
<TimelineConnector />
|
justifyContent: 'center',
|
||||||
</TimelineSeparator>
|
alignItems: 'center',
|
||||||
<TimelineContent color='textPrimary'>
|
}}
|
||||||
<CollapsableCard
|
>
|
||||||
startCollapsed
|
<div style={{ width: '100%' }} />
|
||||||
variant='outlined'
|
<Switch checked={full} onChange={toggleFull} />
|
||||||
header='BSc. Computer Science'
|
<div style={{ width: '100%' }}>
|
||||||
subheader='University of Twente.'
|
<Typography color='textPrimary'>Include activism and volunteer work</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Timeline position='alternate'>
|
||||||
|
<CareerEntry
|
||||||
|
key='bsc'
|
||||||
|
start='Sep. 2014'
|
||||||
|
header='BSc. Computer Science'
|
||||||
|
subheader='Until 2018, University of Twente.'
|
||||||
|
/>
|
||||||
|
|
||||||
|
{full && (
|
||||||
|
<CareerEntry
|
||||||
|
key='iapc'
|
||||||
|
start='Oct. 2014'
|
||||||
|
header='Volunteer and Board Member'
|
||||||
|
subheader={
|
||||||
|
<Fragment>
|
||||||
|
Until 2018, <OutsideLink href='iapc.nl'>IAPC</OutsideLink>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Test
|
<p>
|
||||||
</CollapsableCard>
|
<OutsideLink href='iapc.nl'>IAPC</OutsideLink> is a computer parts and
|
||||||
</TimelineContent>
|
service store completely run by volunteers on the campus of the
|
||||||
</TimelineItem>
|
Universtiy of Twente.
|
||||||
<TimelineItem>
|
</p>
|
||||||
<TimelineOppositeContent color='textPrimary'>10:00 am</TimelineOppositeContent>
|
<p>
|
||||||
<TimelineSeparator>
|
During my active years I helped in the committees for logistics, RMA and
|
||||||
<TimelineDot />
|
PR, as well as being board member for logistics in 2015-2016.
|
||||||
<TimelineConnector />
|
</p>
|
||||||
</TimelineSeparator>
|
</CareerEntry>
|
||||||
<TimelineContent color='textPrimary'>Code</TimelineContent>
|
)}
|
||||||
</TimelineItem>
|
{full && (
|
||||||
</Timeline>
|
<CareerEntry
|
||||||
|
key='stretchers'
|
||||||
|
start='Oct. 2015'
|
||||||
|
header='Trainer, Volunteer and Board Member'
|
||||||
|
subheader={
|
||||||
|
<Fragment>
|
||||||
|
Until 2023,{' '}
|
||||||
|
<OutsideLink href='https://stretchers.nl'>
|
||||||
|
D.B.V. de Stretchers
|
||||||
|
</OutsideLink>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<OutsideLink href='https://stretchers.nl'>
|
||||||
|
D.B.V. de Stretchers
|
||||||
|
</OutsideLink>{' '}
|
||||||
|
is a sports association on the University of Twente where all sports are
|
||||||
|
practiced and the focus is on fun, learning and activities
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
During my active years I was a trainer and assisted in the events
|
||||||
|
committee as well as PR and the website. During the academic year
|
||||||
|
2020-2021 I was the chairman of the association
|
||||||
|
</p>
|
||||||
|
</CareerEntry>
|
||||||
|
)}
|
||||||
|
<CareerEntry
|
||||||
|
key='msc'
|
||||||
|
start='Sep. 2018'
|
||||||
|
header='Master Data Science'
|
||||||
|
subheader='Unfinished, Until 2021, University of Twente.'
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
With courses focused around algorithms, machine learning and data
|
||||||
|
processing.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Completed all required courses, but did not complete the final thesis
|
||||||
|
because of personal reasons.
|
||||||
|
</p>
|
||||||
|
</CareerEntry>
|
||||||
|
{full && (
|
||||||
|
<CareerEntry
|
||||||
|
key='muziekbank'
|
||||||
|
start='Feb. 2022'
|
||||||
|
header='Volunteer Muziekbank'
|
||||||
|
subheader='Until 2023, Muziekbank Enschede'
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
The{' '}
|
||||||
|
<OutsideLink href='http://muziekbank.nl' target='_blank'>
|
||||||
|
muziekbank
|
||||||
|
</OutsideLink>{' '}
|
||||||
|
is a library of vinyls and cds where customers can go to discover new
|
||||||
|
and old music, experience event and rent items like a normal library.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
While being active I manned the counter to assist customers and was
|
||||||
|
involved in processing newly bought cds to be added to the collection
|
||||||
|
</p>
|
||||||
|
</CareerEntry>
|
||||||
|
)}
|
||||||
|
<CareerEntry
|
||||||
|
key='cofano'
|
||||||
|
start='Nov. 2022'
|
||||||
|
header='Full Stack Developer'
|
||||||
|
subheader='Current, Cofano'
|
||||||
|
finalEntry={!full}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Full stack developer on a SaaS project based on Java, Spring Boot, GraphQL,
|
||||||
|
Typescript and React.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Personally dove deep in understanding the fundamentals of frameworks and
|
||||||
|
languages and applying them for major version upgrades and developing on
|
||||||
|
internal tools and libraries.
|
||||||
|
</p>
|
||||||
|
</CareerEntry>
|
||||||
|
{full && (
|
||||||
|
<CareerEntry
|
||||||
|
start='Apr. 2025'
|
||||||
|
header='Trainer and Technical Committee'
|
||||||
|
subheader={
|
||||||
|
<Fragment>
|
||||||
|
Current, Volleyball association{' '}
|
||||||
|
<OutsideLink href='https://twente05.nl'>Twente '05</OutsideLink>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
finalEntry={full}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Timeline>
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,42 @@
|
|||||||
import { type ReactElement } from 'react';
|
import { type ReactElement } from 'react';
|
||||||
import { Card, CardContent, CardHeader } from '@mui/material';
|
import { Link, Stack, Tooltip } from '@mui/material';
|
||||||
|
import { GitHub, Source } from '@mui/icons-material';
|
||||||
|
|
||||||
export function Home(): ReactElement {
|
export function Home(): ReactElement {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Stack width='100%' justifyContent='center' textAlign='center' direction='column'>
|
||||||
<CardHeader title='Home'></CardHeader>
|
<h1>Welcome</h1>
|
||||||
<CardContent>Hello, others</CardContent>
|
<h2>To My Website</h2>
|
||||||
</Card>
|
<p />
|
||||||
|
<p>
|
||||||
|
On this website you can find bits about me and bits by me. I am a software developer
|
||||||
|
and the career tab can tell you some of my history in the world. As proof of being a
|
||||||
|
software developer, this website is entirely self-hosted, including a git repository
|
||||||
|
(and except the DNS servers, I suppose), on a server that I have physically built
|
||||||
|
and maintain.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Besides being a software developer, I also like stories and puzzles a whole lot, so
|
||||||
|
you can also find tabs dedicated to those. Hope you enjoy! I hope to add more
|
||||||
|
puzzle-focused elements to the site in the future.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Use the top bar to find what you are looking for or contact me via email. No direct
|
||||||
|
mailto link here to avoid scraper bots, but my first name and this domain name
|
||||||
|
should do the trick.
|
||||||
|
</p>
|
||||||
|
<Stack marginTop='20px' direction='row' spacing={2} justifyContent='center'>
|
||||||
|
<Link href='https://github.com/WouterLVV' target='_blank'>
|
||||||
|
<Tooltip title='GitHub'>
|
||||||
|
<GitHub />
|
||||||
|
</Tooltip>
|
||||||
|
</Link>
|
||||||
|
<Link href='https://git.cblt.xyz/wouter/WebHome' target='_blank'>
|
||||||
|
<Tooltip title='source'>
|
||||||
|
<Source />
|
||||||
|
</Tooltip>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Fragment, type ReactElement } from 'react';
|
||||||
|
import { OutsideLink } from '../components/OutsideLink.tsx';
|
||||||
|
|
||||||
|
export function Puzzles(): ReactElement {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<p />
|
||||||
|
<p>
|
||||||
|
I have a love for puzzles and puzzle events. In the past I have particapated in and
|
||||||
|
organized the{' '}
|
||||||
|
<OutsideLink href='https://iapandora.nl'>Pandora puzzle event</OutsideLink> at the
|
||||||
|
University of Twente.
|
||||||
|
</p>
|
||||||
|
<p>As such I want to create and share more puzzles here, in time.</p>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Fragment, type ReactElement } from 'react';
|
||||||
|
import { Card, CardContent } from '@mui/material';
|
||||||
|
import { OutsideLink } from '../components/OutsideLink.tsx';
|
||||||
|
|
||||||
|
export function Storytelling(): ReactElement {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<p />
|
||||||
|
<p>
|
||||||
|
I like to consume and tell stories. Besides reading and watching tv and movies, I
|
||||||
|
also like to create my own stories.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I've played my fair share of Dungeons and Dragons and have also DM'ed for a while,
|
||||||
|
and for the{' '}
|
||||||
|
<OutsideLink href='https://iapandora.nl'>Pandora puzzle event</OutsideLink> I have
|
||||||
|
also been responsible for or involved with the story aspect during the two times I
|
||||||
|
was part of the event organisation
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I've also participated in a small short story writing competition for{' '}
|
||||||
|
<OutsideLink href='https://bellettrie.utwente.nl'>Bellettrie</OutsideLink>, the
|
||||||
|
student library of the University of Twente. The theme was 'Fairytale' and you can
|
||||||
|
read my entry <OutsideLink href='assets/Squagon.pdf'>here</OutsideLink>.
|
||||||
|
</p>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
7
src/utils.ts
Normal file
7
src/utils.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
export function useToggle(initialValue?: boolean): [boolean, () => void] {
|
||||||
|
const [state, setState] = useState<boolean>(initialValue ?? false);
|
||||||
|
const toggle = useCallback(() => setState((oldState) => !oldState), []);
|
||||||
|
return [state, toggle];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user