feat: improve cv layout
This commit is contained in:
parent
6d6c468be7
commit
188edc999c
9 changed files with 224 additions and 73 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -23,6 +23,7 @@
|
||||||
"Sitecore",
|
"Sitecore",
|
||||||
"sportank",
|
"sportank",
|
||||||
"Umbraco",
|
"Umbraco",
|
||||||
|
"Yarborough",
|
||||||
"Yarbz"
|
"Yarbz"
|
||||||
],
|
],
|
||||||
"files.autoSave": "off",
|
"files.autoSave": "off",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "www-aaronjy-me",
|
"name": "www-aaronjy-me",
|
||||||
"version": "2.3.0",
|
"version": "2.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import pck from "../../../package.json";
|
||||||
|
|
||||||
function Footer() {
|
function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className={style.footer} data-testid="footer">
|
<footer className={`${style.footer} hide-print`} data-testid="footer">
|
||||||
<hr />
|
<hr />
|
||||||
<nav>
|
<nav>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import styles from "./Header.module.css";
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
return (
|
return (
|
||||||
<header className={styles.header} data-testid="header">
|
<header className={`${styles.header} hide-print`} data-testid="header">
|
||||||
<nav>
|
<nav>
|
||||||
<Link href="/">Home</Link>
|
<Link href="/">Home</Link>
|
||||||
{", "}
|
{", "}
|
||||||
|
|
|
@ -2,8 +2,11 @@ import React from "react";
|
||||||
|
|
||||||
import style from "./Resume.module.css";
|
import style from "./Resume.module.css";
|
||||||
import { markdownToHtml } from "@/services/content-service";
|
import { markdownToHtml } from "@/services/content-service";
|
||||||
|
import { MDXClient } from "next-mdx-remote-client/csr";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
function Resume({
|
function Resume({
|
||||||
|
introMdxSource,
|
||||||
competencies,
|
competencies,
|
||||||
education,
|
education,
|
||||||
certifications,
|
certifications,
|
||||||
|
@ -12,7 +15,7 @@ function Resume({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={style.cv}>
|
<div className={style.cv}>
|
||||||
<ol>
|
<ol className="hide-print">
|
||||||
<li>
|
<li>
|
||||||
<a href="#experience">Professional experience</a>
|
<a href="#experience">Professional experience</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -30,57 +33,73 @@ function Resume({
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<div>
|
<div>
|
||||||
<h2 id="experience">Professional experience</h2>
|
<div className={style.about}>
|
||||||
|
<MDXClient {...introMdxSource} />
|
||||||
{experience?.map((exp, i) => (
|
<span className="print-only">
|
||||||
<div key={i}>
|
See more at <a href="https://aaronjy.me/cv">aaronjy.me/cv</a>
|
||||||
<WorkExperience
|
</span>
|
||||||
employer={exp.employer}
|
</div>
|
||||||
position={exp.position}
|
|
||||||
start={exp.start}
|
|
||||||
end={exp.end}
|
|
||||||
>
|
|
||||||
{markdownToHtml(exp.description)}
|
|
||||||
</WorkExperience>
|
|
||||||
{!!exp.skills?.length && (
|
|
||||||
<details>
|
|
||||||
<summary>Competencies</summary>
|
|
||||||
<>{exp.skills.sort().join(", ")}</>
|
|
||||||
</details>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="sidebar">
|
<div className={style.cvContent}>
|
||||||
<h2 id="competencies">Competencies</h2>
|
<div className={style.experience}>
|
||||||
<ul>
|
<h2 id="experience">Professional experience</h2>
|
||||||
{competencies
|
|
||||||
?.sort((a, b) => a.name > b.name)
|
|
||||||
.map((c, i) => (
|
|
||||||
<li key={i}>{c.name}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 id="certifications">Certifications</h2>
|
{experience?.map((exp, i) => (
|
||||||
<ul>
|
<div key={i}>
|
||||||
{certifications
|
<WorkExperience
|
||||||
?.sort((a, b) => a.name > b.name)
|
employer={exp.employer}
|
||||||
.map((c, i) => (
|
position={exp.position}
|
||||||
<li key={i}>{c.name}</li>
|
start={exp.start}
|
||||||
))}
|
end={exp.end}
|
||||||
</ul>
|
>
|
||||||
|
{markdownToHtml(exp.description)}
|
||||||
<h2 className="languages">Languages</h2>
|
</WorkExperience>
|
||||||
<ul>
|
{!!exp.skills?.length && (
|
||||||
{languages?.sort().map((c, i) => (
|
<details className="hide-print">
|
||||||
<li key={i}>
|
<summary>Competencies</summary>
|
||||||
{c.name} - {c.proficiency}
|
<>{exp.skills.sort().join(", ")}</>
|
||||||
</li>
|
</details>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</div>
|
||||||
|
<div className={style.sidebar}>
|
||||||
|
<h2 id="competencies">Competencies</h2>
|
||||||
|
<ul>
|
||||||
|
{competencies
|
||||||
|
?.sort((a, b) => a.name - b.name)
|
||||||
|
.map((c, i) => (
|
||||||
|
<li key={i}>{c.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2 className="education">Education</h2>
|
<h2 id="certifications">Certifications</h2>
|
||||||
<p>{education.name}</p>
|
<ul>
|
||||||
|
{certifications
|
||||||
|
?.sort((a, b) => a.name > b.name)
|
||||||
|
.map((c, i) => (
|
||||||
|
<li key={i}>{c.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 className="languages">Languages</h2>
|
||||||
|
<ul>
|
||||||
|
{languages?.sort().map((c, i) => (
|
||||||
|
<li key={i}>
|
||||||
|
<strong>{c.name}</strong> - {c.proficiency}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 className="education">Education</h2>
|
||||||
|
<ul>
|
||||||
|
{education
|
||||||
|
?.sort((a, b) => a.name - b.name)
|
||||||
|
.map((c, i) => (
|
||||||
|
<li key={i}>{c.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -90,21 +109,48 @@ export default Resume;
|
||||||
|
|
||||||
function WorkExperience({ position, employer, start, end, children }) {
|
function WorkExperience({ position, employer, start, end, children }) {
|
||||||
return (
|
return (
|
||||||
<div className={style["work-experience"]}>
|
// <div className={style["work-experience"]}>
|
||||||
<div>
|
// <div>
|
||||||
<h3 id={position}>
|
// <h3 id={position}>
|
||||||
{position}
|
// {position}
|
||||||
<br />
|
// <br />
|
||||||
<small>{employer}</small>
|
// <small>{employer}</small>
|
||||||
</h3>
|
// </h3>
|
||||||
<small>
|
// <small>
|
||||||
<time>{start}</time> - <time>{end}</time>
|
// <time>{start}</time> - <time>{end}</time>
|
||||||
</small>
|
// </small>
|
||||||
</div>
|
// </div>
|
||||||
<div
|
// <div
|
||||||
data-test="children"
|
// data-test="children"
|
||||||
dangerouslySetInnerHTML={{ __html: children }}
|
// dangerouslySetInnerHTML={{ __html: children }}
|
||||||
/>
|
// />
|
||||||
</div>
|
// </div>
|
||||||
|
|
||||||
|
<table className={style.experienceTable}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id={position} className={style.position}>
|
||||||
|
{position}
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<span className={style.employer}>{employer}</span>
|
||||||
|
</td>
|
||||||
|
<td className="text-right">
|
||||||
|
{/* <small> */}
|
||||||
|
<time>{start}</time> - <time>{end}</time>
|
||||||
|
{/* </small> */}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<div
|
||||||
|
data-test="children"
|
||||||
|
dangerouslySetInnerHTML={{ __html: children }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
.experienceTable {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
gap: 3rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvContent .experience {
|
||||||
|
flex-basis: 66.66%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvContent .sidebar {
|
||||||
|
flex-basis: 33.33%;
|
||||||
|
position: sticky;
|
||||||
|
top: 10px;
|
||||||
|
left: 0px;
|
||||||
|
height: 1px;
|
||||||
|
/* Needed to make sticky work */
|
||||||
|
}
|
||||||
|
|
||||||
|
.about {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv ol,
|
||||||
|
.cv ul {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.cvContent {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvContent .sidebar {
|
||||||
|
height: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.cvContent {
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvContent .experience {
|
||||||
|
flex-basis: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cvContent .sidebar {
|
||||||
|
flex-basis: 20%;
|
||||||
|
}
|
||||||
|
}
|
0
src/pages/cv/cv.module.css
Normal file
0
src/pages/cv/cv.module.css
Normal file
|
@ -1,9 +1,11 @@
|
||||||
import DefaultLayout from "@/layouts/DefaultLayout/DefaultLayout";
|
|
||||||
import React from "react";
|
|
||||||
import { NextSeo } from "next-seo";
|
|
||||||
import Resume from "@/components/Resume/Resume";
|
import Resume from "@/components/Resume/Resume";
|
||||||
import { fetchCV } from "@/services/content-service";
|
|
||||||
import { FailedFetchCVError } from "@/errors";
|
import { FailedFetchCVError } from "@/errors";
|
||||||
|
import DefaultLayout from "@/layouts/DefaultLayout/DefaultLayout";
|
||||||
|
import { fetchCV } from "@/services/content-service";
|
||||||
|
import { NextSeo } from "next-seo";
|
||||||
|
|
||||||
|
import style from "./cv.module.css";
|
||||||
|
import { serialize } from "next-mdx-remote-client/serialize";
|
||||||
|
|
||||||
export const Title = "CV";
|
export const Title = "CV";
|
||||||
|
|
||||||
|
@ -22,10 +24,20 @@ export async function getStaticProps() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const { competencies, education, languages, certifications, experience } = cv;
|
const {
|
||||||
|
intro,
|
||||||
|
competencies,
|
||||||
|
education,
|
||||||
|
languages,
|
||||||
|
certifications,
|
||||||
|
experience,
|
||||||
|
} = cv;
|
||||||
|
|
||||||
|
const introMdxSource = await serialize({ source: intro });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
introMdxSource,
|
||||||
competencies,
|
competencies,
|
||||||
education,
|
education,
|
||||||
languages,
|
languages,
|
||||||
|
@ -37,6 +49,7 @@ export async function getStaticProps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ResumePage({
|
export default function ResumePage({
|
||||||
|
introMdxSource,
|
||||||
competencies,
|
competencies,
|
||||||
education,
|
education,
|
||||||
certifications,
|
certifications,
|
||||||
|
@ -51,9 +64,11 @@ export default function ResumePage({
|
||||||
title: Title,
|
title: Title,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<h1>{Title}</h1>
|
<h1 className="hide-print">{Title}</h1>
|
||||||
|
<h1 className={`${style.printHeading} print-only`}>Aaron Yarborough</h1>
|
||||||
<section>
|
<section>
|
||||||
<Resume
|
<Resume
|
||||||
|
introMdxSource={introMdxSource}
|
||||||
competencies={competencies}
|
competencies={competencies}
|
||||||
education={education}
|
education={education}
|
||||||
certifications={certifications}
|
certifications={certifications}
|
||||||
|
|
|
@ -22,3 +22,31 @@ td:first-child {
|
||||||
.bold {
|
.bold {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
html {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.3;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-only {
|
||||||
|
display: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue