Initial commit: working SD Park Pass Map app with deploy scripts and .gitignore
This commit is contained in:
141
app/api/availability/route.ts
Normal file
141
app/api/availability/route.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Library branch locations with approximate coordinates
|
||||
const LIBRARY_LOCATIONS: Record<string, { lat: number; lng: number }> = {
|
||||
'Beckwourth': { lat: 32.7157, lng: -117.1611 },
|
||||
'Benjamin': { lat: 32.8328, lng: -117.2713 },
|
||||
'Balboa': { lat: 32.7330, lng: -117.1430 },
|
||||
'Carmel Mountain': { lat: 32.9286, lng: -117.1311 },
|
||||
'Carmel Valley': { lat: 32.9340, lng: -117.2340 },
|
||||
'Central Library': { lat: 32.7216, lng: -117.1574 },
|
||||
'City Heights': { lat: 32.7411, lng: -117.1045 },
|
||||
'Clairemont': { lat: 32.8328, lng: -117.2050 },
|
||||
'College-Rolando': { lat: 32.7482, lng: -117.0704 },
|
||||
'Kensington': { lat: 32.7644, lng: -117.1164 },
|
||||
'La Jolla': { lat: 32.8344, lng: -117.2544 },
|
||||
'Linda Vista': { lat: 32.7714, lng: -117.1789 },
|
||||
'Logan Heights': { lat: 32.7030, lng: -117.1289 },
|
||||
'Malcolm X': { lat: 32.7089, lng: -117.1242 },
|
||||
'Mission Hills': { lat: 32.7469, lng: -117.1978 },
|
||||
'Oak Park': { lat: 32.7328, lng: -117.1461 },
|
||||
'Paradise Hills': { lat: 32.6747, lng: -117.0742 },
|
||||
'Rancho Bernardo': { lat: 33.0200, lng: -117.1156 },
|
||||
'Rancho Penasquitos': { lat: 32.958034, lng: -117.121975 },
|
||||
'San Ysidro': { lat: 32.5592, lng: -117.0431 },
|
||||
'Skyline Hills': { lat: 32.6781, lng: -117.0200 },
|
||||
};
|
||||
|
||||
interface BibItem {
|
||||
branchName: string;
|
||||
availability: {
|
||||
statusType: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface BiblioResponse {
|
||||
entities: {
|
||||
bibItems: Record<string, BibItem>;
|
||||
};
|
||||
}
|
||||
|
||||
interface LocationData {
|
||||
name: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
available: boolean;
|
||||
passType: string;
|
||||
}
|
||||
|
||||
async function fetchAvailabilityData(itemNumber: string): Promise<BibItem[]> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://gateway.bibliocommons.com/v2/libraries/sandiego/bibs/${itemNumber}/availability?locale=en-US`,
|
||||
{
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (compatible; LibraryChecker/1.0)',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data: BiblioResponse = await response.json();
|
||||
return Object.values(data.entities.bibItems || {});
|
||||
} catch (error) {
|
||||
console.error(`Error fetching data for ${itemNumber}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Fetch both types of passes
|
||||
const [passItems, backpackItems] = await Promise.all([
|
||||
fetchAvailabilityData('S161C1690437'), // Pure parking pass
|
||||
fetchAvailabilityData('S161C1805116'), // Backpack with pass
|
||||
]);
|
||||
|
||||
const locationMap = new Map<string, LocationData>();
|
||||
|
||||
// Process parking passes
|
||||
passItems.forEach(item => {
|
||||
const location = LIBRARY_LOCATIONS[item.branchName];
|
||||
if (location && item.branchName) {
|
||||
const isAvailable = item.availability?.statusType === 'AVAILABLE';
|
||||
const existing = locationMap.get(item.branchName);
|
||||
|
||||
if (existing) {
|
||||
// Update existing entry - if either item type is available, mark as available
|
||||
existing.available = existing.available || isAvailable;
|
||||
existing.passType = 'Both';
|
||||
} else {
|
||||
locationMap.set(item.branchName, {
|
||||
name: item.branchName,
|
||||
lat: location.lat,
|
||||
lng: location.lng,
|
||||
available: isAvailable,
|
||||
passType: 'Pass Only',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Process backpack items
|
||||
backpackItems.forEach(item => {
|
||||
const location = LIBRARY_LOCATIONS[item.branchName];
|
||||
if (location && item.branchName) {
|
||||
const isAvailable = item.availability?.statusType === 'AVAILABLE';
|
||||
const existing = locationMap.get(item.branchName);
|
||||
|
||||
if (existing) {
|
||||
// Update existing entry - if either item type is available, mark as available
|
||||
existing.available = existing.available || isAvailable;
|
||||
existing.passType = 'Both';
|
||||
} else {
|
||||
locationMap.set(item.branchName, {
|
||||
name: item.branchName,
|
||||
lat: location.lat,
|
||||
lng: location.lng,
|
||||
available: isAvailable,
|
||||
passType: 'Backpack Only',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const locations = Array.from(locationMap.values());
|
||||
|
||||
return NextResponse.json({
|
||||
locations,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error in API route:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch availability data' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user