diff --git a/README.md b/README.md index df7963a..60f98b3 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,2 @@ -# React + TypeScript + Vite - -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... - }, - }, -]); -``` +# Hi +Nice of you to check out the source of the website. Hopefully you enjoy what you see! \ No newline at end of file diff --git a/assets/Squagon.pdf b/assets/Squagon.pdf new file mode 100644 index 0000000..50c166f Binary files /dev/null and b/assets/Squagon.pdf differ diff --git a/eslint.config.ts b/eslint.config.ts index e684b1f..4d22e49 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,18 +1,18 @@ -import js from "@eslint/js"; -import globals from "globals"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import tseslint from "typescript-eslint"; -import { defineConfig, globalIgnores } from "eslint/config"; +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; +import { defineConfig, globalIgnores } from 'eslint/config'; export default defineConfig([ - globalIgnores(["dist"]), + globalIgnores(['dist', 'node_modules']), { - files: ["**/*.{ts,tsx}"], + files: ['**/*.{ts,tsx}'], extends: [ js.configs.recommended, tseslint.configs.recommended, - reactHooks.configs["recommended-latest"], + reactHooks.configs['recommended-latest'], reactRefresh.configs.vite, ], languageOptions: { diff --git a/index.html b/index.html index b5f8a1c..7cd4d28 100644 --- a/index.html +++ b/index.html @@ -2,10 +2,9 @@ - - cblt-website + Wouter van Veelen
diff --git a/package-lock.json b/package-lock.json index ee4505c..9f5a140 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2412,7 +2412,9 @@ "license": "MIT" }, "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, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d90c503..063190a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "cblt-website", + "name": "webhome", "private": true, "version": "0.0.0", "type": "module", diff --git a/src/App.css b/src/App.css index 7cc43a6..f27c48a 100644 --- a/src/App.css +++ b/src/App.css @@ -81,3 +81,10 @@ body { min-width: 100%; z-index: -100; } + +p { + margin: 0; + + p { + margin-top: 1.5em; + } +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 59defb3..eec37ad 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,8 @@ import { Content } from './layout/Content.tsx'; import { Home } from './pages/Home.tsx'; import { About } from './pages/About.tsx'; import { Career } from './pages/Career.tsx'; +import { Storytelling } from './pages/Storytelling.tsx'; +import { Puzzles } from './pages/Puzzles.tsx'; function App() { return ( @@ -15,6 +17,8 @@ function App() { } /> } /> + } /> + } /> } /> diff --git a/src/components/CareerEntry.tsx b/src/components/CareerEntry.tsx new file mode 100644 index 0000000..80a1d6f --- /dev/null +++ b/src/components/CareerEntry.tsx @@ -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 ( + + {start} + + + {!finalEntry && } + + + + + + ); +} diff --git a/src/components/CollapsableCard.tsx b/src/components/CollapsableCard.tsx index 0f06243..a320b5a 100644 --- a/src/components/CollapsableCard.tsx +++ b/src/components/CollapsableCard.tsx @@ -15,6 +15,7 @@ export function CollapsableCard({ startCollapsed, ...props }: CollapsableCardProps): ReactElement { + const hasContent = children != null; const [collapsed, setCollapsed] = useState(startCollapsed ?? false); const toggleCollapse = () => setCollapsed((oldState) => !oldState); @@ -26,16 +27,22 @@ export function CollapsableCard({ title={header} subheader={subheader} action={ - - {collapsed ? - - : } - + hasContent ? + + {collapsed ? + + : } + + : undefined } /> )} - {children} + {hasContent && ( + + {children} + + )} ); diff --git a/src/components/OutsideLink.tsx b/src/components/OutsideLink.tsx new file mode 100644 index 0000000..4d1205f --- /dev/null +++ b/src/components/OutsideLink.tsx @@ -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 ( + + {children} + + + ); +} diff --git a/src/layout/Content.tsx b/src/layout/Content.tsx index e75fec8..0102d59 100644 --- a/src/layout/Content.tsx +++ b/src/layout/Content.tsx @@ -6,7 +6,14 @@ export type ContentProps = PropsWithChildren; export function Content({ children }: ContentProps): ReactElement { return ( - + theme.palette.text.primary, + }} + > {children} diff --git a/src/layout/TopBar.tsx b/src/layout/TopBar.tsx index 6b2ba2b..7a318e7 100644 --- a/src/layout/TopBar.tsx +++ b/src/layout/TopBar.tsx @@ -2,7 +2,13 @@ import React, { type ReactElement } from 'react'; import { Box, Tab, Tabs } from '@mui/material'; 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 { const { pathname } = useLocation(); diff --git a/src/pages/About.tsx b/src/pages/About.tsx index bd5b83d..033fe5f 100644 --- a/src/pages/About.tsx +++ b/src/pages/About.tsx @@ -1,5 +1,22 @@ import { Fragment, type ReactElement } from 'react'; export function About(): ReactElement { - return Hello, About.; + return ( + +

+

+ 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. +

+

+ 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. +

+
+ ); } diff --git a/src/pages/Career.tsx b/src/pages/Career.tsx index 6120d2e..86f7e6c 100644 --- a/src/pages/Career.tsx +++ b/src/pages/Career.tsx @@ -1,43 +1,153 @@ -import { - Timeline, - TimelineConnector, - TimelineContent, - TimelineDot, - TimelineItem, - TimelineOppositeContent, - TimelineSeparator, -} from '@mui/lab'; -import type { ReactElement } from 'react'; -import { CollapsableCard } from '../components/CollapsableCard.tsx'; +import { Timeline } from '@mui/lab'; +import { Fragment, type ReactElement } from 'react'; +import { Switch, Typography } from '@mui/material'; +import { useToggle } from '../utils.ts'; +import { OutsideLink } from '../components/OutsideLink.tsx'; +import { CareerEntry } from '../components/CareerEntry.tsx'; export function Career(): ReactElement { + const [full, toggleFull] = useToggle(true); return ( - - - 2014 - - - - - - +
+
+ +
+ Include activism and volunteer work +
+
+ + + + {full && ( + + Until 2018, IAPC + + } > - Test - - - - - 10:00 am - - - - - Code - - +

+ IAPC is a computer parts and + service store completely run by volunteers on the campus of the + Universtiy of Twente. +

+

+ During my active years I helped in the committees for logistics, RMA and + PR, as well as being board member for logistics in 2015-2016. +

+ + )} + {full && ( + + Until 2023,{' '} + + D.B.V. de Stretchers + + + } + > +

+ + D.B.V. de Stretchers + {' '} + is a sports association on the University of Twente where all sports are + practiced and the focus is on fun, learning and activities +

+

+ 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 +

+
+ )} + +

+ With courses focused around algorithms, machine learning and data + processing. +

+

+ Completed all required courses, but did not complete the final thesis + because of personal reasons. +

+
+ {full && ( + +

+ The{' '} + + muziekbank + {' '} + 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. +

+

+ While being active I manned the counter to assist customers and was + involved in processing newly bought cds to be added to the collection +

+
+ )} + +

+ Full stack developer on a SaaS project based on Java, Spring Boot, GraphQL, + Typescript and React. +

+

+ 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. +

+
+ {full && ( + + Current, Volleyball association{' '} + Twente '05 + + } + finalEntry={full} + /> + )} + + ); } diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 4920e89..8fd84ce 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,11 +1,42 @@ 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 { return ( - - - Hello, others - + +

Welcome

+

To My Website

+

+

+ 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. +

+

+ 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. +

+

+ 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. +

+ + + + + + + + + + + + +
); } diff --git a/src/pages/Puzzles.tsx b/src/pages/Puzzles.tsx index e69de29..63765de 100644 --- a/src/pages/Puzzles.tsx +++ b/src/pages/Puzzles.tsx @@ -0,0 +1,17 @@ +import { Fragment, type ReactElement } from 'react'; +import { OutsideLink } from '../components/OutsideLink.tsx'; + +export function Puzzles(): ReactElement { + return ( + +

+

+ I have a love for puzzles and puzzle events. In the past I have particapated in and + organized the{' '} + Pandora puzzle event at the + University of Twente. +

+

As such I want to create and share more puzzles here, in time.

+
+ ); +} diff --git a/src/pages/Storytelling.tsx b/src/pages/Storytelling.tsx index e69de29..e235a6e 100644 --- a/src/pages/Storytelling.tsx +++ b/src/pages/Storytelling.tsx @@ -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 ( + +

+

+ I like to consume and tell stories. Besides reading and watching tv and movies, I + also like to create my own stories. +

+

+ I've played my fair share of Dungeons and Dragons and have also DM'ed for a while, + and for the{' '} + Pandora puzzle event I have + also been responsible for or involved with the story aspect during the two times I + was part of the event organisation +

+

+ I've also participated in a small short story writing competition for{' '} + Bellettrie, the + student library of the University of Twente. The theme was 'Fairytale' and you can + read my entry here. +

+
+ ); +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..c1c9543 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,7 @@ +import { useCallback, useState } from 'react'; + +export function useToggle(initialValue?: boolean): [boolean, () => void] { + const [state, setState] = useState(initialValue ?? false); + const toggle = useCallback(() => setState((oldState) => !oldState), []); + return [state, toggle]; +}