Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
79bc6f7
Properly adding first half of Form A.4 -> Internship Details
lumry Apr 12, 2025
9c04266
Completed Designing A4 Form
Apr 13, 2025
32f65c0
Updated with Schema and API Functionality
rithwik-d Apr 14, 2025
fc72491
Updated with Schema and API Functionality
rithwik-d Apr 14, 2025
0a791ac
Completed Schema and API
rithwik-d Apr 14, 2025
ceaa6fd
Merge branch 'GroupB-FormA4-Backend' of https://github.com/IPMS-Proje…
lumry Apr 14, 2025
2080bd4
final commit for sprint2
vijaychirram Apr 14, 2025
3b53dd4
Merge branch 'GroupB-FormA4-Backend' of https://github.com/IPMS-Proje…
rithwik-d Apr 14, 2025
d2566eb
coodrinator dashboard with request detail view and approval login fun…
vijaychirram Apr 14, 2025
1c991ac
Merge branch 'main' into teama/3
Kamal-Poshala Apr 14, 2025
f42b58f
Delete server/utils/logger.test.js
Kamal-Poshala Apr 14, 2025
3aba787
Delete server/utils/cronUtils.test.js
Kamal-Poshala Apr 14, 2025
db48bff
fix tests
Apr 14, 2025
551a5ed
remove unused value 'setRole'
Apr 14, 2025
5c82882
Add ActivateAccount route to router configuration
Charan-Nimmagadda Apr 14, 2025
d01051e
Merge pull request #105 from IPMS-Project/GroupB/development
Charan-Nimmagadda Apr 14, 2025
525a613
Update package.json
vijaychirram Apr 15, 2025
1a5ec75
refactor: clean up code by removing unused files and optimizing email…
Charan-Nimmagadda Apr 15, 2025
11ad8b0
Merge branch 'teama/3' of https://github.com/NNPhaniCharan/IPMS into …
Charan-Nimmagadda Apr 15, 2025
5e1ffad
Merge branch 'main' into teama/3
Kamal-Poshala Apr 15, 2025
1779536
Merge pull request #107 from IPMS-Project/teama/3
Charan-Nimmagadda Apr 15, 2025
2e21af2
Merge branch 'main' of https://github.com/IPMS-Project/IPMS into Grou…
lumry Apr 16, 2025
2ed33b1
Internee Details for Form A.3
lumry Apr 17, 2025
64a5a2f
Merge pull request #115 from IPMS-Project/GroupB/FormA.3Updates
Charan-Nimmagadda Apr 17, 2025
b6c6b75
updated email sending functionality for sprint 2
vijaychirram Apr 20, 2025
6008400
removed coordinator.js
vijaychirram Apr 20, 2025
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
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
]
}
}
Binary file added client/public/OU-IPMS.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 2 additions & 3 deletions client/src/components/Layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ function Layout() {
<div className="App">
<header className="app-header">
<div className="logo-container">
<img src="/OU-Logo.svg" alt="OU Logo" className="ou-logo" />
<h1 style={{ marginLeft: "-25px" }}>IPMS</h1>
<img src="/OU-IPMS.png" alt="OU Logo" className="ou-logo" />
</div>
<nav className="main-nav">
<ul>
Expand Down Expand Up @@ -45,4 +44,4 @@ function Layout() {
);
}

export default Layout;
export default Layout;
125 changes: 99 additions & 26 deletions client/src/pages/A3JobEvaluationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ const evaluationItems = [
const A3JobEvaluationForm = () => {
// Form state management
const [formData, setFormData] = useState({
interneeName: "",
interneeID: "",
interneeEmail: "",
advisorSignature: "",
advisorAgreement: false,
coordinatorSignature: "",
coordinatorAgreement: false,
});
const [errors, setErrors] = useState({});

// Ratings and comments
const [ratings, setRatings] = useState({});
Expand All @@ -58,6 +62,24 @@ const A3JobEvaluationForm = () => {
const [selectedFont, setSelectedFont] = useState(fonts[0]);
const [activeTab, setActiveTab] = useState("type");

// For validation of the form contents
const validateForm = () => {
const newErrors = {};
if (!formData.interneeName?.trim()) newErrors.interneeName = "Name is required.";
if (!/^\d{9}$/.test(formData.interneeID || "")) newErrors.interneeID = "Enter a valid 9-digit Sooner ID.";
if (!/\S+@\S+\.\S+/.test(formData.interneeEmail || "")) newErrors.interneeEmail = "Invalid email.";
if (!formData.advisorSignature) newErrors.advisorSignature = "Signature is required.";
if (!formData.coordinatorSignature) newErrors.coordinatorSignature = "Signature is required.";
evaluationItems.forEach((item) => {
if (!ratings[item]) {
newErrors[`${item}_rating`] = "Please select one of these"; // Error message
}
});

setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};

// Signature canvas ref
const sigCanvasRef = useRef(null);

Expand All @@ -71,6 +93,7 @@ const A3JobEvaluationForm = () => {
// Handle form input changes
const handleChange = (field, value) => {
setFormData((prev) => ({ ...prev, [field]: value }));
setErrors((prev) => ({ ...prev, [field]: undefined }));
};

// Rating selection
Expand Down Expand Up @@ -120,8 +143,8 @@ const A3JobEvaluationForm = () => {
// Submit the form to the backend
const handleSubmit = async (e) => {
e.preventDefault();
if (!formData.advisorAgreement || !formData.coordinatorAgreement) {
alert("Please confirm both signature agreements before submitting.");
if (!validateForm() || !formData.advisorAgreement || !formData.coordinatorAgreement) {
alert("Please confirm internee details and both signature agreements before submitting.");
return;
}
try {
Expand All @@ -130,12 +153,25 @@ const A3JobEvaluationForm = () => {
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ formData, ratings, comments }),
body: JSON.stringify({
interneeName: formData.interneeName,
interneeID: formData.interneeID,
interneeEmail: formData.interneeEmail,
advisorSignature: formData.advisorSignature,
coordinatorSignature: formData.coordinatorSignature,
advisorAgreement: formData.advisorAgreement,
coordinatorAgreement: formData.coordinatorAgreement,
ratings,
comments,
}),
}
);
if (response.ok) {
alert("Evaluation submitted successfully!");
setFormData({
interneeName: "",
interneeID: "",
interneeEmail: "",
advisorSignature: "",
advisorAgreement: false,
coordinatorSignature: "",
Expand Down Expand Up @@ -191,10 +227,33 @@ const A3JobEvaluationForm = () => {
<div className="page-content">
<h2 className="heading-maroon">A.3 – Job Performance Evaluation</h2>
<Container
className="p-4 rounded shadow-lg"
className="p-4 rounded shadow-lg"
style={{ backgroundColor: "#fff", maxWidth: "900px", width: "100%" }}
>
<Form onSubmit={handleSubmit}>
<Row className="justify-content-center">
<Col xs={12}>
<div className="border-box">
<h5 style={{ backgroundColor: '#9d2235', color: 'white', padding: '8px', borderRadius: '5px', textAlign: "center", width: '100%',}}>Internee Details</h5>
<Form.Group controlId="interneeName">
<Form.Label>Name</Form.Label>
<Form.Control type="text" value={formData.interneeName} onChange={(e) => handleChange("interneeName", e.target.value)} isInvalid={!!errors.interneeName} placeholder="Enter full name" style={{ maxWidth: "300px" }}/>
<Form.Text className="text-danger">{errors.interneeName}</Form.Text>

</Form.Group>
<Form.Group controlId="interneeID">
<Form.Label>Sooner ID</Form.Label>
<Form.Control type="text" maxLength={9} value={formData.interneeID} onChange={(e) => handleChange("interneeID", e.target.value)} isInvalid={!!errors.interneeID} placeholder="Enter 9-digit student ID" style={{ maxWidth: "300px" }}/>
<Form.Text className="text-danger">{errors.interneeID}</Form.Text>
</Form.Group>
<Form.Group controlId="interneeEmail">
<Form.Label>Email</Form.Label>
<Form.Control type="email" value={formData.interneeEmail} onChange={(e) => handleChange("interneeEmail", e.target.value)} isInvalid={!!errors.interneeEmail} placeholder="Enter student email" style={{ maxWidth: "300px" }}/>
<Form.Text className="text-danger">{errors.interneeEmail}</Form.Text>
</Form.Group>
</div>
</Col>
</Row>
<Table bordered responsive className="text-center custom-table">
<thead>
<tr>
Expand All @@ -207,41 +266,53 @@ const A3JobEvaluationForm = () => {
<tbody>
{evaluationItems.map((item, index) => (
<tr key={item}>
<td>{item}</td>
<td>
<td>{item}</td>

{/* Radios grouped in one cell */}
<td colSpan={2}>
<div className="d-flex gap-4 align-items-center">
<Form.Check
type="radio"
id={`satisfactory-${index}`}
name={`item-${index}`}
label="Satisfactory"
value="Satisfactory"
checked={ratings[item] === "Satisfactory"}
onChange={() => handleRatingChange(item, "Satisfactory")}
required
isInvalid={!!errors[`${item}_rating`]}
/>
</td>
<td>
<Form.Check
type="radio"
id={`unsatisfactory-${index}`}
name={`item-${index}`}
label="Unsatisfactory"
value="Unsatisfactory"
checked={ratings[item] === "Unsatisfactory"}
onChange={() =>
handleRatingChange(item, "Unsatisfactory")
}
onChange={() => handleRatingChange(item, "Unsatisfactory")}
isInvalid={!!errors[`${item}_rating`]}
/>
</td>
<td>
<Form.Control
as="textarea"
rows={2}
value={comments[item] || ""}
onChange={(e) =>
handleCommentChange(item, e.target.value)
}
placeholder="Enter comments"
style={{ minWidth: "250px" }}
/>
</td>
</tr>
</div>

{/* Show the error below both radio buttons */}
{errors[`${item}_rating`] && (
<Form.Control.Feedback type="invalid" className="d-block mt-1">
{errors[`${item}_rating`]}
</Form.Control.Feedback>
)}
</td>

{/* Comments box */}
<td>
<Form.Control
as="textarea"
rows={2}
value={comments[item] || ""}
onChange={(e) => handleCommentChange(item, e.target.value)}
placeholder="Enter comments"
style={{ minWidth: "250px" }}
/>
</td>
</tr>
))}
</tbody>
</Table>
Expand All @@ -265,6 +336,7 @@ const A3JobEvaluationForm = () => {
>
{renderSignaturePreview("advisorSignature")}
</div>
<Form.Text className="text-danger">{errors.advisorSignature}</Form.Text>
<Form.Check
type="checkbox"
className="mt-2"
Expand Down Expand Up @@ -294,6 +366,7 @@ const A3JobEvaluationForm = () => {
>
{renderSignaturePreview("coordinatorSignature")}
</div>
<Form.Text className="text-danger">{errors.coordinatorSignature}</Form.Text>
<Form.Check
type="checkbox"
className="mt-2"
Expand Down
Loading