Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 20 additions & 15 deletions client/src/pages/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ function Home() {

const handleSubmit = async (e) => {
e.preventDefault();
console.log(`${formData.role} sign in attempted`, formData);

const { email: ouEmail, password, role } = formData;


if (!ouEmail || !password || !role) {
return Swal.fire({
icon: "warning",
Expand All @@ -61,20 +61,24 @@ function Home() {
const data = await response.json();

if (response.ok) {
Swal.fire({
icon: "success",
title: "Login Successful 🌟",
text: `Welcome back, ${role}!`,
});

// Redirect user based on role
if (role === "coordinator") {
navigate("/coordinator-dashboard");
} else if (role === "student") {
navigate("/student-dashboard");
} else if (role === "supervisor") {
navigate("/supervisor-dashboard");
}
const user = data.user;

// Store only required fields
const limitedUserInfo = {
fullName: user.fullName,
id: user._id,
email:user.ouEmail
};

localStorage.setItem("ipmsUser", JSON.stringify(limitedUserInfo));

// Swal.fire({
// icon: "success",
// title: "Login Successful",
// text: `Welcome back, `,
// });

navigate("/student-dashboard");
} else {
Swal.fire({
icon: "error",
Expand Down Expand Up @@ -126,6 +130,7 @@ function Home() {
role: r,
})
}

>
<Icon />
<p className="role-label">
Expand Down
16 changes: 16 additions & 0 deletions client/src/pages/ProtectedRouteStudent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// src/components/ProtectedRouteStudent.js
import React from "react";
import { Navigate } from "react-router-dom";

const ProtectedRouteStudent = ({ children }) => {
const user = JSON.parse(localStorage.getItem("ipmsUser"));

// Check if user info is missing
if (!user || !user.id || !user.fullName) {
return <Navigate to="/" replace />;
}

return children;
};

export default ProtectedRouteStudent;
96 changes: 96 additions & 0 deletions client/src/pages/StudentDashboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import "../styles/StudentDashboard.css"; // Make sure you create this CSS

const StudentDashboard = () => {
const navigate = useNavigate();

const user = JSON.parse(localStorage.getItem("ipmsUser"));
const backendUrl = process.env.REACT_APP_API_URL;
const ouEmail = user?.email;
const [approvalStatus, setApprovalStatus] = useState(false)


useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(`${backendUrl}/api/student`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ ouEmail }),
});

const data = await res.json();
setApprovalStatus(data.approvalStatus);

} catch (err) {
console.error("Error fetching internship data", err);
}
};

if (ouEmail) {
fetchData();
}
}, [ouEmail]);





return (
<div className="student-dashboard">
<div className="dashboard-header">
<h2>Welcome, {user.fullName}</h2>
</div>

<div className="dashboard-card">
<div className="card-section">
<div className="card-content">
<h3>Request Internship (FORM A1)</h3>
<p>Track your internship journey</p>
</div>
<button
className="card-button"
onClick={() => {
if (!approvalStatus) {
navigate("/a1-form");
}
}}
style={{
backgroundColor: approvalStatus ? "#4CAF50" : "",
cursor: approvalStatus ? "default" : "pointer",
}}
>
{approvalStatus ? "Track" : "Request Internship"}
</button>
</div>

<div className="card-section">
<div className="card-content">
<h3>Weekly Report (Form A2)</h3>
{!approvalStatus && (
<p style={{ fontSize: "0.85rem", color: "#888" }}>
Finish your Form A1 first
</p>
)}
</div>
<button
className="card-button"
disabled={!approvalStatus}
style={{
backgroundColor: !approvalStatus ? "#ccc" : "",
cursor: !approvalStatus ? "not-allowed" : "pointer",
}}
>
Request
</button>
</div>
</div>
</div>

);
};

export default StudentDashboard;
15 changes: 13 additions & 2 deletions client/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ import SupervisorDashboard from "./pages/SupervisorDashboard";
import CoordinatorDashboard from "./pages/CoordinatorDashboard";
import CoordinatorRequestDetailView from "./pages/CoordinatorRequestDetailView";
import TokenRenewal from "./pages/TokenRenewal";
import StudentDashboard from "./pages/StudentDashboard";
import ProtectedRouteStudent from "./pages/ProtectedRouteStudent";

// Create and export the router configuration
const router = createBrowserRouter([
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
Expand All @@ -33,11 +35,20 @@ const router = createBrowserRouter([
{
path: "signup",
element: <SignUp />,
},
},
{
path: "weekly-report",
element: <WeeklyProgressReportForm />,
},
{
path: "/student-dashboard",
element: (
<ProtectedRouteStudent>
<StudentDashboard />
</ProtectedRouteStudent>
)
},

{
path: "a1-form",
element: <A1InternshipRequestForm />,
Expand Down
71 changes: 71 additions & 0 deletions client/src/styles/StudentDashboard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.student-dashboard {
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem 1rem;
background-color: #f7f7f7;
min-height: 90vh;
}

.dashboard-header {
width: 100%;
max-width: 900px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}

.user-role {
font-weight: bold;
font-size: 1rem;
color: #5c0a0a;
}

.dashboard-card {
background-color: white;
border-radius: 10px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
padding: 2rem;
width: 100%;
max-width: 900px;
}

.card-section {
background-color: #842020;
border-radius: 10px;
padding: 1.5rem;
margin-bottom: 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.1);
}

.card-content h3 {
margin: 0;
font-size: 1.2rem;
font-weight: 600;
}

.card-content p {
margin: 5px 0 0;
font-size: 0.9rem;
}

.card-button {
background-color: white;
color: #842020;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: 0.3s ease-in-out;
}

.card-button:hover {
background-color: #e6e6e6;
}

2 changes: 2 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const formRoutes = require("./routes/formRoutes");
const emailRoutes = require("./routes/emailRoutes");
const tokenRoutes = require("./routes/token");
const approvalRoutes = require("./routes/approvalRoutes");
const studentRoutes = require("./routes/studentRoutes")

const outcomeRoutes = require("./routes/outcomeRoutes");

Expand Down Expand Up @@ -78,6 +79,7 @@ app.use("/api/token", tokenRoutes);
app.use("/api", approvalRoutes);

app.use("/api/reports", weeklyReportRoutes);
app.use("/api/student",studentRoutes)
app.post("/api/createUser", async (req, res) => {
try {
const { userName, email, password, role } = req.body;
Expand Down
30 changes: 30 additions & 0 deletions server/routes/studentRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const express = require("express");
const router = express.Router();
const InternshipRequest = require("../models/InternshipRequest");
const User = require("../models/User");

// GET internship request by student's ouEmail
router.post("/", async (req, res) => {
const { ouEmail } = req.body;
console.log(ouEmail)
try {
const studentUser = await User.findOne({ email: ouEmail });
if (!studentUser) {
return res.status(404).json({ message: "Student not found" });
}

const internshipData = await InternshipRequest.findOne({ student: studentUser._id });

if (!internshipData) {
return res.status(404).json({ message: "No internship request found for this student" });
}
const approvalStatus = internshipData.status == "approved" ? true : false
return res.status(200).json({ message: "Success" , approvalStatus });
} catch (error) {
console.error("Error fetching internship request:", error);
return res.status(500).json({ message: "Server error" });
}
});


module.exports = router;
4 changes: 4 additions & 0 deletions server/routes/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const crypto = require("crypto");
const bcrypt = require("bcrypt");
const TokenRequest = require("../models/TokenRequest");
const emailService = require("../services/emailService");
const User = require("../models/User")

const JWT_SECRET = process.env.JWT_SECRET;
const FRONTEND_URL = process.env.FRONTEND_URL;
Expand Down Expand Up @@ -51,7 +52,10 @@ router.post("/request", async (req, res) => {
activationLinkSentAt: new Date(),
});



await request.save();


const activationLink = `${FRONTEND_URL}/activate/${plainToken}`;
const emailBody = `
Expand Down