diff --git a/.env b/.env new file mode 100644 index 0000000..a8372e2 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +NEXT_PUBLIC_SUPABASE_URL=https://bxviqdqwynfbhjptbzag.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJ4dmlxZHF3eW5mYmhqcHRiemFnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDcyMjQ2MjIsImV4cCI6MjA2MjgwMDYyMn0.nV_az9_Q0fJtI4-9Tlp4siM6CWVoTdR4t6p9ZOtyaw8 \ No newline at end of file diff --git a/next.config.js b/next.config.js index 9064cb9..6be6cb0 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,16 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true -} - - module.exports = nextConfig \ No newline at end of file + reactStrictMode: true, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + port: '', + pathname: '/images/**', + }, + ], + }, +}; + +module.exports = nextConfig \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 92f2c23..f2a6c17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "autoprefixer": "^10.4.17", "date-fns": "^3.3.1", "gray-matter": "^4.0.3", - "mongodb": "^6.15.0", "next": "^15.2.4", "postcss": "^8.4.33", "react": "^18.2.0", @@ -646,15 +645,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.1.tgz", - "integrity": "sha512-1NCa8GsZ+OFLTw5KkKQS22wLS+Rs+y02sgkhr99Pm4OSXtSDHCJyq0uscPF0qA86ipGYH4PwtC2+a8Y4RKkCcg==", - "license": "MIT", - "dependencies": { - "sparse-bitfield": "^3.0.3" - } - }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.7.tgz", @@ -1122,21 +1112,6 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", - "license": "MIT" - }, - "node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "license": "MIT", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -2063,15 +2038,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bson": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", - "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=16.20.1" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4836,12 +4802,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "license": "MIT" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5365,61 +5325,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mongodb": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz", - "integrity": "sha512-ifBhQ0rRzHDzqp9jAQP6OwHSH7dbYIQjD3SbJs9YYk9AikKEettW/9s/tbSFDTpXcRbF+u1aLrhHxDFaYtZpFQ==", - "dependencies": { - "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.3", - "mongodb-connection-string-url": "^3.0.0" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.2.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongodb-connection-string-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", - "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", - "license": "Apache-2.0", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^14.1.0 || ^13.0.0" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6122,6 +6027,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6769,15 +6675,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "license": "MIT", - "dependencies": { - "memory-pager": "^1.0.2" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7276,18 +7173,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -7659,28 +7544,6 @@ "node": ">= 8" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index bc17315..f7bb2a2 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -111,4 +111,9 @@ export async function getPostData(id: string): Promise { export function getPostsByTag(tag: string): PostMetadata[] { const allPosts = getSortedPostsData(); return allPosts.filter(post => post.tags.includes(tag)); +} + +export function getPostsByAuthor(author: string): PostMetadata[] { + const allPosts = getSortedPostsData(); + return allPosts.filter(post => post.author.includes(author)); } \ No newline at end of file diff --git a/src/pages/authors/[author].tsx b/src/pages/authors/[author].tsx new file mode 100644 index 0000000..297bdb7 --- /dev/null +++ b/src/pages/authors/[author].tsx @@ -0,0 +1,155 @@ +import React, { useEffect, useState } from 'react'; +import { GetStaticProps, GetStaticPaths } from 'next'; +import Layout from '@/components/Layout'; +import PostCard from '@/components/PostCard'; +import { getSortedPostsData, getPostsByAuthor } from '@/lib/markdown'; +import { PostMetadata } from '@/types'; +import { supabase } from '@/utils/supabase'; + +interface AuthorProfile { + name: string; + bio: string; + x_link?: string; + fb_link?: string; + insta_link?: string; + pfp_link?: string; +} + +interface AuthorPageProps { + posts: PostMetadata[]; + author: string; +} + +const AuthorPage: React.FC = ({ posts, author }) => { + const [authorProfile, setAuthorProfile] = useState(null); + + const fetchAuthorProfile = async () => { + const { data, error } = await supabase + .from('authors') + .select('name, bio, x_link, fb_link, insta_link, pfp_link') + .eq('name', author) // Or use slug if you've switched to that + .single(); + + if (error) { + console.warn('No author profile found for:', author, '→', error.message); + } + + if (data) { + setAuthorProfile(data); + } + }; + + useEffect(() => { + fetchAuthorProfile(); + }, [author]); + + return ( + +
+
+ {/* Profile Picture */} + {authorProfile?.pfp_link ? ( + {`${authorProfile.name}'s + ) : ( +
+ ? +
+ )} + +
+ {/* Author Name */} +

+ {authorProfile?.name || author} +

+ + {/* Author Bio */} + {authorProfile?.bio && ( +

{authorProfile.bio}

+ )} + + {/* Social Links */} +
+ {authorProfile?.x_link && ( + + X (Twitter) + + )} + {authorProfile?.fb_link && ( + + Facebook + + )} + {authorProfile?.insta_link && ( + + Instagram + + )} +
+
+
+ + {/* Posts */} +

+ {posts.length} {posts.length === 1 ? 'post' : 'posts'} authored by{' '} + "{authorProfile?.name || author}" +

+ + {posts.length > 0 ? ( +
+ {posts.map((post) => ( + + ))} +
+ ) : ( +
+

No posts found for this author.

+
+ )} +
+
+ ); +}; + +export const getStaticPaths: GetStaticPaths = async () => { + const posts = getSortedPostsData(); + const authors = Array.from(new Set(posts.flatMap(post => post.author))); + + const paths = authors.map((author) => ({ + params: { author }, + })); + + return { + paths, + fallback: false, + }; +}; + +export const getStaticProps: GetStaticProps = async ({ params }) => { + const author = params?.author as string; + const posts = getPostsByAuthor(author); + + return { + props: { + posts, + author, + }, + }; +}; + +export default AuthorPage; diff --git a/src/pages/authors/index.tsx b/src/pages/authors/index.tsx new file mode 100644 index 0000000..a582e4d --- /dev/null +++ b/src/pages/authors/index.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { GetStaticProps } from 'next'; +import Link from 'next/link'; +import Image from 'next/image'; +import Layout from '@/components/Layout'; +import { supabase } from '@/utils/supabase'; + +interface AuthorProfile { + name: string; + bio: string; + x_link?: string; + fb_link?: string; + insta_link?: string; + pfp_link?: string; +} + +interface AuthorsPageProps { + authors: AuthorProfile[]; +} + +const AuthorsPage: React.FC = ({ authors }) => { + return ( + +
+

Meet the Authors

+ +
+ {authors.map((author) => ( + +
+ {`${author.name}'s +
+

{author.name}

+ {/* You can include bio or social icons here */} + + ))} +
+ +
+ + View All Posts + +
+
+
+ ); +}; + +export const getStaticProps: GetStaticProps = async () => { + const { data: authorsData, error } = await supabase + .from('authors') + .select('name, bio, x_link, fb_link, insta_link, pfp_link'); + + if (error || !authorsData) { + console.error('Error fetching authors:', error); + return { props: { authors: [] } }; + } + + return { + props: { + authors: authorsData, + }, + revalidate: 60, + }; +}; + +export default AuthorsPage; diff --git a/src/posts/25-books.md b/src/posts/25-books.md index 1abd949..9ed3859 100644 --- a/src/posts/25-books.md +++ b/src/posts/25-books.md @@ -1,7 +1,7 @@ --- title: "My Top 25 Books (Outside the Bible) in No Particular Order" date: "2025-04-22" -author: "@Auggie2LBCF" +author: "Auggie2LBCF" excerpt: "A curated list of my top 25 books (outside the Bible), spanning theology, Christian living, and even some fiction. These works have deeply shaped my faith, ministry, and personal growth." tags: ["books", "fiction", "theology", "personal life"] coverImage: "/images/25-books.png" diff --git a/src/posts/a-certain-sound.md b/src/posts/a-certain-sound.md index 9a14ad7..ad5df49 100644 --- a/src/posts/a-certain-sound.md +++ b/src/posts/a-certain-sound.md @@ -1,7 +1,7 @@ --- title: "Review - A Certain Sound by Ryan Denton" date: "2025-05-09" -author: "@Auggie2LBCF" +author: "Auggie2LBCF" excerpt: "The title itself, A Certain Sound, draws from 1 Corinthians 14:8, which states, \"For if the trumpet give an uncertain sound, who shall prepare himself to the battle?\"" tags: ["books", "review", "evangelism", "preaching"] coverImage: "/images/a-certain-sound.jpg" diff --git a/src/posts/christian_nationalism.md b/src/posts/christian_nationalism.md index 4d66883..2d452ef 100644 --- a/src/posts/christian_nationalism.md +++ b/src/posts/christian_nationalism.md @@ -1,7 +1,7 @@ --- title: "Against Ethnocentric Christian Nationalism" date: "2025-04-05" -author: "@Auggie2LBCF" +author: "Auggie2LBCF" excerpt: "While Christian principles have historically informed public life within Reformed traditions, Reformed theology itself, grounded in doctrines like the Imago Dei, the universal scope of redemption, and the nature of the Church, stands in opposition to ethnocentric expressions of Christian Nationalism." tags: ["christianity", "nationalism", "theology", "ethics", "identity", "political philosophy", "church and state"] coverImage: "/images/templar.jpeg" diff --git a/src/posts/fourfold.md b/src/posts/fourfold.md index dfc11cb..f334712 100644 --- a/src/posts/fourfold.md +++ b/src/posts/fourfold.md @@ -1,7 +1,7 @@ --- title: "Frank Turek & The Fourfold State of Man" date: "2025-03-18" -author: "@Auggie2LBCF" +author: "Auggie2LBCF" excerpt: "The 'contradiction' that Geisler suggests, is not a contradiction but an incomplete understanding of the abilities of Adam, the abilities of Adam's offspring, and the abilities of those who receive God's abundant provision of grace, who are given the responsibility to freely offer that abundant provision of grace to all those who are Adam's offspring." tags: ["sin", "calvinism", "creation", "fourfold"] coverImage: "/images/thomas-boston.jpg" diff --git a/src/posts/vov-adoration.md b/src/posts/vov-adoration.md index 5e9e064..93b72b5 100644 --- a/src/posts/vov-adoration.md +++ b/src/posts/vov-adoration.md @@ -1,7 +1,7 @@ --- title: "Valley of Vision - Part 1: Adoration" date: "2025-05-13" -author: "@Auggie2LBCF" +author: "Auggie2LBCF" excerpt: "I must reverently adore God, as a Being transcendently bright and blessed, self-existent and self-sufficient, an infinite and eternal Spirit who has all perfections in himself, and give him the glory of his titles and attributes." tags: ["the valley of vision", "prayer", "puritans", "adoration"] coverImage: "/images/valley-of-vision.png"