1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2025-01-03 18:35:00 +01:00

resume tweaks, markdown summaries

This commit is contained in:
Matthias Kretschmann 2019-11-08 23:00:47 +01:00
parent f44e0557ed
commit 9d5e33b146
Signed by: m
GPG Key ID: 606EEEF3C479A91F
16 changed files with 450 additions and 103 deletions

View File

@ -19,7 +19,7 @@
- [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages) - [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages)
- [🐱 GitHub repositories](#-github-repositories) - [🐱 GitHub repositories](#-github-repositories)
- [💅 Theme switcher](#-theme-switcher) - [💅 Theme switcher](#-theme-switcher)
- [🗂 Resume](#-resume) - [🗂 JSON Resume](#-json-resume)
- [🏆 SEO component](#-seo-component) - [🏆 SEO component](#-seo-component)
- [📇 Client-side vCard creation](#-client-side-vcard-creation) - [📇 Client-side vCard creation](#-client-side-vcard-creation)
- [💫 Page transitions](#-page-transitions) - [💫 Page transitions](#-page-transitions)
@ -77,14 +77,14 @@ If you want to know how, have a look at the respective components:
- [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx) - [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx)
- [`src/hooks/use-dark-mode.jsx`](src/hooks/use-dark-mode.jsx) - [`src/hooks/use-dark-mode.jsx`](src/hooks/use-dark-mode.jsx)
### 🗂 Resume ### 🗂 JSON Resume
Resume page based on [JSON Resume](https://jsonresume.org) standard. Resume page based on [JSON Resume](https://jsonresume.org) standard. Most metadata and social profiles are defined in [`content/resume.json`](content/resume.json) and used throughout the site.
If you want to know how, have a look at the respective components: If you want to know how, have a look at the respective components:
- [`src/pages/resume.jsx`](src/pages/resume.jsx)
- [`content/resume.json`](content/resume.json) - [`content/resume.json`](content/resume.json)
- [`src/pages/resume.jsx`](src/pages/resume.jsx)
- [`src/hooks/use-resume.js`](src/hooks/use-resume.js) - [`src/hooks/use-resume.js`](src/hooks/use-resume.js)
### 🏆 SEO component ### 🏆 SEO component

View File

@ -34,7 +34,6 @@
], ],
"location": { "location": {
"city": "Berlin", "city": "Berlin",
"country": "Germany",
"countryCode": "DE" "countryCode": "DE"
} }
}, },
@ -44,7 +43,7 @@
"position": "Lead UI Designer & Developer", "position": "Lead UI Designer & Developer",
"website": "https://oceanprotocol.com", "website": "https://oceanprotocol.com",
"startDate": "2017-01-01", "startDate": "2017-01-01",
"summary": "Leading the UI design & development of Ocean Protocol, iterating on a components-based UI design system spanning all of Ocean Protocol's web properties. Additionally, I conceptualize, execute and iterate on the creative and visual direction of the Ocean Protocol brand.", "summary": "Leading the UI design & development of Ocean Protocol's user interfaces, iterating on a components-based UI design system spanning all of Ocean Protocol's web properties. \n\nConceptualize, execute and iterate on the creative and visual direction of the Ocean Protocol brand.\n\nAs a core developer leading the execution of [multiple user interfaces](/oceanprotocol) and core components.",
"highlights": ["Started the company"] "highlights": ["Started the company"]
}, },
{ {
@ -53,7 +52,7 @@
"website": "https://bigchaindb.com", "website": "https://bigchaindb.com",
"startDate": "2016-12-01", "startDate": "2016-12-01",
"endDate": "2018-12-31", "endDate": "2018-12-31",
"summary": "Leading the UI design & development of all BigchainDB web properties. I created the initial BigchainDB brand and further conceptualized, executed and iterated on the creative and visual direction of BigchainDB. This included creating and iterating on a components-based UI design system for all of BigchainDB's web properties.", "summary": "Leading the UI design & development of all BigchainDB web properties. I created the initial BigchainDB brand and further conceptualized, executed and iterated on the creative and visual direction of BigchainDB. This included creating and iterating on a components-based UI design system for all of [BigchainDB's user interfaces](/bigchaindb).",
"highlights": ["Started the company"] "highlights": ["Started the company"]
}, },
{ {
@ -71,7 +70,7 @@
"website": "https://chartmogul.com", "website": "https://chartmogul.com",
"startDate": "2015-07-15", "startDate": "2015-07-15",
"endDate": "2017-02-01", "endDate": "2017-02-01",
"summary": "Co-designing and leading the UI design & development of various ChartMogul web properties. This included the creation of a components-based UI design system and implementing it across all web touch points. Besides designing and implementing new features, I maintained the front-end of the ChartMogul application and implemented the UI design system by refactoring most of its front-end codebase.", "summary": "Co-designing and leading the UI design & development of various [ChartMogul web properties](/chartmogul). This included the creation of a components-based UI design system and implementing it across all web touch points.\n\nBesides designing and implementing new features, I maintained the front-end of the ChartMogul application and implemented the UI design system by refactoring most of its front-end codebase.",
"highlights": ["Started the company"] "highlights": ["Started the company"]
}, },
{ {
@ -80,7 +79,7 @@
"website": "https://sharethemeal.org", "website": "https://sharethemeal.org",
"startDate": "2014-10-01", "startDate": "2014-10-01",
"endDate": "2015-06-01", "endDate": "2015-06-01",
"summary": "...", "summary": "[app and website](/sharethemeal)",
"highlights": ["Started the company"] "highlights": ["Started the company"]
}, },
{ {
@ -89,21 +88,21 @@
"website": "https://ezeep.com", "website": "https://ezeep.com",
"startDate": "2012-01-01", "startDate": "2012-01-01",
"endDate": "2014-09-01", "endDate": "2014-09-01",
"summary": "Creating an unprecedented, market-leading & award-winning user experience around printing based on the principles of emotional design way ahead of all competitors. This included defining the product based on user & market research in an iterative process and designing & building ezeeps numerous touch points, like the web app, web site, desktop apps for Windows & Mac OS X and apps for iOS & Android. On top of that I created the corporate identity and a consistent visual branding including the logo." "summary": "Creating an unprecedented, market-leading & award-winning user experience around printing based on the principles of emotional design way ahead of all competitors.\n\nThis included defining the product based on user & market research in an iterative process and designing & building [ezeeps numerous touch points](/ezeep), like the web app, web site, desktop apps for Windows & Mac OS X and apps for iOS & Android.\n\nOn top of that I created the corporate identity and a consistent visual branding, including the logo."
}, },
{ {
"company": "Martin Luther University Halle-Wittenberg", "company": "Martin Luther University Halle-Wittenberg",
"position": "UI/UX Designer & Front End Developer", "position": "UI/UX Designer & Front End Developer",
"startDate": "2009-02-01", "startDate": "2009-02-01",
"endDate": "2012-01-01", "endDate": "2012-01-01",
"summary": "Conceptualizing & implementing numerous in-house and public facing interfaces for thousands of students and staff. Additionally, conceptualizing, creating and maintaining the blog network & community for all students & staff." "summary": "Conceptualizing & implementing [numerous in-house and public facing interfaces](/unihalle) for thousands of students and staff. Additionally, conceptualizing, creating and maintaining the blog network & community for all students & staff."
}, },
{ {
"company": "Harz University of Applied Sciences", "company": "Harz University of Applied Sciences",
"position": "Consultant & Teacher", "position": "Consultant & Teacher",
"startDate": "2011-02-01", "startDate": "2011-02-01",
"endDate": "2011-05-01", "endDate": "2011-05-01",
"summary": "Conceptualizing a web design & development university seminar and building a responsive & fluid grid framework with a basic HTML/CSS template for students of Media Informatics at the Harz University of Applied Sciences to learn and use." "summary": "Conceptualizing a web design & development university seminar and building a [responsive & fluid grid framework](https://github.com/kremalicious/hsresponsive) with a basic HTML/CSS template for students of Media Informatics at the Harz University of Applied Sciences to learn and use."
}, },
{ {
"company": "Martin Luther University Halle-Wittenberg", "company": "Martin Luther University Halle-Wittenberg",
@ -144,7 +143,7 @@
{ {
"institution": "Martin Luther University Halle-Wittenberg", "institution": "Martin Luther University Halle-Wittenberg",
"area": "Media/Communication Science & Art History", "area": "Media/Communication Science & Art History",
"studyType": "Bachelor", "studyType": "Bachelor of Arts",
"startDate": "2008-01-01", "startDate": "2008-01-01",
"endDate": "2012-01-01" "endDate": "2012-01-01"
}, },
@ -160,7 +159,8 @@
{ {
"title": "German Design Award", "title": "German Design Award",
"date": "2015-11-01", "date": "2015-11-01",
"awarder": "ezeep GmbH" "awarder": "ezeep GmbH",
"summary": "Nominated in the category _Interactive User Experience (Excellent Communications Design)_"
}, },
{ {
"title": "CeBIT Preview Award", "title": "CeBIT Preview Award",
@ -225,13 +225,13 @@
} }
], ],
"languages": [ "languages": [
{
"language": "German",
"fluency": "Native speaker"
},
{ {
"language": "English", "language": "English",
"fluency": "Advanced speaker" "fluency": "Advanced speaker"
},
{
"language": "German",
"fluency": "Native speaker"
} }
] ]
} }

View File

@ -2,7 +2,7 @@
const path = require('path') const path = require('path')
const remark = require('remark') const remark = require('remark')
const markdown = require('remark-parse') const parse = require('remark-parse')
const html = require('remark-html') const html = require('remark-html')
const axios = require('axios') const axios = require('axios')
const fs = require('fs') const fs = require('fs')
@ -111,7 +111,7 @@ exports.onCreateNode = ({ node, actions }) => {
let descriptionHtml let descriptionHtml
remark() remark()
.use(markdown, { gfm: true, commonmark: true, pedantic: true }) .use(parse, { gfm: true, commonmark: true, pedantic: true })
.use(html) .use(html)
.process(descriptionWithLineBreaks, (err, file) => { .process(descriptionWithLineBreaks, (err, file) => {
if (err) throw Error('Could not transform project description') if (err) throw Error('Could not transform project description')

View File

@ -0,0 +1,276 @@
{
"contentJson": {
"basics": {
"name": "Matthias Kretschmann",
"label": "Designer & Developer",
"picture": {
"childImageSharp": {
"fixed": {
"aspectRatio": 1,
"width": 256,
"height": 256,
"src": "/static/b45f45aa8d98d4e4019a242d38f2f248/c296b/avatar.jpg",
"srcSet": "/static/b45f45aa8d98d4e4019a242d38f2f248/c296b/avatar.jpg 1x,\n/static/b45f45aa8d98d4e4019a242d38f2f248/28b3a/avatar.jpg 1.5x,\n/static/b45f45aa8d98d4e4019a242d38f2f248/72cad/avatar.jpg 2x",
"srcWebp": "/static/b45f45aa8d98d4e4019a242d38f2f248/59c88/avatar.webp",
"srcSetWebp": "/static/b45f45aa8d98d4e4019a242d38f2f248/59c88/avatar.webp 1x,\n/static/b45f45aa8d98d4e4019a242d38f2f248/bd640/avatar.webp 1.5x,\n/static/b45f45aa8d98d4e4019a242d38f2f248/b957b/avatar.webp 2x",
"originalName": "avatar.jpg"
}
}
},
"email": "m@kretschmann.io",
"website": "https://matthiaskretschmann.com",
"summary": "",
"profiles": [
{
"network": "Blog",
"url": "https://kremalicious.com",
"username": null
},
{
"network": "Twitter",
"url": "https://twitter.com/kremalicious",
"username": "kremalicious"
},
{
"network": "GitHub",
"url": "https://github.com/kremalicious",
"username": "kremalicious"
},
{
"network": "Dribbble",
"url": "https://dribbble.com/kremalicious",
"username": "kremalicious"
},
{
"network": "Keybase",
"url": "https://keybase.io/kremalicious",
"username": "kremalicious"
}
],
"location": {
"city": "Berlin",
"countryCode": "DE"
}
},
"education": [
{
"institution": "Self-taught",
"area": "UI Design & Web Development",
"studyType": "Autodidactic",
"startDate": "1999-01-01",
"endDate": "2004-01-01"
},
{
"institution": "Martin Luther University Halle-Wittenberg",
"area": "Media/Communication Science & Art History",
"studyType": "Bachelor of Arts",
"startDate": "2008-01-01",
"endDate": "2012-01-01"
},
{
"institution": "Martin Luther University Halle-Wittenberg",
"area": "Political Science & Sociology",
"studyType": "Magister Artium",
"startDate": "2006-01-01",
"endDate": "2008-01-01"
}
],
"languages": [
{
"language": "English",
"fluency": "Advanced speaker"
},
{
"language": "German",
"fluency": "Native speaker"
}
],
"skills": [
{
"name": "Design",
"level": "Master",
"keywords": [
"Product Design",
"Service Design",
"Interface Design",
"User Experience Design",
"Communication Design",
"Interaction Design",
"Information Architecture",
"Icon Design",
"Web Design",
"Typography",
"Design management"
]
},
{
"name": "Web Development",
"level": "Master",
"keywords": [
"HTML",
"CSS",
"Javascript",
"Node.js",
"npm ecosystem",
"SASS/SCSS",
"Less",
"Stylus",
"Gulp",
"Gatsby",
"React",
"Styled Components",
"JAMstack"
]
},
{
"name": "General Software Development",
"level": "Master",
"keywords": [
"Git",
"GitHub",
"Bash",
"UNIX",
"Agile: Kanban & Scrum",
"Prototyping",
"Incremental"
]
},
{
"name": "DevOps",
"level": "Intermediate",
"keywords": [
"AWS",
"Now",
"Serverless",
"Cloudflare",
"NGINX",
"Apache"
]
}
],
"work": [
{
"company": "Ocean Protocol Foundation",
"position": "Lead UI Designer & Developer",
"website": "https://oceanprotocol.com",
"startDate": "2017-01-01",
"endDate": null,
"summary": "Leading the UI design & development of Ocean Protocol's user interfaces, iterating on a components-based UI design system spanning all of Ocean Protocol's web properties. \n\nConceptualize, execute and iterate on the creative and visual direction of the Ocean Protocol brand.\n\nAs a core developer leading the execution of [multiple user interfaces](/oceanprotocol) and core components.",
"highlights": ["Started the company"]
},
{
"company": "BigchainDB GmbH",
"position": "Lead UI Designer & Developer",
"website": "https://bigchaindb.com",
"startDate": "2016-12-01",
"endDate": "2018-12-31",
"summary": "Leading the UI design & development of all BigchainDB web properties. I created the initial BigchainDB brand and further conceptualized, executed and iterated on the creative and visual direction of BigchainDB. This included creating and iterating on a components-based UI design system for all of [BigchainDB's user interfaces](/bigchaindb).",
"highlights": ["Started the company"]
},
{
"company": "ascribe GmbH",
"position": "UI Designer & Developer",
"website": "https://ascribe.io",
"startDate": "2016-01-01",
"endDate": "2017-12-31",
"summary": "Description...",
"highlights": ["Started the company"]
},
{
"company": "ChartMogul Ltd.",
"position": "Lead UI Engineer",
"website": "https://chartmogul.com",
"startDate": "2015-07-15",
"endDate": "2017-02-01",
"summary": "Co-designing and leading the UI design & development of various [ChartMogul web properties](/chartmogul). This included the creation of a components-based UI design system and implementing it across all web touch points.\n\nBesides designing and implementing new features, I maintained the front-end of the ChartMogul application and implemented the UI design system by refactoring most of its front-end codebase.",
"highlights": ["Started the company"]
},
{
"company": "UN World Food Programme/ShareTheMeal",
"position": "UI Engineer",
"website": "https://sharethemeal.org",
"startDate": "2014-10-01",
"endDate": "2015-06-01",
"summary": "[app and website](/sharethemeal)",
"highlights": ["Started the company"]
},
{
"company": "ezeep GmbH",
"position": "Lead Designer & Front End Developer",
"website": "https://ezeep.com",
"startDate": "2012-01-01",
"endDate": "2014-09-01",
"summary": "Creating an unprecedented, market-leading & award-winning user experience around printing based on the principles of emotional design way ahead of all competitors.\n\nThis included defining the product based on user & market research in an iterative process and designing & building [ezeeps numerous touch points](/ezeep), like the web app, web site, desktop apps for Windows & Mac OS X and apps for iOS & Android.\n\nOn top of that I created the corporate identity and a consistent visual branding, including the logo.",
"highlights": null
},
{
"company": "Martin Luther University Halle-Wittenberg",
"position": "UI/UX Designer & Front End Developer",
"website": null,
"startDate": "2009-02-01",
"endDate": "2012-01-01",
"summary": "Conceptualizing & implementing [numerous in-house and public facing interfaces](/unihalle) for thousands of students and staff. Additionally, conceptualizing, creating and maintaining the blog network & community for all students & staff.",
"highlights": null
},
{
"company": "Harz University of Applied Sciences",
"position": "Consultant & Teacher",
"website": null,
"startDate": "2011-02-01",
"endDate": "2011-05-01",
"summary": "Conceptualizing a web design & development university seminar and building a [responsive & fluid grid framework](https://github.com/kremalicious/hsresponsive) with a basic HTML/CSS template for students of Media Informatics at the Harz University of Applied Sciences to learn and use.",
"highlights": null
},
{
"company": "Martin Luther University Halle-Wittenberg",
"position": "Consultant & Teacher",
"website": null,
"startDate": "2011-02-01",
"endDate": "2011-05-01",
"summary": "Conceptualizing a WordPress-based web design university seminar and building a minimal starting theme for students of media & communication science at the MLU Halle-Wittenberg to learn and use.",
"highlights": null
},
{
"company": "Shortmoves",
"position": "Web Designer & Developer",
"website": null,
"startDate": "2009-01-01",
"endDate": "2010-01-01",
"summary": "Creating & managing the web presence and marketing material of the International Shortfilm Festival Shortmoves in Halle (Saale), Germany.",
"highlights": null
},
{
"company": "Agentur Ahron",
"position": "Co-Founder & Photojournalist & Photographer",
"website": null,
"startDate": "2005-01-01",
"endDate": "2008-12-31",
"summary": "Co-founded and built up a photo agency from the ground up and worked as a photographer ranging from journalistic works for news agencies & newspapers to photographic work for private clients.",
"highlights": null
},
{
"company": "Freelance",
"position": "Designer & Developer",
"website": null,
"startDate": "2004-01-01",
"endDate": null,
"summary": "Numerous projects and clients as a UI/UX Designer, Front End Developer, Icon Designer & Photographer.",
"highlights": null
}
],
"awards": [
{
"title": "German Design Award",
"date": "2015-11-01",
"awarder": "ezeep GmbH",
"summary": "Nominated in the category _Interactive User Experience (Excellent Communications Design)_"
},
{
"title": "CeBIT Preview Award",
"date": "2013-11-01",
"awarder": "ezeep GmbH",
"summary": null
}
]
}
}

View File

@ -49,8 +49,10 @@
"react-helmet": "^5.2.1", "react-helmet": "^5.2.1",
"react-pose": "^4.0.10", "react-pose": "^4.0.10",
"remark": "^11.0.2", "remark": "^11.0.2",
"remark-breaks": "^1.0.3",
"remark-html": "^10.0.0", "remark-html": "^10.0.0",
"remark-parse": "^7.0.2", "remark-parse": "^7.0.2",
"remark-react": "^6.0.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"suncalc": "^1.8.0", "suncalc": "^1.8.0",
"vcf": "^2.0.6" "vcf": "^2.0.6"

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import Helmet from 'react-helmet' import Helmet from 'react-helmet'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useMeta } from '../../hooks/use-meta'` import { useMeta } from '../../hooks/use-meta'
import { useResume } from '../../hooks/use-resume' import { useResume } from '../../hooks/use-resume'
const MetaTags = ({ title, description, url, image, meta }) => { const MetaTags = ({ title, description, url, image, meta }) => {
@ -10,33 +10,34 @@ const MetaTags = ({ title, description, url, image, meta }) => {
({ network }) => network === 'Twitter' ({ network }) => network === 'Twitter'
)[0].username )[0].username
const MetaTags = ({ title, description, url, image, meta }) => ( return (
<Helmet <Helmet
defaultTitle={`${meta.title.toLowerCase()} { ${meta.tagline.toLowerCase()} }`} defaultTitle={`${meta.title.toLowerCase()} { ${meta.tagline.toLowerCase()} }`}
titleTemplate={`%s // ${meta.title.toLowerCase()} { ${meta.tagline.toLowerCase()} }`} titleTemplate={`%s // ${meta.title.toLowerCase()} { ${meta.tagline.toLowerCase()} }`}
title={title} title={title}
> >
<html lang="en" /> <html lang="en" />
{/* General tags */} {/* General tags */}
<meta name="description" content={description} /> <meta name="description" content={description} />
<meta name="image" content={`${meta.url}${image}`} /> <meta name="image" content={`${meta.url}${image}`} />
<link rel="canonical" href={url} /> <link rel="canonical" href={url} />
{/* OpenGraph tags */} {/* OpenGraph tags */}
<meta property="og:url" content={url} /> <meta property="og:url" content={url} />
<meta property="og:title" content={title} /> <meta property="og:title" content={title} />
<meta property="og:description" content={description} /> <meta property="og:description" content={description} />
<meta property="og:image" content={`${meta.url}${image}`} /> <meta property="og:image" content={`${meta.url}${image}`} />
{/* Twitter Card tags */} {/* Twitter Card tags */}
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content={twitterHandle} /> <meta name="twitter:creator" content={twitterHandle} />
<meta name="twitter:title" content={title} /> <meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} /> <meta name="twitter:description" content={description} />
<meta name="twitter:image" content={`${meta.url}${image}`} /> <meta name="twitter:image" content={`${meta.url}${image}`} />
</Helmet> </Helmet>
) )
}
MetaTags.propTypes = { MetaTags.propTypes = {
title: PropTypes.string, title: PropTypes.string,

View File

@ -7,7 +7,6 @@ import { useResume } from '../../hooks/use-resume'
export default function Vcard() { export default function Vcard() {
const metaYaml = useMeta() const metaYaml = useMeta()
const { basics } = useResume() const { basics } = useResume()
const data = useStaticQuery(query)
const photoSrc = basics.picture.childImageSharp.fixed.src const photoSrc = basics.picture.childImageSharp.fixed.src
const { name, label, email, profiles } = basics const { name, label, email, profiles } = basics
@ -56,6 +55,12 @@ export const downloadVcard = (vcard, meta) => {
export const constructVcard = async (dataUrl, meta) => { export const constructVcard = async (dataUrl, meta) => {
const contact = new vCard() const contact = new vCard()
const blog = meta.profiles.filter(({ network }) => network === 'Blog')[0].url
const twitter = meta.profiles.filter(
({ network }) => network === 'Twitter'
)[0].url
const github = meta.profiles.filter(({ network }) => network === 'GitHub')[0]
.url
// stripping this data out of base64 string is required // stripping this data out of base64 string is required
// for vcard to actually display the image for whatever reason // for vcard to actually display the image for whatever reason
@ -66,9 +71,9 @@ export const constructVcard = async (dataUrl, meta) => {
contact.set('email', meta.email) contact.set('email', meta.email)
contact.set('nickname', 'kremalicious') contact.set('nickname', 'kremalicious')
contact.set('url', meta.url, { type: 'Portfolio' }) contact.set('url', meta.url, { type: 'Portfolio' })
contact.add('url', meta.social.Blog, { type: 'Blog' }) contact.add('url', blog, { type: 'Blog' })
contact.add('x-socialprofile', meta.social.Twitter, { type: 'twitter' }) contact.add('x-socialprofile', twitter, { type: 'twitter' })
contact.add('x-socialprofile', meta.social.GitHub, { type: 'GitHub' }) contact.add('x-socialprofile', github, { type: 'GitHub' })
const vcard = contact.toString('3.0') const vcard = contact.toString('3.0')

View File

@ -3,10 +3,12 @@ import PropTypes from 'prop-types'
import posed from 'react-pose' import posed from 'react-pose'
import { moveInTop } from '../atoms/Transitions' import { moveInTop } from '../atoms/Transitions'
import Icon from '../atoms/Icon' import Icon from '../atoms/Icon'
import { useMeta } from '../../hooks/use-meta'
import { useResume } from '../../hooks/use-resume' import { useResume } from '../../hooks/use-resume'
import styles from './Networks.module.scss' import styles from './Networks.module.scss'
const linkClasses = key =>
key === 'Email' ? `u-email ${styles.link}` : `u-url ${styles.link}`
const NetworkLink = ({ name, url }) => ( const NetworkLink = ({ name, url }) => (
<a <a
className={linkClasses(name)} className={linkClasses(name)}
@ -24,25 +26,22 @@ NetworkLink.propTypes = {
} }
export default function Networks({ small, hide }) { export default function Networks({ small, hide }) {
const { social } = useMeta() const { basics } = useResume()
if (hide) return null if (hide) return null
const Animation = posed.aside(moveInTop) const Animation = posed.aside(moveInTop)
const linkClasses = key =>
key === 'Email' ? `u-email ${styles.link}` : `u-url ${styles.link}`
return ( return (
<Animation className={small ? styles.small : styles.networks}> <Animation className={small ? styles.small : styles.networks}>
<NetworkLink name={'Email'} url={`mailto:${basics.email}`} /> <NetworkLink name={'Email'} url={`mailto:${basics.email}`} />
{profiles.map(profile => ( {basics.profiles.map(profile => (
<NetworkLink <NetworkLink
key={profile.network} key={profile.network}
name={profile.network} name={profile.network}
url={profile.url} url={profile.url}
/> />
))} ))}
</Animation> </Animation>
) )
} }

View File

@ -18,9 +18,13 @@ export default function Header({ minimal, isResume }) {
return ( return (
<header className={minimal ? styles.minimal : styles.header}> <header className={minimal ? styles.minimal : styles.header}>
<ThemeSwitch /> <ThemeSwitch />
<LogoUnit minimal={minimal} isResume={isResume} /> {!isResume && (
<Networks hide={minimal} /> <>
<Availability hide={minimal && !availability.status} /> <LogoUnit minimal={minimal} isResume={isResume} />
<Networks hide={minimal} />
<Availability hide={minimal && !availability.status} />
</>
)}
</header> </header>
) )
} }

View File

@ -7,7 +7,6 @@ const query = graphql`
tagline tagline
description description
url url
email
img { img {
childImageSharp { childImageSharp {
resize(width: 980) { resize(width: 980) {
@ -15,21 +14,6 @@ const query = graphql`
} }
} }
} }
avatar {
childImageSharp {
resize {
src
}
}
}
social {
Mail
Blog
Twitter
GitHub
Dribbble
Keybase
}
availability { availability {
status status
available available

View File

@ -23,7 +23,6 @@ const query = graphql`
} }
location { location {
city city
country
countryCode countryCode
} }
} }
@ -52,6 +51,12 @@ const query = graphql`
summary summary
highlights highlights
} }
awards {
title
date
awarder
summary
}
} }
} }
` `

View File

@ -0,0 +1,18 @@
import React from 'react'
import { render } from '@testing-library/react'
import { useStaticQuery } from 'gatsby'
import { useResume } from './use-resume'
import data from '../../jest/__fixtures__/resume.json'
beforeEach(() => {
useStaticQuery.mockImplementationOnce(() => ({ ...data }))
})
describe('useResume', () => {
it('renders correctly', () => {
const { basics } = useResume()
const { container } = render(<div>{basics.name}</div>)
expect(container.textContent).toBe(data.contentJson.basics.name)
})
})

View File

@ -1,14 +1,43 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import remark from 'remark'
import remark2react from 'remark-react'
import parse from 'remark-parse'
import html from 'remark-html'
import breaks from 'remark-breaks'
import styles from './ResumeItem.module.scss' import styles from './ResumeItem.module.scss'
export default function ResumeItem({ workPlace, eduPlace }) { export const markdownOutput = text =>
const title = workPlace ? workPlace.company : eduPlace.institution remark()
const subTitle = workPlace ? workPlace.position : eduPlace.area .use(parse, { gfm: true, commonmark: true, pedantic: true })
const text = workPlace ? workPlace.summary : eduPlace.studyType .use(html)
const { startDate, endDate } = workPlace || eduPlace .use(breaks)
.use(remark2react)
.processSync(text).contents
const dateStart = new Date(startDate).getFullYear() export default function ResumeItem({ workPlace, eduPlace, award }) {
const title = workPlace
? workPlace.company
: award
? award.title
: eduPlace.institution
const subTitle = workPlace
? workPlace.position
: award
? award.awarder
: eduPlace.area
const text = workPlace
? workPlace.summary
: award && award.summary
? award.summary
: eduPlace
? eduPlace.studyType
: null
const { startDate, endDate, date } = workPlace || eduPlace || award
const dateStart = date
? new Date(date).getFullYear()
: new Date(startDate).getFullYear()
const dateEnd = endDate && new Date(endDate).getFullYear() const dateEnd = endDate && new Date(endDate).getFullYear()
const isSameYear = dateStart === dateEnd const isSameYear = dateStart === dateEnd
@ -16,13 +45,15 @@ export default function ResumeItem({ workPlace, eduPlace }) {
<div className={styles.resumeItem}> <div className={styles.resumeItem}>
<span className={styles.time}> <span className={styles.time}>
{dateStart} {dateStart}
{dateEnd ? !isSameYear && `${dateEnd}` : 'present'}{' '} {dateEnd
? !isSameYear && `${dateEnd}`
: !date
? 'present'
: null}{' '}
</span> </span>
<h4 className={styles.title}>{title}</h4> <h4 className={styles.title}>{title}</h4>
<h5 className={styles.subTitle}>{subTitle}</h5> <h5 className={styles.subTitle}>{subTitle}</h5>
<p> {text && markdownOutput(text)}
<em>{text}</em>
</p>
</div> </div>
) )
} }
@ -41,5 +72,11 @@ ResumeItem.propTypes = {
institution: PropTypes.string.isRequired, institution: PropTypes.string.isRequired,
area: PropTypes.string.isRequired, area: PropTypes.string.isRequired,
studyType: PropTypes.string studyType: PropTypes.string
}),
award: PropTypes.shape({
date: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
awarder: PropTypes.string.isRequired,
summary: PropTypes.string
}) })
} }

View File

@ -4,22 +4,27 @@
padding-bottom: $spacer * 3; padding-bottom: $spacer * 3;
padding-left: $spacer; padding-left: $spacer;
position: relative; position: relative;
border-left: 1px solid $brand-grey-light; border-left: 1px solid rgba($brand-grey-light, 0.25);
&::before { &::before {
content: ''; content: '';
display: block; display: block;
width: 1rem; width: $font-size-small;
height: 1rem; height: $font-size-small;
border-radius: 50%; border-radius: 50%;
background: $brand-grey-light; background: $brand-grey-light;
position: absolute; position: absolute;
left: -0.5rem; left: -($font-size-small / 2);
top: 0.1rem;
} }
p:last-child { p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
&:last-child {
border: none;
}
} }
.title { .title {

View File

@ -7,7 +7,8 @@ import styles from './index.module.scss'
import ResumeItem from './ResumeItem' import ResumeItem from './ResumeItem'
export default function Resume() { export default function Resume() {
const { basics, education, languages, work } = useResume() const { basics, education, languages, work, awards } = useResume()
const { name, label, email, website, location } = basics
return ( return (
<> <>
@ -16,25 +17,25 @@ export default function Resume() {
<div className={styles.resume}> <div className={styles.resume}>
<header> <header>
<p>Résumé</p> <p>Résumé</p>
<h1 className={styles.title}>{basics.name}</h1> <h1 className={styles.title}>{name}</h1>
<h2 className={styles.label}>{basics.label}</h2> <h2 className={styles.label}>{label}</h2>
</header> </header>
<div> <div>
<ul className={styles.contact}> <ul className={styles.contact}>
<li> <li>
<a href={basics.website}> <a href={website}>
<LinkIcon type="website" /> <LinkIcon type="website" />
{basics.website.replace('https://', '')} Portfolio
</a> </a>
</li> </li>
<li> <li>
<LinkIcon type="Email" /> <LinkIcon type="Email" />
{basics.email} <a href={`mailto:${email}`}>Email</a>
</li> </li>
<li> <li>
<LinkIcon type="Info" /> <LinkIcon type="Info" />
{basics.location.city} {location.city}, {location.countryCode}
</li> </li>
<li> <li>
<LinkIcon type="Info" /> <LinkIcon type="Info" />
@ -56,6 +57,15 @@ export default function Resume() {
))} ))}
</div> </div>
<div>
<h3 className={styles.subTitle}>Awards</h3>
</div>
<div>
{awards.map(award => (
<ResumeItem key={shortid.generate()} award={award} />
))}
</div>
<div> <div>
<h3 className={styles.subTitle}>Education</h3> <h3 className={styles.subTitle}>Education</h3>
</div> </div>

View File

@ -46,13 +46,14 @@
} }
@media (min-width: $screen-md) { @media (min-width: $screen-md) {
margin-top: $spacer * 2; margin-top: $spacer * 2.25;
} }
} }
.subTitle { .subTitle {
font-size: $font-size-h3; font-size: $font-size-h3;
margin-bottom: 0; margin-bottom: 0;
margin-top: -($spacer / 3);
} }
@media print { @media print {