Skip to content

Commit bdba6fb

Browse files
committed
Added frontend and backend of projects section
1 parent 4291092 commit bdba6fb

File tree

9 files changed

+330
-20
lines changed

9 files changed

+330
-20
lines changed

client/src/App.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import "aos/dist/aos.css";
3939
import Collab from "./component/Collab";
4040
import FAQ from "./component/Faq";
4141
import CreateBlog from "./component/CreateBlog";
42+
import Projects from "./component/Projects";
43+
import UploadProject from "./component/UploadProject";
4244

4345
// Main Layout Component
4446
const Layout = ({ children, mode, setProgress, toggleMode, showAlert }) => {
@@ -53,6 +55,7 @@ const Layout = ({ children, mode, setProgress, toggleMode, showAlert }) => {
5355
blog="Blogs"
5456
discussion="Discussion"
5557
contributors="Contributors"
58+
projects="Projects"
5659
Feedback="Feedback"
5760
showAlert={showAlert}
5861
mode={mode}
@@ -147,6 +150,8 @@ function App() {
147150
<Route exact path='/contactus' element={<ContactUs mode={mode} setProgress={setProgress} showAlert={showAlert} />} />
148151
<Route exact path='/privacypolicy' element={<PrivacyPolicy mode={mode} setProgress={setProgress} showAlert={showAlert} />} />
149152
<Route exact path='/termofuse' element={<TermOfUse mode={mode} setProgress={setProgress} showAlert={showAlert} />} />
153+
<Route exact path='/projects' element={<Projects mode={mode} setProgress={setProgress} showAlert={showAlert} />} />
154+
<Route exact path='/uploadProject' element={<UploadProject mode={mode} setProgress={setProgress} showAlert={showAlert} />} />
150155
<Route exact path='/FAQ' element={<FAQ mode={mode} />} />
151156
<Route exact path='/createBlogPost' element={<CreateBlog />} />
152157
<Route exact path='/read-more-blog/:id' element={<ReadMoreBlog mode={mode} setProgress={setProgress} showAlert={showAlert} />} />

client/src/component/Navbar.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@ function Navbar(props) {
225225
{props.contributors}
226226
</Link>
227227
</li>
228+
<li className="nav-item fs-6">
229+
<Link
230+
className={`nav-link ${location.pathname === "/projects" ? "active" : ""
231+
}`}
232+
aria-current="page"
233+
to="/projects"
234+
>
235+
{props.projects}
236+
</Link>
237+
</li>
228238
</ul>
229239
</div>
230240

@@ -590,6 +600,7 @@ Navbar.propTypes = {
590600
blog: PropTypes.string,
591601
about: PropTypes.string,
592602
contributors: PropTypes.string,
603+
projects: PropTypes.string,
593604
profile: PropTypes.string,
594605
mode: PropTypes.string,
595606
toggleMode: PropTypes.func,

client/src/component/Projects.jsx

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React, { useState, useEffect } from 'react';
2+
import PropTypes from "prop-types";
3+
import { useNavigate } from 'react-router-dom';
4+
import axios from 'axios';
5+
6+
const Projects = ({ mode }) => {
7+
const navigate = useNavigate();
8+
const [projects, setProjects] = useState([]); // State to store fetched projects
9+
const [loading, setLoading] = useState(true); // State for loading state
10+
const [error, setError] = useState(null); // State for error handling
11+
12+
useEffect(() => {
13+
// Fetch the data from the API when the component mounts
14+
const fetchProjects = async () => {
15+
try {
16+
const response = await axios.get('http://localhost:5000/api/showcaseProjects/all-projects');
17+
setProjects(response.data); // Store the fetched projects in state
18+
setLoading(false); // Set loading to false after data is fetched
19+
} catch (err) {
20+
setError('Error fetching projects');
21+
setLoading(false); // Set loading to false if an error occurs
22+
}
23+
};
24+
25+
fetchProjects(); // Call the function to fetch data
26+
}, []); // Empty dependency array means this effect runs once on mount
27+
28+
const handleButtonClick = () => {
29+
navigate('/uploadProject');
30+
};
31+
32+
if (loading) {
33+
return <div>Loading...</div>;
34+
}
35+
36+
if (error) {
37+
return <div>{error}</div>;
38+
}
39+
40+
return (
41+
<div
42+
className={`container mx-auto p-6 mt-32 ${mode === 'dark' ? 'bg-gray-900 text-white' : 'bg-white text-gray-900'}`}
43+
>
44+
<div className="relative mb-12">
45+
<h1 className="text-4xl font-extrabold text-center tracking-tight">
46+
Projects Showcase
47+
</h1>
48+
<button
49+
className={`absolute top-0 right-0 mt-4 mr-6 ${mode === 'dark'
50+
? 'text-white bg-gray-700 hover:bg-gray-600'
51+
: 'text-gray-900 bg-gray-200 hover:bg-gray-300'
52+
} px-4 py-2 rounded-md text-sm font-semibold transition-colors duration-300`}
53+
onClick={handleButtonClick}
54+
>
55+
Add Project
56+
</button>
57+
</div>
58+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
59+
{projects.map((project, index) => (
60+
<div
61+
key={index}
62+
className={`${mode === 'dark' ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'} rounded-lg shadow-xl overflow-hidden transform hover:scale-105 hover:shadow-2xl transition-all duration-300 ease-in-out ${mode === 'dark' ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'}`}
63+
>
64+
<div className="p-6 space-y-4">
65+
<h2 className={`text-2xl font-semibold mb-3 ${mode === 'dark' ? 'text-white' : 'text-gray-800'}`}>
66+
{project.title}
67+
</h2>
68+
<p className={`text-base ${mode === 'dark' ? 'text-gray-300' : 'text-gray-600'}`}>
69+
{project.description}
70+
</p>
71+
<div className="flex justify-start space-x-4 mt-6">
72+
<a
73+
href={project.githubLink}
74+
target="_blank"
75+
rel="noopener noreferrer"
76+
className="inline-block text-white bg-blue-600 px-4 py-2 rounded-md text-sm font-semibold hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400"
77+
>
78+
GitHub
79+
</a>
80+
{project.deploymentLink && (
81+
<a
82+
href={project.deploymentLink}
83+
target="_blank"
84+
rel="noopener noreferrer"
85+
className="inline-block text-white bg-green-600 px-4 py-2 rounded-md text-sm font-semibold hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-400"
86+
>
87+
Live Demo
88+
</a>
89+
)}
90+
</div>
91+
</div>
92+
</div>
93+
))}
94+
</div>
95+
</div>
96+
);
97+
};
98+
99+
Projects.propTypes = {
100+
mode: PropTypes.string,
101+
};
102+
103+
export default Projects;
Lines changed: 154 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,163 @@
1-
import '../css/UploadProject.css'
2-
import PropTypes from 'prop-types';
3-
import AddProject from './AddProject';
1+
import React, { useState } from 'react';
2+
import PropTypes from "prop-types";
3+
import { useNavigate } from 'react-router-dom';
4+
5+
6+
const UploadProject = ({ mode }) => {
7+
const navigate = useNavigate();
8+
const [formData, setFormData] = useState({
9+
title: '',
10+
description: '',
11+
githubLink: '',
12+
deploymentLink: '',
13+
projectFiles: null,
14+
});
15+
16+
const handleChange = (e) => {
17+
const { name, value, type, files } = e.target;
18+
if (type === 'file') {
19+
setFormData((prevData) => ({
20+
...prevData,
21+
[name]: files[0],
22+
}));
23+
} else {
24+
setFormData((prevData) => ({
25+
...prevData,
26+
[name]: value,
27+
}));
28+
}
29+
};
30+
31+
const handleSubmit = async (e) => {
32+
e.preventDefault();
33+
34+
try {
35+
const response = await fetch('http://localhost:5000/api/showcaseProjects/post-project', {
36+
method: 'POST',
37+
headers: {
38+
'Content-Type': 'application/json',
39+
},
40+
body: JSON.stringify(formData),
41+
});
42+
43+
if (!response.ok) {
44+
throw new Error('Failed to submit project');
45+
}
46+
47+
const data = await response.json();
48+
console.log('Project submitted successfully:', data);
49+
navigate('/projects');
50+
51+
// Clear the form after submission
52+
setFormData({
53+
title: '',
54+
description: '',
55+
githubLink: '',
56+
deploymentLink: '',
57+
});
58+
59+
// Optionally, you can redirect the user or display a success message
60+
} catch (error) {
61+
console.error('Error submitting project:', error);
62+
}
63+
};
64+
65+
const themeStyles = mode === 'dark' ? 'bg-gray-800 text-white' : 'bg-white text-gray-900';
466

5-
function UploadProject(props) {
667
return (
7-
<div>
8-
<div className="container">
68+
<div className={`container mx-auto p-6 mt-40 mb-12 max-w-3xl shadow-lg rounded-lg ${themeStyles}`}>
69+
<h1 className={`text-4xl font-bold text-center mb-8 ${mode === 'dark' ? 'text-white' : 'text-gray-900'}`}>
70+
Upload Your Project
71+
</h1>
72+
73+
<form onSubmit={handleSubmit} className="space-y-6">
74+
{/* Project Title */}
75+
<div>
76+
<label htmlFor="title" className={`block text-lg font-medium ${mode === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
77+
Project Title
78+
</label>
79+
<input
80+
type="text"
81+
id="title"
82+
name="title"
83+
value={formData.title}
84+
onChange={handleChange}
85+
className={`w-full mt-2 p-3 border rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none ${mode === 'dark' ? 'bg-gray-700 text-white border-gray-600' : 'bg-white text-gray-900 border-gray-300'}`}
86+
placeholder="Enter your project title"
87+
required
88+
/>
89+
</div>
90+
91+
{/* Project Description */}
92+
<div>
93+
<label htmlFor="description" className={`block text-lg font-medium ${mode === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
94+
Project Description
95+
</label>
96+
<textarea
97+
id="description"
98+
name="description"
99+
value={formData.description}
100+
onChange={handleChange}
101+
className={`w-full mt-2 p-3 border rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none ${mode === 'dark' ? 'bg-gray-700 text-white border-gray-600' : 'bg-white text-gray-900 border-gray-300'}`}
102+
placeholder="Enter a brief description of your project"
103+
rows="4"
104+
required
105+
></textarea>
106+
</div>
107+
108+
{/* GitHub Link */}
109+
<div>
110+
<label htmlFor="githubLink" className={`block text-lg font-medium ${mode === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
111+
GitHub Link
112+
</label>
113+
<input
114+
type="url"
115+
id="githubLink"
116+
name="githubLink"
117+
value={formData.githubLink}
118+
onChange={handleChange}
119+
className={`w-full mt-2 p-3 border rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none ${mode === 'dark' ? 'bg-gray-700 text-white border-gray-600' : 'bg-white text-gray-900 border-gray-300'}`}
120+
placeholder="Enter your project GitHub link"
121+
required
122+
/>
123+
</div>
124+
125+
{/* Deployment Link */}
126+
<div>
127+
<label htmlFor="deploymentLink" className={`block text-lg font-medium ${mode === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
128+
Deployment Link (Optional)
129+
</label>
130+
<input
131+
type="url"
132+
id="deploymentLink"
133+
name="deploymentLink"
134+
value={formData.deploymentLink}
135+
onChange={handleChange}
136+
className={`w-full mt-2 p-3 border rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none ${mode === 'dark' ? 'bg-gray-700 text-white border-gray-600' : 'bg-white text-gray-900 border-gray-300'}`}
137+
placeholder="Enter your project live demo link"
138+
/>
139+
</div>
140+
141+
142+
143+
{/* Submit Button */}
9144
<div className="text-center">
10-
<div className="body">
11-
<h5 className="text-xl font-bold text-blue-500 mb-8">{props.title} <strong>👇</strong></h5>
12-
<AddProject showAlert={props.showAlert} />
13-
</div>
145+
<button
146+
type="submit"
147+
className={`w-full py-3 rounded-md text-lg font-semibold ${mode === 'dark' ? 'bg-blue-700 hover:bg-blue-600' : 'bg-blue-600 hover:bg-blue-700'} text-white focus:outline-none focus:ring-2 focus:ring-blue-400`}
148+
>
149+
Upload Project
150+
</button>
14151
</div>
15-
</div>
152+
</form>
16153
</div>
17-
)
18-
}
154+
);
155+
};
19156

20-
// Props Vadilation
21157
UploadProject.propTypes = {
22-
title: PropTypes.string,
23-
desc: PropTypes.string,
24-
showAlert: PropTypes.func,
158+
159+
mode: PropTypes.string,
160+
25161
};
26162

27-
export default UploadProject
163+
export default UploadProject;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const Project = require('../Models/ProjectSchema.js');
2+
3+
exports.postProject = async (req, res) => {
4+
const { title, description, githubLink, deploymentLink } = req.body;
5+
6+
if (!title || !description) {
7+
return res.status(400).json({ message: 'Title and description are required' });
8+
}
9+
10+
try {
11+
const newProject = new Project({
12+
title,
13+
description,
14+
githubLink,
15+
deploymentLink,
16+
});
17+
18+
await newProject.save();
19+
20+
return res.status(201).json({ message: 'Project created successfully', project: newProject });
21+
} catch (error) {
22+
return res.status(500).json({ message: 'Error creating project', error });
23+
}
24+
};
25+
26+
exports.getAllProjects = async (req, res) => {
27+
try {
28+
const projects = await Project.find();
29+
return res.status(200).json(projects);
30+
} catch (error) {
31+
return res.status(500).json({ message: 'Error fetching projects', error });
32+
}
33+
};

server/Models/ProjectSchema.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const mongoose = require('mongoose');
2+
3+
const projectSchema = new mongoose.Schema({
4+
title: { type: String, required: true },
5+
description: { type: String, required: true },
6+
githubLink: { type: String },
7+
deploymentLink: { type: String },
8+
});
9+
10+
const Project = mongoose.model('Project', projectSchema);
11+
12+
module.exports = Project;

server/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ app.use("/api/feedback", require("./routes/feedback"));
4848
app.use("/api/contact", require("./routes/contact"));
4949
app.use("/api/blog", require("./routes/blog"));
5050
app.use("/api/visitor", require("./routes/visitor"));
51+
app.use("/api/showcaseProjects", require("./routes/projectsRoute"));
5152

5253
// Socket.io connection handling
5354
const users = {};

0 commit comments

Comments
 (0)