diff --git a/package-lock.json b/package-lock.json index ab96bab..d44e147 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,11 @@ "@react-three/fiber": "^9.3.0", "@react-three/postprocessing": "^3.0.4", "framer-motion": "^12.23.24", + "mongoose": "^8.19.1", "next": "15.5.4", "react": "19.1.0", "react-dom": "19.1.0", + "react-icons": "^5.5.0", "react-typed": "^2.0.12" }, "devDependencies": { @@ -783,6 +785,15 @@ "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", "license": "Apache-2.0" }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", + "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@monogrid/gainmap-js": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.1.0.tgz", @@ -1530,12 +1541,27 @@ "meshoptimizer": "~0.22.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, "node_modules/@types/webxr": { "version": "0.5.24", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", "license": "MIT" }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.46.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", @@ -2463,6 +2489,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -2743,7 +2778,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4579,6 +4613,15 @@ "node": ">=4.0" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4937,6 +4980,12 @@ "node": ">= 0.4" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5022,6 +5071,84 @@ "node": ">= 18" } }, + "node_modules/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.19.1.tgz", + "integrity": "sha512-oB7hGQJn4f8aebqE7mhE54EReb5cxVgpCxQCQj0K/cK3q4J3Tg08nFP6sM52nJ4Hlm8jsDnhVYpqIITZUAhckQ==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.20.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, "node_modules/motion-dom": { "version": "12.23.23", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", @@ -5037,11 +5164,31 @@ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", "license": "MIT" }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/n8ao": { @@ -5514,7 +5661,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5562,6 +5708,15 @@ "react": "^19.1.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -6009,6 +6164,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6018,6 +6179,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -6397,6 +6567,18 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/troika-three-text": { "version": "0.52.4", "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", @@ -6707,6 +6889,28 @@ "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", "license": "MIT" }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 34933ea..54c2db9 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "@react-three/fiber": "^9.3.0", "@react-three/postprocessing": "^3.0.4", "framer-motion": "^12.23.24", + "mongoose": "^8.19.1", "next": "15.5.4", "react": "19.1.0", "react-dom": "19.1.0", + "react-icons": "^5.5.0", "react-typed": "^2.0.12" }, "devDependencies": { diff --git a/public/ai.jpg b/public/ai.jpg new file mode 100644 index 0000000..ec698c6 Binary files /dev/null and b/public/ai.jpg differ diff --git a/public/clg.jpg b/public/clg.jpg new file mode 100644 index 0000000..d4af815 Binary files /dev/null and b/public/clg.jpg differ diff --git a/src/app/api/projects/route.js b/src/app/api/projects/route.js new file mode 100644 index 0000000..5b7df48 --- /dev/null +++ b/src/app/api/projects/route.js @@ -0,0 +1,41 @@ +import connectToDatabase from "../../../lib/db"; +import Project from "../../../models/Projects"; + +// 🟢 Create new project +export async function POST(req) { + try { + const { name, techStack, description, githubLink, deployedLink } = await req.json(); + + await connectToDatabase(); + + const newProject = new Project({ + name, + techStack, + description, + githubLink, + deployedLink, + }); + + await newProject.save(); + + return new Response( + JSON.stringify({ message: "Project added successfully!" }), + { status: 201 } + ); + } catch (error) { + console.error(" Error saving project:", error); + return new Response(JSON.stringify({ error: error.message }), { status: 500 }); + } +} + +// 🔵 Fetch all projects +export async function GET() { + try { + await connectToDatabase(); + const projects = await Project.find({}); + return new Response(JSON.stringify(projects), { status: 200 }); + } catch (error) { + console.error(" Error fetching projects:", error); + return new Response(JSON.stringify({ error: error.message }), { status: 500 }); + } +} diff --git a/src/app/components/Navbar.jsx b/src/app/components/Navbar.jsx index 9bdb482..a9e26b2 100644 --- a/src/app/components/Navbar.jsx +++ b/src/app/components/Navbar.jsx @@ -29,7 +29,8 @@ const ANIMATIONS = { export default function Navbar() { const [isOpen, setIsOpen] = useState(false); - const [showDropdown, setShowDropdown] = useState(false); + const [showDropdownResources, setShowDropdownResources] = useState(false); + const [showDropdownProjects, setShowDropdownProjects] = useState(false); useEffect(() => { const onKey = (e) => e.key === "Escape" && setIsOpen(false); @@ -46,8 +47,8 @@ export default function Navbar() { const mainLinks = [ "Home", - "Events", "Projects", + "Events", "Team", "Resources", "Contact", @@ -119,7 +120,6 @@ export default function Navbar() { variants={ANIMATIONS.menuItem} className="flex flex-col items-start w-full" > - {/* Title */} - {/* Subsections with vertical blue line */} setIsOpen(false)} > Online Resources @@ -143,6 +143,7 @@ export default function Navbar() { href="/books" className="text-white text-base hover:text-blue-300" variants={ANIMATIONS.menuItem} + onClick={() => setIsOpen(false)} > Books @@ -151,6 +152,45 @@ export default function Navbar() { ); } + if (item === "Projects") { + return ( + + + Projects + + + + setIsOpen(false)} + > + Upload Projects + + setIsOpen(false)} + > + View Projects + + + + ); + } + return ( {mainLinks.map((item) => { + if (item === "Projects") { + return ( +
setShowDropdownProjects(true)} + onMouseLeave={() => setShowDropdownProjects(false)} + > + + Projects â–¾ + + + + {showDropdownProjects && ( + + setIsOpen(false)} + > + Upload Projects + + setIsOpen(false)} + > + View Projects + + + )} + +
+ ); + } + if (item === "Resources") { return (
setShowDropdown(true)} - onMouseLeave={() => setShowDropdown(false)} + onMouseEnter={() => setShowDropdownResources(true)} + onMouseLeave={() => setShowDropdownResources(false)} > Resources â–¾ - {showDropdown && ( + {showDropdownResources && ( setIsOpen(false)} > Online Resources setIsOpen(false)} > Books diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx new file mode 100644 index 0000000..aaf4efe --- /dev/null +++ b/src/app/projects/page.tsx @@ -0,0 +1,135 @@ +'use client'; + +import React, { useEffect, useState } from "react"; +import { Orbitron } from "next/font/google"; +import { FaGithub } from "react-icons/fa"; + +const orbitron = Orbitron({ + subsets: ["latin"], + weight: ["400", "700"], +}); + +const cardThemes = [ + { + bg: "from-[#0F172A] to-[#1E293B]", + headerBg: "bg-[#1E3A8A]", + accent: "#60A5FA", + }, + { + bg: "from-[#0A0F1E] to-[#112240]", + headerBg: "bg-[#0EA5E9]", + accent: "#38BDF8", + }, + { + bg: "from-[#1E293B] to-[#0F172A]", + headerBg: "bg-[#0284C7]", + accent: "#7DD3FC", + }, +]; + +const ProjectsPage: React.FC = () => { + const [projects, setProjects] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + + useEffect(() => { + const fetchProjects = async () => { + try { + const res = await fetch("/api/projects", { cache: "no-store" }); + const data = await res.json(); + + if (!res.ok) throw new Error(data.error || "Failed to fetch projects"); + setProjects(data); + } catch (err: any) { + console.error("Error fetching projects:", err); + setError(err.message || "Something went wrong"); + } finally { + setLoading(false); + } + }; + + fetchProjects(); + }, []); + + if (loading) { + return ( +
+ Loading Projects... +
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + + return ( +
+

+ PROJECTS +

+ +
+ {projects.map((project, index) => { + const theme = cardThemes[index % cardThemes.length]; + return ( +
+
+

+ {project.name} +

+
+ +
+

+ Tech Stack: {project.techStack} +

+ + {project.description && ( +

{project.description}

+ )} + +
+ {project.deployedLink && ( + + View Project + + )} + + {project.githubLink && ( + + + + )} +
+
+
+ ); + })} +
+
+ ); +}; + +export default ProjectsPage; diff --git a/src/app/upload/page.tsx b/src/app/upload/page.tsx new file mode 100644 index 0000000..d221686 --- /dev/null +++ b/src/app/upload/page.tsx @@ -0,0 +1,197 @@ +'use client'; +import React, { useState } from "react"; +import Image from "next/image"; +import { useRouter } from "next/navigation"; + +const UploadPage: React.FC = () => { + const router = useRouter(); + + const [formData, setFormData] = useState({ + projectName: "", + techStack: "", + description: "", + github: "", + deployed: "", + }); + + const [isSubmitting, setIsSubmitting] = useState(false); + const [message, setMessage] = useState(""); + const [showModal, setShowModal] = useState(false); + + const handleChange = (e: React.ChangeEvent) => { + setFormData({ ...formData, [e.target.id]: e.target.value }); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSubmitting(true); + setMessage(""); + + try { + const res = await fetch("/api/projects", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + name: formData.projectName, + techStack: formData.techStack, + description: formData.description, + githubLink: formData.github, + deployedLink: formData.deployed, + }), + }); + + if (res.ok) { + setMessage(" Project submitted successfully!"); + setShowModal(true); + setFormData({ + projectName: "", + techStack: "", + description: "", + github: "", + deployed: "", + }); + + // Redirect after 2 seconds + setTimeout(() => { + setShowModal(false); + router.push("/projects"); + }, 2000); + } else { + setMessage(" Failed to submit. Try again."); + } + } catch (err) { + console.error(err); + setMessage(" Something went wrong."); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+
+ ml4e +
+ +
+
+

Submit Your Project

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + {message && ( +

{message}

+ )} +
+
+ + {showModal && ( +
+
+

Project Submitted!

+

+ Redirecting you to the projects page... +

+
+
+
+
+
+ )} +
+ ); +}; + +export default UploadPage; diff --git a/src/lib/db.js b/src/lib/db.js new file mode 100644 index 0000000..0b5b641 --- /dev/null +++ b/src/lib/db.js @@ -0,0 +1,16 @@ +import mongoose from "mongoose"; + +const connectToDatabase = async () => { + if (mongoose.connection.readyState >= 1) return; + + try { + await mongoose.connect(process.env.MongoURL, { + dbName: "portfolioDB", + }); + console.log(" Connected to MongoDB"); + } catch (error) { + console.error(" MongoDB connection error:", error); + } +}; + +export default connectToDatabase; diff --git a/src/models/Projects.js b/src/models/Projects.js new file mode 100644 index 0000000..304c0a0 --- /dev/null +++ b/src/models/Projects.js @@ -0,0 +1,14 @@ +import mongoose from "mongoose"; + +const ProjectSchema = new mongoose.Schema( + { + name: { type: String, required: true }, + techStack: { type: String, required: true }, + description: { type: String, required: true }, + deployedLink: { type: String, required: true }, + githubLink: { type: String, required: true }, + }, + { timestamps: true } +); + +export default mongoose.models.Project || mongoose.model("Project", ProjectSchema);