- {/* Logo & Navigation */}
-
-
-
-
-
-
- {/* User Profile & Notifications */}
-
-
-
-
-
-
- JH
-
+interface NavbarProps {
+ userType: "mentor" | "student"; // Determines dashboard type
+ userName: string; // User's name
+}
+
+const Navbar: React.FC
= ({ userType, userName }) => {
+ const [isStudentPopupOpen, setIsStudentPopupOpen] = useState(false);
+ const [isLogoutPopupOpen, setIsLogoutPopupOpen] = useState(false);
+ const logoutPopupRef = useRef(null);
+
+ const openStudentPopup = () => setIsStudentPopupOpen(true);
+ const closeStudentPopup = () => setIsStudentPopupOpen(false);
+ const toggleLogoutPopup = () => setIsLogoutPopupOpen((prev) => !prev);
+
+ const handleLogout = () => {
+ // Clear authentication data
+ localStorage.removeItem("authToken");
+ sessionStorage.removeItem("authToken");
+ document.cookie = "authToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
+
+ // Redirect to login page
+ window.location.href = "/login";
+ };
+
+ // Close logout popup when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ logoutPopupRef.current &&
+ event.target instanceof Node &&
+ !logoutPopupRef.current.contains(event.target)
+ ) {
+ setIsLogoutPopupOpen(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, []);
+
+ return (
+
+ {/* Logo & Navigation */}
+
+
+ {/* Show navigation only for mentors */}
+ {userType === "mentor" && (
+
+ )}
+
+
+ {/* User Profile & Notifications */}
+
+
+
+
+
{userName}
+
+ {userType === "mentor" ? "Senior Mentor" : "Student"}
+
+
+
+
+ {userName.charAt(0)}
+
+ {/* Logout Popup */}
+ {isLogoutPopupOpen && (
+
+
+
+
Are you sure you want to logout?
+
+
+
+
+
+ )}
+
+
+ {/* Conditionally render the StudentDetailsPopup */}
+ {isStudentPopupOpen &&
}
-
-
- );
+ );
};
export default Navbar;
diff --git a/server/src/components/recentReviews.tsx b/server/src/components/recentReviews.tsx
new file mode 100644
index 0000000..14d3186
--- /dev/null
+++ b/server/src/components/recentReviews.tsx
@@ -0,0 +1,178 @@
+import React, { useMemo, useState, useRef, useEffect } from "react";
+import { ExternalLink, StickyNoteIcon, Search, CirclePlus, FilterIcon, ArrowDownUpIcon } from "lucide-react";
+import { Input } from "@/components/ui/input";
+
+type Review = {
+ id: number;
+ status: "approved" | "rejected" | "pending";
+ message: string;
+ date: string;
+};
+
+const mockReviews: Review[] = [
+ { id: 1, status: "approved", message: "Made minor updates and improvements.", date: "03/13/2025" },
+ { id: 2, status: "rejected", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 3, status: "approved", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 4, status: "pending", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 5, status: "rejected", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 6, status: "pending", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 7, status: "approved", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 8, status: "rejected", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 9, status: "rejected", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+ { id: 10, status: "pending", message: "Designed wireframe to enhance the system’s UI/UX.", date: "03/12/2025" },
+];
+
+interface RecentReviewsProps {
+ onAddNewActivity: () => void;
+ onUpdateActivity: () => void;
+}
+
+const RecentReviews: React.FC
= ({ onAddNewActivity, onUpdateActivity }) => {
+ const [filter, setFilter] = useState<"all" | "approved" | "rejected" | "pending">("all");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [isOpen, setIsOpen] = useState(false);
+ const menuRef = useRef(null);
+
+
+ // Close flyout when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
+ setIsOpen(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, []);
+
+ // Memoize filtered submissions to prevent re-renders
+ const filteredSubmissions = useMemo(
+ () =>
+ mockReviews.filter(
+ (sub) =>
+ (filter === "all" || sub.status === filter) &&
+ (sub.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ sub.date.toLowerCase().includes(searchQuery.toLowerCase()))
+ ),
+ [filter, searchQuery]
+ );
+
+ const filteredReviews = filter === "all" ? mockReviews : mockReviews.filter((r) => r.status === filter);
+
+ return (
+
+
+
+
+
All Recent Reviews
+
+
+
+
+ {/* Search Input */}
+
+
+ setSearchQuery(e.target.value)}
+ />
+
+
+
+
+
+ {/* Filter Buttons */}
+
+
+
+
+
+
+
+
+ {isOpen && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ {filteredReviews.length > 0 ? (
+ filteredReviews.map((review) => (
+
+
+ {review.status === "approved" ? (
+
✔
+ ) : review.status === "rejected" ? (
+
❌
+ ) :
⏳}
+
{review.message}
+
+
{review.date}
+
+
+ ))
+ ) : (
+
No records found
+ )}
+
+
+ );
+};
+
+export default RecentReviews;
diff --git a/server/src/components/studentDashboard.tsx b/server/src/components/studentDashboard.tsx
new file mode 100644
index 0000000..180800c
--- /dev/null
+++ b/server/src/components/studentDashboard.tsx
@@ -0,0 +1,50 @@
+import React, { useState } from "react";
+import Navbar from "@/components/navBar";
+import DashboardOverview from "@/components/STdashboardOverview";
+import Calendar from "@/components/STCalendar";
+import Footer from "@/components/footer";
+import RecentReviews from "./recentReviews";
+import Activity from "./activity";
+import AddNewActivity from "./addNewActivity";
+import UpdateActivity from "./updateActivity";
+
+const StudentDashboard = () => {
+ const [isAddNewActivityOpen, setIsAddNewActivityOpen] = useState(false);
+ const openAddNewActivity = () => setIsAddNewActivityOpen(true);
+ const closeAddNewActivity = () => setIsAddNewActivityOpen(false);
+
+ const [isUpdateActivityOpen, setIsUpdateActivityOpen] = useState(false);
+ const openUpdateActivity = () => setIsUpdateActivityOpen(true);
+ const closeUpdateActivity = () => setIsUpdateActivityOpen(false);
+
+ return (
+
+ {/* Navbar */}
+
+
+ {/* Main Content */}
+
+ {/* Left Section */}
+
+
+
+
+
+ {/* Right Section */}
+
+
+
+ {/* Footer */}
+
+
+ {/* Modals */}
+
+
+
+ );
+};
+
+export default StudentDashboard;
diff --git a/server/src/components/ui/tooltip.tsx b/server/src/components/ui/tooltip.tsx
new file mode 100644
index 0000000..28e1918
--- /dev/null
+++ b/server/src/components/ui/tooltip.tsx
@@ -0,0 +1,32 @@
+"use client"
+
+import * as React from "react"
+import * as TooltipPrimitive from "@radix-ui/react-tooltip"
+
+import { cn } from "@/lib/utils"
+
+const TooltipProvider = TooltipPrimitive.Provider
+
+const Tooltip = TooltipPrimitive.Root
+
+const TooltipTrigger = TooltipPrimitive.Trigger
+
+const TooltipContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+TooltipContent.displayName = TooltipPrimitive.Content.displayName
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
diff --git a/server/src/components/updateActivity.tsx b/server/src/components/updateActivity.tsx
new file mode 100644
index 0000000..fd98ce2
--- /dev/null
+++ b/server/src/components/updateActivity.tsx
@@ -0,0 +1,349 @@
+import { useState, useRef, useEffect } from "react";
+import { AlertDialog, AlertDialogContent } from "@/components/ui/alert-dialog";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Card, CardContent } from "@/components/ui/card";
+import { Toaster } from "@/components/ui/toaster";
+import ReactQuill from "react-quill";
+import "react-quill/dist/quill.snow.css";
+import { User, Bot, Activity, Play, SquareX, Send, Wand2, Pencil, ArrowBigRightDash } from "lucide-react";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
+
+interface UpdateActivityProps {
+ isOpen: boolean;
+ onClose: () => void;
+ activity?: { // Add activity prop to receive existing data
+ title: string;
+ description: string;
+ hours: string;
+ };
+}
+
+const UpdateActivity: React.FC = ({ isOpen, onClose, activity }) => {
+ const [title, setTitle] = useState("Made minor updates and improvements");
+ const [description, setDescription] = useState("Resolved a critical login bug that was preventing users from accessing their accounts. Conducted multiple tests to ensure functionality before pushing the fix to GitHub.");
+ const [hours, setHours] = useState("8");
+ const [aiInput, setAiInput] = useState("");
+ const [messages, setMessages] = useState([
+ "Hello! I can help you generate detailed activity descriptions. What would you like to describe?"
+ ]);
+ const [showAISection, setShowAISection] = useState(false);
+
+ const chatEndRef = useRef(null);
+ const flyoutRef = useRef(null);
+ const buttonRef = useRef(null);
+
+ const [editingIndex, setEditingIndex] = useState(null);
+
+ // Handle click outside flyout
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ flyoutRef.current &&
+ !flyoutRef.current.contains(event.target as Node) &&
+ buttonRef.current &&
+ !buttonRef.current.contains(event.target as Node)
+ ) {
+ setShowAISection(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ useEffect(() => {
+ if (isOpen && activity) {
+ setTitle(activity.title || "");
+ setDescription(activity.description || "");
+ setHours(activity.hours || "");
+ }
+ }, [isOpen, activity]); // Trigger when modal opens or activity changes
+
+ // Scroll to bottom of chat
+ useEffect(() => {
+ chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
+ }, [messages]);
+
+ const handleHoursChange = (e: React.ChangeEvent) => {
+ const value = e.target.value;
+ if (/^([1-9]|1[0-2])?$/.test(value)) {
+ setHours(value);
+ }
+ };
+
+ const handleAISuggestion = async () => {
+ if (!aiInput.trim()) return;
+
+ try {
+ const res = await fetch("/api/generateAiResponse", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ userInput: aiInput }),
+ });
+
+ const data = await res.json();
+ if (data.aiResponse) {
+ setMessages((prev) => [...prev, data.aiResponse]);
+ }
+ } catch (error) {
+ console.error("Error fetching AI response:", error);
+ }
+ setAiInput("");
+ };
+
+ const handleSubmit = () => {
+ console.log("Activity Submitted!");
+ onClose();
+ };
+
+ const handleDescriptionChange = (value: string) => {
+ setDescription(value || "
");
+ };
+
+ const handleSendMessage = async () => {
+ if (aiInput.trim() === "") return;
+
+ if (editingIndex !== null) {
+ const newMessages = [...messages];
+ newMessages[editingIndex] = aiInput;
+ if (newMessages[editingIndex + 1]) {
+ newMessages.splice(editingIndex + 1, 1);
+ }
+ setMessages(newMessages);
+ setEditingIndex(null);
+ await handleAISuggestion();
+ } else {
+ setMessages((prev) => [...prev, aiInput]);
+ await handleAISuggestion();
+ }
+ setAiInput("");
+ };
+
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ handleSendMessage();
+ }
+ };
+
+ const handleUseThis = (message: string) => {
+ setDescription(message);
+ };
+
+ const handleRegenerate = async (index: number) => {
+ try {
+ const userMessage = messages[index - 1];
+ const res = await fetch("/api/generateAiResponse", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ userInput: userMessage }),
+ });
+
+ const data = await res.json();
+ if (data.aiResponse) {
+ const newMessages = [...messages];
+ newMessages[index] = data.aiResponse;
+ setMessages(newMessages);
+ }
+ } catch (error) {
+ console.error("Error regenerating response:", error);
+ }
+ };
+
+ const handleEditMessage = (index: number) => {
+ setAiInput(messages[index - 1]);
+ setEditingIndex(index - 1);
+ };
+
+ return (
+
+
+ {/* Main Form Section */}
+
+
+ {/* Close Button */}
+
+
+
+ {/* Added gray background container */}
+
+
+
+
+
+ setTitle(e.target.value)}
+ placeholder="Enter activity title"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Submit, cancel Buttons */}
+
+
+
+
+
+ {/* AI Trigger Button */}
+
+
+ {/* AI Flyout Menu */}
+ {showAISection && (
+
+
+
+
+
+ 🤖 AI Description Assistant
+
+
+
+ {messages.map((msg, index) => (
+
+ {index % 2 === 0 &&
}
+
+
+ {msg}
+ {index % 2 === 0 && index !== 0 && ( // Added index !== 0 condition
+
+
+ {/* Use this */}
+
+
+
+
+ Use this message
+
+
+ {/* Regenerate */}
+
+
+
+
+ Regenerate
+
+
+ {/* Edit */}
+
+
+
+
+ Edit message
+
+
+
+ )}
+
+
+ {index % 2 !== 0 &&
}
+
+ ))}
+
+
+
+
+ setAiInput(e.target.value)}
+ onKeyDown={handleKeyPress}
+ placeholder="Describe your activity..."
+ className="pr-10" // Add right padding to prevent text underlap
+ />
+
+
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default UpdateActivity;
\ No newline at end of file