Refactor single blog posts
This commit is contained in:
		
							parent
							
								
									b99a807788
								
							
						
					
					
						commit
						2ad9e44e19
					
				@ -5,7 +5,11 @@
 | 
			
		||||
 */
 | 
			
		||||
import React from "react";
 | 
			
		||||
import Head from "next/head";
 | 
			
		||||
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
 | 
			
		||||
import type {
 | 
			
		||||
    InferGetStaticPropsType,
 | 
			
		||||
    GetStaticProps,
 | 
			
		||||
    GetStaticPaths,
 | 
			
		||||
} from "next";
 | 
			
		||||
const datasquirel = require("datasquirel");
 | 
			
		||||
 | 
			
		||||
/** ********************************************** */
 | 
			
		||||
@ -28,7 +32,9 @@ import TextShuffler from "../../components/actions/TextShuffler";
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 * @param {Object} props - Server props
 | 
			
		||||
 */
 | 
			
		||||
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
 | 
			
		||||
export default function BlogIndex({
 | 
			
		||||
    blogPost,
 | 
			
		||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
 | 
			
		||||
    // ## Get Contexts
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
@ -52,10 +58,7 @@ export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof g
 | 
			
		||||
        <React.Fragment>
 | 
			
		||||
            <Head>
 | 
			
		||||
                <title>{blogPost.title} | Tben.me Blog</title>
 | 
			
		||||
                <meta
 | 
			
		||||
                    name="description"
 | 
			
		||||
                    content={blogPost.excerpt}
 | 
			
		||||
                />
 | 
			
		||||
                <meta name="description" content={blogPost.excerpt} />
 | 
			
		||||
            </Head>
 | 
			
		||||
            <GeneralLayout>
 | 
			
		||||
                <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
 | 
			
		||||
@ -74,7 +77,9 @@ export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof g
 | 
			
		||||
                        <TextShuffler textInput={blogPost.excerpt} />
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span className="text-base opacity-50">
 | 
			
		||||
                        <TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
 | 
			
		||||
                        <TextShuffler
 | 
			
		||||
                            textInput={blogPost.date_created.substring(0, 24)}
 | 
			
		||||
                        />
 | 
			
		||||
                    </span>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								app/blog/[single]/Main.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								app/blog/[single]/Main.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
"use client";
 | 
			
		||||
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
//* IMPORTS
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
import React from "react";
 | 
			
		||||
import TextShuffler from "../../../components/actions/TextShuffler";
 | 
			
		||||
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
//* Main Function
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
/**
 | 
			
		||||
 * Blog page index
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 */
 | 
			
		||||
export default function Main({ post }: { post: any }) {
 | 
			
		||||
    //* Main Function Return
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <React.Fragment>
 | 
			
		||||
            <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
 | 
			
		||||
                <button
 | 
			
		||||
                    className="bg-transparent text-white/50 border-2 border-solid border-white/20"
 | 
			
		||||
                    onClick={(e) => {
 | 
			
		||||
                        window.history.back();
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                    Back
 | 
			
		||||
                </button>
 | 
			
		||||
                <h1 className="m-0">
 | 
			
		||||
                    <TextShuffler textInput={post.title} />
 | 
			
		||||
                </h1>
 | 
			
		||||
                <span className="text-lg">
 | 
			
		||||
                    <TextShuffler textInput={post.excerpt} />
 | 
			
		||||
                </span>
 | 
			
		||||
                <span className="text-base opacity-50">
 | 
			
		||||
                    <TextShuffler
 | 
			
		||||
                        textInput={post.date_created.substring(0, 24)}
 | 
			
		||||
                    />
 | 
			
		||||
                </span>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <span
 | 
			
		||||
                className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
 | 
			
		||||
                dangerouslySetInnerHTML={{ __html: post.body }}
 | 
			
		||||
            ></span>
 | 
			
		||||
        </React.Fragment>
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								app/blog/[single]/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								app/blog/[single]/page.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,75 @@
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
//* IMPORTS
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { Metadata, ResolvingMetadata } from "next";
 | 
			
		||||
import datasquirel from "datasquirel";
 | 
			
		||||
 | 
			
		||||
import { redirect } from "next/navigation";
 | 
			
		||||
import Main from "./Main";
 | 
			
		||||
 | 
			
		||||
export const revalidate = 3600;
 | 
			
		||||
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
//* Metadata
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
async function getPost({ single }: { single: string }) {
 | 
			
		||||
    const postResponse = await datasquirel.get({
 | 
			
		||||
        key: process.env.DATASQUIREL_API_KEY,
 | 
			
		||||
        db: process.env.DB_NAME,
 | 
			
		||||
        query: "select * from blog_posts where slug = ?",
 | 
			
		||||
        queryValues: [single],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const post =
 | 
			
		||||
        postResponse?.success && postResponse.payload?.[0]
 | 
			
		||||
            ? postResponse.payload[0]
 | 
			
		||||
            : null;
 | 
			
		||||
 | 
			
		||||
    return post;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function generateMetadata(
 | 
			
		||||
    { params, searchParams }: any,
 | 
			
		||||
    parent: ResolvingMetadata
 | 
			
		||||
): Promise<Metadata> {
 | 
			
		||||
    const single = params.single;
 | 
			
		||||
 | 
			
		||||
    const post = await getPost({ single: single });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        title: post.title,
 | 
			
		||||
        description: post.excerpt,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
//* Main Function
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
/**
 | 
			
		||||
 * Blog page index
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 */
 | 
			
		||||
export default async function BlogIndex({
 | 
			
		||||
    params: { single },
 | 
			
		||||
}: {
 | 
			
		||||
    params: any;
 | 
			
		||||
}) {
 | 
			
		||||
    //* Data fetching
 | 
			
		||||
    const post = await getPost({ single: single });
 | 
			
		||||
 | 
			
		||||
    if (!post) {
 | 
			
		||||
        return redirect("/blog");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //* Main Function Return
 | 
			
		||||
    /////////////////////////////////////////////
 | 
			
		||||
    return (
 | 
			
		||||
        <React.Fragment>
 | 
			
		||||
            <Main post={post} />
 | 
			
		||||
        </React.Fragment>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
}
 | 
			
		||||
@ -16,6 +16,8 @@ export const metadata: Metadata = {
 | 
			
		||||
    description: "Tech talks and tutorials by Tben",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const revalidate = 3600;
 | 
			
		||||
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
//* Main Function
 | 
			
		||||
/////////////////////////////////////////////
 | 
			
		||||
@ -33,17 +35,6 @@ export default async function BlogIndex() {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const posts = postsResponse?.success ? postsResponse.payload : [];
 | 
			
		||||
    try {
 | 
			
		||||
        const test = await fetch("http://localhost:5000/api/test");
 | 
			
		||||
        const result = await test.json();
 | 
			
		||||
        console.log(result);
 | 
			
		||||
    } catch (error: any) {
 | 
			
		||||
        console.log(error.message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log("Referer:", headers().get("referer"));
 | 
			
		||||
    console.log("Host:", headers().get("host"));
 | 
			
		||||
    // console.log(nextHeaders.cookies());
 | 
			
		||||
 | 
			
		||||
    //* Main Function Return
 | 
			
		||||
    /////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,14 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @type {import("next").NextConfig}
 | 
			
		||||
 */
 | 
			
		||||
module.exports = {
 | 
			
		||||
    reactStrictMode: true,
 | 
			
		||||
    eslint: {
 | 
			
		||||
        ignoreDuringBuilds: true,
 | 
			
		||||
    },
 | 
			
		||||
    typescript: {
 | 
			
		||||
        ignoreBuildErrors: true,
 | 
			
		||||
    },
 | 
			
		||||
    images: {
 | 
			
		||||
        remotePatterns: [
 | 
			
		||||
            {
 | 
			
		||||
@ -20,8 +26,14 @@ module.exports = {
 | 
			
		||||
                headers: [
 | 
			
		||||
                    { key: "Access-Control-Allow-Credentials", value: "true" },
 | 
			
		||||
                    { key: "Access-Control-Allow-Origin", value: "*" },
 | 
			
		||||
                    { key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
 | 
			
		||||
                    { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
 | 
			
		||||
                    {
 | 
			
		||||
                        key: "Access-Control-Allow-Methods",
 | 
			
		||||
                        value: "GET,OPTIONS,PATCH,DELETE,POST,PUT",
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        key: "Access-Control-Allow-Headers",
 | 
			
		||||
                        value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version",
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1212
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1212
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -26,7 +26,6 @@
 | 
			
		||||
    "homepage": "https://github.com/BenjaminToby/personal_site#readme",
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@barba/core": "^2.9.7",
 | 
			
		||||
        "contentful": "^9.1.32",
 | 
			
		||||
        "cors": "^2.8.5",
 | 
			
		||||
        "datasquirel": "^1.9.5",
 | 
			
		||||
        "gsap": "^3.10.4",
 | 
			
		||||
 | 
			
		||||
@ -1,178 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 * Imports
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 */
 | 
			
		||||
import React from "react";
 | 
			
		||||
import Head from "next/head";
 | 
			
		||||
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
 | 
			
		||||
const datasquirel = require("datasquirel");
 | 
			
		||||
 | 
			
		||||
/** ********************************************** */
 | 
			
		||||
/** ********************************************** */
 | 
			
		||||
/** ********************************************** */
 | 
			
		||||
 | 
			
		||||
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
 | 
			
		||||
import TextShuffler from "../../components/actions/TextShuffler";
 | 
			
		||||
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 * Main Component { Functional }
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 * @param {Object} props - Server props
 | 
			
		||||
 */
 | 
			
		||||
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
 | 
			
		||||
    // ## Get Contexts
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
 | 
			
		||||
    // ## Javascript Variables
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
 | 
			
		||||
    // ## React Hooks { useState, useEffect, useRef, etc ... }
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
 | 
			
		||||
    // ## Function Return
 | 
			
		||||
    return (
 | 
			
		||||
        <React.Fragment>
 | 
			
		||||
            <Head>
 | 
			
		||||
                <title>{blogPost.title} | Tben.me Blog</title>
 | 
			
		||||
                <meta
 | 
			
		||||
                    name="description"
 | 
			
		||||
                    content={blogPost.excerpt}
 | 
			
		||||
                />
 | 
			
		||||
            </Head>
 | 
			
		||||
            <GeneralLayout>
 | 
			
		||||
                <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
 | 
			
		||||
                    <button
 | 
			
		||||
                        className="bg-transparent text-white/50 border-2 border-solid border-white/20"
 | 
			
		||||
                        onClick={(e) => {
 | 
			
		||||
                            window.history.back();
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                        Back
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <h1 className="m-0">
 | 
			
		||||
                        <TextShuffler textInput={blogPost.title} />
 | 
			
		||||
                    </h1>
 | 
			
		||||
                    <span className="text-lg">
 | 
			
		||||
                        <TextShuffler textInput={blogPost.excerpt} />
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span className="text-base opacity-50">
 | 
			
		||||
                        <TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
 | 
			
		||||
                    </span>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <span
 | 
			
		||||
                    className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
 | 
			
		||||
                    dangerouslySetInnerHTML={{ __html: blogPost.body }}
 | 
			
		||||
                ></span>
 | 
			
		||||
            </GeneralLayout>
 | 
			
		||||
        </React.Fragment>
 | 
			
		||||
    );
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Server Side Props or Static Props
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 */
 | 
			
		||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
 | 
			
		||||
    // ## Environment processes
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
 | 
			
		||||
    // ## User Authentication
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
 | 
			
		||||
    // ## Page/Site Data Data Fetching
 | 
			
		||||
    const postsResponse = await datasquirel.get({
 | 
			
		||||
        key: process.env.DATASQUIREL_API_KEY,
 | 
			
		||||
        db: "tbenme",
 | 
			
		||||
        query: `select * from blog_posts WHERE slug='${params?.single}'`,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const post = postsResponse.payload[0];
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
 | 
			
		||||
    // ## Server Props Return
 | 
			
		||||
    return {
 | 
			
		||||
        props: {
 | 
			
		||||
            blogPost: post,
 | 
			
		||||
        },
 | 
			
		||||
        revalidate: 3600,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
    /** ********************************************** */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
/** ****************************************************************************** */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Server Side Props or Static Props
 | 
			
		||||
 * ==============================================================================
 | 
			
		||||
 */
 | 
			
		||||
export const getStaticPaths: GetStaticPaths = async () => {
 | 
			
		||||
    /**
 | 
			
		||||
     * Data fetching
 | 
			
		||||
     *
 | 
			
		||||
     * @abstract fetch date from the server or externnal source
 | 
			
		||||
     */
 | 
			
		||||
    const postsResponse = await datasquirel.get({
 | 
			
		||||
        key: process.env.DATASQUIREL_API_KEY,
 | 
			
		||||
        db: "tbenme",
 | 
			
		||||
        query: `select slug from blog_posts`,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const posts: { slug: string }[] | null = postsResponse.payload;
 | 
			
		||||
 | 
			
		||||
    const paths = posts?.map((entry) => {
 | 
			
		||||
        return {
 | 
			
		||||
            params: { single: entry.slug },
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        paths: paths ? paths : [],
 | 
			
		||||
        fallback: "blocking",
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
@ -40,7 +40,7 @@
 | 
			
		||||
        /* JavaScript Support */
 | 
			
		||||
        // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
 | 
			
		||||
        // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
 | 
			
		||||
        // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
 | 
			
		||||
        "maxNodeModuleJsDepth": 10 /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */,
 | 
			
		||||
        /* Emit */
 | 
			
		||||
        // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
 | 
			
		||||
        // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
 | 
			
		||||
@ -106,6 +106,14 @@
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "pages/_app.js", ".next/types/**/*.ts", "dump/index.jsx", "pages/_document.js"],
 | 
			
		||||
    "include": [
 | 
			
		||||
        "next-env.d.ts",
 | 
			
		||||
        "**/*.ts",
 | 
			
		||||
        "**/*.tsx",
 | 
			
		||||
        "pages/_app.js",
 | 
			
		||||
        ".next/types/**/*.ts",
 | 
			
		||||
        "dump/index.jsx",
 | 
			
		||||
        "pages/_document.js"
 | 
			
		||||
    ],
 | 
			
		||||
    "exclude": ["node_modules"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user