mirror of
https://github.com/Unkn0wnCat/KevinK.dev.js.git
synced 2025-04-28 09:46:52 +02:00
Add way to disable site modules in config
This commit is contained in:
parent
a972bf6234
commit
f704547b25
8 changed files with 359 additions and 210 deletions
|
@ -3,6 +3,11 @@ module.exports = {
|
|||
siteName: "KevinK.dev",
|
||||
siteAuthor: "@Unkn0wnKevin",
|
||||
siteURL: "https://kevink.dev",
|
||||
modules: {
|
||||
blog: true,
|
||||
projects: true,
|
||||
donation: true,
|
||||
},
|
||||
payPalMail: "kevin@1in9.net",
|
||||
siteKeywords:
|
||||
"Kevin Kandlbinder, Kevin, Kandlbinder, Web, Web Developer, Developer, JavaScript, PHP, Java, Photos, Fotos",
|
||||
|
|
175
gatsby-config.js
175
gatsby-config.js
|
@ -1,11 +1,101 @@
|
|||
/* eslint-disable no-undef */
|
||||
const extConfig = require("./config");
|
||||
|
||||
const dynamicPlugins = [];
|
||||
|
||||
if (extConfig.modules.blog) {
|
||||
dynamicPlugins.push({
|
||||
resolve: `gatsby-plugin-feed`,
|
||||
options: {
|
||||
query: `
|
||||
{
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
description
|
||||
siteUrl
|
||||
site_url: siteUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
feeds: [
|
||||
{
|
||||
serialize: ({ query: { site, blog } }) => {
|
||||
return blog.nodes.map((node) => {
|
||||
if (!node.childMdx) return null;
|
||||
|
||||
return {
|
||||
title: node.childMdx.frontmatter.title,
|
||||
description: node.childMdx.excerpt,
|
||||
date: node.childMdx.frontmatter.date,
|
||||
url:
|
||||
site.siteMetadata.siteUrl +
|
||||
`/${
|
||||
node.childMdx.frontmatter.language
|
||||
}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter
|
||||
.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
guid:
|
||||
site.siteMetadata.siteUrl +
|
||||
`/${
|
||||
node.childMdx.frontmatter.language
|
||||
}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter
|
||||
.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
category: node.childMdx.frontmatter.section,
|
||||
};
|
||||
});
|
||||
},
|
||||
query: `
|
||||
{
|
||||
blog: allFile(
|
||||
filter: { sourceInstanceName: { eq: "blogContent" } }
|
||||
) {
|
||||
nodes {
|
||||
childMdx {
|
||||
id
|
||||
body
|
||||
excerpt
|
||||
frontmatter {
|
||||
platform
|
||||
tags
|
||||
title
|
||||
url
|
||||
section
|
||||
language
|
||||
published(formatString: "YYYY/MM")
|
||||
date: published
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
output: "/blog/feed.rss",
|
||||
title: extConfig.siteName + " Blog",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
title: extConfig.siteName,
|
||||
author: extConfig.siteAuthor,
|
||||
siteUrl: extConfig.siteURL,
|
||||
modules: extConfig.modules,
|
||||
keywords: extConfig.siteKeywords,
|
||||
payPalMail: extConfig.payPalMail,
|
||||
contactEmail: extConfig.contactEmail,
|
||||
|
@ -146,89 +236,6 @@ module.exports = {
|
|||
generateMatchPathRewrites: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-plugin-feed`,
|
||||
options: {
|
||||
query: `
|
||||
{
|
||||
site {
|
||||
siteMetadata {
|
||||
title
|
||||
description
|
||||
siteUrl
|
||||
site_url: siteUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
feeds: [
|
||||
{
|
||||
serialize: ({ query: { site, blog } }) => {
|
||||
return blog.nodes.map((node) => {
|
||||
if (!node.childMdx) return null;
|
||||
|
||||
return {
|
||||
title: node.childMdx.frontmatter.title,
|
||||
description: node.childMdx.excerpt,
|
||||
date: node.childMdx.frontmatter.date,
|
||||
url:
|
||||
site.siteMetadata.siteUrl +
|
||||
`/${
|
||||
node.childMdx.frontmatter.language
|
||||
}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter
|
||||
.section + "/"
|
||||
: ""
|
||||
}${
|
||||
node.childMdx.frontmatter.published
|
||||
}/${node.childMdx.frontmatter.url}`,
|
||||
guid:
|
||||
site.siteMetadata.siteUrl +
|
||||
`/${
|
||||
node.childMdx.frontmatter.language
|
||||
}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter
|
||||
.section + "/"
|
||||
: ""
|
||||
}${
|
||||
node.childMdx.frontmatter.published
|
||||
}/${node.childMdx.frontmatter.url}`,
|
||||
category: node.childMdx.frontmatter.section,
|
||||
};
|
||||
});
|
||||
},
|
||||
query: `
|
||||
{
|
||||
blog: allFile(
|
||||
filter: { sourceInstanceName: { eq: "blogContent" } }
|
||||
) {
|
||||
nodes {
|
||||
childMdx {
|
||||
id
|
||||
body
|
||||
excerpt
|
||||
frontmatter {
|
||||
platform
|
||||
tags
|
||||
title
|
||||
url
|
||||
section
|
||||
language
|
||||
published(formatString: "YYYY/MM")
|
||||
date: published
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
output: "/blog/feed.rss",
|
||||
title: extConfig.siteName + " Blog",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...dynamicPlugins,
|
||||
],
|
||||
};
|
||||
|
|
325
gatsby-node.js
325
gatsby-node.js
|
@ -2,6 +2,8 @@
|
|||
const path = require(`path`);
|
||||
const { paginate } = require("gatsby-awesome-pagination");
|
||||
|
||||
const { modules: moduleConfig, languages } = require("./config.js");
|
||||
|
||||
exports.createPages = async ({ actions, graphql, reporter }) => {
|
||||
const { createPage, createRedirect } = actions;
|
||||
|
||||
|
@ -48,129 +50,120 @@ exports.createPages = async ({ actions, graphql, reporter }) => {
|
|||
return;
|
||||
}
|
||||
|
||||
activity.setStatus("Generating project pages...");
|
||||
if (moduleConfig.projects) {
|
||||
activity.setStatus("Generating project pages...");
|
||||
|
||||
result.data.allProjectsJson.nodes.forEach((node) => {
|
||||
if (node.lang === "ignoreme") return;
|
||||
|
||||
reporter.info(
|
||||
"Creating Page: " + `/${node.lang}/projects/${node.urlname}`
|
||||
);
|
||||
|
||||
createPage({
|
||||
path: `/${node.lang}/projects/${node.urlname}`,
|
||||
component: projectTemplate,
|
||||
context: {
|
||||
lang: node.lang,
|
||||
urlname: node.urlname,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
activity.setStatus("Generating blog pages...");
|
||||
|
||||
const blogListingTemplate = path.resolve(`src/templates/blogListing.js`);
|
||||
const blogTemplate = path.resolve(`src/templates/blogPost.js`);
|
||||
|
||||
reporter.info("Creating blog listings...");
|
||||
|
||||
["en", "de"].forEach((lang) =>
|
||||
paginate({
|
||||
createPage,
|
||||
items: result.data.blog.nodes,
|
||||
itemsPerPage: 10,
|
||||
pathPrefix: `/${lang}/blog`,
|
||||
component: blogListingTemplate,
|
||||
context: {
|
||||
lang,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
let processedSections = ["blog"];
|
||||
|
||||
result.data.blog.nodes.forEach((node) => {
|
||||
if (!node.childMdx) return;
|
||||
|
||||
if (
|
||||
!processedSections.includes(
|
||||
node.childMdx.frontmatter.section ?? "blog"
|
||||
)
|
||||
) {
|
||||
processedSections.push(node.childMdx.frontmatter.section);
|
||||
result.data.allProjectsJson.nodes.forEach((node) => {
|
||||
if (node.lang === "ignoreme") return;
|
||||
|
||||
reporter.info(
|
||||
"Creating section listing for " +
|
||||
node.childMdx.frontmatter.section +
|
||||
"..."
|
||||
"Creating Page: " + `/${node.lang}/projects/${node.urlname}`
|
||||
);
|
||||
|
||||
["en", "de"].forEach((lang) =>
|
||||
paginate({
|
||||
createPage,
|
||||
items: result.data.blog.nodes.filter(
|
||||
(e) =>
|
||||
e.childMdx.frontmatter.section ===
|
||||
node.childMdx.frontmatter.section
|
||||
),
|
||||
itemsPerPage: 10,
|
||||
pathPrefix: `/${lang}/blog/${node.childMdx.frontmatter.section}`,
|
||||
component: blogListingTemplate,
|
||||
context: {
|
||||
lang,
|
||||
section: node.childMdx.frontmatter.section,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
createPage({
|
||||
path: `/${node.lang}/projects/${node.urlname}`,
|
||||
component: projectTemplate,
|
||||
context: {
|
||||
lang: node.lang,
|
||||
urlname: node.urlname,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
reporter.info(
|
||||
"Creating Page: " +
|
||||
`/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section ?? "blog"
|
||||
}/${node.childMdx.frontmatter.url}`
|
||||
if (moduleConfig.blog) {
|
||||
activity.setStatus("Generating blog pages...");
|
||||
|
||||
const blogListingTemplate = path.resolve(
|
||||
`src/templates/blogListing.js`
|
||||
);
|
||||
const blogTemplate = path.resolve(`src/templates/blogPost.js`);
|
||||
|
||||
reporter.info("Creating blog listings...");
|
||||
|
||||
languages.forEach((lang) =>
|
||||
paginate({
|
||||
createPage,
|
||||
items: result.data.blog.nodes,
|
||||
itemsPerPage: 10,
|
||||
pathPrefix: `/${lang}/blog`,
|
||||
component: blogListingTemplate,
|
||||
context: {
|
||||
lang,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
createPage({
|
||||
path: `/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
component: blogTemplate,
|
||||
context: {
|
||||
mdxId: node.childMdx.id,
|
||||
lang: node.childMdx.frontmatter.language,
|
||||
},
|
||||
});
|
||||
let processedSections = ["blog"];
|
||||
|
||||
["en", "de"].forEach((lang) => {
|
||||
if (lang === node.childMdx.frontmatter.language) return;
|
||||
result.data.blog.nodes.forEach((node) => {
|
||||
if (!node.childMdx) return;
|
||||
|
||||
createRedirect({
|
||||
fromPath: `/${lang}/blog/${
|
||||
if (
|
||||
!processedSections.includes(
|
||||
node.childMdx.frontmatter.section ?? "blog"
|
||||
)
|
||||
) {
|
||||
processedSections.push(node.childMdx.frontmatter.section);
|
||||
|
||||
reporter.info(
|
||||
"Creating section listing for " +
|
||||
node.childMdx.frontmatter.section +
|
||||
"..."
|
||||
);
|
||||
|
||||
languages.forEach((lang) =>
|
||||
paginate({
|
||||
createPage,
|
||||
items: result.data.blog.nodes.filter(
|
||||
(e) =>
|
||||
e.childMdx.frontmatter.section ===
|
||||
node.childMdx.frontmatter.section
|
||||
),
|
||||
itemsPerPage: 10,
|
||||
pathPrefix: `/${lang}/blog/${node.childMdx.frontmatter.section}`,
|
||||
component: blogListingTemplate,
|
||||
context: {
|
||||
lang,
|
||||
section: node.childMdx.frontmatter.section,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
reporter.info(
|
||||
"Creating Page: " +
|
||||
`/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section ?? "blog"
|
||||
}/${node.childMdx.frontmatter.url}`
|
||||
);
|
||||
|
||||
createPage({
|
||||
path: `/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
toPath: `/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
redirectInBrowser: true,
|
||||
permanent: true,
|
||||
component: blogTemplate,
|
||||
context: {
|
||||
mdxId: node.childMdx.id,
|
||||
lang: node.childMdx.frontmatter.language,
|
||||
},
|
||||
});
|
||||
|
||||
if (node.childMdx.frontmatter.section === "scambox") {
|
||||
languages.forEach((lang) => {
|
||||
if (lang === node.childMdx.frontmatter.language) return;
|
||||
|
||||
createRedirect({
|
||||
fromPath: `/${lang}/scambox/${node.childMdx.frontmatter.url}`,
|
||||
fromPath: `/${lang}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
toPath: `/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter.section + "/"
|
||||
|
@ -181,10 +174,130 @@ exports.createPages = async ({ actions, graphql, reporter }) => {
|
|||
redirectInBrowser: true,
|
||||
permanent: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (node.childMdx.frontmatter.section === "scambox") {
|
||||
createRedirect({
|
||||
fromPath: `/${lang}/scambox/${node.childMdx.frontmatter.url}`,
|
||||
toPath: `/${node.childMdx.frontmatter.language}/blog/${
|
||||
node.childMdx.frontmatter.section
|
||||
? node.childMdx.frontmatter.section + "/"
|
||||
: ""
|
||||
}${node.childMdx.frontmatter.published}/${
|
||||
node.childMdx.frontmatter.url
|
||||
}`,
|
||||
redirectInBrowser: true,
|
||||
permanent: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
activity.setStatus("Pages generated.");
|
||||
activity.end();
|
||||
};
|
||||
|
||||
exports.onCreatePage = ({ page, actions: { deletePage } }) => {
|
||||
if (!moduleConfig.projects) {
|
||||
if (page.path.startsWith("/projects")) {
|
||||
deletePage(page);
|
||||
return;
|
||||
}
|
||||
|
||||
languages.forEach((lng) => {
|
||||
if (page.path.startsWith(`/${lng}/projects`)) {
|
||||
deletePage(page);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!moduleConfig.donation) {
|
||||
if (page.path.startsWith("/donate")) {
|
||||
deletePage(page);
|
||||
return;
|
||||
}
|
||||
|
||||
languages.forEach((lng) => {
|
||||
if (page.path.startsWith(`/${lng}/donate`)) {
|
||||
deletePage(page);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!moduleConfig.blog) {
|
||||
if (page.path.startsWith("/blog")) {
|
||||
deletePage(page);
|
||||
return;
|
||||
}
|
||||
|
||||
languages.forEach((lng) => {
|
||||
if (page.path.startsWith(`/${lng}/blog`)) {
|
||||
deletePage(page);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.createSchemaCustomization = ({ actions }) => {
|
||||
const { createTypes } = actions;
|
||||
const typeDefs = `
|
||||
type MultiLocaleString {
|
||||
en: String
|
||||
de: String
|
||||
}
|
||||
|
||||
|
||||
type CareerJson implements Node {
|
||||
type: String
|
||||
title: MultiLocaleString
|
||||
sortDate(difference: String
|
||||
formatString: String
|
||||
fromNow: Boolean
|
||||
locale: String): Date
|
||||
startDate: MultiLocaleString
|
||||
endDate: MultiLocaleString
|
||||
description: MultiLocaleString
|
||||
externalLink: String
|
||||
}
|
||||
|
||||
type FriendsJson implements Node {
|
||||
profession: String
|
||||
name: String
|
||||
url: String
|
||||
imageURL: String
|
||||
localImage: File
|
||||
}
|
||||
|
||||
type ProjectsJsonLinks {
|
||||
website: String
|
||||
github: String
|
||||
}
|
||||
|
||||
type ProjectsJson implements Node {
|
||||
urlname: String
|
||||
lang: String
|
||||
name: String
|
||||
shortDescription: String
|
||||
links: ProjectsJsonLinks
|
||||
#image: File
|
||||
featured: Int
|
||||
date: String
|
||||
}
|
||||
|
||||
type SocialsJson implements Node {
|
||||
platformName: String
|
||||
platformHandle: String
|
||||
url: String
|
||||
image: String
|
||||
localImage: File
|
||||
}
|
||||
|
||||
type SkillsJson implements Node {
|
||||
name: String
|
||||
href: String
|
||||
type: String
|
||||
}
|
||||
|
||||
`;
|
||||
createTypes(typeDefs);
|
||||
};
|
||||
|
|
|
@ -7,12 +7,15 @@ import { graphql, StaticQuery } from "gatsby";
|
|||
import * as styles from "./navigation.module.scss";
|
||||
import { Fade as Hamburger } from "hamburger-react";
|
||||
import OffScreenNav from "./offscreenNav";
|
||||
import useSiteMetadata from "../helpers/useSiteMetadata";
|
||||
|
||||
const Navigation = ({ isHome }) => {
|
||||
let [atTop, setAtTop] = useState(false);
|
||||
const [offscreenNavActive, setOffscreenNavActive] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { modules } = useSiteMetadata();
|
||||
|
||||
const closeOffscreenNav = () => setOffscreenNavActive(false);
|
||||
|
||||
const updateTransparency = () => {
|
||||
|
@ -87,15 +90,19 @@ const Navigation = ({ isHome }) => {
|
|||
<Link to="/about" activeClassName={styles.active}>
|
||||
<Trans>about.title</Trans>
|
||||
</Link>
|
||||
<Link to="/projects" activeClassName={styles.active}>
|
||||
<Trans>project.plural</Trans>
|
||||
</Link>
|
||||
{modules.projects && (
|
||||
<Link to="/projects" activeClassName={styles.active}>
|
||||
<Trans>project.plural</Trans>
|
||||
</Link>
|
||||
)}
|
||||
<Link to="/social" activeClassName={styles.active}>
|
||||
<Trans>social.title</Trans>
|
||||
</Link>
|
||||
<Link to="/blog" activeClassName={styles.active}>
|
||||
<Trans>blog.title</Trans>
|
||||
</Link>
|
||||
{modules.blog && (
|
||||
<Link to="/blog" activeClassName={styles.active}>
|
||||
<Trans>blog.title</Trans>
|
||||
</Link>
|
||||
)}
|
||||
<div className={styles.hamburger}>
|
||||
<Hamburger
|
||||
toggle={setOffscreenNavActive}
|
||||
|
|
|
@ -6,9 +6,12 @@ import { createPortal } from "react-dom";
|
|||
|
||||
import * as styles from "./navigation.module.scss";
|
||||
import { X } from "lucide-react";
|
||||
import useSiteMetadata from "../helpers/useSiteMetadata";
|
||||
|
||||
const OffScreenNav = ({ active, close }) => {
|
||||
const { t } = useTranslation();
|
||||
const { modules } = useSiteMetadata();
|
||||
|
||||
if (typeof document === "undefined") return <></>;
|
||||
|
||||
return createPortal(
|
||||
|
@ -34,15 +37,19 @@ const OffScreenNav = ({ active, close }) => {
|
|||
<Link to="/about" activeClassName={styles.active}>
|
||||
<Trans>about.title</Trans>
|
||||
</Link>
|
||||
<Link to="/projects" activeClassName={styles.active}>
|
||||
<Trans>project.plural</Trans>
|
||||
</Link>
|
||||
{modules.projects && (
|
||||
<Link to="/projects" activeClassName={styles.active}>
|
||||
<Trans>project.plural</Trans>
|
||||
</Link>
|
||||
)}
|
||||
<Link to="/social" activeClassName={styles.active}>
|
||||
<Trans>social.title</Trans>
|
||||
</Link>
|
||||
<Link to="/blog" activeClassName={styles.active}>
|
||||
<Trans>blog.title</Trans>
|
||||
</Link>
|
||||
{modules.blog && (
|
||||
<Link to="/blog" activeClassName={styles.active}>
|
||||
<Trans>blog.title</Trans>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>,
|
||||
document.getElementById("osnav")
|
||||
|
|
|
@ -26,6 +26,11 @@ const useSiteMetadata = () => {
|
|||
height
|
||||
nationality
|
||||
sameAs
|
||||
modules {
|
||||
blog
|
||||
projects
|
||||
donation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
Loader,
|
||||
} from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useSiteMetadata from "../helpers/useSiteMetadata";
|
||||
|
||||
export const query = graphql`
|
||||
query GetProjectsAndSkills($language: String) {
|
||||
|
@ -98,6 +99,7 @@ export const query = graphql`
|
|||
|
||||
const AboutPage = (props) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { modules } = useSiteMetadata();
|
||||
|
||||
let file = props.data.file;
|
||||
|
||||
|
@ -291,14 +293,16 @@ const AboutPage = (props) => {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<Link className={styles.donationSection} to="/donate">
|
||||
<div>
|
||||
<span>
|
||||
<Trans>about.donationCatchphrase</Trans>
|
||||
</span>
|
||||
<ArrowRight />
|
||||
</div>
|
||||
</Link>
|
||||
{modules.donation && (
|
||||
<Link className={styles.donationSection} to="/donate">
|
||||
<div>
|
||||
<span>
|
||||
<Trans>about.donationCatchphrase</Trans>
|
||||
</span>
|
||||
<ArrowRight />
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -45,6 +45,7 @@ export const query = graphql`
|
|||
const ProjectsPage = ({ data }) => {
|
||||
const { t } = useI18next();
|
||||
const meta = useSiteMetadata();
|
||||
|
||||
return (
|
||||
<Layout
|
||||
title={t("project.plural")}
|
||||
|
|
Loading…
Add table
Reference in a new issue