diff --git a/client/src/pages/A1InternshipRequestForm.js b/client/src/pages/A1InternshipRequestForm.js index 82950c9c..8ab5ac84 100644 --- a/client/src/pages/A1InternshipRequestForm.js +++ b/client/src/pages/A1InternshipRequestForm.js @@ -1,7 +1,6 @@ import React, { useState, useEffect } from "react"; import "../styles/A1InternshipRequestForm.css"; - const outcomeLabels = [ "Problem Solving", "Solution Development", @@ -26,7 +25,7 @@ const signatureFonts = [ { name: "Great Vibes", class: "font-great-vibes" }, { name: "Pacifico", class: "font-pacifico" }, { name: "Satisfy", class: "font-satisfy" }, - { name: "Caveat", class: "font-caveat" } + { name: "Caveat", class: "font-caveat" }, ]; // Signature Font Picker Component @@ -62,9 +61,11 @@ const SignatureInput = ({ id, value, onChange, disabled, placeholder }) => { /> {showFonts && nameInput && (
-
Select a signature style:
+
+ Select a signature style: +
{signatureFonts.map((font) => ( -
selectFont(font.class)} @@ -77,11 +78,7 @@ const SignatureInput = ({ id, value, onChange, disabled, placeholder }) => { {nameInput && (
{nameInput} - +
)}
@@ -118,11 +115,18 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { const isFieldEditable = (fieldType) => { switch (userRole) { case "student": - return !["advisorSignature", "coordinatorApproval", "supervisorComments", "coordinatorComments"].includes(fieldType); + return ![ + "advisorSignature", + "coordinatorApproval", + "supervisorComments", + "coordinatorComments", + ].includes(fieldType); case "supervisor": return ["advisor", "supervisorComments"].includes(fieldType); case "coordinator": - return ["coordinator", "coordinatorComments", "advisor"].includes(fieldType); + return ["coordinator", "coordinatorComments", "advisor"].includes( + fieldType + ); default: return true; } @@ -134,7 +138,7 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { setFormData((prev) => ({ ...prev, interneeName: storedUser.fullName || "", - interneeEmail: storedUser.email || "" + interneeEmail: storedUser.email || "", })); } }, []); @@ -152,7 +156,7 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { } } if (errors[id]) { - setErrors(prev => { + setErrors((prev) => { const newErrors = { ...prev }; delete newErrors[id]; return newErrors; @@ -179,7 +183,9 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { useEffect(() => { const timeout = setTimeout(() => { - const descriptions = formData.tasks.map((task) => task.description.trim()).filter(Boolean); + const descriptions = formData.tasks + .map((task) => task.description.trim()) + .filter(Boolean); if (descriptions.length > 0) { fetch(`${process.env.REACT_APP_API_URL}/api/align-outcomes`, { method: "POST", @@ -189,8 +195,12 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { .then((res) => res.json()) .then((data) => { const updatedTasks = formData.tasks.map((task) => { - const match = data.results.find((r) => r.task === task.description); - return match ? { ...task, outcomes: match.matched_outcomes } : { ...task, outcomes: [] }; + const match = data.results.find( + (r) => r.task === task.description + ); + return match + ? { ...task, outcomes: match.matched_outcomes } + : { ...task, outcomes: [] }; }); setFormData((prev) => ({ ...prev, tasks: updatedTasks })); }) @@ -201,10 +211,14 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { }, [formData.tasks]); const renderOutcomeCell = (task, outcome, key) => { - const normalizedOutcome = outcome.charAt(0).toLowerCase() + outcome.replace(/\s+/g, "").slice(1); + const normalizedOutcome = + outcome.charAt(0).toLowerCase() + outcome.replace(/\s+/g, "").slice(1); const isMatched = task.outcomes.includes(normalizedOutcome); return ( - + { 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.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"; + 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"; - else if (!namePattern.test(formData.workplaceName)) newErrors.workplaceName = "Workplace name should contain only letters and spaces"; - if (formData.website && !formData.website.includes('.')) newErrors.website = "Please enter a valid website address"; + else if (!emailPattern.test(formData.interneeEmail)) + newErrors.interneeEmail = "Invalid email format"; + if (!formData.workplaceName) + newErrors.workplaceName = "Workplace name is required"; + else if (!namePattern.test(formData.workplaceName)) + newErrors.workplaceName = + "Workplace name should contain only letters and spaces"; + if (formData.website && !formData.website.includes(".")) + newErrors.website = "Please enter a valid website address"; if (!formData.phone) newErrors.phone = "Phone is required"; - else if (!phonePattern.test(formData.phone)) newErrors.phone = "Phone must be 10 digits"; + else if (!phonePattern.test(formData.phone)) + newErrors.phone = "Phone must be 10 digits"; if (!formData.startDate) newErrors.startDate = "Start date is required"; if (!formData.endDate) newErrors.endDate = "End date is required"; - if (!formData.advisorName) newErrors.advisorName = "Supervisor name is required"; - else if (!namePattern.test(formData.advisorName)) newErrors.advisorName = "Supervisor name should contain only letters and spaces"; - if (!formData.advisorEmail) newErrors.advisorEmail = "Supervisor email is required"; - 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.advisorName) + newErrors.advisorName = "Supervisor name is required"; + else if (!namePattern.test(formData.advisorName)) + newErrors.advisorName = + "Supervisor name should contain only letters and spaces"; + if (!formData.advisorEmail) + newErrors.advisorEmail = "Supervisor email is required"; + 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.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 (!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"; setErrors(newErrors); @@ -263,11 +305,14 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { const submitFormData = async () => { try { - const response = await fetch(`${process.env.REACT_APP_API_URL}/api/form/submit`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(formData), - }); + const response = await fetch( + `${process.env.REACT_APP_API_URL}/api/form/submit`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(formData), + } + ); if (!response.ok) throw new Error("Failed to submit form"); const data = await response.json(); return data; @@ -279,11 +324,14 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { const sendTaskDescriptions = async (descriptions) => { try { - const response = await fetch(`${process.env.REACT_APP_API_URL}/api/align-outcomes`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ tasks: descriptions }), - }); + const response = await fetch( + `${process.env.REACT_APP_API_URL}/api/align-outcomes`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ tasks: descriptions }), + } + ); if (!response.ok) throw new Error("Failed to send task descriptions"); const data = await response.json(); return data.results.map(({ task, matched_outcomes }) => ({ @@ -299,13 +347,17 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { const handleSubmit = async (e) => { e.preventDefault(); if (!validateForm()) return; - const taskDescriptions = formData.tasks.map((task) => task.description.trim()).filter(Boolean); + const taskDescriptions = formData.tasks + .map((task) => task.description.trim()) + .filter(Boolean); try { const aligned = await sendTaskDescriptions(taskDescriptions); if (aligned && aligned.length > 0) { setFormData((prev) => ({ ...prev, tasks: aligned })); const submissionResponse = await submitFormData(); - const recipient = submissionResponse.manual ? "coordinator for manual review!" : "supervisor!"; + const recipient = submissionResponse.manual + ? "coordinator for manual review!" + : "supervisor!"; setSuccessMsg(`Form submitted successfully and sent to ${recipient}`); setTimeout(() => setSuccessMsg(""), 15000); setFormData(initialState); @@ -319,22 +371,22 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { useEffect(() => { const fonts = [ - 'https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&display=swap', - 'https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap', - 'https://fonts.googleapis.com/css2?family=Pacifico&display=swap', - 'https://fonts.googleapis.com/css2?family=Satisfy&display=swap', - 'https://fonts.googleapis.com/css2?family=Caveat:wght@500&display=swap' + "https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&display=swap", + "https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap", + "https://fonts.googleapis.com/css2?family=Pacifico&display=swap", + "https://fonts.googleapis.com/css2?family=Satisfy&display=swap", + "https://fonts.googleapis.com/css2?family=Caveat:wght@500&display=swap", ]; const links = []; - fonts.forEach(font => { - const link = document.createElement('link'); + fonts.forEach((font) => { + const link = document.createElement("link"); link.href = font; - link.rel = 'stylesheet'; + link.rel = "stylesheet"; document.head.appendChild(link); links.push(link); }); return () => { - links.forEach(link => document.head.removeChild(link)); + links.forEach((link) => document.head.removeChild(link)); }; }, []); @@ -355,115 +407,148 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { Name*:
- - {errors.interneeName &&
{errors.interneeName}
} + {errors.interneeName && ( +
+ {errors.interneeName} +
+ )} Name*:
- - {errors.workplaceName &&
{errors.workplaceName}
} + {errors.workplaceName && ( +
+ {errors.workplaceName} +
+ )} Name*:
- - {errors.advisorName &&
{errors.advisorName}
} + {errors.advisorName && ( +
+ {errors.advisorName} +
+ )} Sooner ID*:
- - {errors.soonerId &&
{errors.soonerId}
} + {errors.soonerId && ( +
+ {errors.soonerId} +
+ )} - Website:
- + - {errors.website &&
{errors.website}
} + {errors.website && ( +
+ {errors.website} +
+ )} - Job Title:
- + Email*:
- - {errors.interneeEmail &&
{errors.interneeEmail}
} + {errors.interneeEmail && ( +
+ {errors.interneeEmail} +
+ )} Phone*:
- - {errors.phone &&
{errors.phone}
} + {errors.phone && ( +
+ {errors.phone} +
+ )} Email*:
- - {errors.advisorEmail &&
{errors.advisorEmail}
} + {errors.advisorEmail && ( +
+ {errors.advisorEmail} +
+ )} Credit Hours*:
- - {errors.creditHours &&
{errors.creditHours}
} + {errors.creditHours && ( +
+ {errors.creditHours} +
+ )} -
-
- -
- {errors.startDate &&
{errors.startDate}
} - - - -
-
- -
- {dateError &&
{dateError}
} - {errors.endDate &&
{errors.endDate}
} - + +
+
+ +
+ {errors.startDate && ( +
{errors.startDate}
+ )} + + + +
+
+ +
+ {dateError &&
{dateError}
} + {errors.endDate && ( +
{errors.endDate}
+ )} + -

Task Details & Program Outcomes*

+

+ Task Details & Program Outcomes + * +

Job Description Details: @@ -528,7 +625,8 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { Task {outcomeLabels.map((label, j) => ( - {label}
+ {label} +
({outcomeDescriptions[j]}) ))} @@ -543,16 +641,26 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { placeholder={`Task ${i + 1}`} value={task.description} onChange={(e) => handleTaskChange(i, e.target.value)} - style={{ width: "100%", padding: "4px", boxSizing: "border-box" }} + style={{ + width: "100%", + padding: "4px", + boxSizing: "border-box", + }} disabled={!isFieldEditable("task")} /> - {outcomeLabels.map((label, j) => renderOutcomeCell(task, label, `${i}-${j}`))} + {outcomeLabels.map((label, j) => + renderOutcomeCell(task, label, `${i}-${j}`) + )} ))} - {errors.tasks &&
{errors.tasks}
} + {errors.tasks && ( +
+ {errors.tasks} +
+ )}

Signatures:

@@ -560,9 +668,10 @@ const A1InternshipRequestForm = ({ userRole = "student" }) => { - Internee Signature*:
+ Internee Signature*: +
- { placeholder="Enter your full name" />
- {errors.interneeSignature &&
{errors.interneeSignature}
} + {errors.interneeSignature && ( +
+ {errors.interneeSignature} +
+ )} - Internship Supervisor Signature:
+ Internship Supervisor Signature: +
- { placeholder="Enter your full name" />
- {errors.advisorSignature &&
{errors.advisorSignature}
} + {errors.advisorSignature && ( +
+ {errors.advisorSignature} +
+ )} - Internship Coordinator Approval:
+ Internship Coordinator Approval: +
- { placeholder="Enter your full name" />
- {errors.coordinatorApproval &&
{errors.coordinatorApproval}
} + {errors.coordinatorApproval && ( +
+ {errors.coordinatorApproval} +
+ )} - {/* - -
-