Add Projects-Module

This commit is contained in:
Kevin Kandlbinder 2020-12-21 21:23:52 +01:00
parent 8757b1afb3
commit 688c63910c
13 changed files with 546 additions and 7 deletions

BIN
content/images/test.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

View file

@ -0,0 +1,12 @@
{
"urlname": "kevink-dev",
"lang": "de",
"name": "KevinK.dev",
"shortDescription": "Diese Seite und der Code dahinter.",
"longDescription": "Schon so lange ich im Internet unterwegs bin war ich fasziniert von der Idee eine eigene Website zu haben. Deshalb habe ich mich an diese Seite gesetzt um mich selbst und meine Projekte zu repräsentieren. Wenn du interessiert an dem Code hinter der Seite bist, schau doch gerne auf GitHub vorbei!",
"links": {
"website": "https://kevink.dev",
"github": "https://github.com"
},
"image": "../images/test.jpg"
}

View file

@ -0,0 +1,12 @@
{
"urlname": "kevink-dev",
"lang": "en",
"name": "KevinK.dev",
"shortDescription": "This site and the code behind the scenes.",
"longDescription": "For as long as I've been poking around the internet I've always been facinated by the idea to have my own website. This is why I've created this site to showcase myself and my projects. If you are interested in the code behind the scenes feel free to look at it on GitHub!",
"links": {
"website": "https://kevink.dev",
"github": "https://github.com"
},
"image": "../images/test.jpg"
}

View file

@ -6,6 +6,13 @@ module.exports = {
},
plugins: [
`gatsby-plugin-sharp`,
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./content/`,
},
},
`gatsby-plugin-sass`,
{
resolve: `gatsby-plugin-manifest`,
@ -45,7 +52,14 @@ module.exports = {
},
keySeparator: false,
nsSeparator: false
},
pages: [
{
matchPath: '/:lang/projects/:urlname',
getLanguageFromPath: true,
excludeLanguages: ['en', 'de']
}
]
}
}
]

38
gatsby-node.js Normal file
View file

@ -0,0 +1,38 @@
const path = require(`path`);
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions
const projectTemplate = path.resolve(`src/templates/project.js`)
const result = await graphql(`
query AllPagesQuery {
allProjectsJson {
nodes {
lang
urlname
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
result.data.allProjectsJson.nodes.forEach((node) => {
console.log("Creating Page: ", `/${node.lang}/projects/${node.urlname}`);
createPage({
path: `/${node.lang}/projects/${node.urlname}`,
component: projectTemplate,
context: {
lang: node.lang,
urlname: node.urlname
}
})
});
}

View file

@ -4,6 +4,7 @@
"datasec": "Datenschutz",
"disclaimer": "Disclaimer",
"projects": "Projekte",
"project": "Projekt",
"social": "Soziales",
"homeHello": "Hallo, ich bin",
"homeMe": "Ich bin",
@ -15,5 +16,10 @@
"donationCatchphrase": "Gefällt dir was du siehst? Spende doch etwas.",
"homeImageCredit": "Portrait aufgenommen von Jannik Kiel",
"de": "Deutsch",
"en": "Englisch"
"en": "Englisch",
"projectAboutHeader": "Über {{projectName}}",
"projectViewGitHub": "Auf GitHub anschauen",
"projectViewWebsite": "Projekt-Website anschauen",
"projectsDescription": "Das ist woran ich grade arbeite oder woran ich gearbeitet habe.",
"projectView": "Anschauen"
}

View file

@ -4,6 +4,7 @@
"datasec": "Data Protection",
"disclaimer": "Disclaimer",
"projects": "Projects",
"project": "Project",
"social": "Social",
"homeHello": "Hello, I am",
"homeMe": "I am",
@ -15,5 +16,10 @@
"donationCatchphrase": "Like what you're seeing? Consider donating.",
"homeImageCredit": "Portrait taken by Jannik Kiel",
"de": "German",
"en": "English"
"en": "English",
"projectAboutHeader": "About {{projectName}}",
"projectViewGitHub": "View on GitHub",
"projectViewWebsite": "View Project-Website",
"projectsDescription": "This is what I am working on or have worked on.",
"projectView": "View"
}

134
package-lock.json generated
View file

@ -2137,6 +2137,11 @@
"defer-to-connect": "^1.0.1"
}
},
"@tokenizer/token": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.1.1.tgz",
"integrity": "sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w=="
},
"@turist/fetch": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/@turist/fetch/-/fetch-7.1.7.tgz",
@ -2345,6 +2350,15 @@
"csstype": "^3.0.2"
}
},
"@types/readable-stream": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz",
"integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==",
"requires": {
"@types/node": "*",
"safe-buffer": "*"
}
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -8832,6 +8846,65 @@
}
}
},
"gatsby-source-filesystem": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/gatsby-source-filesystem/-/gatsby-source-filesystem-2.8.0.tgz",
"integrity": "sha512-wZXwL/B+PvgSAi4Fkrw33yBk6FdDoz6g2mRfS6NxVVQotbA2oNe+jFc+IL0j9/cUMTItP3rW+8V8U9ufASQfAw==",
"requires": {
"@babel/runtime": "^7.12.5",
"better-queue": "^3.8.10",
"chokidar": "^3.4.3",
"file-type": "^16.0.0",
"fs-extra": "^8.1.0",
"gatsby-core-utils": "^1.7.0",
"got": "^9.6.0",
"md5-file": "^5.0.0",
"mime": "^2.4.6",
"pretty-bytes": "^5.4.1",
"progress": "^2.0.3",
"valid-url": "^1.0.9",
"xstate": "^4.14.0"
},
"dependencies": {
"file-type": {
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.1.0.tgz",
"integrity": "sha512-G4Klqf6tuprtG0pC4r9kni4Wv8XhAAsfHphVqsQGA+YiOlPAO40BZduDqKfv0RFsu9q9ZbFObWfwszY/NqhEZw==",
"requires": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.0.3",
"token-types": "^2.0.0",
"typedarray-to-buffer": "^3.1.5"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"requires": {
"pump": "^3.0.0"
}
},
"got": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
"integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
"requires": {
"@sindresorhus/is": "^0.14.0",
"@szmarczak/http-timer": "^1.1.2",
"cacheable-request": "^6.0.0",
"decompress-response": "^3.3.0",
"duplexer3": "^0.1.4",
"get-stream": "^4.1.0",
"lowercase-keys": "^1.0.1",
"mimic-response": "^1.0.1",
"p-cancelable": "^1.0.0",
"to-readable-stream": "^1.0.0",
"url-parse-lax": "^3.0.0"
}
}
}
},
"gatsby-telemetry": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.7.0.tgz",
@ -8853,6 +8926,15 @@
"uuid": "3.4.0"
}
},
"gatsby-transformer-json": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/gatsby-transformer-json/-/gatsby-transformer-json-2.8.0.tgz",
"integrity": "sha512-fKexGFE5omoubberI2LkM9nI73F0TZuVdZsywkyaeTZgv71SftHjiq3xHh4e8PuKjhvR+MIPPsn1owmVb+UMCw==",
"requires": {
"@babel/runtime": "^7.12.5",
"bluebird": "^3.7.2"
}
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@ -13165,6 +13247,11 @@
"sha.js": "^2.4.8"
}
},
"peek-readable": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.0.tgz",
"integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA=="
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@ -14787,6 +14874,27 @@
"util-deprecate": "~1.0.1"
}
},
"readable-web-to-node-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.0.tgz",
"integrity": "sha512-HNmLb3n0SteGAs8HQlErYPGeO+y7cvL/mVUKtXeUkl0iCZ/2GIgKGrCFHyS7UXFnO8uc9U+0y3pYIzAPsjFfvA==",
"requires": {
"@types/readable-stream": "^2.3.9",
"readable-stream": "^3.6.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"readdirp": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
@ -16861,6 +16969,23 @@
"escape-string-regexp": "^1.0.2"
}
},
"strtok3": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.4.tgz",
"integrity": "sha512-rqWMKwsbN9APU47bQTMEYTPcwdpKDtmf1jVhHzNW2cL1WqAxaM9iBb9t5P2fj+RV2YsErUWgQzHD5JwV0uCTEQ==",
"requires": {
"@tokenizer/token": "^0.1.1",
"@types/debug": "^4.1.5",
"peek-readable": "^3.1.0"
},
"dependencies": {
"@types/debug": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
}
}
},
"style-loader": {
"version": "0.23.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
@ -17356,6 +17481,15 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"token-types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-2.0.0.tgz",
"integrity": "sha512-WWvu8sGK8/ZmGusekZJJ5NM6rRVTTDO7/bahz4NGiSDb/XsmdYBn6a1N/bymUHuWYTWeuLUg98wUzvE4jPdCZw==",
"requires": {
"@tokenizer/token": "^0.1.0",
"ieee754": "^1.1.13"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",

View file

@ -28,6 +28,8 @@
"gatsby-plugin-sass": "^2.8.0",
"gatsby-plugin-sharp": "^2.11.1",
"gatsby-plugin-sitemap": "^2.9.0",
"gatsby-source-filesystem": "^2.8.0",
"gatsby-transformer-json": "^2.8.0",
"i18next": "^19.8.4",
"jquery": "^3.5.1",
"node-sass": "^4.14.1",

View file

@ -2,16 +2,55 @@ import React from "react"
import Layout from "../layouts/default";
import { Trans, Link, useI18next } from "gatsby-plugin-react-i18next"
export default function ProjectsPage() {
import styles from "./projects.module.scss";
export const query = graphql`
query GetProjects($language: String!) {
allProjectsJson(filter: {lang: {eq: $language}}) {
nodes {
lang
urlname
name
image {
publicURL
}
shortDescription
}
}
}
`
export default function ProjectsPage({data}) {
const {t} = useI18next();
return (
<Layout module="projects" title={t("projects")}>
<Layout module="projects" title={t("projects")} description={t("projectsDescription")}>
<section>
<div>
<article>
<h1><Trans>projects</Trans></h1>
<p><Trans>projectsDescription</Trans></p>
<div className={styles.projectList}>
{data.allProjectsJson.nodes.map((project) => {
return (
<div className={styles.projectCard}>
{/*<div className="projectCardActivityIndicator activityIndicatorBlue">Live</div>*/}
<div className={styles.projectCardImage} style={{ backgroundImage: "url("+project.image.publicURL+")" }}></div>
<div className={styles.projectCardMeta}>
<span className={styles.projectCardTitle}>{project.name}</span>
<span className={styles.projectCardTeaser}>{project.shortDescription}</span>
</div>
<div className={styles.projectCardCTAContainer}>
<div className={styles.projectCardCTA}><Link to={"/projects/"+project.urlname}><Trans>projectView</Trans></Link></div>
</div>
</div>
);
})}
</div>
{/*<pre>{JSON.stringify(data, null, 2)}</pre>*/}
</article>
</section>
</Layout>
);

View file

@ -0,0 +1,127 @@
@import "../variables";
.projectList {
display: flex;
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
margin: 15px 0;
}
.projectCard {
background: $background;
border-radius: 4px;
overflow: hidden;
margin: 15px;
color: var(--text-color);
display: flex;
flex-direction: column;
flex-grow: 1;
width: calc(100% / 4);
box-shadow: -1px 11px 33px -10px rgba(127,127,127,0.3);
max-width: 300px;
transition: transform .25s, box-shadow .25s;
}
.projectCard:hover {
transform: scale(1.05);
box-shadow: -1px 11px 33px -10px rgba(127,127,127,0.4);
}
@media(max-width: 800px) {
.projectCard {
width: calc((100% / 2) - 30px);
}
}
@media(max-width: 500px) {
.projectCard {
width: 100%;
}
}
.projectCardActivityIndicator {
position: absolute;
margin: 12px;
padding: 2px 5px;
border-radius: 3px;
}
.activityIndicatorGreen {
background: #26de81;
}
.activityIndicatorYellow {
background: #f7b731;
}
.activityIndicatorRed {
background: #fc5c65;
}
.activityIndicatorBlue {
background: #45aaf2;
}
.projectCardImage {
display: block;
height: 150px;
background-position: center;
background-size: cover;
}
.projectCardMeta {
padding: 10px;
flex-grow: 1;
}
.projectCardTitle {
display: block;
font-size: 1.2em;
}
.projectCardCTA {
display: block;
}
.projectCardCTA a {
display: block;
padding: 10px;
text-align: center;
background: var(--accent-color);
color: white;
text-decoration: none;
}
.projectCardCTAContainer {
display: flex;
}
@media(max-width: 900px) {
.projectCardCTAContainer {
flex-direction: column;
}
.projectCardCTA:nth-child(2) {
border-left: none;
}
}
.projectCardCTAContainer > * {
flex-grow: 1;
}
.projectCardCTA {
display: block;
}
.projectCardCTA a {
display: block;
padding: 10px;
text-align: center;
background: $accentColor;
color: white;
text-decoration: none;
}

75
src/templates/project.js Normal file
View file

@ -0,0 +1,75 @@
import React from "react"
import {graphql} from "gatsby"
import {Link, Trans, useTranslation} from 'gatsby-plugin-react-i18next';
import Layout from "../layouts/default";
import styles from "./project.module.scss";
export const query = graphql`
query GetProject($urlname: String!, $lang: String!) {
allProjectsJson(filter: {urlname: {eq: $urlname}, lang: {eq: $lang}}) {
nodes {
lang
urlname
name
links {
github
website
}
image {
publicURL
}
longDescription
shortDescription
}
}
}
`
export default function ProjectTemplate({data}) {
let project = data.allProjectsJson.nodes[0];
let projectName = project.name;
return (
<Layout>
<section className={styles.projectHeader}>
<div>
<div className={styles.headerBackground} style={{backgroundImage: "url("+project.image.publicURL+")"}}></div>
<header>
<div className={styles.headerInner}>
<h1><Trans>project</Trans>: {projectName}</h1>
<span className={styles.postMeta}>{project.shortDescription}</span>
</div>
</header>
<div className={styles.headerPlaceholder}></div>
</div>
</section>
{project.longDescription != null ?
<section className={styles.projectAbout}>
<article>
<h1><Trans projectName={projectName} i18nKey="projectAboutHeader">projectAboutHeader{{projectName}}</Trans></h1>
<p>{project.longDescription}</p>
</article>
</section>
: null}
{project.links.github !== null || project.links.website !== null ?
<section className={styles.projectLinks}>
<div>
<h1>Links</h1>
<div className={styles.linkList}>
{project.links.github !== null ? <a href={project.links.github} target="_blank"><i className="fab fa-github" aria-hidden="true"></i> <Trans>projectViewGitHub</Trans></a> : null}
{project.links.website !== null ? <a href={project.links.website} target="_blank"><i className="fas fa-external-link-alt" aria-hidden="true"></i> <Trans>projectViewWebsite</Trans></a> : null}
</div>
</div>
</section>
: null}
{/*<section>
<div>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
</section>*/}
</Layout>
);
}

View file

@ -0,0 +1,74 @@
@import "../variables";
.projectHeader .headerBackground {
position: absolute;
left: 0;
width: 100%;
height: 200px;
background-position: center;
background-size: cover;
}
.projectHeader header {
position: absolute;
left: 0;
width: 100%;
height: 200px;
background: rgba(0, 0, 0, .5);
}
.projectHeader .headerInner {
max-width: $layoutWidth;
width: 100%;
margin: 0 auto;
padding: 65px 20px 0;
}
.projectHeader .headerInner * {
margin: 0;
color: white;
}
.headerPlaceholder {
width: 100%;
height: 225px;
}
.projectHeader .headerPlaceholder {
width: 100%;
height: 200px;
}
.projectHeader h1 {
font-size: 2em;
}
.projectHeader > div {
padding-bottom: 0 !important;
}
.projectAbout {
background: #060606;
color: white;
}
.projectLinks .linkList {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.projectLinks a {
display: inline-block;
border: thin solid rgb(200, 200, 200);
border-radius: 5px;
padding: 10px 15px;
margin: 5px;
color: $textColor;
text-decoration-skip: none;
text-decoration: underline dotted currentColor;
}
.projectLinks a i.fab, .projectLinks a i.fas, .projectLinks a i.fa, .projectLinks a i.far, .projectLinks a i.fal {
display: inline;
}