Added sorting and filtering for schools and loan repayment

This commit is contained in:
Josh 2025-03-11 17:44:00 +00:00
parent e4d33e42ae
commit 22e7f3bb3f
4 changed files with 220 additions and 48 deletions

View File

@ -7,7 +7,7 @@ function LoanRepayment({
setResults,
setLoading,
}) {
const [expectedSalary, setExpectedSalary] = useState('');
const [expectedSalary, setExpectedSalary] = useState(0);
const [tuitionType, setTuitionType] = useState('inState'); // Tuition type: inState or outOfState
const [interestRate, setInterestRate] = useState(5.5); // Interest rate
const [loanTerm, setLoanTerm] = useState(10); // Loan term in years
@ -86,8 +86,8 @@ function LoanRepayment({
const totalLoanCost = extraMonthlyPayment * monthsWithExtra;
let salary = Number(expectedSalary) || 0;
let netGain = 'N/A';
let monthlySalary = 'N/A';
let netGain = 0;
let monthlySalary = 0;
if (salary > 0) {
const totalSalary = salary * loanTerm;
@ -150,7 +150,8 @@ function LoanRepayment({
<input type="number"
value={currentSalary}
onChange={(e) => setCurrentSalary(e.target.value)}
placeholder="Enter your current salary" />
placeholder="Enter your current salary"
/>
</div>
<div className="input-group">
<label>Expected Salary:</label>

View File

@ -100,40 +100,10 @@
color: #666;
}
/* 🔹 Filter Section Styling */
.filter-section {
background: #f8f8f8;
padding: 10px;
border-radius: 5px;
margin-bottom: 15px;
}
.filter-section h3 {
margin-bottom: 10px;
font-size: 16px;
}
.filter-section label {
display: block;
font-size: 14px;
margin-bottom: 5px;
font-weight: bold;
}
.filter-section input {
width: 100%;
padding: 5px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.filter-section select {
width: 100%;
padding: 5px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
.filter-controls {
display: flex;
align-items: center;
gap: 20px;
}
/* Schools section: Grid layout with clear separation */

View File

@ -1,5 +1,6 @@
import { ClipLoader } from 'react-spinners';
import LoanRepayment from './LoanRepayment.js';
import SchoolFilters from './SchoolFilters';
import './PopoutPanel.css';
import { useState, useEffect } from 'react';
@ -15,21 +16,23 @@ function PopoutPanel({
const [results, setResults] = useState([]); // Store loan repayment calculation results
const [loadingCalculation, setLoadingCalculation] = useState(false);
const [persistedROI, setPersistedROI] = useState({});
const [sortBy, setSortBy] = useState('tuition'); // Default sorting
const [tuitionRange, setTuitionRange] = useState([0, 50000]);
const [distanceRange, setDistanceRange] = useState([0, 200]);
const {
jobDescription = null,
tasks = null,
title = 'Career Details',
economicProjections = {},
economicProjections = {},
salaryData = [],
schools = [],
schools = [],
} = data || {};
useEffect(() => {
setResults([]);
setIsCalculated(false);
}, [schools]);
}, [sortBy, tuitionRange, distanceRange]);
if (!isVisible) return null;
@ -59,6 +62,31 @@ function PopoutPanel({
closePanel(); // Maintain existing close behavior
}
/** 🔹 Apply Sorting & Filtering Directly at Render Time **/
const filteredAndSortedSchools = [...schools]
.filter(school => {
// Convert tuition to a number
const inStateCost = parseFloat(school['In_state cost']);
// Remove " mi" from distance and convert it to a number
const distance = parseFloat(school['distance'].replace(' mi', ''));
console.log(`Filtering School: ${school['INSTNM']} | Tuition: ${inStateCost} | Distance: ${distance}`);
return (
inStateCost >= tuitionRange[0] &&
inStateCost <= tuitionRange[1] &&
distance >= distanceRange[0] &&
distance <= distanceRange[1]
);
})
.sort((a, b) => {
if (sortBy === 'tuition') return a['In_state cost'] - b['In_state cost'];
if (sortBy === 'distance') return a['distance'] - b['distance'];
return 0;
});
return (
<div className="popout-panel">
<button onClick={handleClosePanel}>Close</button>
@ -124,16 +152,74 @@ function PopoutPanel({
)}
</div>
{/* Sorting & Filtering UI */}
<div className="filter-controls">
<div>
<label>Sort by:</label>
<input
type="radio"
name="sort"
value="tuition"
checked={sortBy === 'tuition'}
onChange={() => setSortBy('tuition')}
/> Tuition
<input
type="radio"
name="sort"
value="distance"
checked={sortBy === 'distance'}
onChange={() => setSortBy('distance')}
/> Distance
</div>
<label>Tuition Range: ${tuitionRange[0]} - ${tuitionRange[1]}</label>
<input
type="range"
min="0"
max="50000"
step="500"
value={tuitionRange[0]}
onChange={(e) => setTuitionRange([Number(e.target.value), tuitionRange[1]])}
/>
<input
type="range"
min="0"
max="50000"
step="500"
value={tuitionRange[1]}
onChange={(e) => setTuitionRange([tuitionRange[0], Number(e.target.value)])}
/>
<label>Distance Range: {distanceRange[0]} - {distanceRange[1]} mi</label>
<input
type="range"
min="0"
max="200"
step="5"
value={distanceRange[0]}
onChange={(e) => setDistanceRange([Number(e.target.value), distanceRange[1]])}
/>
<input
type="range"
min="0"
max="200"
step="5"
value={distanceRange[1]}
onChange={(e) => setDistanceRange([distanceRange[0], Number(e.target.value)])}
/>
</div>
{/* Schools Offering Programs Section */}
<h3>Schools Offering Programs</h3>
<div className="schools-offering">
{schools.length > 0 ? (
schools.map((school, index) => (
{filteredAndSortedSchools.length > 0 ? (
filteredAndSortedSchools.map((school, index) => (
<div key={index} className="school-card">
<div><strong>{school['INSTNM']}</strong></div>
<div>Degree Type: {school['CREDDESC'] || 'Degree type information is not available for this program'}</div>
<div>In-State Tuition: ${school['In_state cost'] || 'Tuition information is not available for this school'}</div>
<div>Out-of-State Tuition: ${school['Out_state cost'] || 'Tuition information is not available for this school'}</div>
<div>Degree Type: {school['CREDDESC'] || 'Degree type not available for this program'}</div>
<div>In-State Tuition: ${school['In_state cost'] || 'Tuition not available for this school'}</div>
<div>Out-of-State Tuition: ${school['Out_state cost'] || 'Tuition not available for this school'}</div>
<div>Distance: {school['distance'] || 'Distance to school not available'}</div>
<div>
Website: <a href={school['Website']} target="_blank" rel="noopener noreferrer">{school['Website']}</a>
@ -148,7 +234,7 @@ function PopoutPanel({
{/* Loan Repayment Analysis */}
<h3>Loan Repayment Analysis</h3>
<LoanRepayment
schools={schools.map(school => ({
schools={filteredAndSortedSchools.map(school => ({
schoolName: school['INSTNM'],
inState: parseFloat(school['In_state cost']) || 0,
outOfState: parseFloat(school['Out_state cost']) || 0,

View File

@ -0,0 +1,115 @@
import React, { useState } from 'react';
import './SchoolFilters.css';
function SchoolFilters({ schools, setFilteredSchools }) {
const [sortBy, setSortBy] = useState('tuition'); // Default: Sort by Tuition
const [tuitionRange, setTuitionRange] = useState([0, 50000]); // Example range
const [distanceRange, setDistanceRange] = useState([0, 200]); // Example range
// Sorting function
const sortSchools = (schools) => {
return [...schools].sort((a, b) => {
if (sortBy === 'tuition') return a.inState - b.inState;
if (sortBy === 'distance') return a.distance - b.distance;
return 0;
});
};
// Filtering function
const filterSchools = (schools) => {
return schools.filter((school) =>
school.inState >= tuitionRange[0] &&
school.inState <= tuitionRange[1] &&
school.distance >= distanceRange[0] &&
school.distance <= distanceRange[1]
);
};
// Apply sorting & filtering when values change
const updateFilteredSchools = () => {
let updatedSchools = sortSchools(schools);
updatedSchools = filterSchools(updatedSchools);
setFilteredSchools(updatedSchools);
};
// Effect to update results on change
React.useEffect(() => {
updateFilteredSchools();
}, [sortBy, tuitionRange, distanceRange, schools]);
return (
<div className="filters-container">
<h3>Sort & Filter Schools</h3>
{/* Sorting Options */}
<div className="sorting-options">
<label>Sort by:</label>
<div>
<input
type="radio"
id="sortTuition"
name="sort"
value="tuition"
checked={sortBy === 'tuition'}
onChange={() => setSortBy('tuition')}
/>
<label htmlFor="sortTuition">Tuition</label>
</div>
<div>
<input
type="radio"
id="sortDistance"
name="sort"
value="distance"
checked={sortBy === 'distance'}
onChange={() => setSortBy('distance')}
/>
<label htmlFor="sortDistance">Distance</label>
</div>
</div>
{/* Filtering Options */}
<div className="filtering-options">
<label>Tuition Range: ${tuitionRange[0]} - ${tuitionRange[1]}</label>
<input
type="range"
min="0"
max="50000"
step="500"
value={tuitionRange[0]}
onChange={(e) => setTuitionRange([Number(e.target.value), tuitionRange[1]])}
/>
<input
type="range"
min="0"
max="50000"
step="500"
value={tuitionRange[1]}
onChange={(e) => setTuitionRange([tuitionRange[0], Number(e.target.value)])}
/>
</div>
<div className="filtering-options">
<label>Distance Range: {distanceRange[0]} - {distanceRange[1]} mi</label>
<input
type="range"
min="0"
max="200"
step="5"
value={distanceRange[0]}
onChange={(e) => setDistanceRange([Number(e.target.value), distanceRange[1]])}
/>
<input
type="range"
min="0"
max="200"
step="5"
value={distanceRange[1]}
onChange={(e) => setDistanceRange([distanceRange[0], Number(e.target.value)])}
/>
</div>
</div>
);
}
export default SchoolFilters;