diff --git a/.env.development b/.env.development index 9ca6be9..8258bfa 100755 --- a/.env.development +++ b/.env.development @@ -2,8 +2,10 @@ ONET_USERNAME=aptivaai ONET_PASSWORD=2296ahq REACT_APP_BLS_API_KEY=80d7a65a809a43f3a306a41ec874d231 +GOOGLE_MAPS_API_KEY=AIzaSyCTMgjiHUF2Vl3QriQu2kDEuZWz39ZAR20 REACT_APP_GOOGLE_MAPS_API_KEY=AIzaSyCTMgjiHUF2Vl3QriQu2kDEuZWz39ZAR20 COLLEGE_SCORECARD_KEY = BlZ0tIdmXVGI4G8NxJ9e6dXEiGUfAfnQJyw8bumj -REACT_APP_API_URL=http://localhost:5001 -REACT_APP_ENV=development \ No newline at end of file +REACT_APP_API_URL=https://dev1.aptivaai.com/api +REACT_APP_ENV=production +REACT_APP_OPENAI_API_KEY=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 \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 261c17f..cad17c6 100755 --- a/backend/server.js +++ b/backend/server.js @@ -30,7 +30,7 @@ console.log('Current Working Directory:', process.cwd()); const app = express(); const PORT = 5000; -const allowedOrigins = ['http://localhost:3000', 'http://34.16.120.118:3000', 'https://dev.aptivaai.com']; +const allowedOrigins = ['http://localhost:3000', 'http://34.16.120.118:3000', 'https://dev1.aptivaai.com']; app.disable('x-powered-by'); app.use(bodyParser.json()); @@ -170,7 +170,7 @@ app.post('/api/login', (req, res) => { return res.status(401).json({ error: 'Invalid username or password' }); } - // Generate JWT + const { user_id } = row; // This gets the correct user_id from the row object const token = jwt.sign({ userId: row.user_id }, SECRET_KEY, { expiresIn: '2h' }); res.status(200).json({ token }); }); @@ -184,33 +184,44 @@ app.post('/api/signin', async (req, res) => { return res.status(400).json({ error: 'Both username and password are required' }); } - const query = `SELECT hashed_password, user_id FROM user_auth WHERE username = ?`; + const query = `SELECT user_auth.user_id, user_auth.hashed_password, user_profile.zipcode + FROM user_auth + LEFT JOIN user_profile ON user_auth.user_id = user_profile.user_id + WHERE user_auth.username = ?`; + db.get(query, [username], async (err, row) => { if (err) { console.error('Error querying user_auth:', err.message); return res.status(500).json({ error: 'Failed to query user authentication data' }); } + console.log('Row data:', row); // Log the result of the query + if (!row) { - return res.status(401).json({ error: 'Invalid username or password' }); // User not found + return res.status(401).json({ error: 'Invalid username or password' }); } - try { - const isMatch = await bcrypt.compare(password, row.hashed_password); - - if (isMatch) { - const token = jwt.sign({ userId: row.user_id }, SECRET_KEY, { expiresIn: '1h' }); - res.status(200).json({ message: 'Login successful', token, userId: row.user_id }); - } else { - res.status(401).json({ error: 'Invalid username or password' }); - } - } catch (error) { - console.error('Error comparing passwords:', error.message); - res.status(500).json({ error: 'Failed to compare passwords' }); + // Verify password + const isMatch = await bcrypt.compare(password, row.hashed_password); + if (!isMatch) { + return res.status(401).json({ error: 'Invalid username or password' }); } + + // Ensure that you're using the correct user_id + const { user_id, zipcode } = row; + + console.log('UserID:', user_id); + console.log('ZIP Code:', zipcode); // Log the ZIP code to ensure it's correct + + // Send correct token with user_id + const token = jwt.sign({ userId: user_id }, SECRET_KEY, { expiresIn: '2h' }); + + // You can optionally return the ZIP code or any other data as part of the response + res.status(200).json({ message: 'Login successful', token, userId: user_id, zipcode }); }); }); + // Route to fetch user profile app.get('/api/user-profile', (req, res) => { const token = req.headers.authorization?.split(' ')[1]; diff --git a/backend/server2.js b/backend/server2.js index 62118f6..cda9129 100755 --- a/backend/server2.js +++ b/backend/server2.js @@ -11,22 +11,25 @@ import sqlite3 from 'sqlite3'; import fs from 'fs'; import readline from 'readline'; +// Correct the order of variable initialization const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const rootPath = path.resolve(__dirname, '..'); // Go one level up to the root folder - const env = process.env.NODE_ENV?.trim() || 'development'; // Default to 'development' const envPath = path.resolve(rootPath, `.env.${env}`); // Use root directory for .env files -dotenv.config({ path: envPath }); +// Load environment variables as soon as the server starts +dotenv.config({ path: envPath }); // Ensure this is called at the very top to load all environment variables + +// Logging environment variables for debugging console.log(`Loaded environment variables from: ${envPath}`); console.log('ONET_USERNAME:', process.env.ONET_USERNAME); console.log('ONET_PASSWORD:', process.env.ONET_PASSWORD); -console.log('Google Maps API Key:', process.env.GOOGLE_MAPS_API_KEY) +console.log('Google Maps API Key:', process.env.GOOGLE_MAPS_API_KEY); -const allowedOrigins = ['http://localhost:3000', 'http://34.16.120.118:3000', 'https://dev.aptivaai.com']; +const allowedOrigins = ['http://localhost:3000', 'http://34.16.120.118:3000', 'https://dev1.aptivaai.com']; const mappingFilePath = '/home/jcoakley/aptiva-dev1-app/public/CIP_to_ONET_SOC.xlsx'; const institutionFilePath = path.resolve(rootPath, 'public/Institution_data.json'); @@ -177,11 +180,16 @@ app.get('/api/onet/questions', async (req, res) => { }); // Helper function to geocode an address or zip code -// Function to geocode a ZIP code const geocodeZipCode = async (zipCode) => { + const apiKey = process.env.GOOGLE_MAPS_API_KEY; + if (!apiKey) { + console.error('Google Maps API Key is missing.'); + } else { + console.log('Google Maps API Key loaded:', apiKey); + } try { - const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(userZipcode)}&key=AIzaSyCTMgjiHUF2Vl3QriQu2kDEuZWz39ZAR20`; - console.log('Constructed Geocode URL:', geocodeUrl); // Check if encoding helps + const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(zipCode)}&components=country:US&key=${apiKey}`; + console.log('Constructed Geocode URL:', geocodeUrl); // Log the geocoding URL for debugging const response = await axios.get(geocodeUrl); @@ -197,10 +205,12 @@ const geocodeZipCode = async (zipCode) => { } }; + app.post('/api/maps/distance', async (req, res) => { const { userZipcode, destinations } = req.body; if (!userZipcode || !destinations) { + console.error('Missing required parameters:', { userZipcode, destinations }); return res.status(400).json({ error: 'User ZIP code and destination address are required.' }); } @@ -208,56 +218,33 @@ app.post('/api/maps/distance', async (req, res) => { const googleMapsApiKey = process.env.GOOGLE_MAPS_API_KEY; // Get the API key // Geocode the user's ZIP code - const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${userZipcode}&components=country:US&key=${googleMapsApiKey}`; - const geocodeResponse = await axios.get(geocodeUrl); - - if (geocodeResponse.data.status === 'OK' && geocodeResponse.data.results.length > 0) { - const userLocation = geocodeResponse.data.results[0].geometry.location; // Get lat, lng - const origins = `${userLocation.lat},${userLocation.lng}`; // User's location as lat/lng - - // Call the Distance Matrix API using the geocoded user location and school address - const distanceUrl = `https://maps.googleapis.com/maps/api/distancematrix/json?origins=${origins}&destinations=${encodeURIComponent(destinations)}&units=imperial&key=${googleMapsApiKey}`; - const distanceResponse = await axios.get(distanceUrl); - - if (distanceResponse.data.status !== 'OK') { - return res.status(500).json({ error: 'Error fetching distance from Google Maps API' }); - } - - const { distance, duration } = distanceResponse.data.rows[0].elements[0]; - res.json({ distance: distance.text, duration: duration.text }); - - } else { + const userLocation = await geocodeZipCode(userZipcode); + if (!userLocation) { return res.status(400).json({ error: 'Unable to geocode user ZIP code.' }); } + const origins = `${userLocation.lat},${userLocation.lng}`; // User's location as lat/lng + + console.log('Request Payload:', { userZipcode, destinations }); // Log the parameters + + // Call the Distance Matrix API using the geocoded user location and school address + const distanceUrl = `https://maps.googleapis.com/maps/api/distancematrix/json?origins=${origins}&destinations=${encodeURIComponent(destinations)}&units=imperial&key=${googleMapsApiKey}`; + const distanceResponse = await axios.get(distanceUrl); + console.log('Distance API Request URL:', distanceUrl); // Log the final request URL + + if (distanceResponse.data.status !== 'OK') { + return res.status(500).json({ error: 'Error fetching distance from Google Maps API' }); + } + + const { distance, duration } = distanceResponse.data.rows[0].elements[0]; + res.json({ distance: distance.text, duration: duration.text }); + } catch (error) { console.error('Error during distance calculation:', error); res.status(500).json({ error: 'Internal server error', details: error.message }); } }); -// Route to fetch user profile by ID including ZIP code -app.get('/api/user-profile/:id', (req, res) => { - const { id } = req.params; - - if (!id) { - return res.status(400).json({ error: 'Profile ID is required' }); - } - - const query = `SELECT area, zipcode FROM user_profile WHERE id = ?`; - db.get(query, [id], (err, row) => { - if (err) { - console.error('Error fetching user profile:', err.message); - return res.status(500).json({ error: 'Failed to fetch user profile' }); - } - - if (!row) { - return res.status(404).json({ error: 'Profile not found' }); - } - - res.json({ area: row.area, zipcode: row.zipcode }); - }); -}); // Load the economic projections data from the Excel file const projectionsFilePath = path.resolve(__dirname, '..', 'public', 'occprj.xlsx'); // Adjusted path @@ -318,7 +305,7 @@ app.post('/api/onet/submit_answers', async (req, res) => { try { // URLs for career suggestions and RIASEC scores - const careerUrl = `https://services.onetcenter.org/ws/mnm/interestprofiler/careers?answers=${answers}`; + const careerUrl = `https://services.onetcenter.org/ws/mnm/interestprofiler/careers?answers=${answers}&start=1&end=1000`; const resultsUrl = `https://services.onetcenter.org/ws/mnm/interestprofiler/results?answers=${answers}`; // Fetch career suggestions @@ -333,6 +320,8 @@ app.post('/api/onet/submit_answers', async (req, res) => { } }); + console.log('Career API Response:', JSON.stringify(careerResponse.data, null, 2)); + // Fetch RIASEC scores console.log('Fetching RIASEC scores from:', resultsUrl); const resultsResponse = await axios.get(resultsUrl, { @@ -400,7 +389,51 @@ app.get('/api/onet/career-details/:socCode', async (req, res) => { } }); +// Endpoint to fetch career description and tasks from O*Net +app.get('/api/onet/career-description/:socCode', async (req, res) => { + const { socCode } = req.params; + if (!socCode) { + return res.status(400).json({ error: 'SOC Code is required.' }); + } + + try { + // Fetch career details using the O*Net API for the given SOC code + const response = await axios.get( + `https://services.onetcenter.org/ws/mnm/careers/${socCode}`, + { + auth: { + username: process.env.ONET_USERNAME, + password: process.env.ONET_PASSWORD, + }, + headers: { + 'Accept': 'application/json', + }, + } + ); + + // Check if the response contains the necessary data + if (response.data && response.data.title) { + const { title, what_they_do, on_the_job } = response.data; + + // Extract the tasks from the 'on_the_job' field + const tasks = on_the_job?.task || []; + + // Prepare the data for the frontend + const careerOverview = { + description: what_they_do || 'No description available', + tasks: tasks.length ? tasks : ['No tasks available'], + }; + + res.status(200).json(careerOverview); + } else { + res.status(404).json({ error: 'Career not found for the provided SOC code.' }); + } + } catch (error) { + console.error('Error fetching career description and tasks:', error.message); + res.status(500).json({ error: 'Failed to fetch career description and tasks from O*Net.' }); + } +}); // Route to handle fetching CIP code based on SOC code app.get('/api/cip/:socCode', (req, res) => { @@ -560,6 +593,59 @@ app.get('/api/salary', async (req, res) => { } }); +// Route to fetch job zones and check for missing salary data +app.post('/api/job-zones', async (req, res) => { + const { socCodes } = req.body; + + if (!socCodes || !Array.isArray(socCodes) || socCodes.length === 0) { + return res.status(400).json({ error: "SOC Codes are required." }); + } + + try { + // Ensure SOC codes are formatted correctly (no decimals) + const formattedSocCodes = socCodes.map(code => { + let cleanedCode = code.trim().replace(/\./g, ""); // Remove periods + if (!cleanedCode.includes("-") && cleanedCode.length === 6) { + cleanedCode = cleanedCode.slice(0, 2) + "-" + cleanedCode.slice(2, 6); + } + return cleanedCode.slice(0, 7); // Keep first 7 characters + }); + + const placeholders = formattedSocCodes.map(() => "?").join(","); + const query = ` + SELECT OCC_CODE, JOB_ZONE, + A_MEDIAN, A_PCT10, A_PCT25, A_PCT75 + FROM salary_data + WHERE OCC_CODE IN (${placeholders}) +`; + +const rows = await db.all(query, formattedSocCodes); + +// Log what is being retrieved from the database +console.log("Salary Data Query Results:", rows); + +// Now process `limited_data` flag +const jobZoneMapping = rows.reduce((acc, row) => { + // Convert empty fields or NULL to a falsy value + const isMissingData = [row.A_MEDIAN, row.A_PCT10, row.A_PCT25, row.A_PCT75] + .some(value => value === null || value === '' || value === '#' || value === '*'); + + acc[row.OCC_CODE] = { + job_zone: row.JOB_ZONE, + limited_data: isMissingData ? 1 : 0 // Set limited_data flag correctly + }; + + return acc; +}, {}); + +console.log("Job Zone & Limited Data Mapping:", jobZoneMapping); +res.json(jobZoneMapping); + + } catch (error) { + console.error("Error fetching job zones:", error); + res.status(500).json({ error: "Failed to fetch job zones." }); + } +}); // Route to fetch user profile by ID diff --git a/backend/user_profile b/backend/user_profile new file mode 100644 index 0000000..e69de29 diff --git a/backend/user_profile.db b/backend/user_profile.db index 743c40b..0f2a2f2 100644 Binary files a/backend/user_profile.db and b/backend/user_profile.db differ diff --git a/package-lock.json b/package-lock.json index 1c70feb..8b695bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,28 +70,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -107,9 +107,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz", - "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.26.8.tgz", + "integrity": "sha512-3tBctaHRW6xSub26z7n8uyOTwwUsCdvIug/oxBH9n6yCO5hMj2vwDJAo7RbBMKrM7P+W2j61zLKviJQFGOYKMg==", "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", @@ -132,12 +132,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -158,11 +158,11 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -173,16 +173,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "engines": { @@ -275,9 +275,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "engines": { "node": ">=6.9.0" } @@ -299,13 +299,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -364,23 +364,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -565,7 +565,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", - "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-create-class-features-plugin": "^7.21.0", @@ -856,13 +855,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -888,11 +887,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1080,12 +1079,12 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz", - "integrity": "sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.26.5.tgz", + "integrity": "sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-flow": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-flow": "^7.26.0" }, "engines": { "node": ">=6.9.0" @@ -1095,11 +1094,11 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -1273,11 +1272,11 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1539,12 +1538,12 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", - "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.9.tgz", + "integrity": "sha512-Jf+8y9wXQbbxvVYTM8gO5oEF2POdNji0NMltEkG7FtmzD9PVz7/lxpqSdTvwsjTMU5HIHuDVNf2SOxLkWi+wPQ==", "dependencies": { "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", @@ -1557,6 +1556,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", @@ -1601,11 +1612,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1615,11 +1626,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1629,13 +1640,13 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz", - "integrity": "sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz", + "integrity": "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9" }, @@ -1706,13 +1717,13 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1724,9 +1735,9 @@ "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1737,21 +1748,21 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -1767,17 +1778,17 @@ "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -1849,9 +1860,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1860,28 +1871,28 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1890,9 +1901,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2253,17 +2264,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2787,9 +2787,9 @@ } }, "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -2868,9 +2868,9 @@ } }, "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "optional": true, "bin": { "semver": "bin/semver.js" @@ -3029,9 +3029,9 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==" }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", - "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==" + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz", + "integrity": "sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==" }, "node_modules/@sinclair/typebox": { "version": "0.24.51", @@ -3399,9 +3399,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz", - "integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3439,9 +3439,9 @@ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/http-proxy": { - "version": "1.17.15", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", - "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "version": "1.17.16", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", "dependencies": { "@types/node": "*" } @@ -3483,9 +3483,9 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "version": "22.13.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz", + "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==", "dependencies": { "undici-types": "~6.20.0" } @@ -3514,9 +3514,9 @@ "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==" }, "node_modules/@types/qs": { - "version": "6.9.17", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==" + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -3587,9 +3587,9 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "node_modules/@types/ws": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", - "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", "dependencies": { "@types/node": "*" } @@ -3641,9 +3641,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -3776,9 +3776,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -3832,9 +3832,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -3859,9 +3859,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", - "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", @@ -4122,9 +4122,9 @@ } }, "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "optional": true, "dependencies": { "humanize-ms": "^1.2.1" @@ -4508,6 +4508,14 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4580,9 +4588,9 @@ } }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz", + "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -4718,12 +4726,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4786,9 +4794,9 @@ } }, "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.1.0.tgz", + "integrity": "sha512-f9B1xMdnkCIqe+2dHrJsoQFRz7reChaAHE/65SdaykPklQqhme2WaC08oD3is77x9ff98/9EazAKFDZv5rFEQg==", "dependencies": { "@babel/core": "^7.16.0", "@babel/plugin-proposal-class-properties": "^7.16.0", @@ -4797,6 +4805,7 @@ "@babel/plugin-proposal-numeric-separator": "^7.16.0", "@babel/plugin-proposal-optional-chaining": "^7.16.0", "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", "@babel/plugin-transform-flow-strip-types": "^7.16.0", "@babel/plugin-transform-react-display-name": "^7.16.0", "@babel/plugin-transform-runtime": "^7.16.4", @@ -4994,9 +5003,9 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -5149,9 +5158,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -5223,9 +5232,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001701", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz", + "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==", "funding": [ { "type": "opencollective", @@ -5285,9 +5294,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz", - "integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==", + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -5365,9 +5374,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, "node_modules/clean-css": { "version": "5.3.3", @@ -5584,9 +5593,9 @@ } }, "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", @@ -5674,9 +5683,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", + "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5684,11 +5693,11 @@ } }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -5696,9 +5705,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.39.0.tgz", - "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.41.0.tgz", + "integrity": "sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5857,9 +5866,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -6240,9 +6249,9 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" }, "node_modules/decompress-response": { "version": "6.0.0", @@ -6621,9 +6630,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.76", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", - "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==" + "version": "1.5.109", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.109.tgz", + "integrity": "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==" }, "node_modules/emittery": { "version": "0.8.1", @@ -6675,9 +6684,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -6726,9 +6735,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.8", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.8.tgz", - "integrity": "sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", @@ -6741,10 +6750,11 @@ "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.6", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -6765,11 +6775,12 @@ "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", - "own-keys": "^1.0.0", + "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -6840,9 +6851,9 @@ "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==" }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { "es-errors": "^1.3.0" }, @@ -6851,24 +6862,28 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -7184,9 +7199,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", - "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==", + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -7657,15 +7672,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -7693,14 +7708,24 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==" + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dependencies": { "reusify": "^1.0.4" } @@ -7894,9 +7919,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==" }, "node_modules/follow-redirects": { "version": "1.15.9", @@ -7918,19 +7943,25 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -8036,9 +8067,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -8055,12 +8086,13 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -8227,20 +8259,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", - "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "dunder-proto": "^1.0.0", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "math-intrinsics": "^1.0.0" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8262,6 +8294,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -8731,9 +8775,9 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==" }, "node_modules/http-proxy": { "version": "1.18.1", @@ -8888,9 +8932,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -9033,11 +9077,15 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9072,11 +9120,11 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -9195,11 +9243,14 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9427,11 +9478,11 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9527,9 +9578,9 @@ } }, "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -9571,15 +9622,15 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", - "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", "has-symbols": "^1.1.0", - "reflect.getprototypeof": "^1.0.8", "set-function-name": "^2.0.2" }, "engines": { @@ -9758,17 +9809,6 @@ } } }, - "node_modules/jest-config/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -10124,9 +10164,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -10561,13 +10601,14 @@ } }, "node_modules/jsdom/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.3.tgz", + "integrity": "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -10707,9 +10748,9 @@ } }, "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -10807,9 +10848,9 @@ } }, "node_modules/launch-editor": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", - "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", + "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -11371,9 +11412,9 @@ } }, "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -11408,9 +11449,9 @@ } }, "node_modules/node-abi": { - "version": "3.71.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", - "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "version": "3.74.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", + "integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", "dependencies": { "semver": "^7.3.5" }, @@ -11419,9 +11460,9 @@ } }, "node_modules/node-abi/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -11536,9 +11577,9 @@ } }, "node_modules/node-gyp/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "optional": true, "bin": { "semver": "bin/semver.js" @@ -11633,9 +11674,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", - "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==" + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.18.tgz", + "integrity": "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -11654,9 +11695,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "engines": { "node": ">= 0.4" }, @@ -11850,9 +11891,9 @@ } }, "node_modules/own-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.0.tgz", - "integrity": "sha512-HcuIjzpjrUbqZPGzWHVg95Bc2Y37KoY5n66QQyEGMzrIWVKHsgHcv8/Aq5Cu3qFUQJzMSPVP8MD3oaFoaME1lg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -12169,17 +12210,17 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -12195,7 +12236,7 @@ } ], "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -12678,9 +12719,9 @@ } }, "node_modules/postcss-load-config/node_modules/yaml": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "bin": { "yaml": "bin.mjs" }, @@ -12710,9 +12751,9 @@ } }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -12862,9 +12903,9 @@ } }, "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12888,9 +12929,9 @@ } }, "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -13417,16 +13458,16 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", @@ -13716,6 +13757,14 @@ "rc": "cli.js" } }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -13749,12 +13798,12 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/react-chartjs-2": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", - "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz", + "integrity": "sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==", "peerDependencies": { "chart.js": "^4.1.1", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/react-dev-utils": { @@ -13869,9 +13918,9 @@ } }, "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0.tgz", + "integrity": "sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==" }, "node_modules/react-is": { "version": "17.0.2", @@ -13887,9 +13936,9 @@ } }, "node_modules/react-router": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", - "integrity": "sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.2.0.tgz", + "integrity": "sha512-fXyqzPgCPZbqhrk7k3hPcCpYIlQ2ugIXDboHUzhJISFVy2DEPsmHgN588MyGmkIOv3jDgNfUE3kJi83L28s/LQ==", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^1.0.1", @@ -13910,11 +13959,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.1.tgz", - "integrity": "sha512-vSrQHWlJ5DCfyrhgo0k6zViOe9ToK8uT5XGSmnuC2R3/g261IdIMpZVqfjD6vWSXdnf5Czs4VA/V60oVR6/jnA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.2.0.tgz", + "integrity": "sha512-cU7lTxETGtQRQbafJubvZKHEn5izNABxZhBY0Jlzdv0gqQhCPQt2J8aN5ZPjS6mQOXn5NnirWNh+FpE8TTYN0Q==", "dependencies": { - "react-router": "7.1.1" + "react-router": "7.2.0" }, "engines": { "node": ">=20.0.0" @@ -14005,9 +14054,9 @@ } }, "node_modules/react-scripts/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -14068,17 +14117,17 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", - "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "dunder-proto": "^1.0.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" }, "engines": { @@ -14118,18 +14167,20 @@ } }, "node_modules/regex-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", - "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", + "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { @@ -14340,9 +14391,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -14824,6 +14875,19 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -15009,9 +15073,9 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "optional": true, "dependencies": { "ip-address": "^9.0.5", @@ -15544,11 +15608,14 @@ } }, "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/style-loader": { @@ -15930,9 +15997,9 @@ } }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", + "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -16025,9 +16092,9 @@ } }, "node_modules/terser": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", - "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -16042,9 +16109,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", - "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.12.tgz", + "integrity": "sha512-jDLYqo7oF8tJIttjXO6jBY5Hk8p3A8W4ttih7cCEq64fQFWmgJ4VqAQjKr7WwIDlmXKEc6QeoRb5ecjZ+2afcg==", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -16378,16 +16445,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -16513,9 +16580,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -16532,7 +16599,7 @@ ], "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -16686,9 +16753,9 @@ } }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -16708,9 +16775,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", + "schema-utils": "^4.3.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", + "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, @@ -16811,9 +16878,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "engines": { "node": ">=10.0.0" }, @@ -16893,23 +16960,6 @@ "node": ">=4.0" } }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", diff --git a/salary_info.db b/salary_info.db index 7c94a9c..314397e 100644 Binary files a/salary_info.db and b/salary_info.db differ diff --git a/src/components/CareerSuggestions.js b/src/components/CareerSuggestions.js index 8333a13..a20be08 100644 --- a/src/components/CareerSuggestions.js +++ b/src/components/CareerSuggestions.js @@ -10,18 +10,20 @@ export function CareerSuggestions({ careerSuggestions = [], onCareerClick }) {

Career Suggestions

- {careerSuggestions.map((career) => ( - - ))} + {careerSuggestions.map((career) => { + return ( + + ); + })}
- ); - }; - - export default CareerSuggestions; \ No newline at end of file + ); +} + +export default CareerSuggestions; \ No newline at end of file diff --git a/src/components/Chatbot.css b/src/components/Chatbot.css new file mode 100644 index 0000000..bfd349d --- /dev/null +++ b/src/components/Chatbot.css @@ -0,0 +1,111 @@ +/* Chatbot Container */ +.chatbot-container { + background-color: #ffffff; /* Solid white background */ + border: 1px solid #ccc; /* Light gray border */ + border-radius: 8px; /* Rounded corners */ + padding: 15px; /* Inner padding */ + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */ + width: 350px; /* Chatbot width */ + position: fixed; /* Floating position */ + bottom: 20px; /* Distance from bottom */ + right: 20px; /* Distance from right */ + z-index: 1000; /* Ensure it appears on top */ + font-family: Arial, sans-serif; /* Font for consistency */ + } + + /* Chat Messages */ + .chat-messages { + max-height: 300px; /* Limit height for scrolling */ + overflow-y: auto; /* Enable vertical scrolling */ + margin-bottom: 10px; /* Space below the messages */ + padding-right: 10px; /* Prevent text from touching the edge */ + } + + /* Individual Message */ + .message { + margin: 5px 0; /* Spacing between messages */ + padding: 8px 10px; /* Inner padding for readability */ + border-radius: 6px; /* Rounded message boxes */ + font-size: 14px; /* Readable font size */ + line-height: 1.4; /* Comfortable line spacing */ + } + + /* User Message */ + .message.user { + align-self: flex-end; /* Align user messages to the right */ + background-color: #007bff; /* Blue background for user */ + color: #ffffff; /* White text for contrast */ + } + + /* Bot Message */ + .message.bot { + align-self: flex-start; /* Align bot messages to the left */ + background-color: #f1f1f1; /* Light gray background for bot */ + color: #333333; /* Dark text for readability */ + } + + /* Loading Indicator */ + .message.bot.typing { + font-style: italic; /* Italic text to indicate typing */ + color: #666666; /* Subtle color */ + } + + /* Chat Input Form */ + .chat-input-form { + display: flex; /* Arrange input and button side by side */ + gap: 5px; /* Space between input and button */ + align-items: center; /* Align input and button vertically */ + } + + /* Input Field */ + .chat-input-form input { + flex: 1; /* Take up remaining space */ + padding: 10px; /* Padding inside input */ + border: 1px solid #ccc; /* Light gray border */ + border-radius: 5px; /* Rounded corners */ + font-size: 14px; /* Font size */ + } + + /* Input Focus */ + .chat-input-form input:focus { + outline: none; /* Remove blue outline */ + border-color: #007bff; /* Blue border on focus */ + box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); /* Glow effect */ + } + + /* Send Button */ + .chat-input-form button { + background-color: #007bff; /* Blue background */ + color: #ffffff; /* White text */ + border: none; /* No border */ + padding: 10px 15px; /* Padding inside button */ + border-radius: 5px; /* Rounded corners */ + cursor: pointer; /* Pointer cursor on hover */ + font-size: 14px; /* Font size */ + } + + /* Send Button Hover */ + .chat-input-form button:hover { + background-color: #0056b3; /* Darker blue on hover */ + } + + /* Send Button Disabled */ + .chat-input-form button:disabled { + background-color: #cccccc; /* Gray background when disabled */ + cursor: not-allowed; /* Indicate disabled state */ + } + + /* Scrollbar Styling for Chat Messages */ + .chat-messages::-webkit-scrollbar { + width: 8px; /* Width of scrollbar */ + } + + .chat-messages::-webkit-scrollbar-thumb { + background: #cccccc; /* Gray scrollbar thumb */ + border-radius: 4px; /* Rounded scrollbar */ + } + + .chat-messages::-webkit-scrollbar-thumb:hover { + background: #aaaaaa; /* Darker gray on hover */ + } + \ No newline at end of file diff --git a/src/components/Chatbot.js b/src/components/Chatbot.js new file mode 100644 index 0000000..967df59 --- /dev/null +++ b/src/components/Chatbot.js @@ -0,0 +1,115 @@ +import React, { useState } from "react"; +import axios from "axios"; +import "./Chatbot.css"; + +const Chatbot = ({ context }) => { + const [messages, setMessages] = useState([ + { + role: "assistant", + content: + "Hi! I’m here to help you with career suggestions, ROI analysis, and any questions you have about your career. How can I assist you today?", + }, + ]); + const [input, setInput] = useState(""); + const [loading, setLoading] = useState(false); + + const sendMessage = async (content) => { + const userMessage = { role: "user", content }; + + // Ensure career data is injected into every API call + const contextSummary = ` + You are an advanced AI career advisor for AptivaAI. + Your role is to not only provide career suggestions but to analyze them based on salary potential, job stability, education costs, and market trends. + + Use the following user-specific data: + - Career Suggestions: ${context.careerSuggestions.map((c) => c.title).join(", ") || "No suggestions available."} + - Selected Career: ${context.selectedCareer?.title || "None"} + - Schools: ${context.schools.map((s) => s["INSTNM"]).join(", ") || "No schools available."} + - Median Salary: ${ + context.salaryData.find((s) => s.percentile === "Median")?.value || "Unavailable" + } + - ROI (Return on Investment): If available, use education costs vs. salary potential to guide users. + + **Your response should ALWAYS provide analysis, not just list careers.** + Example responses: + - "If you're looking for a high salary right away, X might be a great option, but it has slow growth." + - "If you prefer job stability, Y is projected to grow in demand over the next 10 years." + - "If work-life balance is a priority, avoid Z as it has high stress and irregular hours." + + If the user asks about "the best career," do not assume a single best choice. Instead, explain trade-offs like: + - "If you want high pay now, X is great, but it has limited upward growth." + - "If you prefer stability, Y is a better long-term bet." +`; + + + const messagesToSend = [ + { role: "system", content: contextSummary }, // Inject AptivaAI data on every request + ...messages, + userMessage, + ]; + + try { + setLoading(true); + const response = await axios.post( + "https://api.openai.com/v1/chat/completions", + { + model: "gpt-3.5-turbo", + messages: messagesToSend, + temperature: 0.7, + }, + { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.REACT_APP_OPENAI_API_KEY}`, + }, + } + ); + + const botMessage = response.data.choices[0].message; + setMessages([...messages, userMessage, botMessage]); + } catch (error) { + console.error("Chatbot Error:", error); + setMessages([ + ...messages, + userMessage, + { role: "assistant", content: "Error: Unable to fetch response. Please try again." }, + ]); + } finally { + setLoading(false); + setInput(""); + } + }; + + const handleSubmit = (e) => { + e.preventDefault(); + if (input.trim()) { + sendMessage(input.trim()); + } + }; + + return ( +
+
+ {messages.map((msg, index) => ( +
+ {msg.content} +
+ ))} + {loading &&
Typing...
} +
+
+ setInput(e.target.value)} + /> + +
+
+ ); +}; + +export default Chatbot; \ No newline at end of file diff --git a/src/components/Dashboard.css b/src/components/Dashboard.css index 2bb7dc1..965e24b 100644 --- a/src/components/Dashboard.css +++ b/src/components/Dashboard.css @@ -1,17 +1,24 @@ /* Dashboard.css */ /* Main Dashboard Layout */ + .dashboard { - display: grid; - grid-template-columns: 1fr 2fr; /* Two columns: careers on the left, RIASEC on the right */ - gap: 20px; - min-height: 100vh; /* Full height */ + min-height: 100vh; padding: 20px; background-color: #f4f7fa; } +/* Main Content Layout: Career Suggestions + RIASEC Scores */ .dashboard-content { - flex-grow: 1; /* Push acknowledgment to the bottom */ + display: flex; + flex-direction: row; + flex-wrap: nowrap; /* This allows the elements to wrap when screen size is small */ + gap: 20px; + align-items: flex-start; + width: 100%; + max-width: 100%; + flex-grow: 1; + min-height: auto; /* Prevents extra spacing */ } /* Sections in Dashboard */ @@ -24,6 +31,12 @@ border-left: 4px solid #6a9fb5; } +.career-button.disabled { + background-color: #ccc; + color: #666; + cursor: not-allowed; +} + .dashboard section:hover { transform: translateY(-3px); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); @@ -38,42 +51,104 @@ h2 { padding-bottom: 4px; } -/* Career Suggestions Grid */ +/* Career Suggestions Section */ .career-suggestions-container { - display: flex; - flex-direction: column; - gap: 10px; + flex: 1.5; /* Ensures it takes the majority of space */ + width: 60%; + max-width: 75%; + background-color: #ffffff; + padding: 15px; + border-radius: 8px; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1); } +/* Career Suggestions Grid */ .career-suggestions-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); /* Flexible grid */ - gap: 10px; /* Even spacing */ + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 10px; padding: 10px; + width: 100%; + justify-content: start; } +/* Career Buttons */ .career-button { - padding: 10px; + padding: 8px 10px; + font-size: 14px; + font-weight: bold; background-color: #007bff; color: white; border: none; - border-radius: 4px; + border-radius: 6px; cursor: pointer; text-align: center; - white-space: normal; /* Allow wrapping */ + white-space: normal; + border-radius: 3px; /* Less rounded */ +} + +.career-button.warning { + border: 2px solid black; /* Example warning border */ + background-color: #f8d7da; /* Example background color */ +} + +/* Warning Icon */ +.warning-icon { + margin-left: 6px; + font-size: 14px; + color: yellow; /* Yellow to indicate limited data */ } .career-button:hover { background-color: #0056b3; } -/* RIASEC Scores and Descriptions */ + +/* RIASEC Section */ .riasec-container { - display: flex; - flex-direction: column; - gap: 20px; + flex: 1; + max-width: 400px; /* Ensure it stays visible */ + min-width: 350px; + position: sticky; + top: 20px; + background-color: #ffffff; + padding: 12px; + border-radius: 8px; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1); } +/* Filter Container */ +.filter-container { + width: 100%; + max-width: 900px; /* Ensures alignment */ + display: flex; + align-items: center; + justify-content: flex-start; + background: #ffffff; + padding: 10px 15px; + border-radius: 8px; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); + margin-bottom: 10px; +} + +/* Style Dropdown */ +.filter-container label { + font-size: 14px; + font-weight: 600; + color: #333; + margin-right: 10px; +} + +.filter-container select { + padding: 8px; + font-size: 14px; + border: 1px solid #ccc; + border-radius: 4px; + background-color: #fff; + cursor: pointer; +} + +/* RIASEC Scores */ .riasec-scores { padding: 15px; border-radius: 8px; @@ -102,15 +177,19 @@ h2 { color: #007bff; } -/* Data Source Acknowledgment */ +/* Acknowledgment Section - Move to Bottom */ .data-source-acknowledgment { - grid-column: span 2; /* Make acknowledgment span both columns */ - margin-top: 20px; - padding: 10px; - border-top: 1px solid #ccc; + width: 100%; + text-align: center; font-size: 12px; color: #666; - text-align: center; + padding: 10px; + border-top: 1px solid #ccc; + position: fixed; + bottom: 0; + left: 0; + background: #ffffff; + box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1); } /* Chart Container */ @@ -119,8 +198,18 @@ h2 { max-width: 600px; margin-left: auto; margin-right: auto; + width: 100%; /* Full width */ } +.chatbot-widget { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1); +} + + /* Sign Out Button */ .sign-out-container { text-align: center; @@ -140,3 +229,34 @@ h2 { .sign-out-btn:hover { background-color: #0056b3; } + + /* Responsive Tweaks */ +@media (max-width: 900px) { + + .dashboard { + flex-direction: column; /* Stacks sections on smaller screens */ + } + + .dashboard-content { + flex-direction: column; /* Stacks elements on smaller screens */ + } + + .filter-container { + width: 100%; /* Full width on mobile */ + max-width: 100%; + } + + .filter-container select { + width: 100%; + } + + .riasec-container { + max-width: 100%; /* Full width on mobile */ + position: relative; + top: unset; + } + + .career-suggestions-container { + max-width: 100%; /* Full width */ + } +} \ No newline at end of file diff --git a/src/components/Dashboard.js b/src/components/Dashboard.js index bc297d5..9733246 100644 --- a/src/components/Dashboard.js +++ b/src/components/Dashboard.js @@ -5,6 +5,8 @@ import { useNavigate, useLocation } from 'react-router-dom'; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend } from 'chart.js'; import { CareerSuggestions } from './CareerSuggestions.js'; import PopoutPanel from './PopoutPanel.js'; +import './PopoutPanel.css'; +import Chatbot from "./Chatbot.js"; import { Bar } from 'react-chartjs-2'; import { fetchSchools } from '../utils/apiUtils.js'; import './Dashboard.css'; @@ -29,10 +31,57 @@ function Dashboard() { const [areaTitle, setAreaTitle] = useState(null); const [userZipcode, setUserZipcode] = useState(null); const [riaSecDescriptions, setRiaSecDescriptions] = useState([]); + const [selectedJobZone, setSelectedJobZone] = useState(''); + const [careersWithJobZone, setCareersWithJobZone] = useState([]); // Store careers with job zone info + + const jobZoneLabels = { + '1': 'Little or No Preparation', + '2': 'Some Preparation Needed', + '3': 'Medium Preparation Needed', + '4': 'Considerable Preparation Needed', + '5': 'Extensive Preparation Needed' + }; // Dynamic API URL const apiUrl = process.env.REACT_APP_API_URL || ''; - const googleMapsApiKey = process.env.REACT_APP_GOOGLE_MAPS_API_KEY; + + // Fetch job zone mappings after career suggestions are loaded + useEffect(() => { + const fetchJobZones = async () => { + if (careerSuggestions.length === 0) return; + + const socCodes = careerSuggestions.map((career) => career.code); + try { + const response = await axios.post(`${apiUrl}/job-zones`, { socCodes }); + const jobZoneData = response.data; + + const updatedCareers = careerSuggestions.map((career) => ({ + ...career, + job_zone: jobZoneData[career.code.slice(0, -3)]?.job_zone || null, // Extract correct value + })); + + setCareersWithJobZone(updatedCareers); // Update state + } catch (error) { + console.error('Error fetching job zone information:', error); + } + }; + + fetchJobZones(); + }, [careerSuggestions, apiUrl]); + + const filteredCareers = selectedJobZone + ? careersWithJobZone.filter(career => { + + return ( + career.job_zone !== null && + career.job_zone !== undefined && + typeof career.job_zone === 'number' && + Number(career.job_zone) === Number(selectedJobZone) + ); + }) + : careersWithJobZone; + + useEffect(() => { let descriptions = []; // Declare outside for scope accessibility @@ -40,8 +89,8 @@ function Dashboard() { const { careerSuggestions: suggestions, riaSecScores: scores } = location.state || {}; descriptions = scores.map((score) => score.description || "No description available."); setCareerSuggestions(suggestions || []); - setRiaSecScores(scores || []); - setRiaSecDescriptions(descriptions); // Set descriptions + setRiaSecScores(scores || []); + setRiaSecDescriptions(descriptions); // Set descriptions } else { console.warn('No data found, redirecting to Interest Inventory'); navigate('/interest-inventory'); @@ -55,11 +104,11 @@ function Dashboard() { const profileResponse = await fetch(`${apiUrl}/user-profile`, { headers: { Authorization: `Bearer ${token}` }, }); - + if (profileResponse.ok) { const profileData = await profileResponse.json(); console.log('Fetched User Profile:', profileData); - + const { state, area, zipcode } = profileData; // Use 'area' instead of 'AREA_TITLE' setUserState(state); setAreaTitle(area && area.trim() ? area.trim() : ''); // Ensure 'area' is set correctly @@ -87,81 +136,45 @@ function Dashboard() { setSalaryData([]); // Reset salary data setEconomicProjections({}); // Reset economic projections setTuitionData([]); // Reset tuition data - + if (!socCode) { console.error('SOC Code is missing'); setError('SOC Code is missing'); return; } - + try { // Step 1: Fetch CIP Code const cipResponse = await fetch(`${apiUrl}/cip/${socCode}`); if (!cipResponse.ok) throw new Error('Failed to fetch CIP Code'); const { cipCode } = await cipResponse.json(); const cleanedCipCode = cipCode.replace('.', '').slice(0, 4); - - // Step 2: Fetch Data in Parallel + + // Step 2: Fetch Job Description and Tasks + const jobDetailsResponse = await fetch(`${apiUrl}/onet/career-description/${socCode}`); + if (!jobDetailsResponse.ok) throw new Error('Failed to fetch job description'); + const { description, tasks } = await jobDetailsResponse.json(); + + // Step 3: Fetch Data in Parallel for other career details const [filteredSchools, economicResponse, tuitionResponse, salaryResponse] = await Promise.all([ fetchSchools(cleanedCipCode, userState), axios.get(`${apiUrl}/projections/${socCode.split('.')[0]}`), - axios.get(`${apiUrl}/tuition`, { - params: { - cipCode: cleanedCipCode, - state: userState - }, - }), - axios.get(`${apiUrl}/salary`, { - params: { - socCode: socCode.split('.')[0], - area: areaTitle - }, - }), + axios.get(`${apiUrl}/tuition`, { params: { cipCode: cleanedCipCode, state: userState }}), + axios.get(`${apiUrl}/salary`, { params: { socCode: socCode.split('.')[0], area: areaTitle }}), ]); - - // Check if `userZipcode` is set correctly - const currentUserZipcode = userZipcode; - if (!currentUserZipcode) { - console.error("User Zipcode is not set correctly:", currentUserZipcode); - return; - } - // Step 3: Add distance information to each school + // Handle Distance Calculation const schoolsWithDistance = await Promise.all(filteredSchools.map(async (school) => { - // Combine Street Address, City, State, and ZIP to form the full school address const schoolAddress = `${school.Address}, ${school.City}, ${school.State} ${school.ZIP}`; - - if (!currentUserZipcode || !schoolAddress) { - console.error('Missing ZIP codes or school address:', { currentUserZipcode, schoolAddress }); - return { ...school, distance: 'Error', duration: 'Error' }; - } - - console.log("Calculating distance for User Zipcode:", currentUserZipcode, "and School Address:", schoolAddress); - - try { - // Send the user's ZIP code and school address to the backend - const response = await axios.post(`${apiUrl}/maps/distance`, { - userZipcode: currentUserZipcode, // Pass the ZIP code (or lat/lng) of the user - destinations: schoolAddress, // Pass the full school address - }); - - const { distance, duration } = response.data; - return { - ...school, // Keep all school data - distance, // Add the distance value - duration, // Add the duration value - }; - } catch (error) { - console.error('Error fetching distance:', error); - return { - ...school, - distance: 'Error', - duration: 'Error', - }; - } + const response = await axios.post(`${apiUrl}/maps/distance`, { + userZipcode, + destinations: schoolAddress, + }); + const { distance, duration } = response.data; + return { ...school, distance, duration }; })); - // Step 4: Format Salary Data + // Process Salary Data const salaryDataPoints = [ { percentile: '10th Percentile', value: salaryResponse.data.A_PCT10 || 0 }, { percentile: '25th Percentile', value: salaryResponse.data.A_PCT25 || 0 }, @@ -169,13 +182,15 @@ function Dashboard() { { percentile: '75th Percentile', value: salaryResponse.data.A_PCT75 || 0 }, { percentile: '90th Percentile', value: salaryResponse.data.A_PCT90 || 0 }, ]; - - // Step 5: Consolidate Career Details + + // Consolidate Career Details with Job Description and Tasks setCareerDetails({ ...career, + jobDescription: description, + tasks: tasks, economicProjections: economicResponse.data, salaryData: salaryDataPoints, - schools: schoolsWithDistance, // Add schools with distances + schools: schoolsWithDistance, tuitionData: tuitionResponse.data, }); } catch (error) { @@ -185,9 +200,8 @@ function Dashboard() { setLoading(false); } }, - [userState, apiUrl, areaTitle] + [userState, apiUrl, areaTitle, userZipcode] ); - const chartData = { labels: riaSecScores.map((score) => score.area), @@ -204,33 +218,48 @@ function Dashboard() { return (
-
- -
+
+ + +
- {/* Right RIASEC Chart + Descriptions */} -
-
-

RIASEC Scores

- +
+
+
-
-

RIASEC Personality Descriptions

- {riaSecDescriptions.length > 0 ? ( -
    - {riaSecDescriptions.map((desc, index) => ( -
  • - {riaSecScores[index]?.area}: {desc} -
  • - ))} -
- ) : ( -

Loading descriptions...

- )} +
+
+

RIASEC Scores

+ +
+
+

RIASEC Personality Descriptions

+ {riaSecDescriptions.length > 0 ? ( +
    + {riaSecDescriptions.map((desc, index) => ( +
  • + {riaSecScores[index]?.area}: {desc} +
  • + ))} +
+ ) : ( +

Loading descriptions...

+ )} +
+ {selectedCareer && ( )} +{/* Pass context to Chatbot */} +
+ +
+ + {/* Acknowledgment Section */}
+ +
+ +
+

Career results and RIASEC scores are provided by O*Net, in conjunction with the @@ -268,4 +334,4 @@ function Dashboard() { ); } -export default Dashboard; +export default Dashboard; \ No newline at end of file diff --git a/src/components/LoanRepayment.css b/src/components/LoanRepayment.css new file mode 100644 index 0000000..bd2543f --- /dev/null +++ b/src/components/LoanRepayment.css @@ -0,0 +1,134 @@ +.loan-repayment-container { + padding: 20px; + background-color: #fafafa; + border-radius: 8px; + } + + .loan-repayment-fields label { + display: block; + margin-bottom: 10px; + font-weight: bold; + font-size: 1rem; + } + + .loan-repayment-fields input, + .loan-repayment-fields select { + height: 40px; /* Set a consistent height */ + padding: 0 10px; /* Add horizontal padding */ + font-size: 1rem; /* Consistent font size */ + width: 100%; /* Make inputs span full width */ + box-sizing: border-box; /* Include padding in total width */ + margin-bottom: 15px; /* Space between fields */ + border: 1px solid #ccc; /* Light border for all fields */ + border-radius: 8px; /* Rounded corners */ + } + + .loan-repayment-fields button { + width: 100%; /* Full width for button */ + padding: 12px; + height: 45px; + margin-top: 10px; + background-color: #4CAF50; + color: white; + border: none; + text-align: center; + font-size: 1.1rem; + display: flex; + justify-content: center; /* Horizontally center text */ + align-items: center; /* Vertically center text */ + cursor: pointer; + border-radius: 8px; + transition: background-color 0.3s; + } + + .loan-repayment-fields button:hover { + background-color: #45a049; + } + + /* Make the container for the fields more clean */ + .loan-repayment-fields { + max-width: 600px; + margin: 0 auto; /* Center the form */ + padding: 20px; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + /* Optional: Adjust margins for form fields */ + .loan-repayment-fields input, + .loan-repayment-fields select, + .loan-repayment-fields button { + margin-top: 5px; + margin-bottom: 10px; + } + + /* Ensure the heading of the Loan Repayment Analysis is centered */ + .loan-repayment-fields h3 { + text-align: center; + margin-bottom: 20px; + } + + button { + background-color: #4CAF50; + color: white; + border: none; + cursor: pointer; + text-align: right; /* Centers the button text */ + } + + button:disabled { + background-color: #ccc; + } + + .results-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); /* Limit the column size to a minimum of 280px */ + gap: 20px; + margin-top: 20px; + width: 100%; + max-width: 1200px; /* Limit the maximum width of the grid */ + margin: 0 auto; /* Center the grid horizontally */ + } + + .results-container h3 { + grid-column: span 3; /* Ensure the header spans across the entire grid */ + text-align: center; /* Align the text to the center */ + margin-bottom: 20px; + } + + .school-result-card { + border: 1px solid #ddd; + padding: 15px; + background-color: #f9f9f9; + border-radius: 8px; + display: grid; + flex-direction: column; + text-align: left; + } + + .school-result-card h4 { + font-size: 1.2rem; + text-align: center; + font-weight: bold; + } + + .school-result-card p { + font-size: 1rem; + margin-bottom: 10px; + } + + .school-result-card .net-gain.positive { + color: #2ecc71; /* Green color for positive values */ + } + + .school-result-card .net-gain.negative { + color: #e74c3c; /* Red color for negative values */ + } + + .error-message { + color: red; + font-weight: bold; + margin-top: 15px; + } + \ No newline at end of file diff --git a/src/components/LoanRepayment.js b/src/components/LoanRepayment.js index a5fd27f..c32d149 100644 --- a/src/components/LoanRepayment.js +++ b/src/components/LoanRepayment.js @@ -1,60 +1,57 @@ import React, { useState } from 'react'; +import './LoanRepayment.css'; -function LoanRepayment({ schools, salaryData, earningHorizon }) { - const [tuitionType, setTuitionType] = useState('inState'); // 'inState' or 'outOfState' - const [interestRate, setInterestRate] = useState(5.5); // Default federal loan interest rate - const [loanTerm, setLoanTerm] = useState(10); // Default loan term (10 years) +function LoanRepayment({ + schools, + salaryData, + setResults, + setLoading, +}) { + const [selectedSalary, setSelectedSalary] = useState('10th Percentile'); + 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 const [extraPayment, setExtraPayment] = useState(0); // Extra monthly payment const [currentSalary, setCurrentSalary] = useState(0); // Current salary input - const [results, setResults] = useState([]); - const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - // Validation function const validateInputs = () => { if (!schools || schools.length === 0) { setError('School data is missing. Loan calculations cannot proceed.'); return false; } - if (interestRate <= 0) { setError('Interest rate must be greater than 0.'); return false; } - if (loanTerm <= 0) { setError('Loan term must be greater than 0.'); return false; } - if (extraPayment < 0) { setError('Extra monthly payment cannot be negative.'); return false; } - if (currentSalary < 0) { setError('Current salary cannot be negative.'); return false; } - - setError(null); // Clear errors if valid + setError(null); return true; }; - // Loan calculation function for all schools const calculateLoanDetails = () => { - if (!validateInputs()) return; // Validate inputs before calculation + if (!validateInputs()) return; + setLoading(true); const schoolResults = schools.map((school) => { const tuition = tuitionType === 'inState' ? school.inState : school.outOfState; const monthlyRate = interestRate / 12 / 100; const loanTermMonths = loanTerm * 12; - // Calculate minimum monthly payment const minimumMonthlyPayment = tuition * (monthlyRate * Math.pow(1 + monthlyRate, loanTermMonths)) / (Math.pow(1 + monthlyRate, loanTermMonths) - 1); - // Total loan cost with extra payments const extraMonthlyPayment = minimumMonthlyPayment + extraPayment; let remainingBalance = tuition; let monthsWithExtra = 0; @@ -68,18 +65,14 @@ function LoanRepayment({ schools, salaryData, earningHorizon }) { const totalLoanCost = extraMonthlyPayment * monthsWithExtra; - // Handle missing salary data - let salary = salaryData && salaryData[0]?.value ? salaryData[0].value : null; + let salary = salaryData.find((point) => point.percentile === selectedSalary)?.value || 0; let netGain = 'N/A'; let monthlySalary = 'N/A'; - if (salary) { - // Calculate net gain - const totalSalary = salary * earningHorizon; - const currentSalaryEarnings = currentSalary * earningHorizon * Math.pow(1.03, earningHorizon); // 3% growth + if (salary > 0) { + const totalSalary = salary * loanTerm; + const currentSalaryEarnings = currentSalary * loanTerm * Math.pow(1.03, loanTerm); netGain = (totalSalary - totalLoanCost - currentSalaryEarnings).toFixed(2); - - // Monthly salary monthlySalary = (salary / 12).toFixed(2); } @@ -87,7 +80,7 @@ function LoanRepayment({ schools, salaryData, earningHorizon }) { ...school, tuition, monthlyPayment: minimumMonthlyPayment.toFixed(2), - totalMonthlyPayment: extraMonthlyPayment.toFixed(2), // Add total payment including extra + totalMonthlyPayment: extraMonthlyPayment.toFixed(2), totalLoanCost: totalLoanCost.toFixed(2), netGain, monthlySalary, @@ -95,102 +88,48 @@ function LoanRepayment({ schools, salaryData, earningHorizon }) { }); setResults(schoolResults); + setLoading(false); }; - return ( -

-

Loan Repayment and ROI Analysis

- -
- - - - - - - - - - - -
- - {/* Error Message */} - {error &&

{error}

} - - {/* Results Display */} - {results.length > 0 && ( -
-

Comparison by School

- {results.map((result, index) => ( -
-

{result.schoolName}

-

Total Tuition: ${result.tuition}

-

Monthly Payment: ${result.monthlyPayment}

-

Total Monthly Payment (with extra): ${result.totalMonthlyPayment}

-

Total Loan Cost: ${result.totalLoanCost}

-

Net Gain: {result.netGain}

-

Monthly Salary (Gross): {result.monthlySalary}

-
- ))}
- )} - - {/* Salary Warning */} - {!salaryData || salaryData.length === 0 ? ( -

Salary data is not available for this profession. Loan calculations are limited.

- ) : null} +
+ + setInterestRate(e.target.value)} /> +
+
+ + setLoanTerm(e.target.value)} /> +
+
+ + setExtraPayment(e.target.value)} /> +
+
+ + setCurrentSalary(e.target.value)} /> +
+
+ + +
+ + + {error &&
{error}
}
); - } -export default LoanRepayment; +export default LoanRepayment; \ No newline at end of file diff --git a/src/components/PopoutPanel.css b/src/components/PopoutPanel.css index 4c87217..e60329f 100644 --- a/src/components/PopoutPanel.css +++ b/src/components/PopoutPanel.css @@ -2,7 +2,7 @@ position: fixed; top: 0; right: 0; - width: 40%; /* Default width for larger screens */ + width: 60%; /* Increase width for larger screens */ height: 100%; background-color: #fff; box-shadow: -3px 0 5px rgba(0, 0, 0, 0.3); @@ -16,14 +16,18 @@ /* Mobile responsiveness */ @media (max-width: 768px) { .popout-panel { - width: 100%; /* Use full width for smaller screens */ - height: 100%; /* Cover full height */ - left: 0; /* Ensure it appears on the left for mobile */ - right: unset; /* Override right alignment */ + width: 100%; /* Use full width for smaller screens */ + height: 100%; /* Cover full height */ + left: 0; /* Ensure it appears on the left for mobile */ + right: unset; /* Override right alignment */ + } + + .schools-offering { + grid-template-columns: 1fr; /* Single column layout for smaller screens */ } } - - /* Close button adjustments for mobile */ + +/* Close button adjustments for mobile */ .close-btn { position: absolute; top: 10px; @@ -37,39 +41,279 @@ z-index: 1001; /* Keep button above the panel */ } - - h3 { - margin-top: 20px; - } - - ul { - list-style: none; - padding: 0; - } - - li { - margin-bottom: 15px; - border-bottom: 1px solid #ddd; - padding-bottom: 10px; - } - - button { - margin-top: 10px; - background-color: #007bff; - color: white; - border: none; - padding: 8px 12px; - cursor: pointer; - font-size: 0.9rem; - } - - button:hover { - background-color: #0056b3; - } - - /* Fix for overflow issue */ -html, body { - overflow-x: hidden; /* Prevent horizontal scrolling */ +/* Job Description and Expected Tasks section */ +.section { + margin-bottom: 20px; } - \ No newline at end of file +.job-description, +.expected-tasks { + padding: 10px; + border: 1px solid #e0e0e0; + border-radius: 8px; + background-color: #f9f9f9; +} + +/* Expected Tasks Styling */ +.expected-tasks { + padding: 15px; + background-color: #fafafa; + border: 1px solid #e0e0e0; + border-radius: 8px; + margin-top: 20px; +} + +.expected-tasks ul { + list-style-position: inside; /* Move the bullets inside, aligning them with text */ + padding-left: 20px; /* Add space between the bullet and the text */ + margin: 0; + text-align: left; /* Align the text to the left */ +} + +.expected-tasks li { + margin-bottom: 15px; /* Space between each task */ + padding-bottom: 10px; + border-bottom: 1px solid #ddd; + font-size: 1rem; +} + +/* Title and task text styling */ +.expected-tasks h3 { + margin-bottom: 15px; + font-size: 1.2rem; + font-weight: bold; + border-bottom: 2px solid #ccc; + padding-bottom: 5px; +} + +.expected-tasks p { + font-size: 1rem; + color: #666; +} + +/* Schools section: Grid layout with clear separation */ +.schools-offering { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); /* Adjust the minimum size of each column */ + gap: 20px; /* Space between columns */ + margin-top: 20px; + width: 100%; /* Ensure it uses full width of its container */ + text-align: center; + justify-content: center; /* Centers grid elements horizontally */ +} + +.no-schools-message { + text-align: center; + width: 100%; + grid-column: 1 / -1; /* Forces the message to span all columns */ + justify-self: center; /* Centers text horizontally */ + align-self: center; /* Centers text vertically */ + font-style: italic; /* Optional: Stylize the message */ + padding: 20px 0; /* Adds spacing */ +} + +.schools-offering .school-card { + border: 1px solid #ddd; + padding: 15px; + background-color: #f9f9f9; + border-radius: 8px; + display: flex; + flex-direction: column; + text-align: left; +} + +.schools-offering .school-card div { + margin-bottom: 8px; +} + +.school-info { + display: flex; + flex-direction: column; + gap: 10px; +} + +/* Salary Data Section */ +.salary-data { + margin-top: 20px; + padding: 15px; + background-color: #fafafa; + border: 1px solid #e0e0e0; + border-radius: 8px; +} + +.salary-data table { + width: 60%; + border-collapse: collapse; + margin: 0 auto 20px; /* This centers the table */ +} + +.salary-data th, .salary-data td { + padding: 10px; + text-align: center; + border-bottom: 1px solid #ddd; +} + +.salary-data th { + background-color: #f0f0f0; + font-weight: bold; +} + +.salary-data td { + font-size: 1rem; + text-align: center; +} + +.salary-data td:last-child { + text-align: center; +} + +/* Economic Projections Section */ +.economic-projections { + margin-top: 20px; + padding: 15px; + background-color: #fafafa; + border: 1px solid #e0e0e0; + border-radius: 8px; +} + +.economic-projections ul { + padding-left: 20px; + list-style-position: inside; + font-size: 1rem; + margin: 0; +} + +.economic-projections li { + margin-bottom: 10px; +} + +/* Loan Repayment Section Styling */ +.loan-repayment-container { + padding: 20px; + background-color: #fafafa; + border-radius: 8px; +} + +.loan-repayment-fields label { + display: block; + margin-bottom: 10px; + font-weight: bold; + font-size: 1rem; +} + +.loan-repayment-fields input, +.loan-repayment-fields select { + height: 40px; + padding: 0 10px; + font-size: 1rem; + width: 100%; + box-sizing: border-box; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 8px; +} + +.loan-repayment-fields button { + width: 100%; + padding: 12px; + height: 45px; + margin-top: 10px; + background-color: #4CAF50; + color: white; + border: none; + text-align: center; + font-size: 1.1rem; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + border-radius: 8px; + transition: background-color 0.3s; +} + +.loan-repayment-fields button:hover { + background-color: #45a049; +} + +.loan-repayment-fields button:disabled { + background-color: #ccc; + cursor: not-allowed; +} + +.loan-repayment-fields { + max-width: 600px; + margin: 0 auto; + padding: 20px; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.loan-repayment-fields input, +.loan-repayment-fields select, +.loan-repayment-fields button { + margin-top: 5px; + margin-bottom: 10px; +} + +.loan-repayment-fields h3 { + text-align: center; + margin-bottom: 20px; +} + +button { + background-color: #4CAF50; + color: white; + border: none; + cursor: pointer; + text-align: right; /* Centers the button text */ +} + +/* Comparison Section Styling */ +.school-comparison-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); /* Dynamic columns */ + gap: 20px; /* Space between cards */ + margin-top: 20px; + width: 100%; /* Ensure it uses full width of its container */ +} + +.school-comparison { + display: grid; + margin-bottom: 25px; + padding: 20px; + border: 1px solid #ddd; + border-radius: 8px; + background-color: #fff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.school-comparison h4 { + font-size: 1.3rem; + font-weight: bold; + margin-bottom: 10px; +} + +/* Section Title Styling */ +h3 { + font-size: 1.2rem; + font-weight: bold; + border-bottom: 2px solid #ccc; + padding-bottom: 5px; + margin-bottom: 10px; +} + + +a { + color: #1a73e8; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* Fix for overflow issue */ +html, body { + overflow-x: hidden; /* Prevent horizontal scrolling */ +} \ No newline at end of file diff --git a/src/components/PopoutPanel.js b/src/components/PopoutPanel.js index dd11f85..57d2b32 100644 --- a/src/components/PopoutPanel.js +++ b/src/components/PopoutPanel.js @@ -1,17 +1,22 @@ import { ClipLoader } from 'react-spinners'; import LoanRepayment from './LoanRepayment.js'; import './PopoutPanel.css'; +import { useState } from 'react'; -function PopoutPanel({ +function PopoutPanel({ data = {}, userState = 'N/A', // Passed explicitly from Dashboard - loading = false, - error = null, - closePanel + loading = false, + error = null, + closePanel, }) { console.log('PopoutPanel Props:', { data, loading, error, userState }); - + const [isCalculated, setIsCalculated] = useState(false); + const [results, setResults] = useState([]); // Store loan repayment calculation results + const [loadingCalculation, setLoadingCalculation] = useState(false); + + // Handle loading state if (loading) { return (
@@ -22,39 +27,17 @@ function PopoutPanel({ ); } - if (error) { - return ( -
- -

Error Loading Career Details

-

{error}

-
- ); - } - - // Handle empty data gracefully - if (!data || Object.keys(data).length === 0) { - return ( -
- -

No Career Data Available

-
- ); - } - // Safely access nested data with fallbacks - const { - title = 'Career Details', - economicProjections = {}, - salaryData = [], - schools = [], - tuitionData = [] + const { + jobDescription = null, // Default to null if not provided + tasks = null, // Default to null if not provided + title = 'Career Details', + economicProjections = {}, + salaryData = [], + schools = [], } = data; - const tenthPercentileSalary = salaryData?.find( - (point) => point.percentile === '10th Percentile' - )?.value || 0; - + // Get program length for calculating tuition const getProgramLength = (degreeType) => { if (degreeType?.includes("Associate")) return 2; if (degreeType?.includes("Bachelor")) return 4; @@ -69,104 +52,123 @@ function PopoutPanel({

{title}

- {/* Schools Offering Programs */} -

Schools Offering Programs

- {Array.isArray(schools) && schools.length > 0 ? ( -
    - {schools.map((school, index) => { - const matchingTuitionData = tuitionData.find( - (tuition) => - tuition['INSTNM']?.toLowerCase().trim() === // Corrected field - school['INSTNM']?.toLowerCase().trim() // Match institution name - ); - console.log('Schools Data in PopoutPanel:', schools); - return ( -
  • - {school['INSTNM']} {/* Updated field */} -
    - Degree Type: {school['CREDDESC'] || 'Degree type information is not available for this program'} {/* Updated field */} -
    - In-State Tuition: ${school['In_state cost'] || 'Tuition information is not available for this school'} {/* Updated field */} -
    - Out-of-State Tuition: ${school['Out_state cost'] || 'Tuition information is not available for this school'} {/* Updated field */} -
    - Distance: {school['distance'] || 'Distance to school not available'} {/* Added Distance field */} -
    - Website: - {/* Updated field */} - {school['Website']} - -
  • - ); - })} -
- ) : ( -

No schools of higher education are available for this career path.

- )} + {/* Job Description and Tasks */} +
+

Job Description

+

{jobDescription || 'No description available'}

+
- - {/* Economic Projections */} -

Economic Projections for {userState}

- {economicProjections && typeof economicProjections === 'object' ? ( -
    -
  • 2022 Employment: {economicProjections['2022 Employment'] || 'N/A'}
  • -
  • 2032 Employment: {economicProjections['2032 Employment'] || 'N/A'}
  • -
  • Total Change: {economicProjections['Total Change'] || 'N/A'}
  • -
- ) : ( -

No economic projections available for this career path.

- )} - - {/* Salary Data Points */} -

Salary Data

- {salaryData && salaryData.length > 0 ? ( - - - - - - - - - {salaryData.map((point, index) => ( - - - - +
+

Expected Tasks

+ {tasks && tasks.length > 0 ? ( +
    + {tasks.map((task, index) => ( +
  • {task}
  • ))} -
-
PercentileSalary
{point.percentile} - {point.value > 0 ? `$${parseInt(point.value, 10).toLocaleString()}` : 'N/A'} -
- ) : ( -

No salary data is available for this career path.

- )} - - {/* Loan Repayment Analysis */} -
-

Loan Repayment Analysis

- { - const years = getProgramLength(school['CREDDESC']); - return { - schoolName: school['INSTNM'], - inState: parseFloat(school['In_state cost'] * years) || 0, - outOfState: parseFloat(school['Out_state cost'] * years) || 0, - }; - })} - salaryData={ - tenthPercentileSalary > 0 - ? [{ percentile: '10th Percentile', value: tenthPercentileSalary, growthRate: 0.03 }] - : [] - } - earningHorizon={10} - /> - {!tenthPercentileSalary && ( -

- Salary data unavailable. Loan details are based on cost alone. -

+ + ) : ( +

No tasks available for this career path.

)}
+ + {/* Schools Offering Programs Section */} +

Schools Offering Programs

+
+ {Array.isArray(schools) && schools.length > 0 ? ( + schools.map((school, index) => ( +
+
{school['INSTNM']}
+
Degree Type: {school['CREDDESC'] || 'Degree type information is not available for this program'}
+
In-State Tuition: ${school['In_state cost'] || 'Tuition information is not available for this school'}
+
Out-of-State Tuition: ${school['Out_state cost'] || 'Tuition information is not available for this school'}
+
Distance: {school['distance'] || 'Distance to school not available'}
+
+ Website: {school['Website']} +
+
+ )) + ) : ( +

No schools of higher education are available in your state for this career path.

+ )} +
+ + {/* Economic Projections */} +
+

Economic Projections for {userState}

+ {economicProjections && typeof economicProjections === 'object' ? ( +
    +
  • 2022 Employment: {economicProjections['2022 Employment'] || 'N/A'}
  • +
  • 2032 Employment: {economicProjections['2032 Employment'] || 'N/A'}
  • +
  • Total Change: {economicProjections['Total Change'] || 'N/A'}
  • +
+ ) : ( +

No economic projections available for this career path.

+ )} +
+ + {/* Salary Data Points */} +
+

Salary Data

+ {salaryData.length > 0 ? ( + + + + + + + + + {salaryData.map((point, index) => ( + + + + + ))} + +
PercentileSalary
{point.percentile}{point.value > 0 ? `$${parseInt(point.value, 10).toLocaleString()}` : 'N/A'}
+ ) : ( +

Salary data is not available.

+ )} +
+ + {/* Loan Repayment Analysis */} +

Loan Repayment Analysis

+ + {/* Loan Repayment Calculation Results */} + { + const years = getProgramLength(school['CREDDESC']); + return { + schoolName: school['INSTNM'], + inState: parseFloat(school['In_state cost'] * years) || 0, + outOfState: parseFloat(school['Out_state cost'] * years) || 0, + degreeType: school['CREDDESC'], + }; + })} + salaryData={salaryData} + setResults={setResults} + setLoading={setLoadingCalculation} + /> + + {/* Results Display */} + {results.length > 0 && ( +
+

Comparisons by School over the life of the loan - assumes a starting salary in the lowest 10%

+ {results.map((result, index) => ( +
+

{result.schoolName} - {result.degreeType || 'Degree type not available'}

+

Total Tuition: ${result.tuition}

+

Monthly Payment: ${result.monthlyPayment}

+

Total Monthly Payment (with extra): ${result.totalMonthlyPayment}

+

Total Loan Cost: ${result.totalLoanCost}

+

+ Net Gain: {result.netGain} +

+

Monthly Salary (Gross): {result.monthlySalary}

+
+ ))} +
+ )}
); } diff --git a/src/components/SignIn.css b/src/components/SignIn.css index a28b1e4..48f7032 100644 --- a/src/components/SignIn.css +++ b/src/components/SignIn.css @@ -32,6 +32,9 @@ max-width: 280px; /* Optional max width */ padding: 10px; background-color: #4CAF50; + justify-content: center; + align-items: center; + text-align: center; color: #fff; border: none; border-radius: 5px; diff --git a/src/components/SignIn.js b/src/components/SignIn.js index b24d103..696d74f 100644 --- a/src/components/SignIn.js +++ b/src/components/SignIn.js @@ -24,7 +24,7 @@ function SignIn({ setIsAuthenticated }) { } try { // Make a POST request to the backend for authentication - const response = await fetch('https://dev.aptivaai.com/api/signin', { + const response = await fetch('https://dev1.aptivaai.com/api/signin', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -45,7 +45,7 @@ function SignIn({ setIsAuthenticated }) { localStorage.setItem('userId', userId); console.log('Token and userId saved in localStorage'); - + console.log('SignIn response data:', data); // Call setIsAuthenticated(true) to update the state setIsAuthenticated(true); navigate('/getting-started'); // Redirect to GettingStarted after SignIn diff --git a/src/utils/apiUtils.js b/src/utils/apiUtils.js index e2d3b71..e25d131 100644 --- a/src/utils/apiUtils.js +++ b/src/utils/apiUtils.js @@ -3,7 +3,6 @@ import axios from 'axios'; //fetch areas by state export const fetchAreasByState = async (state) => { try { - const apiUrl = process.env.REACT_APP_API_URL || ''; const response = await fetch(`${process.env.REACT_APP_API_URL}/Institution_data.json`); if (response.status === 200) { diff --git a/src/utils/fetchJobZones.js b/src/utils/fetchJobZones.js new file mode 100644 index 0000000..ca09c02 --- /dev/null +++ b/src/utils/fetchJobZones.js @@ -0,0 +1,102 @@ +import sqlite3 from "sqlite3"; +import fetch from "node-fetch"; +import dotenv from "dotenv"; + +// Load environment variables +const envFile = process.env.NODE_ENV === "production" ? ".env.production" : ".env.development"; +dotenv.config({ path: envFile }); + +console.log(`šŸ› ļø Loaded environment variables from ${envFile}`); + +// O*Net API Credentials +const ONET_USERNAME = process.env.ONET_USERNAME; +const ONET_PASSWORD = process.env.ONET_PASSWORD; +const BASE_URL_JOB_ZONES = "https://services.onetcenter.org/ws/online/job_zones/"; + +if (!ONET_USERNAME || !ONET_PASSWORD) { + console.error("āŒ O*Net API credentials are missing. Check your .env file."); + process.exit(1); +} + +// Database Path +const DB_PATH = "/home/jcoakley/aptiva-dev1-app/salary_info.db"; + +// Connect to SQLite +const db = new sqlite3.Database(DB_PATH, sqlite3.OPEN_READWRITE, (err) => { + if (err) { + console.error("āŒ Error connecting to database:", err.message); + return; + } + console.log("āœ… Connected to salary_info.db"); +}); + +// ** Function to clean SOC codes (keep hyphen, remove decimals) ** +function formatSOCCode(socCode) { + return socCode.split(".")[0]; // Converts "43-5111.00" → "43-5111" +} + +// ** Step: Fetch and Assign Job Zones (Using Formatted SOC Codes) ** +async function fetchAndAssignJobZones() { + for (let zone = 1; zone <= 5; zone++) { + console.log(`šŸ“” Fetching Job Zone ${zone}`); + + let url = `${BASE_URL_JOB_ZONES}${zone}?start=1&end=500&sort=name`; + let jobZoneOccupations = []; + + try { + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Basic ${Buffer.from(`${ONET_USERNAME}:${ONET_PASSWORD}`).toString("base64")}`, + Accept: "application/json", + }, + }); + + if (!response.ok) { + console.error(`āŒ Failed to fetch Job Zone ${zone}: HTTP Error ${response.status}`); + continue; + } + + const data = await response.json(); + jobZoneOccupations = data.occupation || []; + + } catch (error) { + console.error(`āŒ Error fetching Job Zone ${zone}:`, error.message); + continue; + } + + console.log(`āœ… Retrieved ${jobZoneOccupations.length} occupations for Job Zone ${zone}`); + + // ** Use `serialize()` to enforce sequential updates ** + db.serialize(() => { + db.run("BEGIN TRANSACTION"); // Start transaction + + jobZoneOccupations.forEach((job, index) => { + const soc_code = formatSOCCode(job.code); // Keep hyphen, remove decimal + + setTimeout(() => { + db.run( + "UPDATE salary_data SET JOB_ZONE = ? WHERE OCC_CODE = ?", + [zone, soc_code], + (err) => { + if (err) console.error(`āŒ Error updating JOB_ZONE for ${soc_code}:`, err.message); + } + ); + }, index * 100); // Introduce 100ms delay per record + }); + + db.run("COMMIT"); // End transaction + }); + } + + console.log("\nāœ… All Job Zones assigned."); +} + +// ** Run the Process ** +async function main() { + await fetchAndAssignJobZones(); + + db.close(() => console.log("āœ… Database connection closed.")); +} + +main(); diff --git a/user_profile b/user_profile new file mode 100644 index 0000000..e69de29 diff --git a/user_profile.db b/user_profile.db index 743c40b..786e7d5 100644 Binary files a/user_profile.db and b/user_profile.db differ