Skip to content

Commit

Permalink
Add updateStudentCompany API and route
Browse files Browse the repository at this point in the history
  • Loading branch information
Sagargupta16 committed Jan 30, 2024
1 parent f05cf28 commit 801c220
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 123 deletions.
2 changes: 2 additions & 0 deletions client/src/api/studentAPI.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const updateVerificationStatus = (id, isVerified) => axiosInstance.put(`/
export const updateUserRole = (id, role) => axiosInstance.put(`/users/role/${id}`, { role });

export const deleteStudent = (id) => axiosInstance.delete(`/users/delete/${id}`);

export const updateStudentCompany = (id, companyId) => axiosInstance.put(`/users/company/${id}`, { companyId });
10 changes: 5 additions & 5 deletions client/src/components/Student/StudentTable.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
color: var(--color-bg);
}

.role-dropdown {
.render-dropdown {
display: flex;
align-items: center;
justify-content: center;
Expand All @@ -47,15 +47,15 @@
box-shadow: 0 0 0 0.5px var(--color-white);
}

.role-dropdown:hover {
.render-dropdown:hover {
background-color: var(--color-bg);
}

.role-dropdown:focus {
.render-dropdown:focus {
background-color: var(--color-bg);
}

.role-dropdown option {
.render-dropdown option {
background-color: var(--color-bg);
color: var(--color-white);
margin: 0.5rem 0;
Expand All @@ -66,6 +66,6 @@
transition: all 0.3s ease-in-out;
}

.role-dropdown option:hover {
.render-dropdown option:hover {
background-color: var(--color-bg);
}
109 changes: 75 additions & 34 deletions client/src/components/Student/StudentTable.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useCallback, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { GrValidate } from 'react-icons/gr';
import { MdDelete } from 'react-icons/md';
import { toast } from 'react-toastify';
import { deleteStudent, getStudents, updateUserRole, updateVerificationStatus } from '../../api/studentAPI.jsx';
import { getCompanies } from '../../api/companyApi.jsx';
import { deleteStudent, getStudents, updateStudentCompany, updateUserRole, updateVerificationStatus } from '../../api/studentApi.jsx';
import getUser from '../../utils/user.js';
import AgGridTable from '../AgGridTable/AgGridTable.jsx';
import Modal from '../Modal/Modal.jsx';
Expand All @@ -14,8 +15,24 @@ const StudentTable = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedStudent, setSelectedStudent] = useState(null);
const [selectedStudentDelete, setSelectedStudentDelete] = useState(null);
const [companies, setCompanies] = useState([]);
const user = getUser();

useEffect(() => {
const fetchCompanies = async () => {
try {
const res = await getCompanies();
res.data.forEach((company) => {
company.id = company._id;
});
setCompanies(res.data);
} catch (error) {
console.error('Error fetching companies:', error);
}
};
fetchCompanies();
}, []);

const closeModal = () => {
setIsModalOpen(false);
setSelectedStudent(null);
Expand Down Expand Up @@ -66,6 +83,16 @@ const StudentTable = () => {
}
};

const handleCompanyChange = async (event, params) => {
try {
const res = await updateStudentCompany(params.id, event.target.value);
toast.success(<ToastContent res="success" messages={[res.data.message]} />);
fetchData();
} catch (error) {
toast.error(<ToastContent res="error" messages={[error.response.data.message]} />);
}
};

const verifyButtonRenderer = (params) => {
return (
<button className={`verify-button ${params.data.isVerified ? 'verified' : 'unverified'}`} onClick={() => handleVerifyButtonClick(params.data)}>
Expand All @@ -85,7 +112,7 @@ const StudentTable = () => {
const roleDropdownRenderer = (params) => {
return (
<select
className="role-dropdown"
className="render-dropdown"
value={params.data.role}
onChange={(event) => handleRoleChange(event, params.data)}
disabled={user.id === params.data._id}
Expand All @@ -97,6 +124,21 @@ const StudentTable = () => {
);
};

const companyDropdownRenderer = (params) => {
return (
<select
className="render-dropdown"
value={params.data.placedAt.companyId}
onChange={(event) => handleCompanyChange(event, params.data)}
>
<option value="np">Not Placed</option>
{companies.map((company) => (
<option value={company.id}>{company.name}</option>
))}
</select>
);
};

const modelRenderer = (isModalOpen, closeModal, onConfirm, message, buttonTitle) => {
return <Modal isOpen={isModalOpen} onClose={() => closeModal()} onConfirm={onConfirm} message={message} buttonTitle={buttonTitle} />;
};
Expand All @@ -105,17 +147,6 @@ const StudentTable = () => {
try {
const response = await getStudents();
response.data.users.forEach((student) => {
if (student.placed === true) {
student.placed = student.placedAt.company;
student.ctc = student.placedAt.ctc;
student.ctcBase = student.placedAt.ctcBreakup.base;
student.location = student.placedAt.location;
} else if (student.placed === false) {
student.placed = 'Not Placed';
student.ctc = 0;
student.ctcBase = 0;
student.location = 'N/A';
}
student.id = student._id;
});
response.data.users.sort((a, b) => a.rollNo.localeCompare(b.rollNo));
Expand All @@ -129,9 +160,9 @@ const StudentTable = () => {
field,
headerName,
width,
pinned,
sortable,
resizable,
pinned,
cellRenderer
});

Expand All @@ -140,6 +171,10 @@ const StudentTable = () => {
children
});

const roleFormatter = (params) => {
return params.value === 'student' ? 'Student' : params.value === 'placementCoordinator' ? 'PC' : 'Admin';
};

const actionsColumn = generateNestedColumn('Actions', [
generateColumn(null, 'Delete', 55, 'left', false, false, deleteButtonRenderer),
generateColumn(null, 'Verify', 55, 'left', false, false, verifyButtonRenderer)
Expand All @@ -148,44 +183,50 @@ const StudentTable = () => {
const academicColumn = [
generateNestedColumn('Grades', [
generateNestedColumn('PG', [
generateColumn('pg.cgpa', 'CGPA', 85, null, true, true, (params) => params.value.toFixed(2)),
generateColumn('pg.percentage', '%', 85, null, true, true, (params) => params.value.toFixed(2))
generateColumn('pg.cgpa', 'CGPA', 85, null, true, false, (params) => params.value.toFixed(2)),
generateColumn('pg.percentage', '%', 85, null, true, false, (params) => params.value.toFixed(2))
]),
generateNestedColumn('UG', [
generateColumn('ug.cgpa', 'CGPA', 85, null, true, true, (params) => params.value.toFixed(2)),
generateColumn('ug.percentage', '%', 85, null, true, true, (params) => params.value.toFixed(2))
generateColumn('ug.cgpa', 'CGPA', 85, null, true, false, (params) => params.value.toFixed(2)),
generateColumn('ug.percentage', '%', 85, null, true, false, (params) => params.value.toFixed(2))
]),
generateNestedColumn('HSC', [
generateColumn('hsc.cgpa', 'CGPA', 85, null, true, true, (params) => params.value.toFixed(2)),
generateColumn('hsc.percentage', '%', 85, null, true, true, (params) => params.value.toFixed(2))
generateColumn('hsc.cgpa', 'CGPA', 85, null, true, false, (params) => params.value.toFixed(2)),
generateColumn('hsc.percentage', '%', 85, null, true, false, (params) => params.value.toFixed(2))
]),
generateNestedColumn('SSC', [
generateColumn('ssc.cgpa', 'CGPA', 85, null, true, true, (params) => params.value.toFixed(2)),
generateColumn('ssc.percentage', '%', 85, null, true, true, (params) => params.value.toFixed(2))
generateColumn('ssc.cgpa', 'CGPA', 85, null, true, false, (params) => params.value.toFixed(2)),
generateColumn('ssc.percentage', '%', 85, null, true, false, (params) => params.value.toFixed(2))
])
]),
generateColumn('totalGapInAcademics', 'Gap', 75),
generateColumn('backlogs', 'Backlogs', 85)
];

const roleFormatter = (params) => {
return params.value === 'student' ? 'Student' : params.value === 'placementCoordinator' ? 'PC' : 'Admin';
};
const placementColumn = generateNestedColumn('Placement Details', [
generateColumn(
'placedAt.companyName',
'Company',
275,
null,
true,
true,
user.role === 'admin' || user.role === 'placementCoordinator' ? companyDropdownRenderer : null
),
generateNestedColumn('CTC (LPA)', [
generateColumn('placedAt.ctc', 'CTC', 80, null, true, false, (params) => params.value.toFixed(2)),
generateColumn('placedAt.ctcBase', 'Base', 80, null, true, false, (params) => params.value.toFixed(2))
]),
generateColumn('placedAt.location', 'Location', 100, null, true, false)
]);

const columnDefinitions = [
...(user.role === 'admin' || user.role === 'placementCoordinator' ? [actionsColumn] : []),
generateColumn('role', 'Role', 100, 'left', false, false, user.role === 'admin' ? roleDropdownRenderer : roleFormatter),
generateColumn('name', 'Name', 130, 'left'),
generateColumn('rollNo', 'Roll No', 100),
generateColumn('email', 'Email', 225),
generateNestedColumn('Placement Details', [
generateColumn('placed', 'Company', 100, null, true, true),
generateNestedColumn('CTC (LPA)', [
generateColumn('ctc', 'CTC', 80, null, true, false, (params) => params.value.toFixed(2)),
generateColumn('ctcBase', 'Base', 80, null, true, false, (params) => params.value.toFixed(2))
]),
generateColumn('location', 'location', 100, null, true, true)
]),
placementColumn,
...(user.role === 'admin' || user.role === 'placementCoordinator' ? academicColumn : [])
];

Expand Down
50 changes: 50 additions & 0 deletions server/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const User = require('../models/User');
const Company = require('../models/Company');
const logger = require('../utils/logger');
const validateUser = require('../utils/validateUser');
const { isValidObjectId } = require('mongoose');
Expand Down Expand Up @@ -137,3 +138,52 @@ exports.deleteUser = async (req, res) => {
res.status(500).json({ message: 'Internal server error' });
}
};

// Update Compay of a User
exports.updateCompany = async (req, res) => {
try {
if (!isValidObjectId(req.params.id)) {
return res.status(400).json({ message: 'Invalid user ID' });
}
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
let placedAt = {
companyName: 'Not Placed',
ctc: 0,
ctcBase: 0,
profile: 'N/A',
offer: 'N/A',
location: 'N/A',
bond: 0
};
if(req.body.companyId !== 'np'){
let company = await Company.findById(req.body.companyId);
placedAt = {
companyId: company._id,
companyName: company.name,
ctc: company.ctc,
ctcBase: company.ctcBreakup.base,
profile: company.profile,
offer: company.typeOfOffer,
location: company.locations[0],
bond: company.bond
};
}

const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{
placedAt: placedAt,
placed: req.body.companyId!=='np' ? true : false
},
{ new: true }
);
logger.info(`User company updated: ${updatedUser.name}`);
res.status(200).json({ message: `Company of ${updatedUser.name} updated Successfully` });
} catch (error) {
logger.error(error);
res.status(500).json({ message: 'Internal server error' });
}
};
10 changes: 8 additions & 2 deletions server/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,14 @@ const userSchema = new mongoose.Schema(
totalGapInAcademics: { type: Number, default: 0 },
placed: { type: Boolean, default: false },
placedAt: {
company: { type: mongoose.Schema.Types.ObjectId, ref: 'Company' },
location: { type: String, default: '' }
companyId: { type: String, default: 'Not Placed' },
companyName: { type: String, default: 'Not Placed' },
ctc: { type: Number, default: 0 },
ctcBase: { type: Number, default: 0 },
profile: { type: String, default: 'N/A' },
offer: { type: String, default: 'N/A' },
location: { type: String, default: 'N/A' },
bond: { type: Number, default: 0 }
}
},
{
Expand Down
Loading

0 comments on commit 801c220

Please sign in to comment.