diff --git a/package-lock.json b/package-lock.json index fb5157e..05bd4c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@tailwindcss/forms": "^0.5.9", + "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.15", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -52,7 +53,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.53.0", "react-resizable-panels": "^2.1.3", - "react-router-dom": "^6.22.3", + "react-router-dom": "^6.28.0", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", @@ -2653,6 +2654,15 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20" } }, + "node_modules/@tailwindcss/line-clamp": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz", + "integrity": "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + } + }, "node_modules/@tailwindcss/typography": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz", @@ -5298,6 +5308,7 @@ "version": "6.28.0", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", + "license": "MIT", "dependencies": { "@remix-run/router": "1.21.0", "react-router": "6.28.0" diff --git a/package.json b/package.json index 17f234f..92c785d 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@tailwindcss/forms": "^0.5.9", + "@tailwindcss/line-clamp": "^0.4.4", "@tailwindcss/typography": "^0.5.15", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", @@ -54,7 +55,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.53.0", "react-resizable-panels": "^2.1.3", - "react-router-dom": "^6.22.3", + "react-router-dom": "^6.28.0", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", diff --git a/public/images/blogs/clint-blog.png b/public/images/blogs/clint-blog.png new file mode 100644 index 0000000..f3aa467 Binary files /dev/null and b/public/images/blogs/clint-blog.png differ diff --git a/public/images/blogs/gus-blog.png b/public/images/blogs/gus-blog.png new file mode 100644 index 0000000..1740d8c Binary files /dev/null and b/public/images/blogs/gus-blog.png differ diff --git a/public/images/blogs/lewis-blog.png b/public/images/blogs/lewis-blog.png new file mode 100644 index 0000000..5f77e04 Binary files /dev/null and b/public/images/blogs/lewis-blog.png differ diff --git a/public/images/blogs/willy-blog.png b/public/images/blogs/willy-blog.png new file mode 100644 index 0000000..133c082 Binary files /dev/null and b/public/images/blogs/willy-blog.png differ diff --git a/public/images/blogs/wizard-blog.png b/public/images/blogs/wizard-blog.png new file mode 100644 index 0000000..e58959b Binary files /dev/null and b/public/images/blogs/wizard-blog.png differ diff --git a/scripts/download-images.js b/scripts/download-images.js new file mode 100644 index 0000000..8d9c224 --- /dev/null +++ b/scripts/download-images.js @@ -0,0 +1,33 @@ +import https from 'https'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const characters = ['lewis', 'willy', 'gus', 'wizard', 'clint']; +const imagesDir = path.join(__dirname, '../public/images/blogs'); + +// Create the directory if it doesn't exist +if (!fs.existsSync(imagesDir)) { + fs.mkdirSync(imagesDir, { recursive: true }); +} + +// Download a placeholder image for each character +characters.forEach(character => { + const url = `https://via.placeholder.com/800x600.png?text=${character}`; + const filePath = path.join(imagesDir, `${character}-blog.png`); + + https.get(url, (response) => { + const fileStream = fs.createWriteStream(filePath); + response.pipe(fileStream); + + fileStream.on('finish', () => { + console.log(`Downloaded ${character}-blog.png`); + fileStream.close(); + }); + }).on('error', (err) => { + console.error(`Error downloading ${character}-blog.png:`, err.message); + }); +}); diff --git a/src/App.tsx b/src/App.tsx index 5908213..532cedb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,9 +2,11 @@ import React, { Suspense } from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import MainLayout from './components/layout/MainLayout'; import Home from './pages/Home'; +import Blogs from './pages/Blogs'; +import BlogPost from './pages/BlogPost/BlogPost'; +import { ErrorBoundary } from './components/ErrorBoundary'; // Lazy load other pages -const Blogs = React.lazy(() => import('./pages/Blogs')); const Quiz = React.lazy(() => import('./pages/Quiz')); const Advice = React.lazy(() => import('./pages/Advice')); const Vendors = React.lazy(() => import('./pages/Vendors')); @@ -15,17 +17,20 @@ function App() { return ( - Loading...}> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - + + Loading...}> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); diff --git a/src/components/CommentSection.tsx b/src/components/CommentSection.tsx new file mode 100644 index 0000000..7b70281 --- /dev/null +++ b/src/components/CommentSection.tsx @@ -0,0 +1,111 @@ +import React, { useState, useEffect } from 'react'; + +interface Comment { + id: number; + author: string; + content: string; + timestamp: string; +} + +interface CommentSectionProps { + postId: number; +} + +// Store comments in memory (in a real app, this would be in a database) +const commentsStore: Record = {}; + +export const CommentSection: React.FC = ({ postId }) => { + const [comments, setComments] = useState([]); + const [newComment, setNewComment] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + + // Load comments for this post + useEffect(() => { + setComments(commentsStore[postId] || []); + }, [postId]); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!newComment.trim()) return; + + setIsSubmitting(true); + + // Simulate API delay + await new Promise(resolve => setTimeout(resolve, 500)); + + const comment: Comment = { + id: Date.now(), + author: 'Anonymous Farmer', // Replace with actual user system + content: newComment.trim(), + timestamp: new Date().toISOString(), + }; + + const updatedComments = [comment, ...(commentsStore[postId] || [])]; + commentsStore[postId] = updatedComments; + setComments(updatedComments); + setNewComment(''); + setIsSubmitting(false); + }; + + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); + }; + + return ( +
+

Comments

+ +
+