Skip to content

Commit

Permalink
Update rate limit window for API requests and add new API endpoint fo…
Browse files Browse the repository at this point in the history
…r updating company location
  • Loading branch information
Sagargupta16 committed Feb 1, 2024
1 parent 60751e2 commit 3eb1930
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 52 deletions.
2 changes: 2 additions & 0 deletions client/src/api/studentAPI.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export const updateUserRole = (id, role) => axiosInstance.put(`/users/role/${id}
export const deleteStudent = (id) => axiosInstance.delete(`/users/delete/${id}`);

export const updateStudentCompany = (id, companyId) => axiosInstance.put(`/users/company/${id}`, { companyId });

export const updateStudentCompanyLocation = (id, location) => axiosInstance.put(`/users/companyLocation/${id}`, { location });
108 changes: 75 additions & 33 deletions client/src/components/AuthForms/AuthenticationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ const AuthenticationForm = () => {
const [rollNo, setRollNo] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [pgCgpa, setPgCgpa] = useState('');
const [pgPercentage, setPgPercentage] = useState('');
const [ugCgpa, setUgCgpa] = useState('');
const [ugPercentage, setUgPercentage] = useState('');
const [hscCgpa, setHscCgpa] = useState('');
const [hscPercentage, setHscPercentage] = useState('');
const [sscCgpa, setSscCgpa] = useState('');
const [sscPercentage, setSscPercentage] = useState('');
const [pgCgpa, setPgCgpa] = useState('0');
const [pgPercentage, setPgPercentage] = useState('0');
const [ugCgpa, setUgCgpa] = useState('0');
const [ugPercentage, setUgPercentage] = useState('0');
const [hscCgpa, setHscCgpa] = useState('0');
const [hscPercentage, setHscPercentage] = useState('0');
const [sscCgpa, setSscCgpa] = useState('0');
const [sscPercentage, setSscPercentage] = useState('0');
const [totalGapInAcademics, setTotalGapInAcademics] = useState(0);
const [backlogs, setBacklogs] = useState(0);
const [currentStep, setCurrentStep] = useState(1);
Expand Down Expand Up @@ -135,44 +135,86 @@ const AuthenticationForm = () => {
</div>
<div className={classes['auth-form__item']}>
<label htmlFor="pg">PG</label>
<input
type="text"
placeholder="PG CGPA"
value={pgCgpa}
onChange={(e) => {
const pgCgpaValue = e.target.value;
setPgCgpa(pgCgpaValue);
setPgPercentage((pgCgpaValue * 9.5).toFixed(2));
}}
step="0.01"
/>
<input type="text" placeholder="PG Percentage" value={pgPercentage} onChange={(e) => setPgPercentage(e.target.value)} disabled />
<div className={classes['input-container']}>
<input
type="text"
placeholder="PG CGPA"
value={pgCgpa}
onChange={(e) => {
const pgCgpaValue = e.target.value;
setPgCgpa(pgCgpaValue);
setPgPercentage((pgCgpaValue * 9.5).toFixed(2));
}}
step="0.01"
/>
<div>CGPA</div>
</div>
<div className={classes['input-container']}>
<input type="text" placeholder="PG Percentage" value={pgPercentage} onChange={(e) => setPgPercentage(e.target.value)} disabled />
<div>%</div>
</div>
</div>
<div className={classes['auth-form__item']}>
<label htmlFor="ug">UG</label>
<input type="text" placeholder="UG CGPA" onChange={(e) => setUgCgpa(e.target.value)} value={ugCgpa} step="0.01" />
<input type="text" placeholder="UG Percentage" onChange={(e) => setUgPercentage(e.target.value)} value={ugPercentage} step="0.01" />
<div className={classes['input-container']}>
<input type="text" placeholder="UG CGPA" onChange={(e) => setUgCgpa(e.target.value)} value={ugCgpa} step="0.01" />
<div>CGPA</div>
</div>
<div className={classes['input-container']}>
<input type="text" placeholder="UG Percentage" onChange={(e) => setUgPercentage(e.target.value)} value={ugPercentage} step="0.01" />
<div>%</div>
</div>
</div>
<div className={classes['auth-form__item']}>
<label htmlFor="hsc">12th</label>
<input type="text" placeholder="HSC CGPA" onChange={(e) => setHscCgpa(e.target.value)} value={hscCgpa} step="0.01" />
<input type="text" placeholder="HSC Percentage" onChange={(e) => setHscPercentage(e.target.value)} value={hscPercentage} step="0.01" />
<div className={classes['input-container']}>
<input type="text" placeholder="HSC CGPA" onChange={(e) => setHscCgpa(e.target.value)} value={hscCgpa} step="0.01" />
<div>CGPA</div>
</div>
<div className={classes['input-container']}>
<input
type="text"
placeholder="HSC Percentage"
onChange={(e) => setHscPercentage(e.target.value)}
value={hscPercentage}
step="0.01"
/>
<div>%</div>
</div>
</div>
<div className={classes['auth-form__item']}>
<label htmlFor="ssc">10th</label>
<input type="text" placeholder="SSC CGPA" onChange={(e) => setSscCgpa(e.target.value)} value={sscCgpa} step="0.01" />
<input type="text" placeholder="SSC Percentage" onChange={(e) => setSscPercentage(e.target.value)} value={sscPercentage} step="0.01" />
<div className={classes['input-container']}>
<input type="text" placeholder="SSC CGPA" onChange={(e) => setSscCgpa(e.target.value)} value={sscCgpa} step="0.01" />
<div>CGPA</div>
</div>
<div className={classes['input-container']}>
<input
type="text"
placeholder="SSC Percentage"
onChange={(e) => setSscPercentage(e.target.value)}
value={sscPercentage}
step="0.01"
/>
<div>%</div>
</div>
</div>
<div className={classes['auth-form__item']}>
<label htmlFor="totalGapInAcademics">Gap</label>
<input
type="number"
placeholder="Total Gap in Academics"
value={totalGapInAcademics}
onChange={(e) => setTotalGapInAcademics(e.target.value)}
/>
<div className={classes['input-container']}>
<input
type="number"
placeholder="Total Gap in Academics"
value={totalGapInAcademics}
onChange={(e) => setTotalGapInAcademics(e.target.value)}
/>
<div>Years</div>
</div>
<label htmlFor="backlogs">Backlogs</label>
<input type="number" placeholder="Backlogs" value={backlogs} onChange={(e) => setBacklogs(e.target.value)} />
<div className={classes['input-container']}>
<input type="number" placeholder="Backlogs" value={backlogs} onChange={(e) => setBacklogs(e.target.value)} />
<div>Subjects</div>
</div>
</div>

<div className={classes['auth-form__item_btn']}>
Expand Down
14 changes: 8 additions & 6 deletions client/src/components/AuthForms/auth.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -140,20 +140,17 @@
width: 100%;
}

.email-domain {
.input-container div,
.input-container button {
background: transparent;
color: var(--color-light);
font-size: 1rem;
padding: 0 0.5rem;
}

.password-toggle {
background: transparent;
color: var(--color-light);
font-size: 1rem;
padding-top: 0.3rem;
cursor: pointer;
transition: var(--transition);
padding: 0.3rem 0.5rem 0 0.7rem;
}

.forgot-password {
Expand All @@ -166,3 +163,8 @@
.forgot-password:hover {
color: var(--color-primary);
}

.input-container input:focus + div,
.input-container input:focus + button {
color: var(--color-primary);
}
34 changes: 31 additions & 3 deletions client/src/components/Student/StudentTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { GrValidate } from 'react-icons/gr';
import { MdDelete } from 'react-icons/md';
import { toast } from 'react-toastify';
import { getCompanies } from '../../api/companyApi.jsx';
import { deleteStudent, getStudents, updateStudentCompany, updateUserRole, updateVerificationStatus } from '../../api/studentApi.jsx';
import {
deleteStudent,
getStudents,
updateStudentCompany,
updateStudentCompanyLocation,
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 Down Expand Up @@ -132,6 +139,17 @@ const StudentTable = () => {
return dropdownRenderer(params, options, updateStudentCompany, params.data.placedAt?.companyId);
};

const locationDropdownRenderer = (params) => {
const company = companies.find((company) => company.id === params.data.placedAt?.companyId);
if (!company) return 'N/A';
const options = company.locations.map((location) => ({
value: location,
label: location
}));
options.unshift({ value: 'N/A', label: 'N/A' });
return dropdownRenderer(params, options, updateStudentCompanyLocation, params.data.placedAt?.location);
};

const modelRenderer = (isModalOpen, closeModal, onConfirm, message, buttonTitle) => {
return <Modal isOpen={isModalOpen} onClose={() => closeModal()} onConfirm={onConfirm} message={message} buttonTitle={buttonTitle} />;
};
Expand Down Expand Up @@ -193,12 +211,22 @@ const StudentTable = () => {
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)
generateColumn('placedAt.offer', 'Offer', 80, null, false, false),
generateColumn('placedAt.profileType', 'Profile', 100, null, true, true),
generateColumn(
'placedAt.location',
'Location',
120,
null,
true,
false,
user.role === 'admin' || user.role === 'placementCoordinator' ? locationDropdownRenderer : null
)
]);

const columnDefinitions = [
...(user.role === 'admin' || user.role === 'placementCoordinator' ? [actionsColumn] : []),
generateColumn('role', 'Role', 100, 'left', false, false, user.role === 'admin' ? roleDropdownRenderer : roleFormatter),
generateColumn('role', 'Role', 110, 'left', false, false, user.role === 'admin' ? roleDropdownRenderer : roleFormatter),
generateColumn('name', 'Name', 130, 'left'),
generateColumn('rollNo', 'Roll No', 100),
generateColumn('email', 'Email', 225),
Expand Down
48 changes: 40 additions & 8 deletions server/controllers/userController.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,28 @@ exports.updateCompany = async (req, res) => {
if (!user) {
return res.status(404).json({ message: 'User not found' });
}

let placedAt = {
companyId: 'np',
companyName: 'Not Placed',
ctc: 0,
ctcBase: 0,
profile: 'N/A',
profileType: 'N/A',
offer: 'N/A',
location: 'N/A',
bond: 0
};

if (user.placedAt.companyId !== 'np') {
const company = await Company.findById(user.placedAt.companyId);
const index = company.selectedStudentsRollNo.indexOf(user.rollNo);
if (index > -1) {
company.selectedStudentsRollNo.splice(index, 1);
}
await company.save();
}

if (req.body.companyId !== 'np') {
const company = await Company.findById(req.body.companyId);
placedAt = {
Expand All @@ -165,21 +178,15 @@ exports.updateCompany = async (req, res) => {
ctc: company.ctc,
ctcBase: company.ctcBreakup.base,
profile: company.profile,
profileType: company.profileCategory,
offer: company.typeOfOffer,
location: company.locations[0],
location: 'N/A',
bond: company.bond
};
if (!company.selectedStudentsRollNo.includes(user.rollNo)) {
company.selectedStudentsRollNo.push(user.rollNo);
await company.save();
}
} else {
const company = await Company.findById(user.placedAt.companyId);
const index = company.selectedStudentsRollNo.indexOf(user.rollNo);
if (index > -1) {
company.selectedStudentsRollNo.splice(index, 1);
}
await company.save();
}

const updatedUser = await User.findByIdAndUpdate(
Expand All @@ -197,3 +204,28 @@ exports.updateCompany = async (req, res) => {
res.status(500).json({ message: 'Internal server error' });
}
};

// Update Users Company Location by Default 'N/A'
exports.updateCompanyLocation = 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' });
}
const updatedUser = await User.findByIdAndUpdate(
req.params.id,
{
'placedAt.location': req.body.location
},
{ new: true }
);
logger.info(`User company location updated: ${updatedUser.name}`);
res.status(200).json({ message: `Company location of ${updatedUser.name} updated Successfully` });
} catch (error) {
logger.error(error);
res.status(500).json({ message: 'Internal server error' });
}
};
3 changes: 2 additions & 1 deletion server/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ const userSchema = new mongoose.Schema(
totalGapInAcademics: { type: Number, default: 0 },
placed: { type: Boolean, default: false },
placedAt: {
companyId: { type: String, default: 'Not Placed' },
companyId: { type: String, default: 'np' },
companyName: { type: String, default: 'Not Placed' },
ctc: { type: Number, default: 0 },
ctcBase: { type: Number, default: 0 },
profile: { type: String, default: 'N/A' },
profileType: { type: String, default: 'N/A' },
offer: { type: String, default: 'N/A' },
location: { type: String, default: 'N/A' },
bond: { type: Number, default: 0 }
Expand Down
3 changes: 3 additions & 0 deletions server/routes/userRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ router.delete('/delete/:id', authenticateUser, checkUserRole(['admin', 'placemen
// Update Compay of a User with rate limiting
router.put('/company/:id', authenticateUser, checkUserRole(['admin', 'placementCoordinator']), limiter, userController.updateCompany);

// Update Company Location of a User with rate limiting
router.put('/companyLocation/:id', authenticateUser, checkUserRole(['admin', 'placementCoordinator']), limiter, userController.updateCompanyLocation);

module.exports = router;
2 changes: 1 addition & 1 deletion server/utils/limiter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
windowMs: 5 * 60 * 1000, // 15 minutes
max: 100 // Limit each IP to 100 requests per windowMs
});

Expand Down

0 comments on commit 3eb1930

Please sign in to comment.