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
	 Benjamin Toby
						Benjamin Toby