Archived
Merge pull request #4 from auggie2lbcf/comment-section
add comment section
This commit is contained in:
Generated
-1
@@ -5119,7 +5119,6 @@
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz",
|
||||
"integrity": "sha512-ifBhQ0rRzHDzqp9jAQP6OwHSH7dbYIQjD3SbJs9YYk9AikKEettW/9s/tbSFDTpXcRbF+u1aLrhHxDFaYtZpFQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.1.9",
|
||||
"bson": "^6.10.3",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 415 KiB |
@@ -1,9 +1,15 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
interface CommentFormProps {
|
||||
postId: string;
|
||||
}
|
||||
|
||||
interface Comment {
|
||||
name: string;
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -11,11 +17,29 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchComments = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/comments?postId=${postId}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setComments(data);
|
||||
} else {
|
||||
console.error('Failed to fetch comments');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching comments:', err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchComments();
|
||||
}, [postId]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Basic validation
|
||||
if (!name.trim() || !email.trim() || !comment.trim()) {
|
||||
setError('All fields are required');
|
||||
return;
|
||||
@@ -25,21 +49,31 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// In a real implementation, you would send this data to your API
|
||||
// For now, we'll just simulate a successful submission
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const response = await fetch('/api/comments', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, email, comment, postId }),
|
||||
});
|
||||
|
||||
// Clear form and show success message
|
||||
if (response.ok) {
|
||||
setName('');
|
||||
setEmail('');
|
||||
setComment('');
|
||||
setIsSubmitted(true);
|
||||
setIsSubmitting(false);
|
||||
|
||||
// Reset success message after 5 seconds
|
||||
setTimeout(() => {
|
||||
setIsSubmitted(false);
|
||||
}, 5000);
|
||||
// Fetch updated comments
|
||||
const updatedComments = await fetch(`/api/comments?postId=${postId}`).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
setComments(updatedComments);
|
||||
|
||||
setTimeout(() => setIsSubmitted(false), 5000);
|
||||
} else {
|
||||
const data = await response.json();
|
||||
setError(data.message || 'Something went wrong. Please try again later.');
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Something went wrong. Please try again later.');
|
||||
setIsSubmitting(false);
|
||||
@@ -52,7 +86,7 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
|
||||
{isSubmitted && (
|
||||
<div className="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md mb-6">
|
||||
Your comment has been submitted and is awaiting moderation. Thank you!
|
||||
Your comment has been submitted. Thank you!
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -75,6 +109,12 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="w-full px-4 py-2 border border-primary-300 rounded-md focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
|
||||
required
|
||||
onFocus={() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const savedName = localStorage.getItem('name');
|
||||
if (savedName) setName(savedName);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -88,6 +128,12 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2 border border-primary-300 rounded-md focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
|
||||
required
|
||||
onFocus={() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const savedEmail = localStorage.getItem('email');
|
||||
if (savedEmail) setEmail(savedEmail);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,16 +152,30 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
{/* <div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="save-info"
|
||||
className="h-4 w-4 text-accent border-primary-300 rounded focus:ring-accent"
|
||||
checked={typeof window !== 'undefined' && localStorage.getItem('saveInfo') === 'true'}
|
||||
onChange={(e) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const saveInfo = e.target.checked;
|
||||
localStorage.setItem('saveInfo', saveInfo.toString());
|
||||
if (saveInfo) {
|
||||
localStorage.setItem('name', name);
|
||||
localStorage.setItem('email', email);
|
||||
} else {
|
||||
localStorage.removeItem('name');
|
||||
localStorage.removeItem('email');
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="save-info" className="ml-2 block text-sm text-primary-600">
|
||||
Save my name and email for the next time I comment
|
||||
</label>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
@@ -126,34 +186,26 @@ const CommentSection: React.FC<CommentFormProps> = ({ postId }) => {
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Sample comments - in a real implementation, these would be fetched from an API */}
|
||||
<div className="mt-12">
|
||||
<h3 className="text-xl font-bold mb-6">Comments (2)</h3>
|
||||
<h3 className="text-xl font-bold mb-6">Comments ({comments.length})</h3>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white p-6 rounded-md shadow-sm border border-primary-200">
|
||||
{comments.map((comment) => (
|
||||
<div
|
||||
key={comment.createdAt}
|
||||
className="bg-white p-6 rounded-md shadow-sm border border-primary-200"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h4 className="font-bold">John Calvin</h4>
|
||||
<p className="text-sm text-primary-500">March 10, 2025</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-primary-700">
|
||||
A profound meditation on the doctrine of grace. I particularly appreciate your emphasis on how this truth transforms our daily lives, not just our theological understanding.
|
||||
<h4 className="font-bold">{comment.name}</h4>
|
||||
<p className="text-sm text-primary-500">
|
||||
{new Date(comment.createdAt).toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-md shadow-sm border border-primary-200">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h4 className="font-bold">Martin Luther</h4>
|
||||
<p className="text-sm text-primary-500">March 5, 2025</p>
|
||||
</div>
|
||||
<p className="text-primary-700">{comment.comment}</p>
|
||||
</div>
|
||||
<p className="text-primary-700">
|
||||
This post eloquently articulates what I've been trying to explain to my congregation. The way you connected Scripture with practical application was masterful. I'll be sharing this widely.
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { MongoClient } from 'mongodb';
|
||||
|
||||
const uri = process.env.MONGODB_URI || 'your-mongodb-uri';
|
||||
const dbName = 'confessions-of-grace';
|
||||
|
||||
const client = new MongoClient(uri);
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === 'POST') {
|
||||
const { name, email, comment, postId } = req.body;
|
||||
|
||||
if (!name || !email || !comment || !postId) {
|
||||
return res.status(400).json({ message: 'All fields are required' });
|
||||
}
|
||||
|
||||
try {
|
||||
if (!client.db(dbName)) await client.connect();
|
||||
const db = client.db(dbName);
|
||||
const collection = db.collection('comments');
|
||||
|
||||
await collection.insertOne({ name, email, comment, postId, createdAt: new Date() });
|
||||
|
||||
res.status(201).json({ message: 'Comment submitted successfully' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Internal server error' });
|
||||
}
|
||||
} else if (req.method === 'GET') {
|
||||
const { postId } = req.query;
|
||||
|
||||
if (!postId) {
|
||||
return res.status(400).json({ message: 'Post ID is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
if (!client.db(dbName)) await client.connect();
|
||||
const db = client.db(dbName);
|
||||
const collection = db.collection('comments');
|
||||
|
||||
const comments = await collection
|
||||
.find({ postId })
|
||||
.sort({ createdAt: -1 })
|
||||
.toArray();
|
||||
|
||||
res.status(200).json(comments);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ message: 'Internal server error' });
|
||||
}
|
||||
} else {
|
||||
res.status(405).json({ message: 'Method not allowed' });
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { MongoClient } from 'mongodb';
|
||||
|
||||
const uri = process.env.MONGODB_URI || ''; // Load URI from environment variables
|
||||
const options = {};
|
||||
|
||||
let client: MongoClient;
|
||||
let clientPromise: Promise<MongoClient>;
|
||||
|
||||
if (!process.env.MONGODB_URI) {
|
||||
throw new Error('Please add your MongoDB URI to .env.local');
|
||||
}
|
||||
|
||||
// Reuse the client instance for serverless deployments
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// Create a global instance to preserve the same client during HMR in development
|
||||
if (!(global as typeof globalThis & { _mongoClientPromise?: Promise<MongoClient> })._mongoClientPromise) {
|
||||
client = new MongoClient(uri, options);
|
||||
(global as typeof globalThis & { _mongoClientPromise?: Promise<MongoClient> })._mongoClientPromise = client.connect();
|
||||
}
|
||||
clientPromise = (global as typeof globalThis & { _mongoClientPromise?: Promise<MongoClient> })._mongoClientPromise!;
|
||||
} else {
|
||||
// In production, create a new client connection
|
||||
client = new MongoClient(uri, options);
|
||||
clientPromise = client.connect();
|
||||
}
|
||||
|
||||
export default clientPromise;
|
||||
@@ -8,6 +8,7 @@ import { GetStaticPaths, GetStaticProps } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import CommentSection from "@/components/CommentSection";
|
||||
|
||||
interface PostProps {
|
||||
post: PostData;
|
||||
@@ -97,7 +98,7 @@ const Post: React.FC<PostProps> = ({ post, morePosts }) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <CommentSection postId={post.id} /> */}
|
||||
<CommentSection postId={post.id} />
|
||||
|
||||
<div className="mt-12 pt-6 border-t border-primary-200">
|
||||
<Link href="/" className="text-accent-dark hover:text-accent inline-flex items-center">
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: "Against Ethnocentric Christian Nationalism"
|
||||
date: "2025-04-05"
|
||||
author: "@Auggie2LBCF"
|
||||
excerpt: "TBD"
|
||||
tags: ["christianity", "nationalism", "theology", "ethics", "identity", "political philosophy", "church and state"]
|
||||
coverImage: "/images/templar.jpeg"
|
||||
---
|
||||
## Definitions
|
||||
I will start by defining what *exactly* I mean by each of the words in the title. This is to prevent any confusion with my position and to help see what I am seeing.
|
||||
### Christian
|
||||
With where society is at with Christianity. It will be helpful to fence what I mean by Christian. Here are 5 points that I would use when describing a Christian:
|
||||
1. **Those who have been renerated with an effectual call.** They have been given a new heart and spiritual life (John 3:3), drawing them irresistibly to Christ (2 Timothy 1:9).
|
||||
2. **Those who have a saving faith.** A wholehearted trust and reliance *solely* upon Jesus Christ (Ephesians 2:8-9), this faith is understood as a gift from God (WSC Q. 86).
|
||||
3. **Those who have repenantce unto life.** Saving faith is always accompanied by repentance -a heartfelt sorrow for sin, a turning away from it, and a sincere turning towards God and His commands (Acts 20:21), again, as a grace from God.
|
||||
4. **Those who have been justified.** The believer has been declared righteous by God, not on their merits but solely on Christ's righteousness being imputed (credited) to them (Romans 3:24-25a, Heidelberg Q. 60).
|
||||
5. **Those who have union with Christ.** The believer is united with Christ (2 Corinthians 5:17) and adopted into God's family as His child.
|
||||
|
||||
When I refer to Christian, I do not refer to those who fall into the reformed baptist category, but those who are heirs with Christ with me. This includes people such as Sam Waldron to Mike Winger.
|
||||
|
||||
### Christian Nationalism
|
||||
Christian nationalism is a totality of national action, consisting of civil laws and social customs, conducted by a Christian nation as a Christian nation, in order to procure for itself both earthly and heavenly good in Christ. (*The Case for Christian Nationalism*, Stephen Wolfe)
|
||||
|
||||
### Ethnocentric
|
||||
Ethnocentric is the
|
||||
|
||||
## Systematic Theology
|
||||
Then God said, "Let us make man in our image, after our likeness. And let them have dominion over the fish of the sea and over the birds of the heavens and over the livestock and over all the earth and over every creeping thing that creeps on the earth."
|
||||
## Historical Theology
|
||||
Reference in New Issue
Block a user