From fab2fa24c43afcf881ee35914764610eacae61f4 Mon Sep 17 00:00:00 2001 From: "Kotla.Lokeshwari" Date: Wed, 18 Jun 2025 22:55:30 +0530 Subject: [PATCH 1/5] User Project(s) --- .../profile/ProjectsSec/Project.jsx | 109 +++++++++++-- .../profile/ProjectsSec/ProjectCopo.jsx | 81 ++++++++-- .../profile/ProjectsSec/ProjectModal.jsx | 110 +++++++++++-- server/controllers/projectController.js | 148 ++++++++++++++++++ server/models/Project.js | 12 +- server/routes/projectRoutes.js | 24 +++ server/server.js | 3 + 7 files changed, 441 insertions(+), 46 deletions(-) create mode 100644 server/controllers/projectController.js create mode 100644 server/routes/projectRoutes.js diff --git a/client/src/assets/components/profile/ProjectsSec/Project.jsx b/client/src/assets/components/profile/ProjectsSec/Project.jsx index 433f6d6a..f515b9d2 100644 --- a/client/src/assets/components/profile/ProjectsSec/Project.jsx +++ b/client/src/assets/components/profile/ProjectsSec/Project.jsx @@ -1,22 +1,31 @@ -import { FaPlus, FaArrowRight } from "react-icons/fa"; +import { FaPlus, FaArrowRight,FaCheckCircle,FaTimesCircle } from "react-icons/fa"; import ProjectCopo from "./ProjectCopo"; import ProjectModal from "./ProjectModal"; import useAuth from "../../../../auth/useAuth"; import ProfileContext from "../../../context/ProfileContext"; -import { useState, useContext } from "react"; +import { useState, useContext, useEffect } from "react"; +import { axiosPrivate } from "../../../../api/axios"; + +// Toast imports +import ErrorToast from "../../toast/ErrorToast"; +import { useErrorToast } from "../../toast/useErrorToast"; +import SuccessToast from "../../toast/SuccessToast"; +import { useSuccessToast } from "../../toast/useSuccessToast"; function Project() { const [showAll, setShowAll] = useState(false); const [projects, setProjects] = useState([ { - id: 1, - title: "Mobile Recycle", - duration: "Jan 2025-2026", - description: - "Lorem ipsum dolor sit amet consectetur adipisicing elit. Ut, at dolorem...", - skills: "React.js, Node.js, express.js", - image: - "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTlKC5yJhCCyjyhrC-2XBv3H4i1-ojuLblUlg&s", + title: "", + duration: "", + description: "", + techStack: "", + media: { + images: [], + videos: [], + }, + liveLink: "", + sourceCodeLink: "", }, ]); @@ -26,34 +35,93 @@ function Project() { const isOwnProfile = auth?.username === profile?.username; const isAdminOrOwner = ["admin", "owner"].includes(auth?.role); const canEdit = isOwnProfile || isAdminOrOwner; + const canDelete = isOwnProfile || isAdminOrOwner; const [isModalOpen, setIsModalOpen] = useState(false); const [editProject, setEditProject] = useState(null); + const { + message: errorMessage, + show: showErrorToast, + showError, + } = useErrorToast(); + + const { + message: successMessage, + show: showSuccessToast, + showSuccess, + } = useSuccessToast(); + const handleAdd = () => { setEditProject(null); setIsModalOpen(true); }; + const handleEdit = (project) => { setEditProject(project); setIsModalOpen(true); + console.log("edit project", project._id); + }; + + const fetchProjects = async () => { + try { + const response = await axiosPrivate.get("/project/allProjects", { + headers: { + Authorization: `Bearer ${auth?.accessToken}`, + }, + }); + setProjects(response.data); + } catch (err) { + showError("Failed to fetch projects") + console.log(err); + } }; - const handleSave = (newProject) => { + const handleDelete = async (id) => { + console.log("delete project", id); + try { + const deleteProject = await axiosPrivate.delete(`/project/${id}`, { + headers: { + Authorization: `Bearer ${auth?.accessToken}`, + }, + }); + if(deleteProject.status===200){ + showSuccess("Project deleted successfully") + } + } catch (err) { + showError("Failed to delete project") + } + fetchProjects(); + }; + + const handleSave = async (newProject) => { + try { if (editProject) { setProjects((prev) => prev.map((p) => p.id === editProject.id ? { ...newProject, id: p.id } : p ) ); + showSuccess("Project updated successfully"); } else { const id = Date.now(); setProjects((prev) => [...prev, { ...newProject, id }]); + showSuccess("Project added successfully"); } setIsModalOpen(false); + fetchProjects(); + } catch (err) { + showError("Failed to save project"); + } }; + useEffect(() => { + if (auth?.accessToken) { + fetchProjects(); + } + }, [auth?.accessToken]); + return (
@@ -75,6 +143,8 @@ function Project() { project={project} onEdit={() => handleEdit(project)} canEdit={canEdit} + canDelete={canDelete} + onDelete={handleDelete} /> ))} @@ -96,6 +166,23 @@ function Project() { initialData={editProject} /> )} + + + } + iconBgColor="bg-blue-200" + /> + } + iconBgColor="bg-red-700" + />
); } diff --git a/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx b/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx index 5a8eff12..0bd30f56 100644 --- a/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx +++ b/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx @@ -1,32 +1,85 @@ -import { FaEdit } from "react-icons/fa"; +import { FaEdit, FaGithub, FaGlobe, FaTrash } from "react-icons/fa"; import { useContext } from "react"; import ThemeContext from "../../../context/ThemeContext"; -function ProjectCopo({ project, onEdit, canEdit }) { +function ProjectCopo({ project, onEdit, canEdit ,onDelete }) { const { darkMode } = useContext(ThemeContext); + console.log(project); return (
{canEdit && (
)} -

{project.title}

-
{project.duration}
-

Description: {project.description}

-

Skill: {project.skills}

- {project.image && ( - project - )} + { + ( +
onDelete(project._id)} + > + +
+ ) + } +
+
+
+

{project.title}

+ +

+ Tech Stack: + {project.techStack} +

+

+ Description: + {project.description} +

+
+ + +
+
+

{project.duration}

+ {project?.media?.images && ( + project + )} +
+
); } diff --git a/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx b/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx index bc1f2548..e89aafee 100644 --- a/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx +++ b/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx @@ -1,30 +1,92 @@ import { useState } from "react"; +import { axiosPrivate } from "../../../../api/axios"; +import useAuth from "../../../../auth/useAuth"; function ProjectModal({ onClose, onSave, initialData }) { + const { auth, setAuth } = useAuth(); + const [type, setType] = useState(initialData ? "update" : "Add"); const [form, setForm] = useState({ + id: initialData?._id || "", title: initialData?.title || "", duration: initialData?.duration || "", description: initialData?.description || "", - skills: initialData?.skills || "", - image: initialData?.image || "", + media: initialData?.media || { images: [], videos: [] }, + sourceCodeLink: initialData?.sourceCodeLink || "", + liveLink: initialData?.livelink || "", + techStack: initialData?.techStack || [], }); + console.log("initial data", initialData); const handleChange = (e) => { setForm({ ...form, [e.target.name]: e.target.value }); }; - const handleSubmit = (e) => { + const handleSubmit = async (e) => { e.preventDefault(); - onSave(form); + if (type === "Add") { + console.log(form); + try { + const response = await axiosPrivate.post("/project/create", form, { + headers: { + Authorization: `Bearer ${auth?.accessToken}`, + }, + }); + console.log(response.data); + } catch (err) { + console.log(err); + } + onSave(form); + + } else if (type === "update") { + + const updatedForm = { _id: form.id }; + + for (const key in form) { + const currentVal = form[key]; + const initialVal = initialData?.[key]; + + if (JSON.stringify(currentVal) !== JSON.stringify(initialVal)) { + updatedForm[key] = currentVal; + } + } + + try { + const response = await axiosPrivate.patch( + "/project/update", + updatedForm, + + { + headers: { + Authorization: `Bearer ${auth?.accessToken}`, + }, + } + ); + onClose(false) + console.log("updated project",response.data) + onSave(response.data) + } catch (err) { + console.log(err); + } + } }; - return ( -
-
-

- {initialData ? "Edit Project" : "Add Project"} -

+ const handleImageChange = (e) => { + const url = e.target.value.trim(); + setForm((prev) => ({ + ...prev, + media: { + ...prev.media, + images: url ? [url] : [], + }, + })); + }; + return ( +
+
+

+ {initialData ? "Edit Project" : "Add Project"} +

+ + + + @@ -81,7 +161,7 @@ function ProjectModal({ onClose, onSave, initialData }) { Cancel
diff --git a/server/controllers/projectController.js b/server/controllers/projectController.js new file mode 100644 index 00000000..6f29e9c9 --- /dev/null +++ b/server/controllers/projectController.js @@ -0,0 +1,148 @@ +const Project = require("../models/Project"); +const User = require("../models/User"); + +const createProject = async (req, res) => { + const userId = req.user.id; + + if (!req.user || !req.user.id) { + return res.status(401).json({ messaeg: "unauthorized user" }); + } + + if (!req.body || !req.body.title || !req.body.description) { + return res + .status(400) + .json({ message: "Title and description are required" }); + } + + JSON.stringify(req.body.media); + try { + const { + title, + description, + media, + tags, + userTags, + techStack, + liveLink, + sourceCodeLink, + duration, + } = req.body; + + const slug = title + .toLowerCase() + .trim() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); + + const newProject = await Project.create({ + title, + description, + media, + tags, + userTags, + techStack, + liveLink, + sourceCodeLink, + duration, + slug, + owner: userId, + }); + + const user = await User.findByIdAndUpdate(userId, { + $push: { projects: newProject._id }, + }); + + res.status(201).send({ + message: "Project created successfully", + result: newProject, + }); + } catch (err) { + res.status(500).json({ message: "Error creating project." }); + } +}; + +const deleteProject = async (req, res) => { + if (!req.params || !req.params.id) { + return res.status(400).json({ message: "Project Id is required" }); + } + + if (!req.user || !req.user.id) { + return res.status(401).json({ message: "Unauthorized user" }); + } + try { + + const { id } = req.params; + const project = await Project.findOneAndDelete({ + _id: id, + owner: req.user.id, + }); + + if (!project) { + return res.status(404).json({ message: "Project not found" }); + } + + await User.findByIdAndUpdate(req.user.id, { + $pull: { projects: project._id }, + }); + + return res.status(200).json({ + message: "Project deleted successfully", + reult: project, + }); + } catch (err) { + return res.status(500).json({ message: "Error deleting project" }); + } +}; + +const updateProject = async (req, res) => { + const { _id, owner, ...updates } = req.body; + + if(!req.user || !req.user.id){ + return res.status(401).json({message:"unauthorized user"}) + } + + if(!updates || Object.keys(updates).length===0){ + return res.status(400).json({message:"No updates provided"}) + } + + try { + const project = await Project.findOneAndUpdate( + {_id}, + { $set: updates }, + { new: true, runValidators: true } + ); + + if (!project) { + return res.status(404).json({ message: "Project not found" }); + } + + return res.status(200).json({ + message: "Project updated successfully", + result: project, + }); + + } catch (err) { + return res.status(500).json({ message: "Error updating project" }); + } +}; + +const getAllProjects = async (req, res) => { + if (!req.user || !req.user.id) { + return res.status(401).json({ message: "unauthorized user" }); + } + + try { + + const projects = await Project.find({ owner: req.user.id }); + + if(!projects || projects.length ===0){ + return res.status(404).json({message:"No project found"}) + } + + return res.status(200).json(projects); + } catch (err) { + return res.status(500).json({ message: "Error fetching projects" }); + } +}; + +module.exports = { createProject, deleteProject, getAllProjects ,updateProject}; diff --git a/server/models/Project.js b/server/models/Project.js index 0e9b715e..da3e393e 100644 --- a/server/models/Project.js +++ b/server/models/Project.js @@ -4,10 +4,10 @@ const mongoose = require('mongoose'); const projectSchema = new mongoose.Schema({ title: { type: String, required: true }, description: { type: String, required: true }, - media: { - images: [String], - videos: [String] - }, + media: { + images:{type: [String], default: []}, + videos:{type: [String], default: []} + }, tags: { type: [String], default: [] }, userTags: { type: [String], default: [] }, @@ -16,8 +16,8 @@ const projectSchema = new mongoose.Schema({ liveLink: { type: String, default: '' }, sourceCodeLink: { type: String, default: '' }, - startDate: { type: Date }, - endDate: { type: Date }, + + duration:{type:String,default:Date.now()}, // For Project URL slug: { type: String, unique: true }, diff --git a/server/routes/projectRoutes.js b/server/routes/projectRoutes.js new file mode 100644 index 00000000..81f29a1b --- /dev/null +++ b/server/routes/projectRoutes.js @@ -0,0 +1,24 @@ +const express = require('express'); +const router = express.Router(); +const { verifyJWT } = require('../middleware/verifyJWT'); +const { verifyRoles } = require('../middleware/verifyRoles'); +const {createProject}=require('../controllers/projectController.js') +const {deleteProject}=require('../controllers/projectController.js') +const {getAllProjects}=require('../controllers/projectController.js') +const {updateProject}=require('../controllers/projectController.js') + + +router.route('/create') + .post(verifyJWT, verifyRoles('user', 'admin', 'owner'), createProject) + +router.route('/:id') + .delete(verifyJWT,verifyRoles('user','admin','owner'),deleteProject) + +router.route('/allProjects') + .get(verifyJWT,verifyRoles('user','admin','owner'),getAllProjects) + +router.route('/update') + .patch(verifyJWT,verifyRoles('user','admin','owner'),updateProject) + + +module.exports = router; \ No newline at end of file diff --git a/server/server.js b/server/server.js index 6adfeb13..e96719a2 100644 --- a/server/server.js +++ b/server/server.js @@ -22,6 +22,7 @@ const refreshRoutes = require('./routes/refreshRoutes'); const logoutRoutes = require('./routes/logoutRoutes'); const profileRoutes = require('./routes/profileRoutes'); const blogPostRoutes = require('./routes/blogPostRoutes'); +const projectRoutes=require('./routes/projectRoutes') // Middleware app.use(express.json()); @@ -49,6 +50,8 @@ app.use('/profile', profileRoutes); // Blog Post Routes app.use('/api/posts', blogPostRoutes) +app.use('/project',projectRoutes) + app.listen(port, () => { console.log(`Server running on port: ${port}`); }); \ No newline at end of file From 969ecf00f3b00c8ed0aaa9f9c58ab06b63a033ca Mon Sep 17 00:00:00 2001 From: "Kotla.Lokeshwari" Date: Mon, 23 Jun 2025 17:14:13 +0530 Subject: [PATCH 2/5] made required changes for User Projects --- .../profile/ProjectsSec/Project.jsx | 22 +++++----- .../profile/ProjectsSec/ProjectCopo.jsx | 5 ++- .../profile/ProjectsSec/ProjectModal.jsx | 8 ++-- server/controllers/projectController.js | 8 ++-- server/routes/projectRoutes.js | 40 ++++++++++--------- server/server.js | 2 +- 6 files changed, 44 insertions(+), 41 deletions(-) diff --git a/client/src/assets/components/profile/ProjectsSec/Project.jsx b/client/src/assets/components/profile/ProjectsSec/Project.jsx index f515b9d2..3b4f4863 100644 --- a/client/src/assets/components/profile/ProjectsSec/Project.jsx +++ b/client/src/assets/components/profile/ProjectsSec/Project.jsx @@ -14,8 +14,7 @@ import { useSuccessToast } from "../../toast/useSuccessToast"; function Project() { const [showAll, setShowAll] = useState(false); - const [projects, setProjects] = useState([ - { + const [projects, setProjects] = useState([{ title: "", duration: "", description: "", @@ -25,9 +24,8 @@ function Project() { videos: [], }, liveLink: "", - sourceCodeLink: "", - }, - ]); + sourceCodeLink: "" + }]) const { auth } = useAuth(); const { profile } = useContext(ProfileContext); @@ -66,7 +64,7 @@ function Project() { const fetchProjects = async () => { try { - const response = await axiosPrivate.get("/project/allProjects", { + const response = await axiosPrivate.get("/projects/allProjects", { headers: { Authorization: `Bearer ${auth?.accessToken}`, }, @@ -81,7 +79,7 @@ function Project() { const handleDelete = async (id) => { console.log("delete project", id); try { - const deleteProject = await axiosPrivate.delete(`/project/${id}`, { + const deleteProject = await axiosPrivate.delete(`/projects/${id}`, { headers: { Authorization: `Bearer ${auth?.accessToken}`, }, @@ -105,8 +103,6 @@ function Project() { ); showSuccess("Project updated successfully"); } else { - const id = Date.now(); - setProjects((prev) => [...prev, { ...newProject, id }]); showSuccess("Project added successfully"); } setIsModalOpen(false); @@ -137,7 +133,8 @@ function Project() {
- {(showAll ? projects : projects.slice(0, 2)).map((project) => ( + + {projects.length>0 ? (showAll ? projects : projects.slice(0, 2)).map((project) => ( - ))} + )):
+

No projects were added yet

+

Create a project to see it listed here.

+
} {projects.length > 2 && (
+ ); } diff --git a/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx b/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx index e89aafee..93baf06f 100644 --- a/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx +++ b/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx @@ -12,7 +12,7 @@ function ProjectModal({ onClose, onSave, initialData }) { description: initialData?.description || "", media: initialData?.media || { images: [], videos: [] }, sourceCodeLink: initialData?.sourceCodeLink || "", - liveLink: initialData?.livelink || "", + liveLink: initialData?.liveLink || "", techStack: initialData?.techStack || [], }); @@ -26,12 +26,11 @@ function ProjectModal({ onClose, onSave, initialData }) { if (type === "Add") { console.log(form); try { - const response = await axiosPrivate.post("/project/create", form, { + const response = await axiosPrivate.post("/projects/create", form, { headers: { Authorization: `Bearer ${auth?.accessToken}`, }, }); - console.log(response.data); } catch (err) { console.log(err); } @@ -52,7 +51,7 @@ function ProjectModal({ onClose, onSave, initialData }) { try { const response = await axiosPrivate.patch( - "/project/update", + "/projects/update", updatedForm, { @@ -62,7 +61,6 @@ function ProjectModal({ onClose, onSave, initialData }) { } ); onClose(false) - console.log("updated project",response.data) onSave(response.data) } catch (err) { console.log(err); diff --git a/server/controllers/projectController.js b/server/controllers/projectController.js index 6f29e9c9..a6fdddca 100644 --- a/server/controllers/projectController.js +++ b/server/controllers/projectController.js @@ -5,7 +5,7 @@ const createProject = async (req, res) => { const userId = req.user.id; if (!req.user || !req.user.id) { - return res.status(401).json({ messaeg: "unauthorized user" }); + return res.status(401).json({ messaeg: "Unauthorized user" }); } if (!req.body || !req.body.title || !req.body.description) { @@ -98,7 +98,7 @@ const updateProject = async (req, res) => { const { _id, owner, ...updates } = req.body; if(!req.user || !req.user.id){ - return res.status(401).json({message:"unauthorized user"}) + return res.status(401).json({message:"Unauthorized user"}) } if(!updates || Object.keys(updates).length===0){ @@ -128,7 +128,7 @@ const updateProject = async (req, res) => { const getAllProjects = async (req, res) => { if (!req.user || !req.user.id) { - return res.status(401).json({ message: "unauthorized user" }); + return res.status(401).json({ message: "Unauthorized user" }); } try { @@ -136,7 +136,7 @@ const getAllProjects = async (req, res) => { const projects = await Project.find({ owner: req.user.id }); if(!projects || projects.length ===0){ - return res.status(404).json({message:"No project found"}) + return res.status(200).json({message:"No project found",projects:[]}) } return res.status(200).json(projects); diff --git a/server/routes/projectRoutes.js b/server/routes/projectRoutes.js index 81f29a1b..13273994 100644 --- a/server/routes/projectRoutes.js +++ b/server/routes/projectRoutes.js @@ -1,24 +1,28 @@ -const express = require('express'); +const express = require("express"); const router = express.Router(); -const { verifyJWT } = require('../middleware/verifyJWT'); -const { verifyRoles } = require('../middleware/verifyRoles'); -const {createProject}=require('../controllers/projectController.js') -const {deleteProject}=require('../controllers/projectController.js') -const {getAllProjects}=require('../controllers/projectController.js') -const {updateProject}=require('../controllers/projectController.js') +const { verifyJWT } = require("../middleware/verifyJWT"); +const { verifyRoles } = require("../middleware/verifyRoles"); +const { + createProject, + deleteProject, + getAllProjects, + updateProject, +} = require("../controllers/projectController.js"); +router + .route("/create") + .post(verifyJWT, verifyRoles("user", "admin", "owner"), createProject); -router.route('/create') - .post(verifyJWT, verifyRoles('user', 'admin', 'owner'), createProject) +router + .route("/:id") + .delete(verifyJWT, verifyRoles("user", "admin", "owner"), deleteProject); -router.route('/:id') - .delete(verifyJWT,verifyRoles('user','admin','owner'),deleteProject) +router + .route("/allProjects") + .get(verifyJWT, verifyRoles("user", "admin", "owner"), getAllProjects); -router.route('/allProjects') - .get(verifyJWT,verifyRoles('user','admin','owner'),getAllProjects) +router + .route("/update") + .patch(verifyJWT, verifyRoles("user", "admin", "owner"), updateProject); -router.route('/update') - .patch(verifyJWT,verifyRoles('user','admin','owner'),updateProject) - - -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/server/server.js b/server/server.js index e96719a2..bd33e3c8 100644 --- a/server/server.js +++ b/server/server.js @@ -50,7 +50,7 @@ app.use('/profile', profileRoutes); // Blog Post Routes app.use('/api/posts', blogPostRoutes) -app.use('/project',projectRoutes) +app.use('/projects',projectRoutes) app.listen(port, () => { console.log(`Server running on port: ${port}`); From b4445b9ce9d1f380f099e67a26b1d427c9d069c6 Mon Sep 17 00:00:00 2001 From: "Kotla.Lokeshwari" Date: Mon, 23 Jun 2025 17:30:43 +0530 Subject: [PATCH 3/5] Made changes in Project.jsx --- .../profile/ProjectsSec/Project.jsx | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/client/src/assets/components/profile/ProjectsSec/Project.jsx b/client/src/assets/components/profile/ProjectsSec/Project.jsx index 3b4f4863..cab327cf 100644 --- a/client/src/assets/components/profile/ProjectsSec/Project.jsx +++ b/client/src/assets/components/profile/ProjectsSec/Project.jsx @@ -14,18 +14,7 @@ import { useSuccessToast } from "../../toast/useSuccessToast"; function Project() { const [showAll, setShowAll] = useState(false); - const [projects, setProjects] = useState([{ - title: "", - duration: "", - description: "", - techStack: "", - media: { - images: [], - videos: [], - }, - liveLink: "", - sourceCodeLink: "" - }]) + const [projects, setProjects] = useState([]) const { auth } = useAuth(); const { profile } = useContext(ProfileContext); @@ -93,14 +82,9 @@ function Project() { fetchProjects(); }; - const handleSave = async (newProject) => { + const handleSave = async () => { try { if (editProject) { - setProjects((prev) => - prev.map((p) => - p.id === editProject.id ? { ...newProject, id: p.id } : p - ) - ); showSuccess("Project updated successfully"); } else { showSuccess("Project added successfully"); @@ -134,9 +118,9 @@ function Project() {
- {projects.length>0 ? (showAll ? projects : projects.slice(0, 2)).map((project) => ( + {projects.length>0 ? (showAll ? projects : projects.slice(0, 2)).map((project,index) => ( handleEdit(project)} canEdit={canEdit} From 6da68b4d1e283d19d3768068c1b6efe357fee3c8 Mon Sep 17 00:00:00 2001 From: "Kotla.Lokeshwari" Date: Fri, 27 Jun 2025 19:01:27 +0530 Subject: [PATCH 4/5] Made changes in ProjectCopo.jsx and ProjectModal.jsx --- .../profile/ProjectsSec/ProjectCopo.jsx | 13 +- .../profile/ProjectsSec/ProjectModal.jsx | 129 ++++++++++++++++-- 2 files changed, 125 insertions(+), 17 deletions(-) diff --git a/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx b/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx index 244c8660..8760b699 100644 --- a/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx +++ b/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx @@ -42,36 +42,37 @@ function ProjectCopo({ project, onEdit, canEdit ,onDelete }) {

{project.title}

- Tech Stack: + Tech Stack:{" "} {project.techStack}

- Description: + Description:{" "} {project.description}

+ {project.sourceCodeLink && - - } + {project.liveLink && - + }

{project.duration}

- {project?.media?.images && ( + {project?.media?.images[0] && ( { + projectNameRef.current.focus(); + }, []); + + useEffect(() => { + setValidTitle(TITLE_REGEX.test(form.title)); + setValidDuration(DURATION_REGEX.test(form.duration)); + setValidDescription(DESCRIPTION_REGEX.test(form.description)); + setValidSource(URL_REGEX.test(form.sourceCodeLink)); + setValidLive(URL_REGEX.test(form.liveLink)); + setValidTechStack( + form.techStack.every((t) => TECH_STACK_ITEM_REGEX.test(t)) + ); + }, [form]); + const handleChange = (e) => { - setForm({ ...form, [e.target.name]: e.target.value }); + const { name, value } = e.target; + if (name === "techStack") { + setForm({ + ...form, + techStack: value + .split(",") + + }); + } else { + setForm({ ...form, [name]: value }); + } }; + useEffect(() => { + setErrMsg(""); + }, [ + form.id, + form.title, + form.duration, + form.description, + form.media, + form.sourceCodeLink, + form.liveLink, + form.techStack, + ]); + const handleSubmit = async (e) => { e.preventDefault(); if (type === "Add") { - console.log(form); + const v1 = TITLE_REGEX.test(form.title); + const v2 = DURATION_REGEX.test(form.duration); + const v3 = DESCRIPTION_REGEX.test(form.description); + const v4 = URL_REGEX.test(form.sourceCodeLink); + const v5 = URL_REGEX.test(form.liveLink); + const v6 = form.techStack.every((t) => TECH_STACK_ITEM_REGEX.test(t)); + + if (!v1 || !v2 || !v3 || !v4 || !v5 || !v6) { + showError("Invalid Entry"); + return; + } + try { const response = await axiosPrivate.post("/projects/create", form, { headers: { Authorization: `Bearer ${auth?.accessToken}`, }, }); + showSuccess("Project added Successfully!"); + setSuccess(true); } catch (err) { - console.log(err); + showError(`${JSON.stringify(err.response.data.message).slice(1, -1)}`); + + errRef.current.focus(); } onSave(form); - } else if (type === "update") { - const updatedForm = { _id: form.id }; for (const key in form) { @@ -60,10 +149,12 @@ function ProjectModal({ onClose, onSave, initialData }) { }, } ); - onClose(false) - onSave(response.data) + onClose(false); + onSave(response.data); + showSuccess("Project updated Successfully!"); + setSuccess(true); } catch (err) { - console.log(err); + showError(`${JSON.stringify(err.response.data.message).slice(1, -1)}`); } } }; @@ -93,6 +184,7 @@ function ProjectModal({ onClose, onSave, initialData }) { placeholder="Project Title" value={form.title} onChange={handleChange} + ref={projectNameRef} className="input input-bordered w-full bg-base-100 text-base-content" /> @@ -118,7 +210,7 @@ function ProjectModal({ onClose, onSave, initialData }) { type="text" name="techStack" placeholder="Skills (e.g., React, Node)" - value={form.techStack} + value={form.techStack.join(",")} onChange={handleChange} className="input input-bordered w-full bg-base-100 text-base-content" /> @@ -164,6 +256,21 @@ function ProjectModal({ onClose, onSave, initialData }) {
+ + } + iconBgColor="bg-green-700" + /> + } + iconBgColor="bg-red-700" + />
); } From e5a851a303c633a973ba5725b24c2acf2719e555 Mon Sep 17 00:00:00 2001 From: "Kotla.Lokeshwari" Date: Mon, 30 Jun 2025 13:44:26 +0530 Subject: [PATCH 5/5] Dark mode support , Edge cases added for UserProjects --- .../profile/ProjectsSec/Project.jsx | 8 ++++- .../profile/ProjectsSec/ProjectCopo.jsx | 5 ++- .../profile/ProjectsSec/ProjectModal.jsx | 33 +++++++++---------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/client/src/assets/components/profile/ProjectsSec/Project.jsx b/client/src/assets/components/profile/ProjectsSec/Project.jsx index cab327cf..d609e0ba 100644 --- a/client/src/assets/components/profile/ProjectsSec/Project.jsx +++ b/client/src/assets/components/profile/ProjectsSec/Project.jsx @@ -5,6 +5,7 @@ import useAuth from "../../../../auth/useAuth"; import ProfileContext from "../../../context/ProfileContext"; import { useState, useContext, useEffect } from "react"; import { axiosPrivate } from "../../../../api/axios"; +import ThemeContext from "../../../context/ThemeContext"; // Toast imports import ErrorToast from "../../toast/ErrorToast"; @@ -13,6 +14,7 @@ import SuccessToast from "../../toast/SuccessToast"; import { useSuccessToast } from "../../toast/useSuccessToast"; function Project() { + const { darkMode } = useContext(ThemeContext); const [showAll, setShowAll] = useState(false); const [projects, setProjects] = useState([]) @@ -127,7 +129,11 @@ function Project() { canDelete={canDelete} onDelete={handleDelete} /> - )):
+ )):

No projects were added yet

Create a project to see it listed here.

} diff --git a/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx b/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx index 8760b699..eb1c98ca 100644 --- a/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx +++ b/client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx @@ -43,7 +43,10 @@ function ProjectCopo({ project, onEdit, canEdit ,onDelete }) {

Tech Stack:{" "} - {project.techStack} + {project?.techStack?.map((item,index)=>( + {index > 0 && ", "}{item} + ))} +

Description:{" "} diff --git a/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx b/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx index b01663c1..1ce7ba10 100644 --- a/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx +++ b/client/src/assets/components/profile/ProjectsSec/ProjectModal.jsx @@ -1,7 +1,7 @@ import { useState, useEffect, useRef } from "react"; import { axiosPrivate } from "../../../../api/axios"; import useAuth from "../../../../auth/useAuth"; -import { FaTimesCircle,FaCheckCircle } from "react-icons/fa"; +import { FaTimesCircle, FaCheckCircle } from "react-icons/fa"; // Toast imports import ErrorToast from "../../toast/ErrorToast"; @@ -75,9 +75,8 @@ function ProjectModal({ onClose, onSave, initialData }) { if (name === "techStack") { setForm({ ...form, - techStack: value - .split(",") - + techStack: value.split(",") + .map((t) => t.trim()) }); } else { setForm({ ...form, [name]: value }); @@ -99,19 +98,19 @@ function ProjectModal({ onClose, onSave, initialData }) { const handleSubmit = async (e) => { e.preventDefault(); - if (type === "Add") { - const v1 = TITLE_REGEX.test(form.title); - const v2 = DURATION_REGEX.test(form.duration); - const v3 = DESCRIPTION_REGEX.test(form.description); - const v4 = URL_REGEX.test(form.sourceCodeLink); - const v5 = URL_REGEX.test(form.liveLink); - const v6 = form.techStack.every((t) => TECH_STACK_ITEM_REGEX.test(t)); - - if (!v1 || !v2 || !v3 || !v4 || !v5 || !v6) { - showError("Invalid Entry"); - return; - } + const v1 = TITLE_REGEX.test(form.title); + const v2 = DURATION_REGEX.test(form.duration); + const v3 = DESCRIPTION_REGEX.test(form.description); + const v4 = URL_REGEX.test(form.sourceCodeLink) || form.sourceCodeLink === ""; + const v5 = URL_REGEX.test(form.liveLink) || form.liveLink === ""; + const v6 = form.techStack.every((t) => TECH_STACK_ITEM_REGEX.test(t)); + + if (!v1 || !v2 || !v3 || !v4 || !v5 || !v6) { + showError("Invalid Entry"); + return; + } + if (type === "Add") { try { const response = await axiosPrivate.post("/projects/create", form, { headers: { @@ -210,7 +209,7 @@ function ProjectModal({ onClose, onSave, initialData }) { type="text" name="techStack" placeholder="Skills (e.g., React, Node)" - value={form.techStack.join(",")} + value={form.techStack.join(", ")} onChange={handleChange} className="input input-bordered w-full bg-base-100 text-base-content" />