cropintel / lib /notifications.ts
Jaithra Polavarapu
fix(hf): drop red-circle emoji in alerts; cursor renders above modals
79799b1
Raw
History Blame Contribute Delete
3.38 kB
/**
* Notification system for alerting farmers about nearby outbreaks
*/
export interface OutbreakLocation {
id: string
lat: number
lng: number
crop: string
disease: string
severity: 'low' | 'medium' | 'high'
date: string
description: string
}
export interface FarmerLocation {
id: string
name: string
email?: string
lat: number
lng: number
crops: string[] // Crops they're interested in
radius: number // Alert radius in miles (default 250)
}
export interface Notification {
id: string
farmerId: string
outbreakId: string
distance: number // Distance in miles
message: string
severity: 'low' | 'medium' | 'high'
read: boolean
createdAt: string
}
/**
* Calculate distance between two coordinates using Haversine formula
* Returns distance in miles
*/
export function calculateDistance(
lat1: number,
lng1: number,
lat2: number,
lng2: number
): number {
const R = 3959 // Earth's radius in miles
const dLat = toRad(lat2 - lat1)
const dLng = toRad(lng2 - lng1)
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLng / 2) *
Math.sin(dLng / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
}
function toRad(degrees: number): number {
return (degrees * Math.PI) / 180
}
/**
* Check if a farmer should be notified about an outbreak
*/
export function shouldNotifyFarmer(
farmer: FarmerLocation,
outbreak: OutbreakLocation
): boolean {
// Check if farmer is interested in this crop
if (!farmer.crops.includes(outbreak.crop.toLowerCase())) {
return false
}
// Calculate distance
const distance = calculateDistance(
farmer.lat,
farmer.lng,
outbreak.lat,
outbreak.lng
)
// Check if within alert radius
return distance <= farmer.radius
}
/**
* Generate notification message for an outbreak
*/
export function generateNotificationMessage(
outbreak: OutbreakLocation,
distance: number
): string {
return `${outbreak.severity.toUpperCase()} ALERT: ${outbreak.disease} detected in ${outbreak.crop} ${distance.toFixed(1)} miles away`
}
/**
* Find all farmers who should be notified about an outbreak
*/
export function findAffectedFarmers(
outbreak: OutbreakLocation,
farmers: FarmerLocation[]
): Array<{ farmer: FarmerLocation; distance: number }> {
const affected: Array<{ farmer: FarmerLocation; distance: number }> = []
for (const farmer of farmers) {
if (shouldNotifyFarmer(farmer, outbreak)) {
const distance = calculateDistance(
farmer.lat,
farmer.lng,
outbreak.lat,
outbreak.lng
)
affected.push({ farmer, distance })
}
}
// Sort by distance (closest first)
return affected.sort((a, b) => a.distance - b.distance)
}
/**
* Create notifications for affected farmers
*/
export function createNotifications(
outbreak: OutbreakLocation,
affectedFarmers: Array<{ farmer: FarmerLocation; distance: number }>
): Notification[] {
return affectedFarmers.map(({ farmer, distance }) => ({
id: `${outbreak.id}-${farmer.id}-${Date.now()}`,
farmerId: farmer.id,
outbreakId: outbreak.id,
distance,
message: generateNotificationMessage(outbreak, distance),
severity: outbreak.severity,
read: false,
createdAt: new Date().toISOString(),
}))
}