Skip to content

Commit 21c2bc4

Browse files
authored
Merge pull request #447 from Sawan-Kushwah/add/stories
Build frontend and Backend for Stories Page: Implement POST and GET Routes | clean schema✨🚀
2 parents 49ab3f4 + 1074b08 commit 21c2bc4

File tree

8 files changed

+254
-1
lines changed

8 files changed

+254
-1
lines changed

public/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<title>GlassyUI</title>
3737
</head>
3838

39-
<body>
39+
<body style="background-color: #17202e;">
4040

4141

4242
<!-- Root element for React to render into -->

server/app.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import dotenv from 'dotenv';
33
import connectDB from './utils/db.js';
44
import cors from 'cors';
55
import contactRoutes from './routes/contactRoutes.js';
6+
7+
import stories from './routes/storiesRoutes.js';
8+
69
import newsletterRoutes from './routes/newsletterRoute.js';
710

11+
812
dotenv.config();
913
const app = express();
1014
connectDB();
@@ -19,8 +23,12 @@ app.use(
1923

2024
// Serve static files from the uploads directory
2125
app.use('/api/contact', contactRoutes);
26+
27+
app.use('/api/stories', stories);
28+
2229
app.use('/api/newsletter', newsletterRoutes);
2330

31+
2432
const PORT = process.env.PORT || 5000;
2533
app.listen(PORT, () => {
2634
console.log(`Server running on port ${PORT}`);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// postsController.js
2+
import Post from '../model/postModel.js';
3+
4+
// Fetch all posts
5+
export const getPosts = async (req, res) => {
6+
try {
7+
const posts = await Post.find();
8+
res.status(200).json(posts);
9+
} catch (error) {
10+
res.status(500).json({ message: 'Error fetching posts', error });
11+
}
12+
};
13+
14+
// Save a new post
15+
export const savePost = async (req, res) => {
16+
const { title, content, category, date } = req.body;
17+
18+
try {
19+
const newPost = new Post({ title, content, category, date });
20+
const savedPost = await newPost.save();
21+
res.status(201).json(savedPost);
22+
} catch (error) {
23+
res.status(500).json({ message: 'Error saving post', error });
24+
}
25+
};

server/model/postModel.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// postModel.js
2+
import mongoose from 'mongoose';
3+
4+
const postSchema = new mongoose.Schema({
5+
title: String,
6+
content: String,
7+
category: String,
8+
date: { type: Date, default: Date.now },
9+
});
10+
11+
const Post = mongoose.model('Post', postSchema);
12+
13+
export default Post;

server/routes/storiesRoutes.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import express from 'express';
2+
import { getPosts, savePost } from '../controllers/postsController.js';
3+
4+
const router = express.Router();
5+
6+
router.get('/getposts', getPosts);
7+
router.post('/saveposts', savePost);
8+
9+
export default router;

src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import ProductCardDetailsPage from './components/ProductCardDetailsPage';
4646
import ContactUs from './components/ContactUs';
4747
import AiChatbot from './components/AIChatbot';
4848
import { TermsOfUse } from './components/TermsOfUse';
49+
50+
import Stories from './components/Stories';
4951
import Register from './login/SignUp';
5052
import SignIn from './login/SignIn';
5153

@@ -120,8 +122,12 @@ const App: React.FC = () => {
120122
<Route path='/gallery-details' element={<GalleryDetailsPage />} />
121123
<Route path='/contact' element={<ContactUs />} />
122124
<Route path='/termsOfUse' element={<TermsOfUse />} />
125+
126+
<Route path='/stories' element={<Stories />} />
127+
123128
<Route path='/signup' element={<Register />} />
124129
<Route path='/signin' element={<SignIn />} />
130+
125131
<Route path='*' element={<NotFoundPage />} />
126132
</Routes>
127133
<ConditionalFooter />

src/components/Header.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ const Header: React.FC = () => {
7575
Contact Us
7676
</Link>
7777
</li>
78+
<li style={liStyle} className='navbar-item'>
79+
<Link
80+
to='/stories'
81+
style={linkStyle}
82+
onMouseEnter={e => (e.currentTarget.style.color = '#fde047')}
83+
onMouseLeave={e => (e.currentTarget.style.color = 'white')}
84+
>
85+
Stories
86+
</Link>
87+
</li>
7888
</ul>
7989
{userLoggedIn && currentUser ? (
8090
<UserAccount

src/components/Stories.tsx

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import React, { useState, useEffect } from 'react';
2+
3+
interface Post {
4+
title: string;
5+
content: string;
6+
category: string;
7+
date: string;
8+
}
9+
10+
const Stories = () => {
11+
const [posts, setPosts] = useState<Post[]>([]);
12+
const [title, setTitle] = useState('');
13+
const [content, setContent] = useState('');
14+
const [category, setCategory] = useState('');
15+
16+
useEffect(() => {
17+
// Fetch posts from the backend API
18+
const fetchPosts = async () => {
19+
try {
20+
const response = await fetch(
21+
'http://localhost:5000/api/stories/getposts',
22+
);
23+
if (response.ok) {
24+
const data = await response.json();
25+
setPosts(data);
26+
} else {
27+
console.error('Failed to fetch posts');
28+
}
29+
} catch (error) {
30+
console.error('Error fetching posts:', error);
31+
}
32+
};
33+
34+
fetchPosts();
35+
}, []);
36+
37+
const handleSubmit = async (e: React.FormEvent) => {
38+
e.preventDefault();
39+
40+
if (title && content && category) {
41+
const newPost = {
42+
title,
43+
content,
44+
category,
45+
date: new Date().toISOString(),
46+
};
47+
48+
try {
49+
const response = await fetch(
50+
'http://localhost:5000/api/stories/saveposts',
51+
{
52+
method: 'POST',
53+
headers: {
54+
'Content-Type': 'application/json',
55+
},
56+
body: JSON.stringify(newPost),
57+
},
58+
);
59+
60+
if (response.ok) {
61+
const savedPost = await response.json();
62+
setPosts([savedPost, ...posts]); // Add the new post to state
63+
} else {
64+
console.error('Failed to save the post');
65+
}
66+
} catch (error) {
67+
console.error('Error saving the post:', error);
68+
}
69+
70+
// Clear form fields
71+
setTitle('');
72+
setContent('');
73+
setCategory('');
74+
}
75+
};
76+
77+
return (
78+
<>
79+
<h1 className='text-3xl font-bold text-center mb-5 text-gray-100 mt-20'>
80+
Real Stories, Real Advice: Share Your Experience
81+
</h1>
82+
83+
<div className='flex flex-col lg:flex-row items-start gap-8 px-6 lg:px-20 mb-14'>
84+
{/* Left side - Posts */}
85+
<div className='flex-1 space-y-6'>
86+
{posts.length === 0 ? (
87+
<p className='text-gray-400 text-center'>
88+
No posts yet. Share your experience!
89+
</p>
90+
) : (
91+
posts.map((post, index) => (
92+
<div
93+
key={index}
94+
className='bg-gray-800 text-white shadow-lg rounded-xl p-8 border border-gray-700 hover:shadow-xl transition-all duration-300 ease-in-out'
95+
>
96+
<h3 className='text-2xl font-bold text-gray-100 mb-2'>
97+
{post.title}
98+
</h3>
99+
<p className='text-sm text-indigo-400 font-medium mb-6'>
100+
{post.category}
101+
</p>
102+
<p className='text-gray-300 leading-relaxed mb-4'>
103+
{post.content}
104+
</p>
105+
<div className='flex items-center justify-between'>
106+
<p className='text-xs text-gray-500'>
107+
{new Date(post.date).toLocaleDateString()}
108+
</p>
109+
<button className='text-indigo-500 text-sm font-medium border border-indigo-100 rounded-full px-4 py-1 hover:bg-indigo-700 transition-colors'>
110+
Read More
111+
</button>
112+
</div>
113+
</div>
114+
))
115+
)}
116+
</div>
117+
118+
{/* Right side - Form */}
119+
<div className='w-full lg:w-1/3 bg-gray-800 p-6 rounded-lg shadow-md'>
120+
<form className='space-y-4'>
121+
<input
122+
type='text'
123+
placeholder='Title of your story'
124+
value={title}
125+
onChange={e => setTitle(e.target.value)}
126+
className='w-full p-3 rounded-md border border-gray-600 bg-gray-700 text-white focus:outline-none focus:border-blue-500'
127+
/>
128+
<textarea
129+
placeholder='Write about your story...'
130+
value={content}
131+
onChange={e => setContent(e.target.value)}
132+
className='w-full p-3 h-32 rounded-md border border-gray-600 bg-gray-700 text-white focus:outline-none focus:border-blue-500'
133+
></textarea>
134+
<select
135+
value={category}
136+
onChange={e => setCategory(e.target.value)}
137+
className='block w-full px-4 py-2 bg-gray-700 text-white border border-gray-600 rounded-md shadow-sm focus:border-indigo-500 focus:ring-indigo-500'
138+
>
139+
<option value='' disabled>
140+
Select Category
141+
</option>
142+
143+
<optgroup label='GlassyUI-Components'>
144+
<option value='GlassyUI Introduction'>
145+
GlassyUI Introduction
146+
</option>
147+
<option value='Customizing GlassyUI Components'>
148+
Customizing GlassyUI Components
149+
</option>
150+
<option value='Advanced GlassyUI Techniques'>
151+
Advanced GlassyUI Techniques
152+
</option>
153+
<option value='GlassyUI Best Practices'>
154+
GlassyUI Best Practices
155+
</option>
156+
<option value='Contributing to GlassyUI'>
157+
Contributing to GlassyUI
158+
</option>
159+
<option value='React and GlassyUI Integration'>
160+
React and GlassyUI Integration
161+
</option>
162+
<option value='GlassyUI in Real Projects'>
163+
GlassyUI in Real Projects
164+
</option>
165+
<option value='GlassyUI Updates'>GlassyUI Updates</option>
166+
</optgroup>
167+
</select>
168+
169+
<button
170+
onClick={handleSubmit}
171+
className='w-full bg-blue-500 hover:bg-blue-600 hover:text-white text-white font-semibold py-2 rounded-md focus:outline-none'
172+
>
173+
Post Experience
174+
</button>
175+
</form>
176+
</div>
177+
</div>
178+
</>
179+
);
180+
};
181+
182+
export default Stories;

0 commit comments

Comments
 (0)