Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Fix validation error message displayed prematurely on page load for form submission" #47

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
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
156 changes: 111 additions & 45 deletions pages/college_predictor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,11 @@ const fuseOptions = {
isCaseSensitive: false,
includeScore: false,
shouldSort: true,
includeMatches: false,
findAllMatches: false,
minMatchCharLength: 1,
location: 0,
threshold: 0.3,
distance: 100,
useExtendedSearch: true,
ignoreLocation: true,
ignoreFieldNorm: false,
fieldNormWeight: 1,
keys: ["Institute", "State", "Academic Program Name"],
};
Expand All @@ -28,17 +24,20 @@ const CollegePredictor = () => {
const router = useRouter();
const [filteredData, setFilteredData] = useState([]);
const [fullData, setFullData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [queryObject, setQueryObject] = useState({});
const [rankError, setRankError] = useState(null);
const [inputError, setInputError] = useState(null);
const [showResults, setShowResults] = useState(false);
const [submitted, setSubmitted] = useState(false); // Track form submission

useEffect(() => {
setQueryObject(router.query);
}, [router.query]);

const fuse = new Fuse(filteredData, fuseOptions);

// Search Function for fuse
const searchFun = (e) => {
if (e.target.value === "") {
setFilteredData(fullData);
Expand All @@ -49,76 +48,106 @@ const CollegePredictor = () => {
setFilteredData(result.map((r) => r.item));
};

const validateInputs = () => {
const { exam, rank } = queryObject;

const newRank = parseInt(rank, 10);
if (isNaN(newRank) || newRank <= 0) {
setRankError("Please enter a valid positive rank.");
return false;
}

const examConfig = examConfigs[exam];
if (!exam || !examConfig) {
setInputError(
"Please select a valid exam and provide all required details."
);
return false;
}

for (const field of examConfig.fields) {
if (!queryObject[field.name]) {
setInputError(`Missing required field: ${field.label}`);
return false;
}
}

setRankError(null);
setInputError(null);
return true;
};

const fetchData = async (query) => {
setIsLoading(true);
setError(null);

try {
const params = new URLSearchParams(Object.entries(query));
const queryString = params.toString();
if (queryString === "") return;
const response = await fetch(`/api/exam-result?${queryString}`);
const data = await response.json();
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
throw new Error(data.error || `HTTP error! status: ${response.status}`);
}
const data = await response.json();
setFilteredData(data);
setFullData(data);
} catch (error) {
console.error("Error fetching data:", error);
setError("Failed to fetch college predictions. Please try again.");
setError(
error.message ||
"Failed to fetch college predictions. Please try again."
);
setFilteredData([]);
} finally {
setIsLoading(false);
}
};

const handleQueryObjectChange = (key) => async (selectedOption) => {
const newQueryObject = {
const handleQueryObjectChange = (key) => (selectedOption) => {
setQueryObject({
...queryObject,
[key]: selectedOption.label,
};
setQueryObject(newQueryObject);
const params = new URLSearchParams(Object.entries(newQueryObject));
const queryString = params.toString();
router.push(`/college_predictor?${queryString}`);
await fetchData(newQueryObject);
});
};

const handleRankChange = async (e) => {
const newQueryObject = {
const handleRankChange = (e) => {
const newRank = parseInt(e.target.value, 10);
setQueryObject({
...queryObject,
rank: e.target.value,
};
setQueryObject(newQueryObject);
const params = new URLSearchParams(Object.entries(newQueryObject));
const queryString = params.toString();
router.push(`/college_predictor?${queryString}`);
await fetchData(newQueryObject);
rank: newRank,
});
};

useEffect(() => {
fetchData(router.query);
}, [router.query]);
const handleSearchClick = () => {
setSubmitted(true); // Mark as submitted
if (validateInputs()) {
setShowResults(true);
fetchData(queryObject);
} else {
setShowResults(false);
}
};

const renderQueryDetails = () => {
const examConfig = examConfigs[router.query.exam];
if (!examConfig) return null;

return (
<div className="flex flex-col justify-center items-start sm:items-center mb-4 gap-2">
<p className="text-sm md:text-base font-semibold">
<p className="text-sm md:text-base font-semibold">
Exam: {router.query.exam}
</p>
{examConfig.fields.map((field) => (
<div className="flex items-center justify-center gap-2">
<label
key={field.name}
className="font-semibold text-sm md:text-base "
>
<div
className="flex items-center justify-center gap-2"
key={field.name}
>
<label className="font-semibold text-sm md:text-base">
{field.label}
</label>
<Dropdown
className="text-sm md:text-base "
className="text-sm md:text-base"
options={field.options.map((option) =>
typeof option === "string"
? { value: option, label: option }
Expand All @@ -130,17 +159,27 @@ const CollegePredictor = () => {
</div>
))}
<div className="flex gap-2 items-center">
<label className="block text-sm md:text-base font-semibold text-gray-700 mb-2 ">
<label
htmlFor="categoryRank"
className="block text-sm md:text-base font-semibold text-gray-700 mb-2"
>
Enter Category Rank
</label>
<input
id="categoryRank"
type="number"
value={queryObject.rank}
value={queryObject.rank || ""}
onChange={handleRankChange}
className="border border-gray-300 rounded text-center"
className="border border-gray-300 rounded text-center p-1"
placeholder="Enter your rank"
min="1"
/>
</div>
{submitted && rankError && (
<div className="text-red-600 text-sm mt-2" role="alert">
{rankError}
</div>
)}
</div>
);
};
Expand All @@ -151,18 +190,41 @@ const CollegePredictor = () => {
<title>College Predictor - Result</title>
</Head>
<div className="flex flex-col items-center p-4">
<div className="flex flex-col items-center justify-center w-full sm:w-5/6 md:w-3/4 bg-white p-6 rounded-lg shadow-lg">
<div className="flex flex-col items-center justify-center w-full sm:w-5/6 md:w-3/4 bg-white p-6 rounded-lg shadow-lg">
<h1 className="text-2xl font-bold mb-4 text-center">
{getConstants().TITLE}
</h1>
{renderQueryDetails()}
<button
onClick={handleSearchClick}
className="bg-blue-500 text-white px-4 py-2 rounded-md mt-4"
>
Search Colleges
</button>
{isLoading ? (
<div className="flex items-center justify-center flex-col mt-2">
<div className="border-t-2 border-transparent border-[#B52326] rounded-full w-8 h-8 animate-spin mb-2"></div>
<div
className="border-t-2 border-transparent border-[#B52326] rounded-full w-8 h-8 animate-spin mb-2"
role="status"
>
<span className="sr-only">Loading...</span>
</div>
<p>Loading your college predictions...</p>
</div>
) : error ? (
<div className="text-red-600 text-center">{error}</div>
<div className="text-red-600 text-center" role="alert">
{error}
</div>
) : submitted && inputError ? (
<div className="text-center text-red-600 mt-4">
<p>{inputError}</p>
</div>
) : !submitted ? (
<div className="text-center text-red-600 mt-4">
<p>
Please enter correct credentials to see college predictions.
</p>
</div>
) : filteredData.length === 0 ? (
<div className="text-center">
<p>No colleges found matching your criteria.</p>
Expand All @@ -171,18 +233,22 @@ const CollegePredictor = () => {
) : (
<>
<div className="mb-4 w-full flex flex-col left justify-center items-center">
<label className="block text-md font-semibold text-gray-700 content-center mx-2">
<label
htmlFor="search"
className="block text-md font-semibold text-gray-700 content-center mx-2"
>
Search: &#128269;
</label>
<input
id="search"
onChange={searchFun}
placeholder="Name / State / Program"
className="border border-gray-300 rounded text-center h-fit p-1 sm:w-5/12 w-3/4"
/>
</div>
<h3 className="text-lg md:text-xl mb-4 text-center font-bold">
<h2 className="text-lg md:text-xl mb-4 text-center font-bold">
Predicted colleges and courses for you:
</h3>
</h2>
<div className="w-full overflow-x-auto">
<PredictedCollegeTables
data={filteredData}
Expand Down
Loading