dev1/backend/utils/apiUtils.js

88 lines
3.1 KiB
JavaScript

// src/utils/apiUtils.js
import axios from 'axios';
/* ------------------------------------------------------------------
Single authority for “baseURL + path” so we never worry
about trailing slashes again.
------------------------------------------------------------------*/
const BASE = (
import.meta.env.VITE_API_BASE ||
process.env.REACT_APP_API_URL ||
''
).replace(/\/+$/, ''); // trim *all* trailing “/”
export const api = (path = '') =>
`${BASE}${path.startsWith('/') ? '' : '/'}${path}`;
/* ------------------------------------------------------------------
Fetch areas-by-state (static JSON in public/ or served by nginx)
------------------------------------------------------------------*/
export const fetchAreasByState = async (state) => {
try {
// NOTE: if Institution_data.json is in /public, nginx serves it
const res = await fetch(api('/Institution_data.json'));
if (!res.ok) throw new Error(`HTTP ${res.status}`);
// Adjust this part if your JSON structure is different
const json = await res.json();
return json[state]?.areas || [];
} catch (err) {
console.error('Error fetching areas:', err.message);
return [];
}
};
/* ------------------------------------------------------------------
Client-side Google Maps geocode
------------------------------------------------------------------*/
export async function clientGeocodeZip(zip) {
const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY ??
process.env.REACT_APP_GOOGLE_MAPS_API_KEY;
const url = `https://maps.googleapis.com/maps/api/geocode/json` +
`?address=${encodeURIComponent(zip)}&key=${apiKey}`;
const resp = await axios.get(url);
const { status, results } = resp.data;
if (status === 'OK' && results.length) {
return results[0].geometry.location; // { lat, lng }
}
throw new Error('Geocoding failed.');
}
/* ------------------------------------------------------------------
Haversine distance helper (miles)
------------------------------------------------------------------*/
export function haversineDistance(lat1, lon1, lat2, lon2) {
const R = 3959; // earth radius in miles
const toRad = (v) => (v * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) ** 2;
return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
/* ------------------------------------------------------------------
Fetch schools for one or many CIP prefixes
------------------------------------------------------------------*/
export async function fetchSchools(cipCodes) {
try {
// 1) Ensure array-ness, then join with commas
const codes = Array.isArray(cipCodes) ? cipCodes : [cipCodes];
const cipParam = codes.join(',');
// 2) Hit backend
const res = await axios.get(api('/api/schools'), {
params: { cipCodes: cipParam },
});
return res.data;
} catch (err) {
console.error('Error fetching schools:', err.message);
return [];
}
}