diff --git a/.gitignore b/.gitignore
index 7f5b38cc..c67172a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,8 @@ pids
*.seed
*.pid.lock
+
+
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
diff --git a/client/src/pages/A1InternshipRequestForm.js b/client/src/pages/A1InternshipRequestForm.js
index 82950c9c..ee307f08 100644
--- a/client/src/pages/A1InternshipRequestForm.js
+++ b/client/src/pages/A1InternshipRequestForm.js
@@ -20,7 +20,6 @@ const outcomeDescriptions = [
"Apply computer science algorithms to create practical solutions",
];
-// Signature font options
const signatureFonts = [
{ name: "Dancing Script", class: "font-dancing-script" },
{ name: "Great Vibes", class: "font-great-vibes" },
@@ -29,7 +28,7 @@ const signatureFonts = [
{ name: "Caveat", class: "font-caveat" }
];
-// Signature Font Picker Component
+
const SignatureInput = ({ id, value, onChange, disabled, placeholder }) => {
const [showFonts, setShowFonts] = useState(false);
const [selectedFont, setSelectedFont] = useState(signatureFonts[0].class);
@@ -91,7 +90,7 @@ const SignatureInput = ({ id, value, onChange, disabled, placeholder }) => {
const A1InternshipRequestForm = ({ userRole = "student" }) => {
const initialState = {
interneeName: "",
- soonerId: "",
+ // soonerId: "",
interneeEmail: "",
workplaceName: "",
website: "",
@@ -102,10 +101,8 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => {
advisorJobTitle: "",
advisorEmail: "",
interneeSignature: "",
- advisorSignature: "",
- coordinatorApproval: "",
creditHours: "",
- tasks: Array(5).fill({ description: "", outcomes: [] }), // Updated for outcomes
+ tasks: Array(5).fill({ description: "", outcomes: [] }),
supervisorComments: "",
coordinatorComments: "",
};
@@ -223,15 +220,15 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => {
const validateForm = () => {
const namePattern = /^[A-Za-z\s]+$/;
- const numberPattern = /^[0-9]+$/;
+ // const numberPattern = /^[0-9]+$/;
const phonePattern = /^[0-9]{10}$/;
const emailPattern = /^[\w.-]+@([\w-]+\.)+[\w-]{2,4}$/;
const newErrors = {};
if (!formData.interneeName) newErrors.interneeName = "Internee name is required";
else if (!namePattern.test(formData.interneeName)) newErrors.interneeName = "Name should contain only letters and spaces";
- if (!formData.soonerId) newErrors.soonerId = "Sooner ID is required";
- else if (!numberPattern.test(formData.soonerId)) newErrors.soonerId = "Sooner ID should be numeric";
+ //if (!formData.soonerId) newErrors.soonerId = "Sooner ID is required";
+ //else if (!numberPattern.test(formData.soonerId)) newErrors.soonerId = "Sooner ID should be numeric";
if (!formData.interneeEmail) newErrors.interneeEmail = "Email is required";
else if (!emailPattern.test(formData.interneeEmail)) newErrors.interneeEmail = "Invalid email format";
if (!formData.workplaceName) newErrors.workplaceName = "Workplace name is required";
@@ -247,12 +244,6 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => {
else if (!emailPattern.test(formData.advisorEmail)) newErrors.advisorEmail = "Invalid supervisor email format";
if (!formData.interneeSignature) newErrors.interneeSignature = "Internee signature is required";
else if (!namePattern.test(formData.interneeSignature)) newErrors.interneeSignature = "Signature should contain only letters and spaces";
- if (formData.advisorSignature && !namePattern.test(formData.advisorSignature)) {
- newErrors.advisorSignature = "Signature should contain only letters and spaces";
- }
- if (formData.coordinatorApproval && !namePattern.test(formData.coordinatorApproval)) {
- newErrors.coordinatorApproval = "Approval should contain only letters and spaces";
- }
if (!formData.creditHours) newErrors.creditHours = "Please select credit hours";
const tasksFilled = formData.tasks.filter((task) => task.description.trim() !== "").length >= 3;
if (!tasksFilled) newErrors.tasks = "At least 3 tasks are required";
@@ -354,7 +345,7 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => {
- Name*:
+ First Name*:
{
|
-
- Sooner ID*:
-
- {errors.soonerId && {errors.soonerId} }
- |
-
- Website:
-
- {errors.website && {errors.website} }
- |
-
- Job Title:
-
- |
-
+
+
+ Last Name:
+
+ {errors.website && {errors.website} }
+ |
+
+
+
+ Website:
+
+ {errors.website && {errors.website} }
+ |
+
+
+
+ Job Title:
+
+ |
+
+
Email*:
@@ -431,7 +425,7 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => {
value={formData.interneeEmail}
onChange={handleInputChange}
disabled
- // disabled={!isFieldEditable("interneeEmail")}
+
/>
{errors.interneeEmail && {errors.interneeEmail} }
|
@@ -559,7 +553,7 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => {
- |
+ |
Internee Signature*:
{
{errors.interneeSignature && {errors.interneeSignature} }
|
-
- Internship Supervisor Signature:
-
-
-
- {errors.advisorSignature && {errors.advisorSignature} }
- |
-
- Internship Coordinator Approval:
-
-
-
- {errors.coordinatorApproval && {errors.coordinatorApproval} }
- |
-
- {/*
-
-
-
- |
-
-
-
- |
-
*/}
+
diff --git a/client/src/styles/A1InternshipRequestForm.css b/client/src/styles/A1InternshipRequestForm.css
index a3255251..4355861e 100644
--- a/client/src/styles/A1InternshipRequestForm.css
+++ b/client/src/styles/A1InternshipRequestForm.css
@@ -217,3 +217,55 @@
max-width: 1200px;
border-radius: 12px;
}
+
+ .success-msg, .error-msg {
+ animation: fadeIn 0.5s;
+ }
+ @keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+
+ .required-asterisk {
+ color: #d93025;
+ font-weight: bold;
+ font-size: 1.2em;
+ margin-left: 2px;
+ vertical-align: middle;
+ }
+
+ button[type="submit"] {
+ box-shadow: 0 2px 8px rgba(44, 110, 203, 0.08);
+ font-weight: 600;
+ letter-spacing: 0.5px;
+ }
+ button[type="submit"]:active {
+ background: #1e5dad;
+ box-shadow: 0 1px 2px #777;
+ }
+
+ input, select, textarea {
+ margin-bottom: 10px;
+ }
+ textarea {
+ min-height: 90px;
+ resize: vertical;
+ }
+
+ .section-card {
+ background: #f7fafd;
+ border-radius: 8px;
+ box-shadow: 0 1px 8px rgba(44, 110, 203, 0.04);
+ padding: 18px 20px 14px 20px;
+ margin-bottom: 28px;
+ border-left: 5px solid #007bff22;
+ }
+
+ .section-title {
+ color: #2451a0;
+ border-left: 4px solid #007bffcc;
+ padding-left: 10px;
+ margin-bottom: 16px;
+ font-size: 1.18rem;
+ background: transparent;
+ }
diff --git a/server/models/InternshipRequest.js b/server/models/InternshipRequest.js
index d66fbf62..37a9792f 100644
--- a/server/models/InternshipRequest.js
+++ b/server/models/InternshipRequest.js
@@ -22,11 +22,7 @@ const Task = new mongoose.Schema({
});
const formA1 = new mongoose.Schema({
- // student: {
- // type: ObjectId,
- // required: true,
- // ref: 'UserTokenRequest'
- // },
+
student:{
name:{
type: String,
@@ -39,11 +35,7 @@ const formA1 = new mongoose.Schema({
},
},
...formMetadata,
- // student: {
- // type: ObjectId,
- // required: true,
- // ref: 'UserTokenRequest'
- // },
+
workplace: {
name: {
type: String,
@@ -77,11 +69,7 @@ const formA1 = new mongoose.Schema({
type: [Task],
required: true
},
- // status: {
- // type: String,
- // required: true,
- // enum: ['draft', 'submitted','pending manual review' ,'approved']
- // },
+
approvals: {
type: [String],
enum: ["advisor", "coordinator"],
diff --git a/server/routes/formRoutes.js b/server/routes/formRoutes.js
index 8fe44896..7540747d 100644
--- a/server/routes/formRoutes.js
+++ b/server/routes/formRoutes.js
@@ -3,15 +3,12 @@ const router = express.Router();
const InternshipRequest = require("../models/InternshipRequest");
const { insertFormData } = require("../services/insertData");
-// router.post("/internshiprequests/:id/approve", approveSubmission);
-// router.post("/internshiprequests/:id/reject", rejectSubmission);
-// UPDATED: GET route to fetch internship requests pending supervisor action
router.get("/internshiprequests", async (req, res) => {
try {
const requests = await InternshipRequest.find({
supervisor_status: "pending",
- // approvals: "advisor", // advisor has approved
+
supervisor_status: { $in: [null, "pending"] } // not yet reviewed by supervisor
}).sort({ createdAt: 1 }) .populate("student", "userName") // oldest first
@@ -25,12 +22,9 @@ router.get("/internshiprequests", async (req, res) => {
// Validate required fields
function validateFormData(formData) {
const requiredFields = [
- 'soonerId',
'workplaceName',
- 'website',
'phone',
'advisorName',
- 'advisorJobTitle',
'advisorEmail',
'creditHours',
'startDate',
@@ -44,23 +38,11 @@ function validateFormData(formData) {
}
}
- if (!/^[0-9]{9}$/.test(formData.soonerId))
- return `Sooner ID must be a 9-digit number, not ${formData.soonerId}`;
if (!Array.isArray(formData.tasks) || formData.tasks.length === 0) {
return 'Tasks must be a non-empty array';
}
- // for (const [index, task] of formData.tasks.entries()) {
- // if (!task.description || !task.outcomes) {
- // return `Task at index ${index} is missing description or outcomes`;
- // }
- // }
-
- // uncomment below if student has to fill in task outcomes
- // const filledTasks = formData.tasks.filter((task) => task.description && task.outcomes );
- // if (filledTasks.length < 3)
- // return `At least 3 tasks must have description and outcomes; only ${filledTasks.length} do`;
-
+
const tasks = formData.tasks;
console.log(tasks);
if (tasks.filter((task) => task.description && task.description.trim() !== '').length < 3)