-
Notifications
You must be signed in to change notification settings - Fork 36
feat: implement live grievance map #466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -90,6 +90,20 @@ def create_grievance(self, grievance_data: Dict[str, Any], db: Session = None) - | |||||
| longitude = location_data.get('longitude') if isinstance(location_data, dict) else None | ||||||
| address = location_data.get('address') if isinstance(location_data, dict) else None | ||||||
|
|
||||||
| # Validate geo coordinates | ||||||
| if latitude is not None and longitude is not None: | ||||||
| try: | ||||||
| latitude = float(latitude) | ||||||
| longitude = float(longitude) | ||||||
|
|
||||||
| if not (-90 <- latitude <=90): | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo
🐛 Proposed fix- if not (-90 <- latitude <=90):
+ if not (-90 <= latitude <= 90):📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| raise ValueError("Invalid latitude range") | ||||||
| if not(-180 <= longitude <=180): | ||||||
| raise ValueError("Invalid longitude range") | ||||||
| except(ValueError, TypeError): | ||||||
| print("Invalid latitude/longitude provided") | ||||||
| latitude = None | ||||||
| longitude = None | ||||||
| # Create grievance | ||||||
| grievance = Grievance( | ||||||
| unique_id=unique_id, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -85,7 +85,44 @@ def get_grievances( | |
| except Exception as e: | ||
| logger.error(f"Error getting grievances: {e}", exc_info=True) | ||
| raise HTTPException(status_code=500, detail="Failed to retrieve grievances") | ||
|
|
||
| @router.get("/api/grievances/map") | ||
| def get_grievances_for_map( | ||
| status: Optional[str] = None, | ||
| db: Session = Depends(get_db) | ||
| ): | ||
|
Comment on lines
+88
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check app-level middleware, auth schemes, and dependencies
rg -n --type py -C3 "add_middleware|HTTPBearer|get_current_user|Depends.*auth|oauth2_scheme"Repository: RohanExploit/VishwaGuru Length of output: 2683 🏁 Script executed: #!/bin/bash
# Check how the grievances router is registered in the main app
rg -n --type py -C5 "grievances.*router|include_router.*grievance"Repository: RohanExploit/VishwaGuru Length of output: 791 🏁 Script executed: #!/bin/bash
# Check frontend getMapData for auth headers
rg -n --type js -C5 "getMapData|Authorization.*token|Bearer"Repository: RohanExploit/VishwaGuru Length of output: 2906 🏁 Script executed: #!/bin/bash
# Also check the grievances router file to see if other endpoints have explicit auth
fd -t f "grievances.py" | xargs cat -n | head -200Repository: RohanExploit/VishwaGuru Length of output: 9107 🏁 Script executed: #!/bin/bash
# Find the main app file to understand the setup
fd -t f -e py "main.py|app.py" | head -5Repository: RohanExploit/VishwaGuru Length of output: 112 Add authentication to The endpoint currently lacks explicit auth dependency and is fully public. No app-level middleware guards the grievances router—only CORS and GZip are configured in Add 🧰 Tools🪛 Ruff (0.15.2)[warning] 91-91: Do not perform function call (B008) 🤖 Prompt for AI Agents |
||
| """ | ||
| Optimized endpoint for map rendering. | ||
| Returns only geo + minimal fields. | ||
| """ | ||
| query = db.query( | ||
| Grievance.id, | ||
| Grievance.category, | ||
| Grievance.severity, | ||
| Grievance.status, | ||
| Grievance.latitude, | ||
| Grievance.longitude, | ||
| Grievance.assigned_authority | ||
| ).filter( | ||
| Grievance.latitude.isnot(None), | ||
| Grievance.longitude.isnot(None) | ||
| ) | ||
| if status: | ||
| query = query.filter(Grievance.status == status) | ||
| results = query.all() | ||
|
|
||
| return [ | ||
| { | ||
| "id": g.id, | ||
| "category": g.category, | ||
| "severity": g.severity.value, | ||
| "status": g.status.value, | ||
| "latitude": g.latitude, | ||
| "longitude": g.longitude, | ||
| "authority": g.assigned_authority, | ||
|
|
||
| } | ||
| for g in results | ||
| ] | ||
|
Comment on lines
+88
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New map endpoint has no error handling and no result limit. Unlike every other endpoint in this file, 🛡️ Proposed fix — add try/except and a configurable limit `@router.get`("/api/grievances/map")
def get_grievances_for_map(
status: Optional[str] = None,
+ limit: int = Query(500, ge=1, le=2000, description="Max markers to return"),
db: Session = Depends(get_db)
):
"""
Optimized endpoint for map rendering.
Returns only geo + minimal fields.
"""
+ try:
query = db.query(
Grievance.id,
Grievance.category,
Grievance.severity,
Grievance.status,
Grievance.latitude,
Grievance.longitude,
Grievance.assigned_authority
).filter(
Grievance.latitude.isnot(None),
Grievance.longitude.isnot(None)
)
if status:
query = query.filter(Grievance.status == status)
- results = query.all()
+ results = query.limit(limit).all()
return [
{
"id": g.id,
"category": g.category,
"severity": g.severity.value,
"status": g.status.value,
"latitude": g.latitude,
"longitude": g.longitude,
"authority": g.assigned_authority,
}
for g in results
]
+ except Exception as e:
+ logger.error(f"Error fetching map data: {e}", exc_info=True)
+ raise HTTPException(status_code=500, detail="Failed to retrieve map data")🧰 Tools🪛 Ruff (0.15.2)[warning] 91-91: Do not perform function call (B008) 🤖 Prompt for AI Agents |
||
| @router.get("/api/grievances/{grievance_id}", response_model=GrievanceSummaryResponse) | ||
| def get_grievance(grievance_id: int, db: Session = Depends(get_db)): | ||
| """Get detailed grievance information with escalation history""" | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,7 @@ import Footer from './components/Footer'; | |||||
| import FloatingButtonsManager from './components/FloatingButtonsManager'; | ||||||
| import LoadingSpinner from './components/LoadingSpinner'; | ||||||
| import { DarkModeProvider, useDarkMode } from './contexts/DarkModeContext'; | ||||||
| import GrievanceMap from "./views/GrievanceMap"; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Every other view component (lines 14–44) is loaded with ♻️ Proposed fix — convert to lazy import-import GrievanceMap from "./views/GrievanceMap";
+const GrievanceMap = React.lazy(() => import('./views/GrievanceMap'));The existing 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| // Lazy Load Views | ||||||
| const Landing = React.lazy(() => import('./views/Landing')); | ||||||
|
|
@@ -69,7 +70,9 @@ function AppContent() { | |||||
|
|
||||||
| // Safe navigation helper | ||||||
| const navigateToView = useCallback((view) => { | ||||||
| const validViews = ['home', 'map', 'report', 'action', 'mh-rep', 'pothole', 'garbage', 'vandalism', 'flood', 'infrastructure', 'parking', 'streetlight', 'fire', 'animal', 'blocked', 'tree', 'pest', 'smart-scan', 'grievance-analysis', 'noise', 'safety-check', 'insight', 'my-reports', 'grievance', 'login', 'signup']; | ||||||
|
|
||||||
| const validViews = ['home', 'map', 'report', 'action', 'mh-rep', 'pothole', 'garbage', 'vandalism', 'flood', 'infrastructure', 'parking', 'streetlight', 'fire', 'animal', 'blocked', 'tree', 'pest', 'smart-scan', 'grievance-analysis', 'noise', 'safety-check', 'my-reports', 'grievance', 'login', 'signup','grievance-map']; | ||||||
|
|
||||||
| if (validViews.includes(view)) { | ||||||
| navigate(view === 'home' ? '/' : `/${view}`); | ||||||
| } else { | ||||||
|
|
@@ -352,12 +355,21 @@ function AppContent() { | |||||
| <GrievanceView /> | ||||||
| </ProtectedRoute> | ||||||
| } /> | ||||||
|
|
||||||
| <Route path="/insight" element={ | ||||||
| <ProtectedRoute> | ||||||
| <CivicInsight /> | ||||||
| </ProtectedRoute> | ||||||
| } /> | ||||||
|
|
||||||
| <Route path= "/grievance-map" element={ | ||||||
| <ProtectedRoute> | ||||||
| <GrievanceMap/> | ||||||
| </ProtectedRoute> | ||||||
|
|
||||||
| } /> | ||||||
| <Route path="*" element={<NotFound />} /> | ||||||
|
|
||||||
| </Routes> | ||||||
| </Suspense> | ||||||
| </main> | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,30 +3,52 @@ | |||||||||||||||||
| const API_BASE = import.meta.env.VITE_API_URL || ''; | ||||||||||||||||||
|
|
||||||||||||||||||
| export const grievancesApi = { | ||||||||||||||||||
| // Get list of grievances with escalation history | ||||||||||||||||||
|
|
||||||||||||||||||
| // Create grievance | ||||||||||||||||||
| async create(data) { | ||||||||||||||||||
| const response = await fetch(`${API_BASE}/api/grievances`,{ | ||||||||||||||||||
| method: 'POST', | ||||||||||||||||||
| headers :{ | ||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||
| }, | ||||||||||||||||||
| body: JSON.stringify(data), | ||||||||||||||||||
| }); | ||||||||||||||||||
| if(!response.ok) { | ||||||||||||||||||
| throw new Error(`HTTP error! status: ${response.status}`); | ||||||||||||||||||
| } | ||||||||||||||||||
| return response.json(); | ||||||||||||||||||
| }, | ||||||||||||||||||
| //Get list of grievances | ||||||||||||||||||
| async getAll(params = {}) { | ||||||||||||||||||
| const queryParams = new URLSearchParams(); | ||||||||||||||||||
| if (params.status) queryParams.append('status', params.status); | ||||||||||||||||||
| if (params.category) queryParams.append('category', params.category); | ||||||||||||||||||
| if (params.limit) queryParams.append('limit', params.limit); | ||||||||||||||||||
| if (params.offset) queryParams.append('offset', params.offset); | ||||||||||||||||||
| if(params.status) queryParams.append('status', params.status); | ||||||||||||||||||
| if(params.category) queryParams.append('category', params.category); | ||||||||||||||||||
| if(params.limit) queryParams.append('limit', params.limit); | ||||||||||||||||||
| if(params.offset) queryParams.append('offset', params.offset); | ||||||||||||||||||
|
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allow Truthy checks skip ✅ Suggested fix- if(params.limit) queryParams.append('limit', params.limit);
- if(params.offset) queryParams.append('offset', params.offset);
+ if (params.limit != null) queryParams.append('limit', params.limit);
+ if (params.offset != null) queryParams.append('offset', params.offset);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| const response = await fetch(`${API_BASE}/api/grievances?${queryParams}`); | ||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||
| if(!response.ok) { | ||||||||||||||||||
| throw new Error(`HTTP error! status: ${response.status}`); | ||||||||||||||||||
| } | ||||||||||||||||||
| return response.json(); | ||||||||||||||||||
| }, | ||||||||||||||||||
|
|
||||||||||||||||||
| // Get single grievance with escalation history | ||||||||||||||||||
| //Get single grievance | ||||||||||||||||||
| async getById(grievanceId) { | ||||||||||||||||||
| const response = await fetch(`${API_BASE}/api/grievances/${grievanceId}`); | ||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||
| if(!response.ok) { | ||||||||||||||||||
| throw new Error(`HTTP error! status: ${response.status}`); | ||||||||||||||||||
| } | ||||||||||||||||||
| return response.json(); | ||||||||||||||||||
| }, | ||||||||||||||||||
|
|
||||||||||||||||||
| //Get map data | ||||||||||||||||||
| async getMapData() { | ||||||||||||||||||
| const response = await fetch(`${API_BASE}/api/grievances/map`); | ||||||||||||||||||
| if(!response.ok) { | ||||||||||||||||||
| throw new Error(`HTTP error! status: ${response.status}`); | ||||||||||||||||||
| } | ||||||||||||||||||
| return response.json(); | ||||||||||||||||||
| }, | ||||||||||||||||||
|
|
||||||||||||||||||
| // Get escalation statistics | ||||||||||||||||||
| async getStats() { | ||||||||||||||||||
| const response = await fetch(`${API_BASE}/api/escalation-stats`); | ||||||||||||||||||
|
|
@@ -40,9 +62,6 @@ export const grievancesApi = { | |||||||||||||||||
| async escalate(grievanceId, reason) { | ||||||||||||||||||
| const response = await fetch(`${API_BASE}/api/grievances/${grievanceId}/escalate?reason=${encodeURIComponent(reason)}`, { | ||||||||||||||||||
| method: 'POST', | ||||||||||||||||||
| headers: { | ||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||
| }, | ||||||||||||||||||
| }); | ||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||
| throw new Error(`HTTP error! status: ${response.status}`); | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import './index.css' | |
| import App from './App.jsx' | ||
| import './i18n' // Initialize i18n | ||
| import './offlineQueue' // Initialize offline queue listeners | ||
| import "leaflet/dist/leaflet.css"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate Leaflet CSS import — Global stylesheets are idempotent but importing from two places is redundant. Since 🤖 Prompt for AI Agents |
||
|
|
||
| createRoot(document.getElementById('root')).render( | ||
| <StrictMode> | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,102 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, {useEffect, useState} from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import {MapContainer, TileLayer, Marker, Popup} from "react-leaflet"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import MarkerClusterGroup from "react-leaflet-cluster"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import L from "leaflet"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { grievancesApi} from "../api"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import "leaflet/dist/leaflet.css"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const GrievanceMap = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const [grievances , setGrievances] = useState([]); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| loadMapData(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const loadMapData = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await grievancesApi.getMapData(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = Array.isArray(response)?response: response.data; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("Map data:", data); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove 🐛 Proposed fix- console.log("Map data:", data);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| setGrievances(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch(err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Error loading map data:", err); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+15
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No loading or error feedback — the map silently stays empty on slow networks or API failures.
♻️ Suggested pattern const GrievanceMap = () => {
const [grievances, setGrievances] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
const loadMapData = async () => {
try {
const response = await grievancesApi.getMapData();
const data = Array.isArray(response) ? response : response.data;
setGrievances(data);
} catch(err) {
console.error("Error loading map data:", err);
+ setError("Failed to load grievance data.");
+ } finally {
+ setLoading(false);
}
};Then render a spinner or error banner inside the 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| const getColor = (status) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (status?.toLowerCase()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| case "open": return "red"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| case "in_progress": return "orange"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| case "resolved": return "green"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| case "escalated": return "purple"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| default: return "blue"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const markerIcon = (status) => | ||||||||||||||||||||||||||||||||||||||||||||||||||
| L.divIcon({ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| className: "custom-marker", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| html: `<div style=" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| background:${getColor(status)}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| width:14px; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| height:14px; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| border-radius:50%; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| border: 2px solid white; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| box-shadow: 0 0 4px rgba(0,0,0,0.4); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "></div>`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| iconSize: [16,16], | ||||||||||||||||||||||||||||||||||||||||||||||||||
| iconAnchor: [8, 8], | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="h-screen w-full"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <MapContainer | ||||||||||||||||||||||||||||||||||||||||||||||||||
| center={[20.5937, 78.9629]} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| zoom={5} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{height: "100%", width:"100%"}} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
♻️ Suggested fix- <div className="h-screen w-full">
+ <div className="h-[calc(100vh-64px)] w-full"> {/* adjust 64px to actual Navbar height */}or using Tailwind - <div className="h-screen w-full">
+ <div className="flex-1 w-full min-h-0">📝 Committable suggestion
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <TileLayer | ||||||||||||||||||||||||||||||||||||||||||||||||||
| attribution='© <a href="https://www.openstreetmap.org/copyright"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| OpenStreetMap </a> contributors' | ||||||||||||||||||||||||||||||||||||||||||||||||||
| url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <MarkerClusterGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {grievances.map((g)=> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| g.latitude && g.longitude ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <Marker | ||||||||||||||||||||||||||||||||||||||||||||||||||
| key = {g.id || g._id} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| position={[parseFloat(g.latitude), parseFloat(g.longitude)]} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| icon={markerIcon(g.status)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <Popup> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="p-1"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <h3 className="font-bold border-b mb-1">{g.category}</h3> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-xs"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Status: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {g.status} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-xs"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Severity: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {g.severity} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-xs"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Authority: | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {g.assigned_authority} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </Popup> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </Marker> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ): null | ||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </MarkerClusterGroup> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </MapContainer> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| export default GrievanceMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -269,11 +269,13 @@ const ReportForm = ({ setView, setLoading, setError, setActionPlan, loading }) = | |||||||||||||||||
| if (navigator.geolocation) { | ||||||||||||||||||
| navigator.geolocation.getCurrentPosition( | ||||||||||||||||||
| (position) => { | ||||||||||||||||||
| const lat = position.coords.latitude; | ||||||||||||||||||
| const lng = position.coords.longitude; | ||||||||||||||||||
| setFormData(prev => ({ | ||||||||||||||||||
| ...prev, | ||||||||||||||||||
| latitude: position.coords.latitude, | ||||||||||||||||||
| longitude: position.coords.longitude, | ||||||||||||||||||
| location: `Lat: ${position.coords.latitude.toFixed(4)}, Long: ${position.coords.longitude.toFixed(4)}` | ||||||||||||||||||
| latitude: lat, | ||||||||||||||||||
| longitude: lng, | ||||||||||||||||||
| location: `Lat: ${lat.toFixed(4)}, Long: ${lng.toFixed(4)}` | ||||||||||||||||||
| })); | ||||||||||||||||||
| setGettingLocation(false); | ||||||||||||||||||
| }, | ||||||||||||||||||
|
|
@@ -283,10 +285,11 @@ const ReportForm = ({ setView, setLoading, setError, setActionPlan, loading }) = | |||||||||||||||||
| setGettingLocation(false); | ||||||||||||||||||
| } | ||||||||||||||||||
| ); | ||||||||||||||||||
| } else { | ||||||||||||||||||
| setError("Geolocation is not supported by this browser."); | ||||||||||||||||||
| setGettingLocation(false); | ||||||||||||||||||
| } | ||||||||||||||||||
| // else { | ||||||||||||||||||
| // setError("Geolocation is not supported by this browser."); | ||||||||||||||||||
| // setGettingLocation(false); | ||||||||||||||||||
| // } | ||||||||||||||||||
|
Comment on lines
+289
to
+292
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Missing fallback when geolocation is unsupported leaves gettingLocation true and no error, which can permanently disable the Get Pin button on unsupported browsers. Prompt for AI agents
Suggested change
|
||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| const checkNearbyIssues = async () => { | ||||||||||||||||||
|
|
||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Latitude range check negates latitude (
-90 <- latitude <=90), so valid values like 90 are rejected. This should compare latitude directly.Prompt for AI agents