Shramkoweb/Portfolio

View on GitHub
pages/snippets/[slug].tsx

Summary

Maintainability
C
7 hrs
Test Coverage
import Head from 'next/head';
import { GetStaticPathsResult, GetStaticPropsContext, GetStaticPropsResult } from 'next';
import { ParsedUrlQuery } from 'querystring';
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote';

import { getSnippetBySlug, getSnippetSlugs } from '@/lib/snippets/api';
import { compileMDX } from '@/lib/scripts/compiler';
import { Snippet } from '@/lib/types';

import { MDXComponents } from '@/components/mdx-components';
import { useEffect } from 'react';

type SnippetPageProps = Pick<Snippet, 'data'> & {
  content: MDXRemoteSerializeResult;
  slug: string;
};

function SnippetPage(props: SnippetPageProps) {
  const {
    content,
    slug,
    data: {
      title, heading, description, createDate, updateData, keywords,
    },
  } = props;

  const formattedDate = new Date(createDate).toLocaleDateString('en-us', {
    dateStyle: 'medium',
  });

  useEffect(() => {
    const registerView = () => fetch(`/api/views/${slug}`, {
      method: 'POST',
    });

    registerView();
  }, [slug]);

  return (
    <>
      <Head>
        <title>{title}</title>
        <meta content={description} name="description" key="description" />
        <meta property="og:type" content="article" key="og:type" />
        <meta property="og:title" content={title} key="og:title" />
        <meta
          property="og:site_name"
          content="Snippets | Serhii Shramko"
          key="og:site_name"
        />
        <meta
          property="og:description"
          content={description}
          key="og:description"
        />
        <meta name="twitter:title" content={title} key="twitter:title" />
        <meta
          name="twitter:description"
          content={description}
          key="twitter:description"
        />
        <meta
          property="article:published_time"
          content={new Date(createDate).toISOString()}
          key="article:published_time"
        />
        {updateData && (
          <meta
            property="article:modified_time"
            content={new Date(updateData).toISOString()}
            key="article:modified_time"
          />
        )}

        <meta name="keywords" key="keywords" content={keywords.join(', ')} />
        <meta
          property="article:section"
          content="Technology"
          key="article:section"
        />
        <meta
          property="article:author"
          content="https://shramko.dev"
          key="article:author"
        />
      </Head>
      <article className="flex flex-col justify-center items-start max-w-3xl mx-auto mb-16 w-full">
        <div className="flex justify-between w-full mb-8">
          <div>
            <h1 className="font-bold text-3xl md:text-5xl tracking-tight mb-4 text-black dark:text-white">
              {heading}
            </h1>
            <p className="text-gray-700 dark:text-gray-300">
              Serhii Shramko /
              <time dateTime={new Date(createDate).toISOString()}>
                {' '}
                {formattedDate}
              </time>
            </p>
          </div>
        </div>
        <div className="prose dark:prose-dark w-full">
          {/* eslint-disable-next-line react/jsx-props-no-spreading */}
          <MDXRemote {...content} components={MDXComponents} />
        </div>
      </article>
    </>
  );
}

interface Params extends ParsedUrlQuery {
  slug: string;
}

export async function getStaticProps({
  params,
}: GetStaticPropsContext<Params>): Promise<
  GetStaticPropsResult<SnippetPageProps>
  > {
  const { data, content } = await getSnippetBySlug(params?.slug);
  const html = await compileMDX(content);

  return {
    props: {
      data,
      slug: params?.slug as string,
      content: html,
    },
  };
}

export async function getStaticPaths(): Promise<GetStaticPathsResult<Params>> {
  const slugs = await getSnippetSlugs();

  return {
    paths: slugs.map((slug: string) => ({
      params: {
        slug,
      },
    })),
    fallback: false,
  };
}

export default SnippetPage;