Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions backend/grievance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 24, 2026

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
Check if this issue is valid — if so, understand the root cause and fix it. At backend/grievance_service.py, line 99:

<comment>Latitude range check negates latitude (`-90 <- latitude <=90`), so valid values like 90 are rejected. This should compare latitude directly.</comment>

<file context>
@@ -90,6 +90,20 @@ def create_grievance(self, grievance_data: Dict[str, Any], db: Session = None) -
+                    latitude = float(latitude)
+                    longitude = float(longitude)
+
+                    if not (-90 <- latitude <=90):
+                        raise ValueError("Invalid latitude range")
+                    if not(-180 <= longitude <=180):
</file context>
Suggested change
if not (-90 <- latitude <=90):
if not (-90 <= latitude <= 90):
Fix with Cubic

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Typo <- silently rejects latitude 90 (North Pole).

-90 <- latitude is parsed by Python as -90 < -latitude, turning the range check into -90 ≤ latitude < 90 — latitude exactly 90 raises ValueError and strips coordinates.

🐛 Proposed fix
-                    if not (-90 <- latitude <=90):
+                    if not (-90 <= latitude <= 90):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not (-90 <- latitude <=90):
if not (-90 <= latitude <= 90):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/grievance_service.py` at line 99, The latitude range check uses the
incorrect operator sequence "-90 <- latitude <=90" which is parsed as "-90 <
-latitude" and wrongly rejects latitude == 90; update the condition (wherever
latitude is validated in grievance_service.py, e.g., the block using the
variable latitude) to use a proper inclusive range check like "-90 <= latitude
<= 90" (negated as needed, e.g., if not (-90 <= latitude <= 90)) so latitude 90
is accepted and the ValueError/coordinate stripping no longer occurs.

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,
Expand Down
39 changes: 38 additions & 1 deletion backend/routers/grievances.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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 -200

Repository: 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 -5

Repository: RohanExploit/VishwaGuru

Length of output: 112


Add authentication to /api/grievances/map endpoint.

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 main.py. This allows unauthenticated access to latitude, longitude, and assigned_authority for every grievance in the database. The frontend's getMapData() call in frontend/src/api/grievances.js confirms this pattern by using a bare fetch() without an Authorization header, unlike other API calls that use getHeaders() with Bearer tokens.

Add current_user: User = Depends(get_current_active_user) to the function signature to match the auth pattern already defined in backend/dependencies.py.

🧰 Tools
🪛 Ruff (0.15.2)

[warning] 91-91: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routers/grievances.py` around lines 88 - 92, The
get_grievances_for_map endpoint is missing auth; update its signature to require
an authenticated user by adding the dependency parameter current_user: User =
Depends(get_current_active_user) to the function get_grievances_for_map, and
ensure you import User and get_current_active_user from backend.dependencies (or
wherever they’re defined) so the endpoint uses the same auth pattern as other
routes.

"""
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

New map endpoint has no error handling and no result limit.

Unlike every other endpoint in this file, get_grievances_for_map has no try/except: a transient DB error returns an unlogged 500. More critically, query.all() fetches every geo-tagged grievance with no pagination — this will become a memory and latency issue as the dataset grows.

🛡️ 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 Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routers/grievances.py` around lines 88 - 125, get_grievances_for_map
currently calls query.all() with no error handling or limit; wrap the DB work
(the query, optional status filter, .limit(...), and query.all() usage) in a
try/except that catches SQL/DB errors, logs the exception (use the module logger
or raise a fastapi.HTTPException with status_code=500 and a generic message) and
returns an appropriate 500 response; add a new optional parameter like limit:
int = 100 (and enforce a sane max, e.g., min(limit, 1000)) and apply it with
query = query.limit(limit) before .all(); keep the existing field mapping from
results (g.id, g.category, g.severity, g.status, g.latitude, g.longitude,
g.assigned_authority) but ensure you still access enum values safely
(g.severity.value / g.status.value) inside the try block so exceptions are
handled.

@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"""
Expand Down
5 changes: 5 additions & 0 deletions backend/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
import shutil
import logging
import io

import secrets
import string

import uuid

from typing import Optional

from backend.cache import user_upload_cache
Expand Down Expand Up @@ -325,3 +329,4 @@ def generate_reference_id() -> str:
timestamp = datetime.now().strftime('%Y%m%d-%H%M%S')
random_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=4))
return f"VOICE-{timestamp}-{random_suffix}"

14 changes: 13 additions & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

GrievanceMap is eagerly imported, breaking code-splitting for the entire app.

Every other view component (lines 14–44) is loaded with React.lazy(). This eager import forces Leaflet, react-leaflet, and react-leaflet-cluster (~150 KB+ minified) into the initial bundle, increasing load time for all users regardless of whether they visit the map.

♻️ Proposed fix — convert to lazy import
-import GrievanceMap from "./views/GrievanceMap";
+const GrievanceMap = React.lazy(() => import('./views/GrievanceMap'));

The existing <Suspense fallback={<LoadingSpinner size="lg" />}> wrapper at line 208 already covers all lazy routes, so no other changes are needed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import GrievanceMap from "./views/GrievanceMap";
const GrievanceMap = React.lazy(() => import('./views/GrievanceMap'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/App.jsx` at line 11, GrievanceMap is being eagerly imported
which pulls heavy map libs into the initial bundle; change the top-level import
of GrievanceMap to a lazy load by replacing the static import with React.lazy(()
=> import("./views/GrievanceMap")) and keep the existing <Suspense
fallback={<LoadingSpinner size="lg" />}> wrapper that already wraps the routes
so the lazy route is suspended correctly; update any references to the component
name GrievanceMap (used in your routes) to use the lazy-loaded symbol.


// Lazy Load Views
const Landing = React.lazy(() => import('./views/Landing'));
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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>
Expand Down
45 changes: 32 additions & 13 deletions frontend/src/api/grievances.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Allow 0 values for limit/offset.

Truthy checks skip 0, which is often a valid value (especially for offset). Prefer explicit != null checks.

✅ 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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 != null) queryParams.append('limit', params.limit);
if (params.offset != null) queryParams.append('offset', params.offset);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/api/grievances.js` around lines 24 - 27, The current truthy
checks skip numeric 0 for params.limit and params.offset; change the conditions
to explicit null/undefined checks so 0 is accepted — e.g., replace
"if(params.limit)" and "if(params.offset)" with checks like "if (params.limit !=
null)" and "if (params.offset != null)" around the queryParams.append calls in
the grievances API (the params.limit/params.offset branches that call
queryParams.append).


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`);
Expand All @@ -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}`);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate Leaflet CSS import — GrievanceMap.jsx also imports leaflet/dist/leaflet.css.

Global stylesheets are idempotent but importing from two places is redundant. Since main.jsx is the global entry point, the import in GrievanceMap.jsx (line 6) can be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/main.jsx` at line 7, Remove the duplicate global stylesheet
import by deleting the line importing "leaflet/dist/leaflet.css" from
GrievanceMap.jsx so the single import in main.jsx remains the authoritative
global import; locate the import statement in GrievanceMap.jsx (the `import
"leaflet/dist/leaflet.css";` line) and remove it, leaving any component-specific
styles intact.


createRoot(document.getElementById('root')).render(
<StrictMode>
Expand Down
102 changes: 102 additions & 0 deletions frontend/src/views/GrievanceMap.jsx
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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove console.log — it dumps sensitive grievance data (lat/lon, authority, status) to the browser console in production.

🐛 Proposed fix
-            console.log("Map data:", data);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log("Map data:", data);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/views/GrievanceMap.jsx` at line 19, Remove the unconditional
console.log that prints sensitive grievance data (the line console.log("Map
data:", data) in the GrievanceMap.jsx component) — either delete it completely
or wrap it in a development-only guard (e.g., NODE_ENV check) and/or log a
non-sensitive, sanitized summary instead; locate the log in the GrievanceMap
component where the variable data is available and replace the console.log with
a conditional/dev-only or sanitized logging approach.


setGrievances(data);
} catch(err) {
console.error("Error loading map data:", err);
}
};
Comment on lines +15 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

No loading or error feedback — the map silently stays empty on slow networks or API failures.

loadMapData errors are swallowed into console.error. Add a loading / error state and render appropriate feedback in the map container.

♻️ 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 <div className="h-screen w-full"> wrapper before displaying the map.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/views/GrievanceMap.jsx` around lines 15 - 25, The map currently
swallows errors and provides no loading feedback; update GrievanceMap.jsx by
adding local state via useState for loading and error (e.g., const [loading,
setLoading] = useState(false); const [error, setError] = useState(null)), then
modify loadMapData to setLoading(true) and setError(null) at start, assign
setGrievances(data) on success and setLoading(false), and in the catch
setError(err) and setLoading(false); finally render conditional UI inside the
<div className="h-screen w-full"> wrapper to show a spinner when loading and an
error banner/message when error is non-null before rendering the map.

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

h-screen causes the map to overflow below the Navbar.

AppContent renders a <Navbar /> above <main> (see App.jsx lines 205–206). With h-screen on the root div, the map's total height equals the full viewport, pushing content below the fold. Use a calculated height or CSS flex-grow to fill only the remaining viewport.

♻️ 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 flex-1:

-    <div className="h-screen w-full">
+    <div className="flex-1 w-full min-h-0">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div className="h-screen w-full">
<MapContainer
center={[20.5937, 78.9629]}
zoom={5}
style={{height: "100%", width:"100%"}}
return (
<div className="h-[calc(100vh-64px)] w-full"> {/* adjust 64px to actual Navbar height */}
<MapContainer
center={[20.5937, 78.9629]}
zoom={5}
style={{height: "100%", width:"100%"}}
Suggested change
return (
<div className="h-screen w-full">
<MapContainer
center={[20.5937, 78.9629]}
zoom={5}
style={{height: "100%", width:"100%"}}
return (
<div className="flex-1 w-full min-h-0">
<MapContainer
center={[20.5937, 78.9629]}
zoom={5}
style={{height: "100%", width:"100%"}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/views/GrievanceMap.jsx` around lines 49 - 54, The root div in
the GrievanceMap component uses Tailwind's h-screen which forces the map to fill
the full viewport and push content below the Navbar; update the container (the
div that wraps MapContainer in GrievanceMap.jsx) to not use h-screen but instead
use a flexible height (e.g., replace h-screen with flex-1 or apply a calculated
height like calc(100vh - <navbarHeight>)) and ensure the parent layout uses a
column flex so MapContainer can grow to fill remaining space; target the
GrievanceMap component's root div and the MapContainer wrapper when making this
change.

>
<TileLayer
attribution='&copy; <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;
12 changes: 10 additions & 2 deletions frontend/src/views/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { createPortal } from 'react-dom';
import { useNavigate } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';

import { motion, AnimatePresence } from 'framer-motion';

import {
AlertTriangle, MapPin, Search, Activity, Camera, Trash2, ThumbsUp, Brush,
Droplets, Zap, Truck, Flame, Dog, XCircle, Lightbulb, TreeDeciduous, Bug,
Expand Down Expand Up @@ -253,7 +255,13 @@ const Home = ({ setView, fetchResponsibilityMap, recentIssues, handleUpvote, loa
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ delay: itemIdx * 0.05 }}
onClick={() => setView(item.id)}
onClick={() => {
if(item.id === "map"){
navigate("/grievance-map");
} else {
setView(item.id)
}
}}
className="group bg-white/50 dark:bg-gray-900/50 backdrop-blur-xl rounded-[2rem] border border-white/50 dark:border-gray-800/50 p-8 flex flex-col items-start gap-6 hover:shadow-2xl hover:bg-white dark:hover:bg-gray-800 transition-all duration-300 h-56"
>
<div className={`${item.bg} ${item.color} p-4 rounded-2xl shadow-sm transition-transform group-hover:scale-110 duration-300`}>
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/views/ReportForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
},
Expand All @@ -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
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 24, 2026

Choose a reason for hiding this comment

The 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
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/views/ReportForm.jsx, line 289:

<comment>Missing fallback when geolocation is unsupported leaves gettingLocation true and no error, which can permanently disable the Get Pin button on unsupported browsers.</comment>

<file context>
@@ -283,10 +285,11 @@ const ReportForm = ({ setView, setLoading, setError, setActionPlan, loading }) =
-      setError("Geolocation is not supported by this browser.");
-      setGettingLocation(false);
     }
+    //  else {
+    //   setError("Geolocation is not supported by this browser.");
+    //   setGettingLocation(false);
</file context>
Suggested change
// else {
// setError("Geolocation is not supported by this browser.");
// setGettingLocation(false);
// }
else {
setError("Geolocation is not supported by this browser.");
setGettingLocation(false);
}
Fix with Cubic

};

const checkNearbyIssues = async () => {
Expand Down