diff --git a/src/components/Section.tsx b/src/components/Section.tsx
index 0573d63..38c4ea9 100644
--- a/src/components/Section.tsx
+++ b/src/components/Section.tsx
@@ -3,12 +3,12 @@ import { mergeSxProps } from '../utils'
export type SectionProps = {
- title: string
+ title?: string
} & StackProps
export const Section = ({title, children, sx, ...rest}: SectionProps) => (
- {title}
+ {title && {title}}
{children}
)
diff --git a/src/configs/projects.json b/src/configs/projects.json
new file mode 100644
index 0000000..7bf3a4f
--- /dev/null
+++ b/src/configs/projects.json
@@ -0,0 +1,87 @@
+{
+ "Pie-Hub": {
+ "relevancy": [
+ "fe",
+ "be"
+ ],
+ "date": {
+ "start": 2021
+ },
+ "tech": [
+ "Raspberry Pi 4",
+ "General Purpose I/O",
+ "I2C",
+ "Python",
+ "Bash",
+ "UNIX services",
+ "Node.js",
+ "TypeScript"
+ ],
+ "summary": "React app with back end running on a Raspberry Pi. Shows useful information in one place including: room temperature and humidity, task list from Notion, and time.",
+ "description": [
+ "React app hosted on a single-board computer (RPi) along with a RESTful API written with Express.js",
+ "Wrote a proxy service for Notion (the app then displays a task list)",
+ "Connected an indoor temperature and humidity sensor",
+ "Wired up a relay board to control appliances through software"
+ ]
+ },
+ "Rychly Portfolio": {
+ "tech": [
+ "React"
+ ],
+ "date": {
+ "start": 2023,
+ "ongoing": true
+ },
+ "summary": "Responsive React app that shows off who I am as a professional and a bit more.",
+ "description": [
+ "The 'Fast' Portfolio ([What does Rychly mean?](https://translate.google.com/details?sl=cs&tl=en&text=rychl%C3%BD&op=translate))",
+ "Responsive design, uses Material UI and React Router",
+ "Content can be easily updated through YAML configuration files",
+ "Main page filters content based on the selected type of developer"
+ ]
+ },
+ "Game Development in Haskell": {
+ "relevancy": [
+ "fe",
+ "be"
+ ],
+ "date": {
+ "finish": "May 2021"
+ },
+ "tech": [
+ "Haskell",
+ "apecs (Entity Component System)"
+ ],
+ "summary": "Bachelor's thesis exploring the limits of functional programming when used in realtime applications, specifically games.",
+ "description": [
+ "Bachelor's thesis exploring the limits of functional programming when used in realtime applications, specifically games."
+ ]
+ },
+ "pure-asteroids": {
+ "date": {
+ "finish": "Apr 2021"
+ },
+ "tech": [
+ "Haskell",
+ "SDL2"
+ ],
+ "summary": "An atempt at a purely functional game engine focusing on type safety and clarity.",
+ "description": [
+ "An atempt at a purely functional game engine focusing on type safety and clarity."
+ ]
+ },
+ "hAsteroids": {
+ "date": {
+ "finish": "Apr 2021"
+ },
+ "tech": [
+ "Haskell",
+ "SDL2"
+ ],
+ "summary": "Game in Haskell built using the apecs library.",
+ "description": [
+ "Game in Haskell built using the apecs library."
+ ]
+ }
+}
diff --git a/src/configs/projects.yaml b/src/configs/projects.yaml
new file mode 100644
index 0000000..0dd24f2
--- /dev/null
+++ b/src/configs/projects.yaml
@@ -0,0 +1,64 @@
+Pie-Hub:
+ relevancy: [fe, be]
+ date:
+ start: 2021
+ tech:
+ - Raspberry Pi 4
+ - General Purpose I/O
+ - I2C
+ - Python
+ - Bash
+ - UNIX services
+ - Node.js
+ - TypeScript
+ summary: "React app with back end running on a Raspberry Pi. Shows useful information in one place including: room temperature and humidity, task list from Notion, and time."
+ description:
+ - React app hosted on a single-board computer (RPi) along with a RESTful API written with Express.js
+ - Wrote a proxy service for Notion (the app then displays a task list)
+ - Connected an indoor temperature and humidity sensor
+ - Wired up a relay board to control appliances through software
+
+
+Rychly Portfolio:
+ tech:
+ - React
+ date:
+ start: 2023
+ ongoing: true
+ summary: Responsive React app that shows off who I am as a professional and a bit more.
+ description:
+ - The 'Fast' Portfolio ([What does Rychly mean?](https://translate.google.com/details?sl=cs&tl=en&text=rychl%C3%BD&op=translate))
+ - Responsive design, uses Material UI and React Router
+ - Content can be easily updated through YAML configuration files
+ - Main page filters content based on the selected type of developer
+
+Game Development in Haskell:
+ relevancy: [fe, be]
+ date:
+ finish: May 2021
+ tech:
+ - Haskell
+ - apecs (Entity Component System)
+ summary: Bachelor's thesis exploring the limits of functional programming when used in realtime applications, specifically games.
+ description:
+ - Bachelor's thesis exploring the limits of functional programming when used in realtime applications, specifically games.
+
+pure-asteroids:
+ date:
+ finish: Apr 2021
+ tech:
+ - Haskell
+ - SDL2
+ summary: An atempt at a purely functional game engine focusing on type safety and clarity.
+ description:
+ - An atempt at a purely functional game engine focusing on type safety and clarity.
+
+hAsteroids:
+ date:
+ finish: Apr 2021
+ tech:
+ - Haskell
+ - SDL2
+ summary: Game in Haskell built using the apecs library.
+ description:
+ - Game in Haskell built using the apecs library.
diff --git a/src/pages/LandingPage/LandingPage.tsx b/src/pages/LandingPage/LandingPage.tsx
index 113735e..7119973 100644
--- a/src/pages/LandingPage/LandingPage.tsx
+++ b/src/pages/LandingPage/LandingPage.tsx
@@ -5,6 +5,7 @@ import { FilterSelector } from './FilterSelector'
import { ExperienceList } from './ExperienceList'
import { EducationList } from './EducationList'
import { Todo } from '../../components/Todo'
+import { ProjectList } from './ProjectList'
export const LandingPage = () => (
@@ -45,7 +46,7 @@ export const LandingPage = () => (
-
+
)
diff --git a/src/pages/LandingPage/index.tsx b/src/pages/LandingPage/index.tsx
index ee110fd..581cbe6 100644
--- a/src/pages/LandingPage/index.tsx
+++ b/src/pages/LandingPage/index.tsx
@@ -1,3 +1 @@
-import { LandingPage } from './LandingPage'
-
-export { LandingPage }
+export { LandingPage } from './LandingPage'
diff --git a/src/pages/Things/Project.tsx b/src/pages/Things/Project.tsx
new file mode 100644
index 0000000..39e7794
--- /dev/null
+++ b/src/pages/Things/Project.tsx
@@ -0,0 +1,39 @@
+import { Card, CardProps, Typography } from '@mui/material'
+import { ListFromMd } from '../../components/ListFromMd'
+import _ from 'lodash'
+import { SpanFromMd } from '../../components/SpanFromMd'
+
+
+// TODO flag for projects that are displayed on the landing page vs only on the things page
+type ProjectDetails = {
+ name: string
+ summary: string
+ description: string[]
+ tech: string[]
+ date: {
+ start?: string | number
+ finish?: string | number
+ ongoing?: boolean
+ }
+}
+
+type ProjectProps = {
+ details: ProjectDetails
+ summary?: boolean
+} & CardProps
+
+export const Project = ({ details, summary, ...props }: ProjectProps) => (
+
+ {details.name}
+
+ {[
+ details.date.start,
+ details.date.ongoing ? 'Ongoing' : details.date.finish,
+ ].filter(_.identity).join('–')}
+
+ {summary
+ ?
+ :
+ }
+
+)
\ No newline at end of file
diff --git a/src/pages/Things/Things.tsx b/src/pages/Things/Things.tsx
index e695a2b..100c821 100644
--- a/src/pages/Things/Things.tsx
+++ b/src/pages/Things/Things.tsx
@@ -1,12 +1,37 @@
import { Box, Typography } from '@mui/material'
-import { Todo } from '../../components/Todo'
+import rawProjects from '../../configs/projects.json'
+import { unKeyBy } from '../../utils'
+import { useMemo } from 'react'
+import { Project } from './Project'
+import { Section } from '../../components/Section'
-export const Things = () => (
-
- I like people. They are what matters.
- But as an engineer I like things even more. Designing them. Making them.
- Here are some of my favorite things I have made, including software and hardware.
-
-
-)
+export const getProjects = () => unKeyBy(rawProjects, 'name')
+
+export const Things = () => {
+ const projects = useMemo(getProjects, [])
+
+ return (
+
+ I like people. They are what matters.
+
+ But as an engineer I like
+
+ Things
+
+
even more
+
+
+ I enjoy designing them, making them, and improving them.
+
+ Here are some of my favorites that I have made, including software and hardware.
+
+ {projects.map((project, i) => )}
+
+
+ )}
diff --git a/src/pages/Things/index.tsx b/src/pages/Things/index.tsx
new file mode 100644
index 0000000..e626a85
--- /dev/null
+++ b/src/pages/Things/index.tsx
@@ -0,0 +1 @@
+export { Things } from './Things'
diff --git a/src/utils.ts b/src/utils.ts
index 08b2346..475b622 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -5,9 +5,14 @@ import { Converter } from 'showdown'
type ObjectOfObjects = { [k: string]: object }
-/** Sort of the reverse to `keyBy` from lodash */
-export const unKeyBy = (obj: T, keyProp: string) =>
- _.map(obj, (val, key) => ({...val, [keyProp]: key }))
+/** Sort of the reverse of `keyBy` from lodash */
+export const unKeyBy = (
+ obj: T,
+ keyProp: KeyProp,
+) => _.map(obj, (val, key) => (
+ {...val, [keyProp]: key }
+ ) as T[keyof T] & { [K in KeyProp]: keyof T }
+ )
/** Mainly useful when we know `o` has a single key */
export const getFirstKey = >(o: Obj) => Object.keys(o)[0]