From 5493f0c140900999957eef1013ca592650e39ab2 Mon Sep 17 00:00:00 2001 From: Guus van Meerveld Date: Sun, 10 Mar 2024 01:35:10 +0100 Subject: [PATCH] added basic cv page --- data/cv.json | 56 +++++++++++++++ package.json | 2 + public/cv.jpg | Bin 0 -> 31781 bytes src/app/(landing)/Header.tsx | 34 ++++----- src/app/cv/Cv.tsx | 136 +++++++++++++++++++++++++++++++++++ src/app/cv/page.tsx | 12 ++++ src/models/cv.ts | 46 ++++++++++++ src/utils/cv.ts | 16 +++++ src/utils/json.ts | 23 ++++++ src/utils/landing.ts | 20 +----- yarn.lock | 10 +++ 11 files changed, 318 insertions(+), 37 deletions(-) create mode 100644 data/cv.json create mode 100644 public/cv.jpg create mode 100644 src/app/cv/Cv.tsx create mode 100644 src/app/cv/page.tsx create mode 100644 src/models/cv.ts create mode 100644 src/utils/cv.ts create mode 100644 src/utils/json.ts diff --git a/data/cv.json b/data/cv.json new file mode 100644 index 0000000..7fbefdb --- /dev/null +++ b/data/cv.json @@ -0,0 +1,56 @@ +{ + "fullName": "Guus van Meerveld", + "role": "Computer programmer", + "description": "As a computer programmer working with precision and being methodical are of incredible importance, as a simple minor oversight can change people's lives. This is exactly the way I prefer to work. Methodically approaching a problem, taking it apart step by step and finding an optimal, yet creative solution.", + "contact": { + "website": "https://guusvanmeerveld.dev", + "email": "contact@guusvanmeerveld.dev", + "linkedIn": "https://linkedin.com/in/guus-van-meerveld-038357210", + "git": "https://github.com/guusvanmeerveld" + }, + "skills": [ + { + "name": "DevOps", + "year": "1/1/2022", + "value": 0.8 + }, + { + "name": "Linux", + "year": "1/1/2020", + "value": 0.9 + }, + { + "name": "Web development", + "year": "1/1/2017", + "value": 0.7 + } + ], + "programmingLanguages": [ + { + "name": "Rust", + "year": "1/1/2022", + "value": 0.8 + }, + { "name": "Typescript & Javascript", "value": 0.9, "year": "1/1/2017" }, + { "name": "Java", "value": 0.6, "year": "1/8/2022" }, + { "name": "Python", "value": 0.6, "year": "1/2/2023" }, + { "name": "Scala", "value": 0.5, "year": "1/8/2023" } + ], + "education": [ + { + "title": "Bachelor Artificial Intelligence", + "timeFrame": "2022 - Present", + "institution": "Radboud University", + "location": "Nijmegen, Netherlands", + "skills": ["Mathematics", "Neuroscience", "Computer science"] + }, + { + "title": "VWO (NT&G)", + "timeFrame": "2016 - 2022", + "institution": "RSG Pantarijn MHV Wageningen", + "location": "Wageningen, Netherlands", + "skills": ["Biology", "Physics", "Mathematics"] + } + ], + "experience": [] +} diff --git a/package.json b/package.json index 587b9e0..983d39e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "bcrypt": "^5.1.0", "framer-motion": "^11.0.8", "fs-extra": "^11.2.0", + "humanize-duration": "^3.31.0", "iron-session": "^6.3.1", "next": "^14.1.1", "next-themes": "^0.2.1", @@ -39,6 +40,7 @@ "@trivago/prettier-plugin-sort-imports": "^4.0.0", "@types/bcrypt": "^5.0.0", "@types/fs-extra": "^11.0.4", + "@types/humanize-duration": "^3.27.4", "@types/node": "^15.12.1", "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", diff --git a/public/cv.jpg b/public/cv.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89ae87061dc05afb3cce84469a6c1ef3516cab2d GIT binary patch literal 31781 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 = ({ {socials.map((social) => ( - - - + + + + + ))} diff --git a/src/app/cv/Cv.tsx b/src/app/cv/Cv.tsx new file mode 100644 index 0000000..2161248 --- /dev/null +++ b/src/app/cv/Cv.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { Card, CardBody } from "@nextui-org/card"; +import { Divider } from "@nextui-org/divider"; +import { Image } from "@nextui-org/image"; +import { Listbox, ListboxItem } from "@nextui-org/listbox"; +import { Progress } from "@nextui-org/progress"; +import { Spacer } from "@nextui-org/spacer"; +import { Component } from "@typings/component"; +import humanizeDuration from "humanize-duration"; +import NextImage from "next/image"; + +import CvProps, { + Education as EducationProps, + Skill as SkillProps +} from "@models/cv"; + +const Skill: Component<{ skill: SkillProps }> = ({ skill }) => { + const duration = new Date().getTime() - skill.year.getTime(); + + const durationInYears = humanizeDuration(duration, { + units: ["y"], + round: true + }); + + return ( +
+ +
+ ); +}; + +const Education: Component<{ education: EducationProps }> = ({ education }) => { + return ( + + +
+
+

{education.title}

+

+ {education.timeFrame} +

+
+
+

{education.institution}

+

+ {education.location} +

+
+
+ + + + + {education.skills.map((skill) => ( + {skill} + ))} + +
+
+ ); +}; + +export const Cv: Component<{ data: CvProps }> = ({ data }) => { + return ( +
+
+ {`Professional + + + +
+
+

{data.fullName}

+ +

{data.role}

+
+
+
+ + + +

Professional profile

+

{data.description}

+ + + +
+
+

Skills

+ + {data.skills.map((skill) => ( + + ))} + + + +

Programming Languages

+ + {data.programmingLanguages.map((skill) => ( + + ))} + +
+ +
+

Education

+ + {data.education.map((education) => { + return ( + <> + + + + ); + })} +
+
+
+ ); +}; diff --git a/src/app/cv/page.tsx b/src/app/cv/page.tsx new file mode 100644 index 0000000..ebaaed5 --- /dev/null +++ b/src/app/cv/page.tsx @@ -0,0 +1,12 @@ +import { Cv } from "./Cv"; + +import { dataDirLocation } from "@utils/constants"; +import { readCvJson } from "@utils/cv"; + +export default async function Page() { + const cv = await readCvJson(dataDirLocation); + + return ; +} + +export const revalidate = 3600; diff --git a/src/models/cv.ts b/src/models/cv.ts new file mode 100644 index 0000000..a7d05a3 --- /dev/null +++ b/src/models/cv.ts @@ -0,0 +1,46 @@ +import z from "zod"; + +const SkillModel = z.object({ + name: z.string(), + year: z.coerce.date(), + value: z.number().min(0).max(1) +}); + +export type Skill = z.infer; + +const EducationModel = z.object({ + title: z.string(), + timeFrame: z.string(), + institution: z.string(), + location: z.string(), + skills: z.string().array() +}); + +export type Education = z.infer; + +const ExperienceModel = z.object({ + title: z.string(), + timeFrame: z.string(), + role: z.string(), + description: z.string() +}); + +export const CvPropsModel = z.object({ + fullName: z.string(), + role: z.string(), + description: z.string(), + contact: z.object({ + website: z.string(), + email: z.string().email(), + linkedIn: z.string().url(), + git: z.string().url() + }), + skills: SkillModel.array(), + programmingLanguages: SkillModel.array(), + education: EducationModel.array(), + experience: ExperienceModel.array() +}); + +export type CvProps = z.infer; + +export default CvProps; diff --git a/src/utils/cv.ts b/src/utils/cv.ts new file mode 100644 index 0000000..266bc5a --- /dev/null +++ b/src/utils/cv.ts @@ -0,0 +1,16 @@ +import { readJson } from "fs-extra"; +import path from "path"; + +import { readAndParseJsonFile } from "./json"; + +import { cache } from "react"; + +import CvProps, { CvPropsModel } from "@models/cv"; + +export const readCvJson = cache( + async (dataDirLocation: string): Promise => { + const cvJsonLocation = path.join(dataDirLocation, "cv.json"); + + return await readAndParseJsonFile(cvJsonLocation, CvPropsModel); + } +); diff --git a/src/utils/json.ts b/src/utils/json.ts new file mode 100644 index 0000000..85e52d1 --- /dev/null +++ b/src/utils/json.ts @@ -0,0 +1,23 @@ +import { readJson } from "fs-extra"; +import z from "zod"; + +import exists from "@utils/fileExists"; + +export const readAndParseJsonFile = async ( + location: string, + model: z.ZodType +): Promise => { + const fileExists = await exists(location); + + if (!fileExists) { + throw new Error(`Could not find json file at: ${location}`); + } + + const rawJson: unknown = await readJson(location); + + const result = model.safeParse(rawJson); + + if (!result.success) throw new Error(`Failed to parse json: ${result.error}`); + + return result.data; +}; diff --git a/src/utils/landing.ts b/src/utils/landing.ts index 45478b7..debd293 100644 --- a/src/utils/landing.ts +++ b/src/utils/landing.ts @@ -2,33 +2,17 @@ import { readFile, readJson } from "fs-extra"; import path from "path"; import { avatarFileFormat } from "./constants"; +import { readAndParseJsonFile } from "./json"; import { cache } from "react"; import Landing, { LandingModel } from "@models/landing"; -import exists from "@utils/fileExists"; - export const readLandingJson = cache( async (dataDirLocation: string): Promise => { const landingJsonLocation = path.join(dataDirLocation, "landing.json"); - const fileExists = await exists(landingJsonLocation); - - if (!fileExists) { - throw new Error( - `Could not find landing json file at: ${landingJsonLocation}` - ); - } - - const rawJson: unknown = await readJson(landingJsonLocation); - - const landingResult = LandingModel.safeParse(rawJson); - - if (!landingResult.success) - throw new Error(`Failed to parse landing json: ${landingResult.error}`); - - return landingResult.data; + return await readAndParseJsonFile(landingJsonLocation, LandingModel); } ); diff --git a/yarn.lock b/yarn.lock index 9b97a51..750bce3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2199,6 +2199,11 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== +"@types/humanize-duration@^3.27.4": + version "3.27.4" + resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.27.4.tgz#51d6d278213374735440bc3749de920935e9127e" + integrity sha512-yaf7kan2Sq0goxpbcwTQ+8E9RP6HutFBPv74T/IA/ojcHKhuKVlk2YFYyHhWZeLvZPzzLE3aatuQB4h0iqyyUA== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -3732,6 +3737,11 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +humanize-duration@^3.31.0: + version "3.31.0" + resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.31.0.tgz#a0384d22555024cd17e6e9f8561540d37756bf4c" + integrity sha512-fRrehgBG26NNZysRlTq1S+HPtDpp3u+Jzdc/d5A4cEzOD86YLAkDaJyJg8krSdCi7CJ+s7ht3fwRj8Dl+Btd0w== + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"