diff --git a/gatsby-node.ts b/gatsby-node.ts
index 0643119..338e8dc 100644
--- a/gatsby-node.ts
+++ b/gatsby-node.ts
@@ -10,8 +10,10 @@ import {
} from './src/common/constants';
import {
type AbsolutePathString,
+ type EmptyObject,
EntryPage,
type IndexPageContext,
+ type PageMetadata,
type ProjectPageContext,
type ResumePageContext,
} from './src/common/types';
@@ -133,7 +135,10 @@ function sortByCreatedAt(a: Queries.GithubRepo, b: Queries.GithubRepo) {
// Create the resume page
function createResumePage(githubRepos: Queries.GithubRepo[]) {
+ const path = '/resume';
+ const pageMetadata: PageMetadata | EmptyObject = getPageMetadata(path) || {};
const context: ResumePageContext = {
+ pageMetadata,
githubRepos: getSubsetOfGithubRepos(
githubRepos,
EntryPage.Resume,
@@ -142,9 +147,9 @@ function createResumePage(githubRepos: Queries.GithubRepo[]) {
};
createPage({
- path: '/resume',
+ path: path,
component: RESUME_PAGE_TEMPLATE,
- socialImageComponent: INDEX_OG_IMAGE_TEMPLATE,
+ socialImageComponent: OTHER_OG_IMAGE_TEMPLATE,
context: context,
});
}
@@ -256,7 +261,6 @@ export const createPages: GatsbyNode['createPages'] = async ({ graphql }) => {
// TODO: Re-enable this when project pages are implemented
// createProjectPages(githubRepos);
createIndexPage(githubRepos, authorBioHtml);
- // TODO: Re-enable this when the resume page is implemented
createResumePage(githubRepos);
createRedirects();
};
diff --git a/src/common/types.ts b/src/common/types.ts
index eb3f168..d659ab7 100644
--- a/src/common/types.ts
+++ b/src/common/types.ts
@@ -355,18 +355,34 @@ export type GithubRepo = {
// export type GithubRepo = Queries.GithubReposQuery;
-// Page context for the index page
+// Page context to add to the index page
export type IndexPageContext = {
githubRepos: Queries.GithubRepo[];
authorBioHtml: string;
};
-// Page context for project pages
+// Page context to add to project pages
export type ProjectPageContext = {
githubRepo: Queries.GithubRepo;
};
-// Page context for the resume page
-export type ResumePageContext = {
+// Page context to add to the resume page
+export type ResumePageContext = PageMetadataProp & {
githubRepos: Queries.GithubRepo[];
};
+
+// Page context for the privacy policy page
+export type PrivacyPageContext = PageMetadataProp;
+
+// Page context for the 404 page
+export type NotFoundPageContext = PageMetadataProp;
+
+// Page context for the index social image page
+export type IndexSocialImagePageContext = IndexPageContext & ImageMetadataProp;
+
+// Page context for other social image pages
+export type OtherSocialImagePageContext = PageMetadataProp & ImageMetadataProp;
+
+// Page context for project social image pages
+export type ProjectSocialImagePageContext = ProjectPageContext &
+ ImageMetadataProp;
diff --git a/src/common/utils.ts b/src/common/utils.ts
index 828cab5..9e8ca47 100644
--- a/src/common/utils.ts
+++ b/src/common/utils.ts
@@ -5,7 +5,7 @@
import { panic } from '../node/logger';
import { getSiteMetadata } from './config-manager';
-import type { PropsWithClassName, SentenceString } from './types';
+import type { PropsWithClassName, SentenceString, UrlString } from './types';
// Constants
@@ -195,8 +195,10 @@ export function removeTrailingSlash(path: string) {
}
// Remove the protocol from a URL
-export function removeProtocol(url: string) {
- return url.replace(/.*?:\/\//g, '');
+export function removeProtocol(url: UrlString | URL) {
+ const urlString = url instanceof URL ? url.toString() : url;
+
+ return urlString.replace(/.*?:\/\//g, '');
}
// Return the first n elements of an array
diff --git a/src/components/layout/resume-header.tsx b/src/components/layout/resume-header.tsx
index ab19ffd..e29c826 100644
--- a/src/components/layout/resume-header.tsx
+++ b/src/components/layout/resume-header.tsx
@@ -11,7 +11,7 @@ import {
} from '@fortawesome/free-solid-svg-icons';
import { getSiteMetadata } from '../../common/config-manager';
import { type CityAndStateString, TooltipPosition } from '../../common/types';
-import { removeProtocol } from '../../common/utils';
+import { getAbsoluteUrl, removeProtocol } from '../../common/utils';
import { GhostButton } from '../input/ghost-button';
import { GhostButtonLink } from '../links/ghost-button-link';
import { Heading } from '../text/heading';
@@ -19,9 +19,8 @@ import { Heading } from '../text/heading';
// Constants
const SITE_METADATA = getSiteMetadata();
-const CONTACT_URL = '/#contact';
-const PLACEHOLDER_PHONE = '(***) ***-****';
-const PLACEHOLDER_EMAIL = '*****@*****.com';
+const CONTACT_PATH = '/contact';
+const CONTACT_URL = removeProtocol(getAbsoluteUrl(CONTACT_PATH));
const COMMON_GHOST_BUTTON_LINK_PROPS = {
tooltipPosition: TooltipPosition.Left,
className: '!p-0',
@@ -44,19 +43,21 @@ export function ResumeHeader() {
+ {phone && (
+ -
+
+
+ )}
-
-
- -
-
- {metadata.title}
+ {pageMetadata.title}
-
+
{/* OpenGraph meta tags */}
-
-
+
+
@@ -80,12 +82,12 @@ export function PageHead({
{/* Twitter meta tags */}
-
+
-
+
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 5d56565..9417768 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -7,7 +7,7 @@ import type { HeadProps, PageProps } from 'gatsby';
import { useRef } from 'react';
import { getSiteMetadata } from '../common/config-manager';
import type {
- PageMetadataProp,
+ NotFoundPageContext,
SocialImagesMetadataProp,
} from '../common/types';
import { getAbsoluteUrl } from '../common/utils';
@@ -18,9 +18,7 @@ import { PageHead } from '../components/seo/page-head';
// Types
-interface PageContext {
- pageContext: PageMetadataProp & SocialImagesMetadataProp;
-}
+type PageContext = NotFoundPageContext & SocialImagesMetadataProp;
// Constants
@@ -29,7 +27,7 @@ const SITE_METADATA = getSiteMetadata();
// biome-ignore lint/style/noDefaultExport: Pages must use default exports
export default function NotFoundPage({
pageContext: { pageMetadata },
-}: PageContext & PageProps) {
+}: PageProps) {
// Cat ASCII art from https://emojicombos.com/cat
const sadCat = [
' \uFF0F\uFF1E\u3000\u0020\u30D5',
@@ -77,7 +75,7 @@ export default function NotFoundPage({
export const Head = ({
location,
pageContext: { pageMetadata, socialImagesMetadata },
-}: PageContext & HeadProps) => {
+}: HeadProps) => {
const pageTitle = `${pageMetadata.title} | ${SITE_METADATA.shortTitle}`;
const metadata = {
title: pageTitle,
@@ -94,7 +92,7 @@ export const Head = ({
return (
);
};
diff --git a/src/pages/privacy-policy.tsx b/src/pages/privacy-policy.tsx
index ff28760..af3720a 100644
--- a/src/pages/privacy-policy.tsx
+++ b/src/pages/privacy-policy.tsx
@@ -8,7 +8,7 @@ import { graphql } from 'gatsby';
import { useRef } from 'react';
import { getSiteMetadata } from '../common/config-manager';
import type {
- PageMetadataProp,
+ PrivacyPageContext,
SocialImagesMetadataProp,
} from '../common/types';
import { getAbsoluteUrl } from '../common/utils';
@@ -20,19 +20,7 @@ import { Article } from '../components/text/article';
// Types
-interface PageContextProp {
- pageContext: PageMetadataProp & SocialImagesMetadataProp;
-}
-
-interface DataProp {
- data: {
- file: {
- childMarkdownRemark: {
- html: string;
- };
- };
- };
-}
+type PageContext = PrivacyPageContext & SocialImagesMetadataProp;
// Constants
@@ -42,8 +30,8 @@ const SITE_METADATA = getSiteMetadata();
export default function PrivacyPolicyPage({
data,
pageContext: { pageMetadata },
-}: PageContextProp & DataProp & PageProps) {
- const articleHtml = data.file.childMarkdownRemark.html;
+}: PageProps) {
+ const articleHtml = data?.file?.childMarkdownRemark?.html;
return (
@@ -55,7 +43,7 @@ export default function PrivacyPolicyPage({
className="items-center"
>
@@ -66,7 +54,7 @@ export default function PrivacyPolicyPage({
export const Head = ({
location,
pageContext: { pageMetadata, socialImagesMetadata },
-}: PageContextProp & DataProp & HeadProps) => {
+}: HeadProps) => {
const pageTitle = `${pageMetadata.title} | ${SITE_METADATA.shortTitle}`;
const metadata = {
title: pageTitle,
@@ -91,7 +79,7 @@ export const Head = ({
return (
);
};
diff --git a/src/templates/page/index.tsx b/src/templates/page/index.tsx
index a623bba..4466f46 100644
--- a/src/templates/page/index.tsx
+++ b/src/templates/page/index.tsx
@@ -8,7 +8,7 @@ import {
faArrowUpRightFromSquare,
} from '@fortawesome/free-solid-svg-icons';
import { useInView } from 'framer-motion';
-import type { HeadProps } from 'gatsby';
+import type { HeadProps, PageProps } from 'gatsby';
import { Suspense, lazy, useCallback, useRef } from 'react';
import {
getEmploymentRoles,
@@ -34,9 +34,7 @@ import { Timeline } from '../../components/timeline';
// Types
-interface Props {
- pageContext: SocialImagesMetadataProp & IndexPageContext;
-}
+type PageContext = IndexPageContext & SocialImagesMetadataProp;
// Constants
@@ -52,7 +50,7 @@ const ContactForm = lazy(() =>
// biome-ignore lint/style/noDefaultExport: Templates must use default exports
export default function IndexPageTemplate({
pageContext: { githubRepos, authorBioHtml },
-}: Props) {
+}: PageProps) {
const inViewTriggerRef = useRef(null);
const expandTitle = useInView(inViewTriggerRef, USE_IN_VIEW_OPTIONS);
const sections = [
@@ -156,7 +154,7 @@ export default function IndexPageTemplate({
export const Head = ({
location,
pageContext: { socialImagesMetadata },
-}: Props & HeadProps) => {
+}: HeadProps) => {
const metadata = {
title: SITE_METADATA.title,
shortTitle: SITE_METADATA.shortTitle,
@@ -176,7 +174,7 @@ export const Head = ({
return (
);
};
diff --git a/src/templates/page/project.tsx b/src/templates/page/project.tsx
index 3f6ce52..c97c6a8 100644
--- a/src/templates/page/project.tsx
+++ b/src/templates/page/project.tsx
@@ -3,7 +3,7 @@
--------------------------------------------------
*/
-import type { HeadProps } from 'gatsby';
+import type { HeadProps, PageProps } from 'gatsby';
import { useRef } from 'react';
import { getSiteMetadata } from '../../common/config-manager';
import type {
@@ -20,9 +20,7 @@ import { DateRange } from '../../components/text/date-range';
// Types
-interface Props {
- pageContext: SocialImagesMetadataProp & ProjectPageContext;
-}
+type PageContext = ProjectPageContext & SocialImagesMetadataProp;
// Constants
@@ -31,8 +29,7 @@ const SITE_METADATA = getSiteMetadata();
// biome-ignore lint/style/noDefaultExport: Templates must use default exports
export default function ProjectPageTemplate({
pageContext: { githubRepo },
-}: Props) {
- // Dates are serialized when passed through page context, so we need to deserialize them
+}: PageProps) {
const updatedAt = new Date(githubRepo.updatedAt);
return (
@@ -75,7 +72,7 @@ export default function ProjectPageTemplate({
export const Head = ({
location,
pageContext: { githubRepo, socialImagesMetadata },
-}: HeadProps & Props) => {
+}: HeadProps) => {
const pageTitle = `${githubRepo.name} | ${SITE_METADATA.shortTitle}`;
const metadata = {
title: pageTitle,
@@ -126,7 +123,7 @@ export const Head = ({
return (
);
};
diff --git a/src/templates/page/resume.tsx b/src/templates/page/resume.tsx
index 00c721c..144c67c 100644
--- a/src/templates/page/resume.tsx
+++ b/src/templates/page/resume.tsx
@@ -28,20 +28,7 @@ import { Article } from '../../components/text/article';
// Types
-type PageContext = SocialImagesMetadataProp & ResumePageContext;
-
-type Data = {
- resumeSummary: {
- childMarkdownRemark: {
- html: string;
- };
- };
- resumeHighlights: {
- childMarkdownRemark: {
- html: string;
- };
- };
-};
+type PageContext = ResumePageContext & SocialImagesMetadataProp;
// Constants
@@ -54,7 +41,7 @@ const VOLUNTEERING_ROLES = limit(getVolunteeringRoles(), 1);
export default function ResumePageTemplate({
data,
pageContext: { githubRepos },
-}: PageProps) {
+}: PageProps) {
const sections = [
{
title: 'Summary',
@@ -81,8 +68,8 @@ export default function ResumePageTemplate({
ref: useRef(null),
},
] as PageSection[];
- const summaryHtml = data.resumeSummary.childMarkdownRemark.html;
- const highlightsHtml = data.resumeHighlights.childMarkdownRemark.html;
+ const summaryHtml = data?.resumeSummary?.childMarkdownRemark?.html;
+ const highlightsHtml = data?.resumeHighlights?.childMarkdownRemark?.html;
return (
@@ -92,10 +79,12 @@ export default function ResumePageTemplate({
dividerClassName="!pb-4"
responsive={false}
>
-
+ {summaryHtml && (
+
+ )}
-
+ {highlightsHtml && (
+
+ )}
) => {
+}: HeadProps) => {
const pageMetadata = getPageMetadata('/resume');
const pageTitle = `${pageMetadata.title} | ${SITE_METADATA.shortTitle}`;
const metadata = {
@@ -199,7 +190,7 @@ export const Head = ({
path={location.pathname}
theme={ThemeType.Light}
className="bg-base-100"
- {...{ metadata, structuredData, socialImagesMetadata }}
+ {...{ pageMetadata: metadata, structuredData, socialImagesMetadata }}
/>
);
};
diff --git a/src/templates/social-image/index.tsx b/src/templates/social-image/index.tsx
index 6e36d6b..3a5bf36 100644
--- a/src/templates/social-image/index.tsx
+++ b/src/templates/social-image/index.tsx
@@ -4,20 +4,16 @@
*/
import type { PageProps } from 'gatsby';
-import type { ImageMetadataProp } from '../../common/types';
+import type { IndexSocialImagePageContext } from '../../common/types';
import { GhostButton } from '../../components/input/ghost-button';
import { HeroHeader } from '../../components/layout/hero-header';
import { SignatureGhostButton } from '../../components/seo/signature-ghost-button';
import { SocialImage } from '../../components/seo/social-image';
-interface PageContext {
- pageContext: ImageMetadataProp;
-}
-
// biome-ignore lint/style/noDefaultExport: Templates must use default exports
export default function IndexSocialImageTemplate({
pageContext: { imageMetadata },
-}: PageContext & PageProps) {
+}: PageProps) {
return (
) {
const renderButton = useCallback(
(({ className, tooltipPosition }) => (
) {
const renderButton = useCallback(
(({ className, tooltipPosition }) => (
-
+
);