Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 98 additions & 11 deletions client/src/assets/components/profile/ProjectsSec/Project.jsx
Original file line number Diff line number Diff line change
@@ -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: "",
},
]);

Expand All @@ -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 (
<div className="max-w-[90%] lg:max-w-4xl mx-auto p-6 bg-base-100 rounded-lg shadow-md mt-5 relative">
<div className="flex items-center justify-between font-semibold">
Expand All @@ -75,6 +143,8 @@ function Project() {
project={project}
onEdit={() => handleEdit(project)}
canEdit={canEdit}
canDelete={canDelete}
onDelete={handleDelete}
/>
))}

Expand All @@ -96,6 +166,23 @@ function Project() {
initialData={editProject}
/>
)}

<SuccessToast
message={successMessage}
show={showSuccessToast}
status="success"
icon={
<FaCheckCircle className="text-green-600 text-4xl bg-transparent p-0 m-0" />
}
iconBgColor="bg-blue-200"
/>
<ErrorToast
message={errorMessage}
show={showErrorToast}
status="error"
icon={<FaTimesCircle className="text-red-600 text-4xl" />}
iconBgColor="bg-red-700"
/>
</div>
);
}
Expand Down
81 changes: 67 additions & 14 deletions client/src/assets/components/profile/ProjectsSec/ProjectCopo.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="relative mt-3 p-3 pl-5 bg-base-300 rounded-lg">
{canEdit && (
<div
className={`absolute shadow-md top-3 p-2 rounded-3xl right-4 cursor-pointer text-xl text-primary hover:opacity-100
${darkMode ? "bg-[rgb(42,48,60)] opacity-90" : "bg-[#ffffffa6] opacity-70"}`}
${
darkMode
? "bg-[rgb(42,48,60)] opacity-90"
: "bg-[#ffffffa6] opacity-70"
}`}
onClick={onEdit}
>
<FaEdit />
</div>
)}
<h1 className="font-medium">{project.title}</h1>
<h6>{project.duration}</h6>
<h3>Description: {project.description}</h3>
<h3>Skill: {project.skills}</h3>
{project.image && (
<img
className="h-[100px] mt-2 rounded"
src={project.image}
alt="project"
/>
)}
{
(
<div
className={`absolute shadow-md top-14 p-2 rounded-3xl right-4 cursor-pointer text-xl text-primary hover:opacity-100
${
darkMode
? "bg-[rgb(42,48,60)] opacity-90"
: "bg-[#ffffffa6] opacity-70"
}`}
onClick={()=>onDelete(project._id)}
>
<FaTrash/>
</div>
)
}
<div className="grid grid-cols-[60%_40%] gap-3">
<div className="left flex flex-col items-start gap-3 justify-between">
<div className="flex flex-col gap-3">
<h1 className="font-bold text-xl mt-2">{project.title}</h1>

<h3 className="font-semibold">
Tech Stack:
<span className="font-normal">{project.techStack}</span>
</h3>
<h3 className="font-semibold">
Description:
<span className="font-normal">{project.description}</span>
</h3>
</div>

<div className="flex gap-5">
<a
href={project.sourceCodeLink}
target="_blank"
rel="noopener noreferrer"
>
<FaGithub size={30} color="black" />
</a>
<a
href={project.livelink}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-black hover:underline text-2xl"
>
<FaGlobe />
</a>
</div>
</div>
<div className="right flex mt-2 flex-col items-end mr-[18%]">
<h3 className="sm:mr-6">{project.duration}</h3>
{project?.media?.images && (
<img
className="h-[150px] mt-2 rounded sm:mr-5"
src={project.media.images[0]}
alt="project"
/>
)}
</div>
</div>
</div>
);
}
Expand Down
Loading