Remove notification system and fix data refresh issues
- Removed NotificationPanel component and related files - Removed notifications API endpoint and utils - Fixed data refresh functionality with cache-busting - Added refreshing state with better UI feedback - Added no-cache headers to API endpoint - Improved refresh button with loading state
This commit is contained in:
89
app/utils/location.ts
Normal file
89
app/utils/location.ts
Normal file
@ -0,0 +1,89 @@
|
||||
// Geolocation utilities
|
||||
|
||||
export function calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number {
|
||||
const R = 3959; // Earth's radius in miles
|
||||
const dLat = (lat2 - lat1) * Math.PI / 180;
|
||||
const dLng = (lng2 - lng1) * Math.PI / 180;
|
||||
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
||||
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
return R * c;
|
||||
}
|
||||
|
||||
export async function getCurrentLocation(): Promise<{ lat: number; lng: number } | null> {
|
||||
return new Promise((resolve) => {
|
||||
if (!navigator.geolocation) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
resolve({
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
resolve(null);
|
||||
},
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// San Diego ZIP code validation and coordinates
|
||||
const SAN_DIEGO_ZIP_COORDINATES: Record<string, { lat: number; lng: number }> = {
|
||||
'92101': { lat: 32.7157, lng: -117.1611 }, // Downtown
|
||||
'92102': { lat: 32.7030, lng: -117.1289 }, // Logan Heights
|
||||
'92103': { lat: 32.7328, lng: -117.1461 }, // Core-Columbia
|
||||
'92104': { lat: 32.7482, lng: -117.0704 }, // College-Rolando
|
||||
'92105': { lat: 32.7089, lng: -117.1242 }, // Southeastern San Diego
|
||||
'92106': { lat: 32.7330, lng: -117.1430 }, // Point Loma
|
||||
'92107': { lat: 32.7469, lng: -117.1978 }, // Ocean Beach
|
||||
'92108': { lat: 32.7469, lng: -117.1978 }, // Mission Valley
|
||||
'92109': { lat: 32.7714, lng: -117.1789 }, // Pacific Beach
|
||||
'92110': { lat: 32.7714, lng: -117.1789 }, // Mission Bay
|
||||
'92111': { lat: 32.8328, lng: -117.2050 }, // Clairemont
|
||||
'92113': { lat: 32.6747, lng: -117.0742 }, // Southeastern San Diego
|
||||
'92114': { lat: 32.7030, lng: -117.1289 }, // Southeastern San Diego
|
||||
'92115': { lat: 32.7411, lng: -117.1045 }, // City Heights
|
||||
'92116': { lat: 32.7644, lng: -117.1164 }, // Kensington
|
||||
'92117': { lat: 32.8328, lng: -117.2050 }, // Clairemont
|
||||
'92118': { lat: 32.6144, lng: -117.0845 }, // Coronado
|
||||
'92119': { lat: 32.7482, lng: -117.0704 }, // College Area
|
||||
'92120': { lat: 32.7482, lng: -117.0704 }, // College Area
|
||||
'92121': { lat: 32.9340, lng: -117.2340 }, // Sorrento Valley
|
||||
'92122': { lat: 32.8344, lng: -117.2544 }, // La Jolla
|
||||
'92123': { lat: 32.7714, lng: -117.1789 }, // Serra Mesa
|
||||
'92124': { lat: 32.7714, lng: -117.1789 }, // Serra Mesa
|
||||
'92126': { lat: 32.9286, lng: -117.1311 }, // Carmel Mountain
|
||||
'92127': { lat: 33.0200, lng: -117.1156 }, // Rancho Bernardo
|
||||
'92128': { lat: 32.958034, lng: -117.121975 }, // Rancho Penasquitos
|
||||
'92129': { lat: 32.958034, lng: -117.121975 }, // Rancho Penasquitos
|
||||
'92130': { lat: 32.9340, lng: -117.2340 }, // Carmel Valley
|
||||
'92131': { lat: 32.9286, lng: -117.1311 }, // Carmel Mountain
|
||||
'92132': { lat: 32.8344, lng: -117.2544 }, // La Jolla
|
||||
'92133': { lat: 32.7157, lng: -117.1611 }, // Naval Base
|
||||
'92134': { lat: 32.7157, lng: -117.1611 }, // Naval Base
|
||||
'92135': { lat: 32.7157, lng: -117.1611 }, // Naval Base
|
||||
'92136': { lat: 32.7330, lng: -117.1430 }, // Point Loma
|
||||
'92137': { lat: 32.7330, lng: -117.1430 }, // Point Loma
|
||||
'92138': { lat: 32.6144, lng: -117.0845 }, // Naval Air Station
|
||||
'92139': { lat: 32.6747, lng: -117.0742 }, // Paradise Hills
|
||||
'92140': { lat: 32.6144, lng: -117.0845 }, // Coronado
|
||||
'92145': { lat: 32.7157, lng: -117.1611 }, // Naval Medical Center
|
||||
'92147': { lat: 32.7330, lng: -117.1430 }, // Point Loma
|
||||
'92154': { lat: 32.5592, lng: -117.0431 }, // San Ysidro
|
||||
'92155': { lat: 32.5592, lng: -117.0431 }, // San Ysidro
|
||||
'92173': { lat: 32.6781, lng: -117.0200 }, // Skyline Hills
|
||||
};
|
||||
|
||||
export function isValidSanDiegoZip(zip: string): boolean {
|
||||
return zip in SAN_DIEGO_ZIP_COORDINATES;
|
||||
}
|
||||
|
||||
export function getCoordinatesFromZip(zip: string): { lat: number; lng: number } | null {
|
||||
return SAN_DIEGO_ZIP_COORDINATES[zip] || null;
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
// Simple notification storage using localStorage
|
||||
export interface NotificationRequest {
|
||||
id: string;
|
||||
libraryName: string;
|
||||
passTypes: ('Pass Only' | 'Backpack Only' | 'Both')[];
|
||||
email?: string; // Optional for future email notifications
|
||||
createdAt: string;
|
||||
lastChecked?: string;
|
||||
}
|
||||
|
||||
export class NotificationManager {
|
||||
private static STORAGE_KEY = 'park-pass-notifications';
|
||||
|
||||
static getNotifications(): NotificationRequest[] {
|
||||
if (typeof window === 'undefined') return [];
|
||||
try {
|
||||
const stored = localStorage.getItem(this.STORAGE_KEY);
|
||||
return stored ? JSON.parse(stored) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static addNotification(request: Omit<NotificationRequest, 'id' | 'createdAt'>): NotificationRequest {
|
||||
const newRequest: NotificationRequest = {
|
||||
...request,
|
||||
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const notifications = this.getNotifications();
|
||||
notifications.push(newRequest);
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(notifications));
|
||||
}
|
||||
|
||||
return newRequest;
|
||||
}
|
||||
|
||||
static removeNotification(id: string): void {
|
||||
const notifications = this.getNotifications().filter(n => n.id !== id);
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(notifications));
|
||||
}
|
||||
}
|
||||
|
||||
static updateLastChecked(id: string): void {
|
||||
const notifications = this.getNotifications();
|
||||
const notification = notifications.find(n => n.id === id);
|
||||
if (notification) {
|
||||
notification.lastChecked = new Date().toISOString();
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(notifications));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Geolocation utilities
|
||||
export function calculateDistance(lat1: number, lng1: number, lat2: number, lng2: number): number {
|
||||
const R = 3959; // Earth's radius in miles
|
||||
const dLat = (lat2 - lat1) * Math.PI / 180;
|
||||
const dLng = (lng2 - lng1) * Math.PI / 180;
|
||||
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
||||
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
return R * c;
|
||||
}
|
||||
|
||||
export async function getCurrentLocation(): Promise<{ lat: number; lng: number } | null> {
|
||||
return new Promise((resolve) => {
|
||||
if (!navigator.geolocation) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
resolve({
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude,
|
||||
});
|
||||
},
|
||||
() => {
|
||||
resolve(null);
|
||||
},
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Web Push Notification utilities
|
||||
export class WebPushManager {
|
||||
static async requestPermission(): Promise<boolean> {
|
||||
if (!('Notification' in window)) {
|
||||
console.log('This browser does not support notifications');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Notification.permission === 'granted') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Notification.permission === 'denied') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const permission = await Notification.requestPermission();
|
||||
return permission === 'granted';
|
||||
}
|
||||
|
||||
static async showNotification(title: string, body: string, options?: NotificationOptions): Promise<void> {
|
||||
if (!await this.requestPermission()) {
|
||||
console.log('Notification permission denied');
|
||||
return;
|
||||
}
|
||||
|
||||
new Notification(title, {
|
||||
body,
|
||||
icon: '/favicon.ico',
|
||||
badge: '/favicon.ico',
|
||||
tag: 'park-pass-notification',
|
||||
requireInteraction: true,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
static isSupported(): boolean {
|
||||
return 'Notification' in window;
|
||||
}
|
||||
|
||||
static getPermissionStatus(): NotificationPermission {
|
||||
return Notification.permission;
|
||||
}
|
||||
}
|
||||
|
||||
// Zip code to coordinates lookup (San Diego area zip codes)
|
||||
const ZIP_COORDINATES: Record<string, { lat: number; lng: number; area: string }> = {
|
||||
// Central San Diego
|
||||
'92101': { lat: 32.7157, lng: -117.1611, area: 'Downtown' },
|
||||
'92102': { lat: 32.7081, lng: -117.1367, area: 'Golden Hill' },
|
||||
'92103': { lat: 32.7441, lng: -117.1739, area: 'Hillcrest' },
|
||||
'92104': { lat: 32.7489, lng: -117.1364, area: 'Normal Heights' },
|
||||
'92105': { lat: 32.7089, lng: -117.1242, area: 'City Heights' },
|
||||
'92106': { lat: 32.7330, lng: -117.1430, area: 'Point Loma' },
|
||||
'92107': { lat: 32.7572, lng: -117.2528, area: 'Ocean Beach' },
|
||||
'92108': { lat: 32.7678, lng: -117.1189, area: 'Mission Valley' },
|
||||
'92109': { lat: 32.7889, lng: -117.2306, area: 'Pacific Beach' },
|
||||
'92110': { lat: 32.7742, lng: -117.1936, area: 'Mission Bay' },
|
||||
'92111': { lat: 32.7714, lng: -117.1789, area: 'Linda Vista' },
|
||||
'92113': { lat: 32.6781, lng: -117.0200, area: 'Skyline' },
|
||||
'92114': { lat: 32.7030, lng: -117.1289, area: 'Logan Heights' },
|
||||
'92115': { lat: 32.7328, lng: -117.1461, area: 'Oak Park' },
|
||||
'92116': { lat: 32.7644, lng: -117.1164, area: 'Kensington' },
|
||||
'92117': { lat: 32.8328, lng: -117.2050, area: 'Clairemont' },
|
||||
'92118': { lat: 32.6100, lng: -117.0842, area: 'Coronado' },
|
||||
'92119': { lat: 32.7482, lng: -117.0704, area: 'College Area' },
|
||||
'92120': { lat: 32.7678, lng: -117.0731, area: 'Del Cerro' },
|
||||
'92121': { lat: 32.8797, lng: -117.2072, area: 'Sorrento Valley' },
|
||||
'92122': { lat: 32.8544, lng: -117.2144, area: 'University City' },
|
||||
'92123': { lat: 32.8100, lng: -117.1350, area: 'Serra Mesa' },
|
||||
'92124': { lat: 32.8086, lng: -117.0547, area: 'Tierrasanta' },
|
||||
'92126': { lat: 32.8831, lng: -117.1558, area: 'Mira Mesa' },
|
||||
'92127': { lat: 32.9581, lng: -117.1089, area: 'Rancho Penasquitos' },
|
||||
'92128': { lat: 33.0200, lng: -117.1156, area: 'Rancho Bernardo' },
|
||||
'92129': { lat: 32.9584, lng: -117.1253, area: 'Rancho Penasquitos' },
|
||||
'92130': { lat: 32.9340, lng: -117.2340, area: 'Carmel Valley' },
|
||||
'92131': { lat: 32.9286, lng: -117.1311, area: 'Carmel Mountain' },
|
||||
'92132': { lat: 32.6747, lng: -117.0742, area: 'Paradise Hills' },
|
||||
'92154': { lat: 32.5592, lng: -117.0431, area: 'San Ysidro' },
|
||||
'92173': { lat: 32.6400, lng: -117.0200, area: 'San Diego East' },
|
||||
};
|
||||
|
||||
export function getCoordinatesFromZip(zipCode: string): { lat: number; lng: number; area: string } | null {
|
||||
return ZIP_COORDINATES[zipCode] || null;
|
||||
}
|
||||
|
||||
export function isValidSanDiegoZip(zipCode: string): boolean {
|
||||
return zipCode in ZIP_COORDINATES;
|
||||
}
|
||||
Reference in New Issue
Block a user