Added sorting and filtering for schools and loan repayment
This commit is contained in:
parent
ffe8e1fe72
commit
75e967c9f2
@ -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>
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
115
src/components/SchoolFilters
Normal file
115
src/components/SchoolFilters
Normal 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;
|
Loading…
Reference in New Issue
Block a user