-
- Welcome to Profile Page.
-
-
- Here, you can manage and update your personal information to ensure
- that your account reflects your preferences. This is where you can
- adjust your settings, change your details, and keep your profile
- up-to-date.
+
+ {/* Left Panel */}
+
+
+ Edit Your Profile
+
+
+ Manage and update your personal information. Your profile details are used
+ in applications submitted to project creators.
-
- For selection purposes, certain data you provide here will be required
- and used in applications submitted to project creators. This may
- include your name, relevant skills or qualifications, and other
- necessary details for selection.
+
+ For selection purposes, certain data like your name, skills, and qualifications
+ will be shared with project owners.
- {/* Main Profile Edit Form */}
-
+ {/* Form */}
+
{/* Full Name */}
- Full Name
-
+
+
+ {/* Email */}
+
+ E-mail Address
setFirstName(e.target.value)}
- placeholder="First name*"
- className="px-4 py-4 w-[11rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
+ type="email"
+ value={email}
+ onChange={(e) => setEmail(e.target.value)}
+ placeholder="E-mail Address"
+ className="w-full px-4 py-3 text-sm bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all"
/>
+
+
+ {/* Institute */}
+
+ Institute Name
setLastName(e.target.value)}
- placeholder="Last name*"
- className="px-4 py-4 w-[11rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
+ value={instituteName}
+ onChange={(e) => setInstituteName(e.target.value)}
+ placeholder="Institute name"
+ className="w-full px-4 py-3 text-sm bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all"
/>
- {/* Email */}
-
E-mail Address
-
setEmail(e.target.value)}
- placeholder="E-mail Address"
- className="px-4 py-4 w-[40rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
- />
-
- {/* Institute */}
-
Institute Name
-
setInstituteName(e.target.value)}
- placeholder="Institute name"
- className="px-4 py-4 w-[40rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
- />
-
{/* Location */}
-
Location
-
setLocation(e.target.value)}
- placeholder="Location (e.g., City, Country)"
- className="px-4 py-4 w-[40rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
- />
-
- {/* Skills */}
-
Skills
-
+
+ Location
setNewSkill(e.target.value)}
- placeholder="Enter Skill"
- className="px-4 py-4 w-[15rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
+ value={location}
+ onChange={(e) => setLocation(e.target.value)}
+ placeholder="City, Country"
+ className="w-full px-4 py-3 text-sm bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all"
/>
-
- Add
-
- {/* Display Skills List */}
-
- {skills.length > 0 ? (
- skills.map((item, index) => (
-
-
{item}
-
removeSkill(index)}
- className="rotate-45 bg-white px-2 rounded-full text-gray border border-gray-400 text-2xl text-black"
+ {/* Skills */}
+
+
Skills
+
+
setNewSkill(e.target.value)}
+ onKeyDown={(e) => e.key === "Enter" && (e.preventDefault(), addSkill())}
+ placeholder="Enter a skill"
+ className="flex-1 px-4 py-3 text-sm bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all"
+ />
+
+
+ Add
+
+
+
+ {skills.length > 0 ? (
+ skills.map((item, index) => (
+
- +
-
-
- ))
- ) : (
-
No skills added yet!
- )}
+
{item}
+
removeSkill(index)}
+ className="hover:text-red-500 transition-colors"
+ >
+
+
+
+ ))
+ ) : (
+
No skills added yet
+ )}
+
{/* Social Links */}
-
Social Links
-
setGithub(e.target.value)}
- placeholder="Github URL"
- className="px-4 py-4 w-[40rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
- />
-
setLinkedin(e.target.value)}
- placeholder="LinkedIn URL"
- className="px-4 py-4 mt-6 w-[40rem] text-xl text-gray-600 font-dmsans border rounded-md border-gray-400 placeholder:text-xl placeholder:text-gray-500"
- />
-
- {/* Checkbox Agreement */}
-
+
+
+ {/* Checkbox */}
+
setIsChecked(!isChecked)}
- className="size-4"
+ className="w-4 h-4 rounded accent-brand-500"
/>
-
- I hereby certify that, to the best of my knowledge, the provided
- information is true and accurate.
+
+ I hereby certify that the provided information is true and accurate.
- {/* Submit Button */}
+ {/* Submit */}
- Submit
+ {isSubmitting ? "Saving..." : "Save Profile"}
diff --git a/app/(root)/components/OnboardingModal.tsx b/app/(root)/components/OnboardingModal.tsx
new file mode 100644
index 0000000..a613d19
--- /dev/null
+++ b/app/(root)/components/OnboardingModal.tsx
@@ -0,0 +1,152 @@
+"use client";
+import React, { useState } from "react";
+import axios from "axios";
+import { toast } from "sonner";
+import { User } from "@/app/types/user";
+import useAuthStore from "@/app/store/useAuthStore";
+import { motion } from "framer-motion";
+import { Compass, Sparkles } from "lucide-react";
+
+interface OnboardingProps {
+ onComplete: (user: User) => void;
+}
+
+const OnboardingModal = ({ onComplete }: OnboardingProps) => {
+ const { user, setUser } = useAuthStore();
+ const [firstName, setFirstName] = useState(user?.name?.split(" ")[0] || "");
+ const [lastName, setLastName] = useState(user?.name?.split(" ").slice(1).join(" ") || "");
+ const [instituteName, setInstituteName] = useState("");
+ const [location, setLocation] = useState("");
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!firstName || !lastName || !instituteName) {
+ toast.error("Please fill in all required fields.");
+ return;
+ }
+
+ setIsSubmitting(true);
+ const toastId = toast.loading("Setting up your profile...");
+
+ const updatedProfileData = {
+ firstName,
+ lastName,
+ name: `${firstName} ${lastName}`,
+ collegeDetails: { name: instituteName },
+ location,
+ };
+
+ try {
+ await axios.put(
+ `${process.env.NEXT_PUBLIC_CLIENT_URL}/api/user/${user?.id}`,
+ updatedProfileData
+ );
+
+ const completeUser = {
+ ...user,
+ ...updatedProfileData,
+ id: user?.id || "",
+ } as User;
+
+ setUser(completeUser);
+ toast.success("Welcome to DevGuild!", { id: toastId });
+ onComplete(completeUser);
+ } catch (error) {
+ console.error("Error setting up profile:", error);
+ toast.error("Failed to complete setup. Please try again.", { id: toastId });
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ Welcome to DevGuild
+
+
+ Let's complete your Guild Card before you explore the platform.
+ This information connects you with the right teams.
+
+
+
+
+
+
+ );
+};
+
+export default OnboardingModal;
diff --git a/app/(root)/components/ProfileCard.tsx b/app/(root)/components/ProfileCard.tsx
index 8d0e061..1792939 100644
--- a/app/(root)/components/ProfileCard.tsx
+++ b/app/(root)/components/ProfileCard.tsx
@@ -4,60 +4,101 @@ import Badge from "./Badge";
import Signature from "./Signature";
import { FaGithub, FaLinkedin } from "react-icons/fa";
import { User } from "@/app/types/user";
+import { MapPin, Building, Code2, Trophy } from "lucide-react";
const ProfileCard = ({ profileData }: { profileData: User }) => {
return (
-
+
-
+ {/* Left: Badge + Signature */}
+
-
-
- {profileData.name}
-
-
-
- Projects Completed:
-
- 68
-
-
-
- Location:
-
-
- {profileData.location}
-
-
-
-
- Institute:
-
-
- {profileData.collegeDetails?.name}
-
-
-
-
- Skills:
-
-
- {profileData.skills}
-
+
+ {/* Right: Info */}
+
+
+ {profileData.name || `${profileData.firstName} ${profileData.lastName}`}
+
+
+
+
+
+
+ Projects Completed:
+
+ {profileData.projectsCompleted ?? 0}
+
+
+
+
+
+
+
+ Location:
+ {profileData.location || "Not set"}
+
+
+
+
+
+
+ Institute:
+
+ {profileData.collegeDetails?.name || "Not set"}
+
+
+
+
+
+
+
+
Skills:
+
+ {profileData.skills && profileData.skills.length > 0 ? (
+ profileData.skills.map((skill, i) => (
+
+ {skill}
+
+ ))
+ ) : (
+ No skills added
+ )}
+
+
+
-
-
- Other Accounts:
-
-
-
-
-
-
-
+ {/* Social Links */}
+
+
Socials:
+ {profileData.github && (
+
+
+
+ )}
+ {profileData.linkedin && (
+
+
+
+ )}
+ {!profileData.github && !profileData.linkedin && (
+
No links added
+ )}
diff --git a/app/(root)/components/SignInButton.tsx b/app/(root)/components/SignInButton.tsx
index 84ef738..91eb936 100644
--- a/app/(root)/components/SignInButton.tsx
+++ b/app/(root)/components/SignInButton.tsx
@@ -7,7 +7,7 @@ export default function SignInButton() {
if (status === "loading") {
return (
-
+
Loading...
);
@@ -19,8 +19,8 @@ export default function SignInButton() {
return (
signIn("google")}
+ className="bg-gradient-to-r from-red-600 to-red-700 hover:from-red-700 hover:to-red-800 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-md hover:shadow-lg active:scale-[0.98]"
+ onClick={() => signIn("google", { callbackUrl: "/project" })}
>
Sign in with Google
diff --git a/app/(root)/components/SignOutButton.tsx b/app/(root)/components/SignOutButton.tsx
index 5dd5c20..8de42c8 100644
--- a/app/(root)/components/SignOutButton.tsx
+++ b/app/(root)/components/SignOutButton.tsx
@@ -2,14 +2,15 @@
import { signOut } from "next-auth/react";
import { useSession } from "next-auth/react";
+import { LogOut } from "lucide-react";
export default function SignOutButton() {
const { status } = useSession();
if (status === "loading") {
return (
-
- Loading...
+
+ ...
);
}
@@ -20,10 +21,12 @@ export default function SignOutButton() {
return (
signOut()}
+ title="Sign out"
>
- Sign out
+
+ Sign out
);
}
diff --git a/app/(root)/components/Signature.tsx b/app/(root)/components/Signature.tsx
index 1445757..b42a82f 100644
--- a/app/(root)/components/Signature.tsx
+++ b/app/(root)/components/Signature.tsx
@@ -2,6 +2,7 @@
import Image from "next/image";
import React, { useState } from "react";
+import { Upload, ImageIcon } from "lucide-react";
const SignatureCard = () => {
const [image, setImage] = useState(null);
@@ -16,30 +17,32 @@ const SignatureCard = () => {
};
return (
-
- {/* Image Preview */}
+
{image ? (
) : (
-
-
No Signature Selected
+
)}
- {/* File Input & Upload Button */}
-
+
-
diff --git a/app/(root)/dashboard/AnnouncementsList.tsx b/app/(root)/dashboard/AnnouncementsList.tsx
new file mode 100644
index 0000000..29a17cb
--- /dev/null
+++ b/app/(root)/dashboard/AnnouncementsList.tsx
@@ -0,0 +1,80 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { Clock } from "lucide-react";
+import Link from "next/link";
+
+interface Notification {
+ _id: string;
+ type: string;
+ title: string;
+ message: string;
+ link?: string;
+ read: boolean;
+ createdAt: string;
+}
+
+const AnnouncementsList = () => {
+ const [announcements, setAnnouncements] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const fetchAnnouncements = async () => {
+ try {
+ const response = await axios.get("/api/notifications");
+ // Filter for announcements (system or project updates)
+ const filtered = response.data.filter(
+ (n: Notification) => n.type === "system" || n.type === "project_update"
+ );
+ setAnnouncements(filtered);
+ } catch (error) {
+ console.error("Error fetching announcements:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchAnnouncements();
+ }, []);
+
+ if (loading) {
+ return (
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+ );
+ }
+
+ if (announcements.length === 0) {
+ return (
+
+ No announcements at the moment.
+
+ );
+ }
+
+ return (
+
+ {announcements.map((item) => (
+
+
+ {item.title}
+
+
+ {new Date(item.createdAt).toLocaleDateString()}
+
+
+
{item.message}
+
+ ))}
+
+ );
+};
+
+export default AnnouncementsList;
diff --git a/app/(root)/dashboard/UserProject.tsx b/app/(root)/dashboard/UserProject.tsx
index fddfdb5..729bac8 100644
--- a/app/(root)/dashboard/UserProject.tsx
+++ b/app/(root)/dashboard/UserProject.tsx
@@ -1,316 +1,134 @@
"use client";
-
-import React, { useState } from 'react';
-import { IoLocationOutline } from "react-icons/io5";
-const projects = [
- {
- id: 1,
- title: "E-Commerce Website",
- domain: "Web Development",
- location: "Pccoer Ravet",
- description:
- "A full-stack e-commerce website with authentication and payment integration. s ",
- },
- {
- id: 2,
- title: "AI Chatbot",
- domain: "Machine Learning",
- location: "MIT Pune",
- description:
- "A chatbot powered by AI to assist with customer queries and automate responses. It uses NLP to understand user intent, providing instant and accurate replies. The chatbot integrates with multiple platforms like websites and messaging apps, helping businesses reduce response time and improve customer engagement through continuous learning and smart recommendations.",
- },
- {
- id: 3,
- title: "IoT Smart Home",
- domain: "Internet of Things",
- location: "COEP Pune",
- description:
- "An IoT-based smart home system to control and monitor devices remotely. It enables users to automate lighting, security cameras, and appliances using a mobile app. With real-time data analytics and voice assistant integration, the system enhances convenience, energy efficiency, and security while allowing remote access and control from anywhere in the world.",
- },
-];
-
-
-// Define the Project type
-interface Project {
- id: number;
- title: string;
- domain: string;
- location: string;
- description: string;
+import React, { useEffect, useState, useCallback } from "react";
+import axios from "axios";
+import useAuthStore from "@/app/store/useAuthStore";
+import { FolderOpen, ExternalLink, Plus } from "lucide-react";
+import Link from "next/link";
+import { motion } from "framer-motion";
+
+interface SimpleProject {
+ _id: string;
+ title: string;
+ description?: string;
+ domains?: string[];
+ location?: string;
}
const UserProject = () => {
-
- const openModal = (project: Project) => {
- setProject(project);
- setIsModalOpen(true);
- };
-
- const [isModalOpen, setIsModalOpen] = useState(false); // Modal state
- const [isFormOpen, setIsFormOpen] = useState(false); // Form state
- const [project, setProject] = useState()
- return (
-
- {projects.map((project) => (
-
-
-
-
- {/* Background Rectangle */}
-
-
-
-
- {/* "S" Character SVG Positioned in Center */}
-
-
-
-
-
-
{project.title}
-
-
- Domains :{" "}
- {project.domain}
-
-
{project.location}
-
-
-
-
-
- Join Meet
-
- openModal(project)}
- className=" border-slate-300 border px-3 py-2 rounded-lg ">
- View Details
-
-
-
-
-
- ))}
-
- setIsFormOpen(true)}
- className="bg-blue-500 text-white px-6 py-2 rounded-lg transition">
- Create new Project
-
-
-
- {/* Modal */}
- {isModalOpen && project && (
-
-
-
-
-
-
- {/* Background Rectangle */}
-
-
-
-
- {/* "S" Character SVG Positioned in Center */}
-
-
-
-
-
-
-
{project.title}
-
-
- Domains :
- {project.domain}
-
-
{project.location}
-
-
-
-
-
- Join Meet
-
- setIsModalOpen(false)}
- className=" border-slate-300 border px-3 py-2 rounded-lg ">
- Go Back
-
-
-
-
-
-
Description:
-
- {project.description}
-
-
-
-
Minimum Requirements :
-
- {project.description}
-
-
-
-
Possible Responsibilities :
-
- {project.description}
-
-
-
- {/* {Teammates} */}
-
-
-
- {/* Background Rectangle */}
-
-
-
-
- {/* "S" Character SVG Positioned in Center */}
-
-
-
-
-
-
Rohit Tare
-
setIsModalOpen(false)}
- className=" bg-red-600 border px-2 ">
- Kick Out
-
-
-
+ const { user } = useAuthStore();
+ const [createdProjects, setCreatedProjects] = useState
([]);
+ const [joinedProjects, setJoinedProjects] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [tab, setTab] = useState<"created" | "joined">("created");
+
+ const fetchProjects = useCallback(async () => {
+ if (!user?.id) return;
+ setLoading(true);
+ try {
+ const [createdRes, joinedRes] = await Promise.all([
+ axios.get(`${process.env.NEXT_PUBLIC_CLIENT_URL}/api/list/created/${user.id}`),
+ axios.get(`${process.env.NEXT_PUBLIC_CLIENT_URL}/api/list/joined/${user.id}`),
+ ]);
+ setCreatedProjects(createdRes.data);
+ setJoinedProjects(joinedRes.data);
+ } catch (error) {
+ console.error("Error fetching user projects:", error);
+ } finally {
+ setLoading(false);
+ }
+ }, [user?.id]);
+
+ useEffect(() => {
+ fetchProjects();
+ }, [fetchProjects]);
+
+ const projects = tab === "created" ? createdProjects : joinedProjects;
+
+ return (
+
+ {/* Sub-tabs */}
+
+ setTab("created")}
+ className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
+ tab === "created"
+ ? "bg-brand-500/10 text-brand-500 dark:text-brand-400"
+ : "text-theme-secondary hover:bg-theme-tertiary"
+ }`}
+ >
+ Created ({createdProjects.length})
+
+ setTab("joined")}
+ className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
+ tab === "joined"
+ ? "bg-brand-500/10 text-brand-500 dark:text-brand-400"
+ : "text-theme-secondary hover:bg-theme-tertiary"
+ }`}
+ >
+ Joined ({joinedProjects.length})
+
+
+
+ {loading ? (
+
+ {[1, 2].map((i) => (
+
+ ))}
+
+ ) : projects.length === 0 ? (
+
+
+
+ {tab === "created" ? "No projects created yet" : "No projects joined yet"}
+
+ {tab === "created" && user?.id && (
+
+
+ Create your first project
+
+ )}
+
+ ) : (
+
+ {projects.map((project, i) => (
+
+
+
+
+
+
{project.title}
+ {project.description && (
+
{project.description}
+ )}
+ {project.domains && project.domains.length > 0 && (
+
+ {project.domains.map((d, di) => (
+
+ {d}
+
+ ))}
+ )}
+
+
- )}
-
- {isFormOpen && (
-
- )}
-
+
+
+ ))}
- )
-}
+ )}
+
+ );
+};
-export default UserProject
+export default UserProject;
diff --git a/app/(root)/dashboard/page.tsx b/app/(root)/dashboard/page.tsx
index bc6f797..8415711 100644
--- a/app/(root)/dashboard/page.tsx
+++ b/app/(root)/dashboard/page.tsx
@@ -1,50 +1,64 @@
+"use client";
import React from "react";
import TabsComponent2 from "../TabsComponent2";
-import TableComponent from "../TableComponent";
+
import UserProject from "./UserProject";
-import Image from "next/image";
+import useAuthStore from "@/app/store/useAuthStore";
+import { motion } from "framer-motion";
+import { LayoutDashboard, Sparkles } from "lucide-react";
+
+import AnnouncementsList from "./AnnouncementsList";
-// Tab contents with table components
const tabContents = [
- ,
- ,
+ ,
+ ,
];
-const page = () => {
- const tabTitles = ["Announcements (10)", "Projects(02)"];
+const Page = () => {
+ const { user } = useAuthStore();
+ const tabTitles = ["Announcements", "Projects"];
+
return (
-
-
Hello, Rohit!
-
-
-
Welcome to User Dashboard.
-
- Here, you can stay up-to-date with all the latest notifications and
- announcements related to your selected projects. Get real-time
- updates, important alerts, and stay informed about key developments
- and opportunities. Keep everything at your fingertips and never miss
- an important detail about your ongoing collaborations.
+
+
+ Hello, {user?.firstName || "Developer"}!
+
+
+
+
+
+
+
Welcome to your Dashboard
+
+
+ Stay up-to-date with notifications and announcements for your projects.
+ Get real-time updates, important alerts, and never miss key developments.
-
-
- Dashboard
-
+
+
+
+ Dashboard
+
+
);
};
-export default page;
+export default Page;
diff --git a/app/(root)/feedback/page.tsx b/app/(root)/feedback/page.tsx
index f19662a..15ba972 100644
--- a/app/(root)/feedback/page.tsx
+++ b/app/(root)/feedback/page.tsx
@@ -1,63 +1,123 @@
-import Image from "next/image";
-import React from "react";
+"use client";
+import React, { useState } from "react";
+import useAuthStore from "@/app/store/useAuthStore";
+import { motion } from "framer-motion";
+import { MessageSquare, Send, Sparkles } from "lucide-react";
+import { toast } from "sonner";
const Page = () => {
+ const { user } = useAuthStore();
+ const [feedback, setFeedback] = useState("");
+ const [isChecked, setIsChecked] = useState(false);
+
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!feedback || !isChecked) return;
+ setIsSubmitting(true);
+
+ try {
+ const response = await fetch("/api/feedback", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ feedback, consent: isChecked }),
+ });
+
+ if (!response.ok) throw new Error("Failed to submit feedback");
+
+ toast.success("Feedback submitted! Thank you for your input.");
+ setFeedback("");
+ setIsChecked(false);
+ } catch {
+ toast.error("An error occurred. Please try again.");
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
return (
-
-
Hello, Rohit!
-
-
-
Welcome to Feedback Page.
-
- Your feedback is important to us! Share your thoughts, suggestions,
- or any ideas for improvement. Help us make the website better by
- letting us know how we can enhance your experience. We value your
- input!
+
+
+ Hello, {user?.firstName || "Developer"}!
+
+
+
+
+
+
+
Share Your Feedback
+
+
+ Your feedback helps us improve! Share your thoughts, suggestions, or ideas
+ to make DevGuild better for everyone.
-
+
-
Submit Your Feedback
-
-
- {/* Input Field */}
-
-
-
-
- **Note: Please don't include any sensitive information
-
-
-
-
- We may email you for more information or updates
-
-
+
+ Submit Your Feedback
+
+
+
+ setFeedback(e.target.value)}
+ className="w-full h-48 p-4 resize-none text-sm bg-transparent text-theme-primary placeholder:text-theme-tertiary focus:outline-none"
+ rows={6}
+ required
+ />
+
+
+
+ **Note: Please don't include any sensitive information
+
+
+
+ setIsChecked(!isChecked)}
+ className="w-4 h-4 accent-brand-500"
+ required
+ />
+
+ We may email you for more information or updates
+
+
- {/* Buttons */}
-
- Submit
+
+ {isSubmitting ? "Submitting..." : "Submit Feedback"}
-
-
+
+
);
};
diff --git a/app/(root)/hackathons/page.tsx b/app/(root)/hackathons/page.tsx
new file mode 100644
index 0000000..3641ecd
--- /dev/null
+++ b/app/(root)/hackathons/page.tsx
@@ -0,0 +1,148 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { motion } from "framer-motion";
+import { Trophy, Calendar, MapPin, Plus, ExternalLink, Users } from "lucide-react";
+import Link from "next/link";
+import axios from "axios";
+
+interface Hackathon {
+ _id: string;
+ title: string;
+ description: string;
+ url: string;
+ startDate: string;
+ endDate: string;
+ location: string;
+ organizer: string;
+ tags: string[];
+}
+
+export default function HackathonsPage() {
+ const [hackathons, setHackathons] = useState
([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const fetchHackathons = async () => {
+ try {
+ const response = await axios.get("/api/hackathons");
+ setHackathons(response.data);
+ } catch (error) {
+ console.error("Error fetching hackathons:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchHackathons();
+ }, []);
+
+ return (
+
+
+
+
+
Hackathons
+
+
+
+ Submit Event
+
+
+
+
+ Find a Hackathon Team
+
+ Browse upcoming hackathons and post projects specifically for these events to find the perfect teammates.
+ All events are community-submitted.
+
+
+
+ {loading ? (
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+ ) : hackathons.length === 0 ? (
+
+
No upcoming hackathons found.
+
+ Be the first to submit one
+
+
+ ) : (
+
+ {hackathons.map((h, i) => (
+
+
+
+
+
{h.description}
+
+
+
+
+ {new Date(h.startDate).toLocaleDateString()} - {new Date(h.endDate).toLocaleDateString()}
+
+
+
+ {h.location}
+
+
+
+ {h.organizer}
+
+
+
+ {h.tags && h.tags.length > 0 && (
+
+ {h.tags.map((tag) => (
+
+ {tag}
+
+ ))}
+
+ )}
+
+
+
+
+ Find a Team for This Event
+
+
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/app/(root)/hackathons/submit/page.tsx b/app/(root)/hackathons/submit/page.tsx
new file mode 100644
index 0000000..60b20eb
--- /dev/null
+++ b/app/(root)/hackathons/submit/page.tsx
@@ -0,0 +1,137 @@
+"use client";
+
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+import { motion } from "framer-motion";
+import { ArrowLeft, Send } from "lucide-react";
+import Link from "next/link";
+import axios from "axios";
+import { toast } from "sonner";
+
+export default function SubmitHackathonPage() {
+ const router = useRouter();
+ const [formData, setFormData] = useState({
+ title: "",
+ description: "",
+ url: "",
+ startDate: "",
+ endDate: "",
+ location: "",
+ organizer: "",
+ tags: "",
+ });
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleChange = (e: React.ChangeEvent) => {
+ setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setIsSubmitting(true);
+
+ try {
+ const payload = {
+ ...formData,
+ tags: formData.tags.split(",").map((t) => t.trim()).filter(Boolean),
+ };
+
+ await axios.post("/api/hackathons", payload);
+ toast.success("Hackathon submitted successfully!");
+ router.push("/hackathons");
+ } catch (error) {
+ console.error(error);
+ toast.error("Failed to submit hackathon.");
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+
+
+
Submit a Hackathon
+
+
+
+
+
+
+
+ Hackathon Title
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+ Registration URL
+
+
+
+
+ Tags (comma separated)
+
+
+
+
+
+
+ {isSubmitting ? "Submitting..." : "Submit Hackathon"}
+
+
+ Cancel
+
+
+
+
+
+ );
+}
diff --git a/app/(root)/help/page.tsx b/app/(root)/help/page.tsx
index ca12836..4b08d67 100644
--- a/app/(root)/help/page.tsx
+++ b/app/(root)/help/page.tsx
@@ -1,130 +1,112 @@
"use client";
-import { Search } from "lucide-react";
-import { motion } from "framer-motion";
+import { Search, HelpCircle, ChevronDown, ChevronUp } from "lucide-react";
+import { motion, AnimatePresence } from "framer-motion";
import React, { useState } from "react";
-import Image from "next/image";
+import useAuthStore from "@/app/store/useAuthStore";
const questions = [
- {
- id: 1,
- question: "How to contribute to open source projects?",
- answer:
- "You can start by finding beginner-friendly issues on GitHub repositories and making small contributions.",
- color: "bg-blue-100",
- },
- {
- id: 2,
- question: "What is the best way to learn React?",
- answer:
- "The best way is to go through the official documentation, build projects, and follow interactive tutorials.",
- color: "bg-yellow-100",
- },
- {
- id: 3,
- question: "How can I improve my DSA skills?",
- answer:
- "Practice problems on platforms like LeetCode, CodeChef, and GeeksforGeeks regularly.",
- color: "bg-green-100",
- },
- {
- id: 4,
- question: "What are the best resources for learning Next.js?",
- answer:
- "You can explore the official Next.js documentation, freeCodeCamp tutorials, and YouTube playlists for structured learning.",
- color: "bg-purple-100",
- },
- {
- id: 5,
- question: "How do I stay consistent with coding?",
- answer:
- "Setting a schedule, joining coding communities, and working on projects can help build consistency.",
- color: "bg-red-100",
- },
- {
- id: 6,
- question: "What are some good projects for beginners?",
- answer:
- "Todo apps, weather apps, and portfolio websites are great starting projects for beginners.",
- color: "bg-orange-100",
- },
+ { id: 1, question: "How to contribute to open source projects?", answer: "You can start by finding beginner-friendly issues on GitHub repositories and making small contributions." },
+ { id: 2, question: "What is the best way to learn React?", answer: "The best way is to go through the official documentation, build projects, and follow interactive tutorials." },
+ { id: 3, question: "How can I improve my DSA skills?", answer: "Practice problems on platforms like LeetCode, CodeChef, and GeeksforGeeks regularly." },
+ { id: 4, question: "What are the best resources for learning Next.js?", answer: "You can explore the official Next.js documentation, freeCodeCamp tutorials, and YouTube playlists." },
+ { id: 5, question: "How do I stay consistent with coding?", answer: "Setting a schedule, joining coding communities, and working on projects can help build consistency." },
+ { id: 6, question: "What are some good projects for beginners?", answer: "Todo apps, weather apps, and portfolio websites are great starting projects for beginners." },
];
const Page = () => {
+ const { user } = useAuthStore();
const [openQuestion, setOpenQuestion] = useState(null);
+ const [searchQuery, setSearchQuery] = useState("");
return (
-
-
Hello, Rohit!
-
-
-
Welcome to Help Page.
-
- Have any questions or need assistance? Our Help page is the perfect
- place to reach out. Submit your doubts or concerns, and our admin
- team will be happy to assist you in resolving any issues or
- answering your queries about the website.
+
+
+ Hello, {user?.firstName || "Developer"}!
+
+
+
+
+
Need Help?
+
+ Submit your questions or issues and our team will help you resolve them.
+ Check the FAQs below for common questions.
-
+
-
Help
-
-
-
-
-
+ {/* Search */}
+ Help
+
+
+
+ setSearchQuery(e.target.value)}
+ className="w-full pl-11 pr-4 py-3 bg-theme-card border border-theme-secondary rounded-xl text-sm text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all"
+ />
+
+
Search
- FAQs
-
-
- {questions.map((q) => (
+ {/* FAQs */}
+
FAQs
+
+ {questions
+ .filter((q) =>
+ q.question.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ q.answer.toLowerCase().includes(searchQuery.toLowerCase())
+ )
+ .map((q, i) => (
-
- setOpenQuestion(openQuestion === q.id ? null : q.id)
- }
- whileHover={{ scale: 1.02 }}
+ setOpenQuestion(openQuestion === q.id ? null : q.id)}
>
-
- {q.question}
- {openQuestion === q.id ? "▲" : "▼"}
-
-
- {openQuestion === q.id && (
-
- {q.answer}
-
- )}
+ {q.question}
+ {openQuestion === q.id ? (
+
+ ) : (
+
+ )}
+
+
+ {openQuestion === q.id && (
+
+ {q.answer}
+
+ )}
+
))}
-
);
};
diff --git a/app/(root)/home/page.tsx b/app/(root)/home/page.tsx
index 94b5fa5..bc98627 100644
--- a/app/(root)/home/page.tsx
+++ b/app/(root)/home/page.tsx
@@ -1,49 +1,128 @@
-import { Search } from "lucide-react";
+"use client";
+
+import { Search, ArrowRight, Sparkles } from "lucide-react";
import Image from "next/image";
-import React from "react";
-import Hero from "../components/Hero";
+import React, { useState } from "react";
+import { useRouter } from "next/navigation";
+import { motion } from "framer-motion";
+
+// Stats are currently hidden pending real backend endpoints
+
+const Page = () => {
+ const [domain, setDomain] = useState("");
+ const [institute, setInstitute] = useState("");
+ const router = useRouter();
+
+ const handleSearch = () => {
+ const params = new URLSearchParams();
+ if (domain) params.set("domain", domain);
+ if (institute) params.set("institute", institute);
+ router.push(`/project?${params.toString()}`);
+ };
-const page = () => {
return (
-
-
-
-
- A Platform where Developers Connect,Communicate and Collaborate..
-
-
Domain
-
-
-
-
-
Institute
-
-
-
-
-
- Search
-
+
+ {/* Background decorations */}
+
-
-
+
+
+ {/* Hero Section */}
+
+
+
+
+ Developer Collaboration Platform
+
+
+
+
+ Find Your{" "}
+ Dream Team
+
+ Build Something{" "}
+ Amazing
+
+
+
+ A platform where developers connect, communicate, and collaborate.
+ Browse projects, find teammates, and turn your ideas into reality.
+
+
+ {/* Search Section */}
+
+
+
+ Domain
+
+
+
+ setDomain(e.target.value)}
+ placeholder="e.g. Web Development, Machine Learning..."
+ className="w-full pl-12 pr-4 py-3.5 bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all duration-200"
+ />
+
+
+
+
+
+ Institute
+
+
+
+ setInstitute(e.target.value)}
+ placeholder="e.g. MIT, Stanford, IIT..."
+ className="w-full pl-12 pr-4 py-3.5 bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all duration-200"
+ />
+
+
+
+
+ Search Projects
+
+
+
+
+
+
+ {/* Hero visual */}
+
+
+
);
};
-export default page;
+export default Page;
diff --git a/app/(root)/layout.tsx b/app/(root)/layout.tsx
index 812aaee..e189083 100644
--- a/app/(root)/layout.tsx
+++ b/app/(root)/layout.tsx
@@ -7,8 +7,48 @@ import { useState, useRef, useEffect } from "react";
import SignInButton from "./components/SignInButton";
import { fetchUserData } from "../utils/userActions";
import useAuthStore from "../store/useAuthStore";
+import useThemeStore from "../store/useThemeStore";
import SignOutButton from "./components/SignOutButton";
import { Toaster } from "sonner";
+import axios from "axios";
+import {
+ Home,
+ User,
+ LayoutDashboard,
+ Briefcase,
+ HelpCircle,
+ MessageSquare,
+ FolderOpen,
+ Trophy,
+ MessageCircleQuestion,
+ Settings,
+ Sun,
+ Moon,
+ Plus,
+ Menu,
+ X,
+ Bell,
+} from "lucide-react";
+
+import OnboardingModal from "./components/OnboardingModal";
+import { User as UserType } from "@/app/types/user";
+
+const sidebarLinks = [
+ { href: "/home", label: "Home", icon: Home },
+ { href: "/profile", label: "Guild Card", icon: User },
+ { href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
+ { href: "/application", label: "Applications", icon: Briefcase },
+ { href: "/help", label: "Help", icon: HelpCircle },
+ { href: "/feedback", label: "Feedback", icon: MessageSquare },
+];
+
+const navLinks = [
+ { href: "/project", label: "Projects", icon: FolderOpen },
+ { href: "/hackathons", label: "Hackathons", icon: Trophy },
+ { href: "/leaderboard", label: "Leaderboard", icon: Trophy },
+ { href: "/qna", label: "QnA", icon: MessageCircleQuestion },
+ { href: "/settings", label: "Settings", icon: Settings },
+];
export default function RootLayout({
children,
@@ -16,9 +56,11 @@ export default function RootLayout({
children: React.ReactNode;
}) {
const [isOpen, setIsOpen] = useState(false);
+ const [showOnboarding, setShowOnboarding] = useState(false);
const sidebarRef = useRef
(null);
const pathname = usePathname();
const router = useRouter();
+ const { isDark, toggleTheme } = useThemeStore();
const toggleSidebar = () => {
setIsOpen(!isOpen);
@@ -49,335 +91,277 @@ export default function RootLayout({
useEffect(() => {
async function getUser() {
- const user = await fetchUserData();
- if (user) {
- setUser({
- id: user._id,
- name: `${user.firstName} ${user.lastName}`,
- email: user.email,
- image: user.image,
- codingPlatforms: user.codingPlatforms,
- firstName: user.firstName,
- lastName: user.lastName,
- gender: user.gender,
- skills: user.skills,
- profilePic: user.profilePic,
- collegeDetails: user.collegeDetails || { name: user.instituteName },
- github: user.github,
- linkedin: user.linkedin,
- location: user.location,
- projectsCompleted: user.projectsCompleted,
- rank: user.rank,
- projectIds: user.projectIds,
- });
+ try {
+ const user = await fetchUserData();
+ if (user) {
+ setUser({
+ id: user._id,
+ name: `${user.firstName} ${user.lastName}`,
+ email: user.email,
+ image: user.image,
+ codingPlatforms: user.codingPlatforms,
+ firstName: user.firstName,
+ lastName: user.lastName,
+ gender: user.gender,
+ skills: user.skills,
+ profilePic: user.profilePic,
+ collegeDetails: user.collegeDetails || { name: user.instituteName },
+ github: user.github,
+ linkedin: user.linkedin,
+ location: user.location,
+ projectsCompleted: user.projectsCompleted,
+ rank: user.rank,
+ projectIds: user.projectIds,
+ });
+
+ // Check if onboarding is needed
+ if (!user.firstName || !user.lastName || !user.collegeDetails?.name && !user.instituteName) {
+ setShowOnboarding(true);
+ } else {
+ setShowOnboarding(false);
+ }
+ }
+ } catch {
+ // User not logged in
}
}
-
getUser();
}, [setUser]);
+ const isActive = (href: string) => pathname === href || pathname.startsWith(href + "/");
+
+ const handleOnboardingComplete = (completedUser: UserType) => {
+ setUser(completedUser);
+ setShowOnboarding(false);
+ };
+
+ const [unreadCount, setUnreadCount] = useState(0);
+
+ useEffect(() => {
+ async function getUnreadCount() {
+ if (user) {
+ try {
+ const res = await axios.get("/api/notifications");
+ const unread = res.data.filter((n: { read: boolean }) => !n.read).length;
+ setUnreadCount(unread);
+ } catch (error) {
+ console.error("Failed to fetch unread notifications", error);
+ }
+ }
+ }
+ getUnreadCount();
+ }, [user]);
+
return (
<>
-
-
-
+
+ {showOnboarding && user && (
+
+ )}
+
+ {/* ===== SIDEBAR ===== */}
+ {isOpen && (
+
setIsOpen(false)}
/>
+ )}
-
{
- setIsOpen(false);
- }}
- >
-
- Home
-
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Guild Card
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Dashboard
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Application
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Help
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Feedback
-
-
-
- {/* For Mobile Devices Only */}
-
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Projects
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- Leaderboard
-
-
-
{
- setIsOpen(false);
- }}
- >
-
- QnA
-
-
-
{
- setIsOpen(false);
- }}
+ >
+ {/* Close button (mobile) */}
+
setIsOpen(false)}
+ className="absolute top-4 right-4 p-1 rounded-lg hover:bg-theme-tertiary text-theme-secondary transition-colors lg:hidden"
>
-
+
+
+ {/* Logo */}
+
+
+
+
+ {/* Sidebar Nav Links */}
+
+ {sidebarLinks.map((link) => {
+ const Icon = link.icon;
+ const active = isActive(link.href);
+ return (
+ setIsOpen(false)}
+ >
+
+ {link.label}
+ {active && (
+
+ )}
+
+ );
+ })}
+
+
+ {/* Divider */}
+
+
+ {/* Mobile-only: extra nav links */}
+
+ {navLinks.map((link) => {
+ const Icon = link.icon;
+ const active = isActive(link.href);
+ return (
+ setIsOpen(false)}
+ >
+
+ {link.label}
+
+ );
+ })}
+
+
+ {/* Theme toggle at bottom */}
+
+
- Settings
-
-
+ {isDark ?
:
}
+
{isDark ? "Light Mode" : "Dark Mode"}
+
+
+ {/* ===== MAIN CONTENT ===== */}
-
-
+ {/* Header */}
+
+
+ {/* Hamburger */}
- ☰
+
+
+ {/* Logo (desktop) */}
-
-
- Projects
-
-
-
-
- Leaderboard
-
-
-
-
- QnA
-
-
-
-
- Settings
-
-
+
+ {/* Desktop nav links */}
+
+ {navLinks.map((link) => {
+ const active = isActive(link.href);
+ return (
+
+ {link.label}
+
+ );
+ })}
+
-
+
+ {/* Right side */}
+
+ {/* Theme toggle (desktop) */}
+
+ {isDark ? : }
+
+
+ {/* Create Project */}
{
router.push(`/project/create/${user?.id}`);
}}
- className="bg-[#416dff] text-white px-4 py-2 rounded-md"
+ className="flex items-center gap-2 bg-gradient-to-r from-brand-500 to-purple-600 hover:from-brand-600 hover:to-purple-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 shadow-md hover:shadow-lg active:scale-[0.98]"
>
- Create A Project
+
+ Create Project
+
+ {/* Notifications */}
+ {user && (
+
+
+ {unreadCount > 0 && (
+
+ )}
+
+ )}
+
+ {/* User */}
{user ? (
-
-
+
+
@@ -386,9 +370,9 @@ export default function RootLayout({
)}
-
+
-
{children}
+
{children}
>
);
diff --git a/app/(root)/leaderboard/layout.tsx b/app/(root)/leaderboard/layout.tsx
index e87cd23..b564801 100644
--- a/app/(root)/leaderboard/layout.tsx
+++ b/app/(root)/leaderboard/layout.tsx
@@ -2,103 +2,59 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import React from "react";
+import { Trophy } from "lucide-react";
+import { motion } from "framer-motion";
+
+const ranks = [
+ { href: "/leaderboard/s", label: "S", color: "text-amber-500", activeBg: "bg-amber-500/10 dark:bg-amber-500/15", activeBorder: "border-amber-500" },
+ { href: "/leaderboard/a+", label: "A+", color: "text-red-500", activeBg: "bg-red-500/10 dark:bg-red-500/15", activeBorder: "border-red-500" },
+ { href: "/leaderboard/a", label: "A", color: "text-red-400", activeBg: "bg-red-400/10 dark:bg-red-400/15", activeBorder: "border-red-400" },
+ { href: "/leaderboard/b+", label: "B+", color: "text-blue-500", activeBg: "bg-blue-500/10 dark:bg-blue-500/15", activeBorder: "border-blue-500" },
+ { href: "/leaderboard/b", label: "B", color: "text-blue-400", activeBg: "bg-blue-400/10 dark:bg-blue-400/15", activeBorder: "border-blue-400" },
+ { href: "/leaderboard/c", label: "C", color: "text-purple-500", activeBg: "bg-purple-500/10 dark:bg-purple-500/15", activeBorder: "border-purple-500" },
+ { href: "/leaderboard/d", label: "D", color: "text-green-500", activeBg: "bg-green-500/10 dark:bg-green-500/15", activeBorder: "border-green-500" },
+ { href: "/leaderboard/e", label: "E", color: "text-gray-500", activeBg: "bg-gray-500/10 dark:bg-gray-500/15", activeBorder: "border-gray-500" },
+];
const LeaderboardLayout = ({ children }: { children: React.ReactNode }) => {
const pathname = usePathname();
+
return (
-
-
Leaderboard
+
+
+
+ Leaderboard
+
+
+ {/* Rank tabs */}
+
+ {ranks.map((rank) => {
+ const active = pathname === rank.href;
+ return (
+
+
+ {rank.label}
+
+
+ );
+ })}
+
-
-
-
- S Rank
-
-
-
-
- A+ Rank
-
-
-
-
- A Rank
-
-
-
-
- B+ Rank
-
-
-
-
- B Rank
-
-
-
-
- C Rank
-
-
-
-
- D Rank
-
-
-
-
- E Rank
-
-
-
{children}
);
diff --git a/app/(root)/leaderboard/page.tsx b/app/(root)/leaderboard/page.tsx
index 11f3dde..741a4db 100644
--- a/app/(root)/leaderboard/page.tsx
+++ b/app/(root)/leaderboard/page.tsx
@@ -1,83 +1,121 @@
-import React from "react";
+"use client";
+import React, { useEffect, useState } from "react";
+import { motion } from "framer-motion";
+import { Medal, Trophy } from "lucide-react";
+import axios from "axios";
-const page = () => {
- const data = [
- {
- rank: "#1",
- name: "Pranav Ramane",
- institute: "Pimpri Chinchwad College of Engineering & Research",
- points: 3000,
- },
- {
- rank: "#2",
- name: "Adarsh Thakare",
- institute: "Pimpri Chinchwad College of Engineering & Research",
- points: 3000,
- },
- {
- rank: "#3",
- name: "Prajyot Tayde",
- institute: "Pimpri Chinchwad College of Engineering & Research",
- points: 3000,
- },
- {
- rank: "#4",
- name: "Rohit Tare",
- institute: "Pimpri Chinchwad College of Engineering & Research",
- points: 3000,
- },
- {
- rank: "#5",
- name: "Mayur Tummewar",
- institute: "Pimpri Chinchwad College of Engineering & Research",
- points: 3000,
- },
+interface LeaderboardUser {
+ _id: string;
+ rank: string;
+ name: string;
+ institute: string;
+ points: number;
+ profilePic?: string;
+}
- // Add more data here
- ];
- return (
- <>
-
- {/* Table Head */}
-
-
-
- Rank
-
-
- User Name
-
-
- User Institute
-
-
- Points
-
-
-
+const medalColors: Record = {
+ "#1": "text-amber-400",
+ "#2": "text-gray-400",
+ "#3": "text-amber-600",
+};
+
+const Page = () => {
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(true);
- {/* Table Body */}
-
- {data.map((user, index) => (
-
-
- {user.rank}
-
-
- {user.name}
-
-
- {user.institute}
-
-
- {user.points}
-
+ useEffect(() => {
+ const fetchLeaderboard = async () => {
+ try {
+ const response = await axios.get("/api/leaderboard");
+ setData(response.data);
+ } catch (error) {
+ console.error("Error fetching leaderboard", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchLeaderboard();
+ }, []);
+
+ if (loading) {
+ return (
+
+
+
+
+ {[1, 2, 3, 4, 5].map((i) => (
+
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ Leaderboard
+
+
+
+
+
+
+ Rank
+ User Name
+ Institute
+ Points
- ))}
-
-
- >
+
+
+ {data.length === 0 ? (
+
+
+ No data available for this rank yet.
+
+
+ ) : (
+ data.map((user, index) => (
+
+
+
+ {medalColors[user.rank] && (
+
+ )}
+ {user.rank}
+
+
+ {user.name}
+ {user.institute}
+
+ {user.points.toLocaleString()}
+
+
+ ))
+ )}
+
+
+
+
);
};
-export default page;
+export default Page;
diff --git a/app/(root)/notification/[id]/page.tsx b/app/(root)/notification/[id]/page.tsx
index 4e4dac4..594ad63 100644
--- a/app/(root)/notification/[id]/page.tsx
+++ b/app/(root)/notification/[id]/page.tsx
@@ -1,52 +1,107 @@
"use client";
import { useParams } from "next/navigation";
-import React from "react";
-import { FaUser } from "react-icons/fa";
-const dummyEmails = [
- {
- id: "1",
- sender: "Radhika Gupta",
- subject: "New Post for You",
- body: "To every woman who sometimes wonders, can I do it, here is a thought...",
- date: "March 17, 2025",
- },
- {
- id: "2",
- sender: "Nikhil Kamath",
- subject: "Indian Market Insights",
- body: "Digressing from the post, but it's about time we need an Indian answer to...",
- date: "March 14, 2025",
- },
- {
- id: "3",
- sender: "Nikhil Kamath",
- subject: "Indian Market Trends",
- body: "Digressing from the post, but it's about time we need an Indian answer to...",
- date: "March 12, 2025",
- },
-];
+import React, { useEffect, useState } from "react";
+import { ArrowLeft, Clock } from "lucide-react";
+import Link from "next/link";
+import axios from "axios";
+
+interface Notification {
+ _id: string;
+ type: string;
+ title: string;
+ message: string;
+ createdAt: string;
+}
const EmailDetailsPage = () => {
- const params = useParams();
- const email = dummyEmails.find((item) => item.id === params.id);
+ const params = useParams();
+ const [notification, setNotification] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(false);
- if (!email) {
- return Email not found
;
- }
+ useEffect(() => {
+ const fetchNotification = async () => {
+ try {
+ const response = await axios.get(`/api/notifications/${params.id}`);
+ setNotification(response.data);
+ } catch (err) {
+ console.error(err);
+ setError(true);
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchNotification();
+ }, [params.id]);
+ if (loading) {
return (
-
-
-
{email.subject}
-
{email.sender}
-
{email.body}
-
Received on: {email.date}
+
+ );
+ }
+
+ if (error || !notification) {
+ return (
+
+
Notification not found
+
+ Back to notifications
+
+
);
+ }
+
+ return (
+
+
+
+ Back to notifications
+
+
+
+
{notification.title}
+
+
+
+
+ {notification.type === "system" ? "S" : "U"}
+
+
+
+
+ {notification.type === "system" ? "System" : "User"}
+
+
+
+ {new Date(notification.createdAt).toLocaleString()}
+
+
+
+
+
{notification.message}
+
+
+ );
};
export default EmailDetailsPage;
-
diff --git a/app/(root)/notification/page.tsx b/app/(root)/notification/page.tsx
index 1f949de..f5c2861 100644
--- a/app/(root)/notification/page.tsx
+++ b/app/(root)/notification/page.tsx
@@ -1,11 +1,157 @@
-import React from 'react'
+"use client";
+import React, { useEffect, useState, useCallback } from "react";
+import { Bell, Inbox } from "lucide-react";
+import { motion } from "framer-motion";
+import { useRouter } from "next/navigation";
+import axios from "axios";
+import { toast } from "sonner";
+import { useSession } from "next-auth/react";
+
+interface Notification {
+ _id: string;
+ type: string;
+ title: string;
+ message: string;
+ link?: string;
+ read: boolean;
+ createdAt: string;
+}
+
+const Page = () => {
+ const router = useRouter();
+ const { data: session } = useSession();
+ const [notifications, setNotifications] = useState
([]);
+ const [loading, setLoading] = useState(true);
+
+ const fetchNotifications = useCallback(async () => {
+ if (!session) return;
+ try {
+ const response = await axios.get("/api/notifications");
+ setNotifications(response.data);
+ } catch (error) {
+ console.error("Error fetching notifications:", error);
+ } finally {
+ setLoading(false);
+ }
+ }, [session]);
+
+ useEffect(() => {
+ fetchNotifications();
+ }, [fetchNotifications]);
+
+ const handleNotificationClick = async (n: Notification) => {
+ if (!n.read) {
+ try {
+ await axios.patch("/api/notifications", { notificationIds: [n._id] });
+ setNotifications((prev) =>
+ prev.map((notif) =>
+ notif._id === n._id ? { ...notif, read: true } : notif
+ )
+ );
+ } catch {
+ console.error("Error marking read:");
+ }
+ }
+ if (n.link) {
+ router.push(n.link);
+ } else {
+ router.push(`/notification/${n._id}`);
+ }
+ };
+
+ const markAllRead = async () => {
+ try {
+ await axios.patch("/api/notifications", {});
+ setNotifications((prev) =>
+ prev.map((notif) => ({ ...notif, read: true }))
+ );
+ toast.success("All notifications marked as read");
+ } catch {
+ toast.error("Failed to mark all as read");
+ }
+ };
+
+ const unreadCount = notifications.filter((n) => !n.read).length;
+
+ if (loading) {
+ return (
+
+
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+
+ );
+ }
-const page = () => {
return (
-
-
+
+
+
+
+ Notifications
+
+ {unreadCount} new
+
+
+
+ {unreadCount > 0 && (
+
+ Mark all read
+
+ )}
+
+
+ {notifications.length === 0 ? (
+
+
+
+
+
No notifications
+
You're all caught up!
+
+ ) : (
+
+ {notifications.map((n, i) => (
+
handleNotificationClick(n)}
+ className={`glass-card p-4 cursor-pointer hover:border-brand-500/30 transition-all duration-200 ${
+ !n.read ? "border-l-4 border-l-brand-500" : "opacity-70"
+ }`}
+ >
+
+
+
+
+ {n.type === "system" ? "S" : "U"}
+
+
+
{n.title}
+
+
+ {new Date(n.createdAt).toLocaleDateString()}
+
+
+ {n.message}
+
+ ))}
+
+ )}
- )
-}
+ );
+};
-export default page
+export default Page;
diff --git a/app/(root)/page.tsx b/app/(root)/page.tsx
new file mode 100644
index 0000000..bc98627
--- /dev/null
+++ b/app/(root)/page.tsx
@@ -0,0 +1,128 @@
+"use client";
+
+import { Search, ArrowRight, Sparkles } from "lucide-react";
+import Image from "next/image";
+import React, { useState } from "react";
+import { useRouter } from "next/navigation";
+import { motion } from "framer-motion";
+
+// Stats are currently hidden pending real backend endpoints
+
+const Page = () => {
+ const [domain, setDomain] = useState("");
+ const [institute, setInstitute] = useState("");
+ const router = useRouter();
+
+ const handleSearch = () => {
+ const params = new URLSearchParams();
+ if (domain) params.set("domain", domain);
+ if (institute) params.set("institute", institute);
+ router.push(`/project?${params.toString()}`);
+ };
+
+ return (
+
+ {/* Background decorations */}
+
+
+
+ {/* Hero Section */}
+
+
+
+
+ Developer Collaboration Platform
+
+
+
+
+ Find Your{" "}
+ Dream Team
+
+ Build Something{" "}
+ Amazing
+
+
+
+ A platform where developers connect, communicate, and collaborate.
+ Browse projects, find teammates, and turn your ideas into reality.
+
+
+ {/* Search Section */}
+
+
+
+ Domain
+
+
+
+ setDomain(e.target.value)}
+ placeholder="e.g. Web Development, Machine Learning..."
+ className="w-full pl-12 pr-4 py-3.5 bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all duration-200"
+ />
+
+
+
+
+
+ Institute
+
+
+
+ setInstitute(e.target.value)}
+ placeholder="e.g. MIT, Stanford, IIT..."
+ className="w-full pl-12 pr-4 py-3.5 bg-theme-card border border-theme-secondary rounded-xl text-theme-primary placeholder:text-theme-tertiary focus:outline-none focus:ring-2 focus:ring-brand-500/40 focus:border-brand-500 transition-all duration-200"
+ />
+
+
+
+
+ Search Projects
+
+
+
+
+
+
+ {/* Hero visual */}
+
+
+
+
+
+ );
+};
+
+export default Page;
diff --git a/app/(root)/profile/page.tsx b/app/(root)/profile/page.tsx
index 88a65da..edd6b9e 100644
--- a/app/(root)/profile/page.tsx
+++ b/app/(root)/profile/page.tsx
@@ -5,6 +5,8 @@ import AchievementsCard from "../components/AchievementsCard";
import EditProfile from "../components/EditProfile";
import useAuthStore from "@/app/store/useAuthStore";
import { User } from "@/app/types/user";
+import { motion } from "framer-motion";
+import { toast } from "sonner";
const Page = () => {
const { user } = useAuthStore();
@@ -21,7 +23,7 @@ const Page = () => {
collegeDetails: user?.collegeDetails || { name: "" },
github: user?.github || "",
linkedin: user?.linkedin || "",
- location: user?.location || "Maharashtra, India",
+ location: user?.location || "",
projectsCompleted: user?.projectsCompleted || 0,
rank: user?.rank,
projectIds: user?.projectIds || [],
@@ -32,6 +34,7 @@ const Page = () => {
if (user) {
setProfileData((prevData) => ({
...prevData,
+ name: user.name || `${user.firstName} ${user.lastName}`,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
@@ -56,47 +59,59 @@ const Page = () => {
};
return (
- <>
-