Skip to content

feat: Add comprehensive AI detectors for traffic, safety, and facilities#404

Open
RohanExploit wants to merge 4 commits intomainfrom
feat-new-detectors-ui-17892053941000703438
Open

feat: Add comprehensive AI detectors for traffic, safety, and facilities#404
RohanExploit wants to merge 4 commits intomainfrom
feat-new-detectors-ui-17892053941000703438

Conversation

@RohanExploit
Copy link
Owner

@RohanExploit RohanExploit commented Feb 16, 2026

This PR introduces several new AI-powered detectors to the platform, leveraging CLIP (Contrastive Language-Image Pre-training) via Hugging Face API for zero-shot classification.

New Detectors:

  • Traffic Sign Detector: Identifies damaged or vandalized traffic signs.
  • Abandoned Vehicle Detector: Detects abandoned or wrecked vehicles.
  • Public Facilities Detector: Checks for broken benches and playground equipment.
  • Construction Safety Detector: Identifies unsafe scaffolding and missing barriers.
  • Waste Sorter: Classifies waste types for proper disposal.
  • Water Leak & Crowd Density: Refactored existing implementations to use the new API pattern.

Technical Details:

  • Backend: New endpoints added to backend/routers/detection.py that call into backend/hf_api_service.py. The service uses clip-vit-base-patch32 for efficient zero-shot classification.
  • Frontend: New React components added for each detector using react-webcam. The UI is consistent with existing detectors. Home.jsx is updated to categorize these new tools.
  • Testing: Backend unit tests added for new logic. Frontend verification script added to ensure UI navigability and rendering.

Note: The implementation prioritizes resource efficiency by using lightweight API calls to a shared model rather than loading heavy models locally.


PR created automatically by Jules for task 17892053941000703438 started by @RohanExploit


Summary by cubic

Add four CLIP-based detectors (Traffic Sign, Abandoned Vehicle, Public Facilities, Construction Safety) with backend endpoints, routes, and simple camera UIs. Netlify builds are fixed by running the build from the frontend directory and publishing the correct path.

  • New Features

    • Backend: add /api/detect-traffic-sign, /api/detect-abandoned-vehicle, /api/detect-public-facilities, /api/detect-construction-safety using clip-vit-base-patch32.
    • Frontend: new screens and routes (/traffic-sign, /abandoned-vehicle, /public-facilities, /construction-safety); updated Home and added a /home route.
    • Tests: unit tests for new endpoints; Playwright script (verification/verify_detectors.py) to validate navigation and pages.
  • Refactors

    • CrowdDetector and WaterLeakDetector now use detectorsApi; new methods added to api/detectors.js.
    • Netlify: netlify.toml now runs "cd frontend && npm install && npm run build" and publishes "frontend/dist"; removes base; build tools moved to dependencies to ensure production installs.

Written for commit fbd4f8e. Summary will update on new commits.

- Backend:
  - Add `detect_traffic_sign_clip`, `detect_abandoned_vehicle_clip`, `detect_public_facilities_clip`, `detect_construction_safety_clip`, `detect_waste_clip` in `backend/hf_api_service.py` using CLIP.
  - Expose new endpoints in `backend/routers/detection.py`.
  - Add backend tests in `backend/tests/test_new_detectors.py`.
- Frontend:
  - Create `TrafficSignDetector.jsx`, `AbandonedVehicleDetector.jsx`, `PublicFacilitiesDetector.jsx`, `ConstructionSafetyDetector.jsx`, `WasteDetector.jsx`.
  - Update `CrowdDetector.jsx` and `WaterLeakDetector.jsx` to use centralized API client.
  - Register new routes in `App.jsx`.
  - Add detector buttons to `Home.jsx` categories.
  - Update `api/detectors.js` with new methods.
  - Update English translations in `locales/en.json`.
- Verification:
  - Add `verification/verify_detectors.py` playwright script for UI testing.
Copilot AI review requested due to automatic review settings February 16, 2026 18:07
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@github-actions
Copy link

🙏 Thank you for your contribution, @RohanExploit!

PR Details:

Quality Checklist:
Please ensure your PR meets the following criteria:

  • Code follows the project's style guidelines
  • Self-review of code completed
  • Code is commented where necessary
  • Documentation updated (if applicable)
  • No new warnings generated
  • Tests added/updated (if applicable)
  • All tests passing locally
  • No breaking changes to existing functionality

Review Process:

  1. Automated checks will run on your code
  2. A maintainer will review your changes
  3. Address any requested changes promptly
  4. Once approved, your PR will be merged! 🎉

Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Warning

Rate limit exceeded

@RohanExploit has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 59 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-new-detectors-ui-17892053941000703438

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link

netlify bot commented Feb 16, 2026

Deploy Preview for fixmybharat failed. Why did it fail? →

Name Link
🔨 Latest commit fbd4f8e
🔍 Latest deploy log https://app.netlify.com/projects/fixmybharat/deploys/699366691359700008a29251

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

9 issues found across 23 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/TrafficSignDetector.jsx">

<violation number="1" location="frontend/src/TrafficSignDetector.jsx:36">
P1: Potential runtime crash: `setDetections(data.detections)` is called before the null check. If the API returns `null`/`undefined` for `detections`, the component state becomes non-array, causing `detections.length` and `detections.map()` in the JSX to throw a `TypeError` and crash the component. Guard the value before setting state.</violation>
</file>

<file name="frontend/src/ConstructionSafetyDetector.jsx">

<violation number="1" location="frontend/src/ConstructionSafetyDetector.jsx:36">
P1: If `data.detections` is `undefined` (e.g., unexpected API response shape), `setDetections(data.detections)` sets state to `undefined`. On the next render, `detections.length` in the JSX will throw a `TypeError`. Use a fallback to guarantee the state is always an array.</violation>
</file>

<file name="frontend/src/App.jsx">

<violation number="1" location="frontend/src/App.jsx:314">
P2: All new detector routes' `onBack` callbacks navigate to `'/'` which renders the `Landing` page (intercepted by the `isLandingPage` guard), not the `Home` dashboard. Since this PR specifically changed `navigateToView('home')` to use `'/home'` instead of `'/'`, the `onBack` callbacks should likely also navigate to `'/home'` for consistency.</violation>
</file>

<file name="backend/routers/detection.py">

<violation number="1" location="backend/routers/detection.py:446">
P2: This endpoint uses raw `await image.read()` without the validation and optimization that most other CLIP-based endpoints in this file perform via `process_uploaded_image(image)`. This skips file size limits, MIME type validation, image integrity checks, resizing, and EXIF stripping. The same issue applies to `detect_construction_safety_endpoint` below.

Consider using `process_uploaded_image` for consistency and security:</violation>
</file>

<file name="frontend/src/WaterLeakDetector.jsx">

<violation number="1" location="frontend/src/WaterLeakDetector.jsx:80">
P2: Potential `TypeError` if `apiClient.postForm` returns `null` (when response content-type is not JSON). Use optional chaining to safely access `data.detections`.</violation>
</file>

<file name="frontend/src/PublicFacilitiesDetector.jsx">

<violation number="1" location="frontend/src/PublicFacilitiesDetector.jsx:36">
P1: If `data.detections` is `null` or `undefined`, this sets state to a non-array value, causing `detections.length` and `detections.map(...)` in the JSX to throw a `TypeError` on the next render. Use a fallback to ensure state is always an array.</violation>
</file>

<file name="verification/verify_detectors.py">

<violation number="1" location="verification/verify_detectors.py:19">
P2: Bare `except: pass` silently swallows all exceptions, including `KeyboardInterrupt` and `SystemExit`. Catch `Exception` (or better, `playwright.sync_api.TimeoutError`) to only suppress the expected timeout while allowing critical signals to propagate.</violation>

<violation number="2" location="verification/verify_detectors.py:57">
P2: Missing `wait_for_timeout()` after scroll. The Traffic Sign section waits 1 s after `scrollBy` before checking visibility, but this section does not, causing a potential race where the button is not yet in the viewport when `is_visible()` is called.</violation>
</file>

<file name="frontend/src/AbandonedVehicleDetector.jsx">

<violation number="1" location="frontend/src/AbandonedVehicleDetector.jsx:36">
P1: If `data.detections` is `undefined` or `null`, `setDetections(data.detections)` stores a non-array value in state. This will cause a runtime crash on the next render when the JSX evaluates `detections.length > 0`, since `undefined`/`null` has no `.length` property. Use a fallback to ensure state is always an array.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


const data = await detectorsApi.trafficSign(formData);

setDetections(data.detections);
Copy link

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

Choose a reason for hiding this comment

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

P1: Potential runtime crash: setDetections(data.detections) is called before the null check. If the API returns null/undefined for detections, the component state becomes non-array, causing detections.length and detections.map() in the JSX to throw a TypeError and crash the component. Guard the value before setting state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/TrafficSignDetector.jsx, line 36:

<comment>Potential runtime crash: `setDetections(data.detections)` is called before the null check. If the API returns `null`/`undefined` for `detections`, the component state becomes non-array, causing `detections.length` and `detections.map()` in the JSX to throw a `TypeError` and crash the component. Guard the value before setting state.</comment>

<file context>
@@ -0,0 +1,93 @@
+
+        const data = await detectorsApi.trafficSign(formData);
+
+        setDetections(data.detections);
+        if (!data.detections || data.detections.length === 0) {
+            alert("No specific traffic sign detected.");
</file context>
Fix with Cubic


const data = await detectorsApi.constructionSafety(formData);

setDetections(data.detections);
Copy link

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

Choose a reason for hiding this comment

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

P1: If data.detections is undefined (e.g., unexpected API response shape), setDetections(data.detections) sets state to undefined. On the next render, detections.length in the JSX will throw a TypeError. Use a fallback to guarantee the state is always an array.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/ConstructionSafetyDetector.jsx, line 36:

<comment>If `data.detections` is `undefined` (e.g., unexpected API response shape), `setDetections(data.detections)` sets state to `undefined`. On the next render, `detections.length` in the JSX will throw a `TypeError`. Use a fallback to guarantee the state is always an array.</comment>

<file context>
@@ -0,0 +1,93 @@
+
+        const data = await detectorsApi.constructionSafety(formData);
+
+        setDetections(data.detections);
+        if (!data.detections || data.detections.length === 0) {
+            alert("No safety violations detected.");
</file context>
Fix with Cubic


const data = await detectorsApi.publicFacilities(formData);

setDetections(data.detections);
Copy link

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

Choose a reason for hiding this comment

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

P1: If data.detections is null or undefined, this sets state to a non-array value, causing detections.length and detections.map(...) in the JSX to throw a TypeError on the next render. Use a fallback to ensure state is always an array.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/PublicFacilitiesDetector.jsx, line 36:

<comment>If `data.detections` is `null` or `undefined`, this sets state to a non-array value, causing `detections.length` and `detections.map(...)` in the JSX to throw a `TypeError` on the next render. Use a fallback to ensure state is always an array.</comment>

<file context>
@@ -0,0 +1,93 @@
+
+        const data = await detectorsApi.publicFacilities(formData);
+
+        setDetections(data.detections);
+        if (!data.detections || data.detections.length === 0) {
+            alert("No damage detected.");
</file context>
Fix with Cubic


const data = await detectorsApi.abandonedVehicle(formData);

setDetections(data.detections);
Copy link

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

Choose a reason for hiding this comment

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

P1: If data.detections is undefined or null, setDetections(data.detections) stores a non-array value in state. This will cause a runtime crash on the next render when the JSX evaluates detections.length > 0, since undefined/null has no .length property. Use a fallback to ensure state is always an array.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/AbandonedVehicleDetector.jsx, line 36:

<comment>If `data.detections` is `undefined` or `null`, `setDetections(data.detections)` stores a non-array value in state. This will cause a runtime crash on the next render when the JSX evaluates `detections.length > 0`, since `undefined`/`null` has no `.length` property. Use a fallback to ensure state is always an array.</comment>

<file context>
@@ -0,0 +1,93 @@
+
+        const data = await detectorsApi.abandonedVehicle(formData);
+
+        setDetections(data.detections);
+        if (!data.detections || data.detections.length === 0) {
+            alert("No abandoned vehicle detected.");
</file context>
Fix with Cubic

Comment on lines +314 to +320
<Route path="/traffic-sign" element={<TrafficSignDetector onBack={() => navigate('/')} />} />
<Route path="/abandoned-vehicle" element={<AbandonedVehicleDetector onBack={() => navigate('/')} />} />
<Route path="/public-facilities" element={<PublicFacilitiesDetector onBack={() => navigate('/')} />} />
<Route path="/construction-safety" element={<ConstructionSafetyDetector onBack={() => navigate('/')} />} />
<Route path="/water-leak" element={<WaterLeakDetector onBack={() => navigate('/')} />} />
<Route path="/crowd" element={<CrowdDetector onBack={() => navigate('/')} />} />
<Route path="/waste" element={<WasteDetector onBack={() => navigate('/')} />} />
Copy link

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

Choose a reason for hiding this comment

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

P2: All new detector routes' onBack callbacks navigate to '/' which renders the Landing page (intercepted by the isLandingPage guard), not the Home dashboard. Since this PR specifically changed navigateToView('home') to use '/home' instead of '/', the onBack callbacks should likely also navigate to '/home' for consistency.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/App.jsx, line 314:

<comment>All new detector routes' `onBack` callbacks navigate to `'/'` which renders the `Landing` page (intercepted by the `isLandingPage` guard), not the `Home` dashboard. Since this PR specifically changed `navigateToView('home')` to use `'/home'` instead of `'/'`, the `onBack` callbacks should likely also navigate to `'/home'` for consistency.</comment>

<file context>
@@ -293,6 +311,13 @@ function AppContent() {
             <Route path="/smart-scan" element={<SmartScanner onBack={() => navigate('/')} />} />
             <Route path="/grievance-analysis" element={<GrievanceAnalysis onBack={() => navigate('/')} />} />
             <Route path="/noise" element={<NoiseDetector onBack={() => navigate('/')} />} />
+            <Route path="/traffic-sign" element={<TrafficSignDetector onBack={() => navigate('/')} />} />
+            <Route path="/abandoned-vehicle" element={<AbandonedVehicleDetector onBack={() => navigate('/')} />} />
+            <Route path="/public-facilities" element={<PublicFacilitiesDetector onBack={() => navigate('/')} />} />
</file context>
Suggested change
<Route path="/traffic-sign" element={<TrafficSignDetector onBack={() => navigate('/')} />} />
<Route path="/abandoned-vehicle" element={<AbandonedVehicleDetector onBack={() => navigate('/')} />} />
<Route path="/public-facilities" element={<PublicFacilitiesDetector onBack={() => navigate('/')} />} />
<Route path="/construction-safety" element={<ConstructionSafetyDetector onBack={() => navigate('/')} />} />
<Route path="/water-leak" element={<WaterLeakDetector onBack={() => navigate('/')} />} />
<Route path="/crowd" element={<CrowdDetector onBack={() => navigate('/')} />} />
<Route path="/waste" element={<WasteDetector onBack={() => navigate('/')} />} />
<Route path="/traffic-sign" element={<TrafficSignDetector onBack={() => navigate('/home')} />} />
<Route path="/abandoned-vehicle" element={<AbandonedVehicleDetector onBack={() => navigate('/home')} />} />
<Route path="/public-facilities" element={<PublicFacilitiesDetector onBack={() => navigate('/home')} />} />
<Route path="/construction-safety" element={<ConstructionSafetyDetector onBack={() => navigate('/home')} />} />
<Route path="/water-leak" element={<WaterLeakDetector onBack={() => navigate('/home')} />} />
<Route path="/crowd" element={<CrowdDetector onBack={() => navigate('/home')} />} />
<Route path="/waste" element={<WasteDetector onBack={() => navigate('/home')} />} />
Fix with Cubic

@router.post("/api/detect-public-facilities")
async def detect_public_facilities_endpoint(request: Request, image: UploadFile = File(...)):
try:
image_bytes = await image.read()
Copy link

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

Choose a reason for hiding this comment

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

P2: This endpoint uses raw await image.read() without the validation and optimization that most other CLIP-based endpoints in this file perform via process_uploaded_image(image). This skips file size limits, MIME type validation, image integrity checks, resizing, and EXIF stripping. The same issue applies to detect_construction_safety_endpoint below.

Consider using process_uploaded_image for consistency and security:

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/routers/detection.py, line 446:

<comment>This endpoint uses raw `await image.read()` without the validation and optimization that most other CLIP-based endpoints in this file perform via `process_uploaded_image(image)`. This skips file size limits, MIME type validation, image integrity checks, resizing, and EXIF stripping. The same issue applies to `detect_construction_safety_endpoint` below.

Consider using `process_uploaded_image` for consistency and security:</comment>

<file context>
@@ -436,3 +438,37 @@ async def detect_abandoned_vehicle_endpoint(request: Request, image: UploadFile
+@router.post("/api/detect-public-facilities")
+async def detect_public_facilities_endpoint(request: Request, image: UploadFile = File(...)):
+    try:
+        image_bytes = await image.read()
+    except Exception as e:
+        logger.error(f"Invalid image file: {e}", exc_info=True)
</file context>
Fix with Cubic

if (response.ok) {
const data = await response.json();
const data = await detectorsApi.waterLeak(formData);
if (data.detections) {
Copy link

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

Choose a reason for hiding this comment

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

P2: Potential TypeError if apiClient.postForm returns null (when response content-type is not JSON). Use optional chaining to safely access data.detections.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/WaterLeakDetector.jsx, line 80:

<comment>Potential `TypeError` if `apiClient.postForm` returns `null` (when response content-type is not JSON). Use optional chaining to safely access `data.detections`.</comment>

<file context>
@@ -77,13 +76,8 @@ const WaterLeakDetector = ({ onBack }) => {
-                if (response.ok) {
-                    const data = await response.json();
+                const data = await detectorsApi.waterLeak(formData);
+                if (data.detections) {
                     drawDetections(data.detections, context);
                 }
</file context>
Suggested change
if (data.detections) {
if (data?.detections) {
Fix with Cubic

traffic_btn.click()
else:
print("Traffic Sign button NOT visible. Scrolling...")
page.evaluate("window.scrollBy(0, 500)")
Copy link

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

Choose a reason for hiding this comment

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

P2: Missing wait_for_timeout() after scroll. The Traffic Sign section waits 1 s after scrollBy before checking visibility, but this section does not, causing a potential race where the button is not yet in the viewport when is_visible() is called.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At verification/verify_detectors.py, line 57:

<comment>Missing `wait_for_timeout()` after scroll. The Traffic Sign section waits 1 s after `scrollBy` before checking visibility, but this section does not, causing a potential race where the button is not yet in the viewport when `is_visible()` is called.</comment>

<file context>
@@ -0,0 +1,249 @@
+                 traffic_btn.click()
+            else:
+                 print("Traffic Sign button NOT visible. Scrolling...")
+                 page.evaluate("window.scrollBy(0, 500)")
+                 page.wait_for_timeout(1000)
+                 if traffic_btn.is_visible():
</file context>
Fix with Cubic


try:
page.wait_for_load_state("networkidle", timeout=5000)
except:
Copy link

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

Choose a reason for hiding this comment

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

P2: Bare except: pass silently swallows all exceptions, including KeyboardInterrupt and SystemExit. Catch Exception (or better, playwright.sync_api.TimeoutError) to only suppress the expected timeout while allowing critical signals to propagate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At verification/verify_detectors.py, line 19:

<comment>Bare `except: pass` silently swallows all exceptions, including `KeyboardInterrupt` and `SystemExit`. Catch `Exception` (or better, `playwright.sync_api.TimeoutError`) to only suppress the expected timeout while allowing critical signals to propagate.</comment>

<file context>
@@ -0,0 +1,249 @@
+
+        try:
+             page.wait_for_load_state("networkidle", timeout=5000)
+        except:
+             pass
+
</file context>
Fix with Cubic

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces comprehensive AI-powered detectors using CLIP (Contrastive Language-Image Pre-training) via Hugging Face API for zero-shot classification. The changes add six new detection capabilities while refactoring two existing detectors to use a consistent API pattern.

Changes:

  • Added four new detector endpoints and components: Traffic Sign, Abandoned Vehicle, Public Facilities, and Construction Safety detectors
  • Refactored Water Leak and Crowd detectors to use centralized API service
  • Updated UI to categorize new detectors in appropriate categories (Road & Traffic, Environment & Safety, Management)

Reviewed changes

Copilot reviewed 14 out of 23 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
backend/hf_api_service.py Added detect_public_facilities_clip and detect_construction_safety_clip functions using CLIP model
backend/routers/detection.py Added four new detection endpoints (/api/detect-traffic-sign, /api/detect-abandoned-vehicle, /api/detect-public-facilities, /api/detect-construction-safety) and imported new detector functions
backend/tests/test_new_detectors.py Added unit tests for new detection endpoints with mocked HuggingFace API responses
frontend/src/TrafficSignDetector.jsx New React component for traffic sign detection using react-webcam with capture-analyze workflow
frontend/src/AbandonedVehicleDetector.jsx New React component for abandoned vehicle detection with similar UI pattern
frontend/src/PublicFacilitiesDetector.jsx New React component for public facilities damage detection
frontend/src/ConstructionSafetyDetector.jsx New React component for construction safety violation detection
frontend/src/WaterLeakDetector.jsx Refactored to use centralized detectorsApi instead of direct fetch calls
frontend/src/CrowdDetector.jsx Refactored to use centralized detectorsApi instead of direct fetch calls
frontend/src/App.jsx Added lazy-loaded imports, routes, and navigation support for all new detectors; added duplicate /home route
frontend/src/views/Home.jsx Added new detector buttons to Road & Traffic, Environment & Safety, and Management categories; imported motion from framer-motion
frontend/src/api/detectors.js Added API methods for six new/refactored detectors using createDetectorApi helper
frontend/src/locales/en.json Added translations for publicFacilities and constructionSafety
verification/verify_detectors.py New Playwright-based verification script to test UI navigation and rendering of all new detectors
verification/error_abandoned_vehicle.png Binary test artifact PNG file committed to repository

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +443 to +474
@router.post("/api/detect-public-facilities")
async def detect_public_facilities_endpoint(request: Request, image: UploadFile = File(...)):
try:
image_bytes = await image.read()
except Exception as e:
logger.error(f"Invalid image file: {e}", exc_info=True)
raise HTTPException(status_code=400, detail="Invalid image file")

try:
client = get_http_client(request)
detections = await detect_public_facilities_clip(image_bytes, client=client)
return {"detections": detections}
except Exception as e:
logger.error(f"Public facilities detection error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")


@router.post("/api/detect-construction-safety")
async def detect_construction_safety_endpoint(request: Request, image: UploadFile = File(...)):
try:
image_bytes = await image.read()
except Exception as e:
logger.error(f"Invalid image file: {e}", exc_info=True)
raise HTTPException(status_code=400, detail="Invalid image file")

try:
client = get_http_client(request)
detections = await detect_construction_safety_clip(image_bytes, client=client)
return {"detections": detections}
except Exception as e:
logger.error(f"Construction safety detection error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error")
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The new detector endpoints use plain await image.read() instead of the await process_uploaded_image(image) helper used by other endpoints (like detect-water-leak at line 216 and detect-crowd at line 244). This bypasses important security validation including file size limits, MIME type checks, EXIF stripping, and image resizing. This creates a security vulnerability and inconsistency with established patterns. Use _, image_bytes = await process_uploaded_image(image) to match the existing pattern.

Copilot uses AI. Check for mistakes.
import Webcam from 'react-webcam';
import { detectorsApi } from './api/detectors';

const PublicFacilitiesDetector = () => {
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The component doesn't accept or use the onBack prop that is passed from App.jsx (line 316), but other detector components like NoiseDetector and WasteDetector do. While not strictly necessary if users can navigate back via browser controls, this creates an inconsistency in the component API. Consider adding the prop to the component signature for consistency: const PublicFacilitiesDetector = ({ onBack }) => { even if not actively used, or add a back button that calls it.

Suggested change
const PublicFacilitiesDetector = () => {
const PublicFacilitiesDetector = ({ onBack }) => {

Copilot uses AI. Check for mistakes.
import Webcam from 'react-webcam';
import { detectorsApi } from './api/detectors';

const ConstructionSafetyDetector = () => {
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The component doesn't accept or use the onBack prop that is passed from App.jsx (line 317), but other detector components like NoiseDetector and WasteDetector do. While not strictly necessary if users can navigate back via browser controls, this creates an inconsistency in the component API. Consider adding the prop to the component signature for consistency: const ConstructionSafetyDetector = ({ onBack }) => { even if not actively used, or add a back button that calls it.

Suggested change
const ConstructionSafetyDetector = () => {
const ConstructionSafetyDetector = ({ onBack }) => {

Copilot uses AI. Check for mistakes.
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', 'login', 'signup', 'traffic-sign', 'abandoned-vehicle', 'public-facilities', 'construction-safety', 'water-leak', 'crowd', 'waste'];
if (validViews.includes(view)) {
navigate(view === 'home' ? '/' : `/${view}`);
navigate(view === 'home' ? '/home' : `/${view}`);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The navigation logic was changed so that navigating to 'home' now goes to '/home' instead of '/'. While both routes render the same Home component (lines 208-228), this changes user-facing URLs and could break bookmarks or external links. This change should be documented in the PR description as it's a user-facing behavior change. Consider whether this is intentional or if the original behavior should be preserved.

Suggested change
navigate(view === 'home' ? '/home' : `/${view}`);
navigate(view === 'home' ? '/' : `/${view}`);

Copilot uses AI. Check for mistakes.
Comment on lines +218 to +228
<Route
path="/home"
element={
<Home
setView={navigateToView}
fetchResponsibilityMap={fetchResponsibilityMap}
recentIssues={recentIssues}
handleUpvote={handleUpvote}
/>
}
/>
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The Home component is now rendered at both '/' and '/home' routes with identical props. This creates code duplication - consider extracting the common Home route element to a variable or using a single route with both paths: <Route path={['/', '/home']} element={...} /> (React Router v6 syntax) or redirect one to the other to maintain a single source of truth.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +20
try:
page.wait_for_load_state("networkidle", timeout=5000)
except:
pass
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Bare except clause on lines 19-20 silently catches all exceptions including SystemExit and KeyboardInterrupt. While this is just a verification script and not production code, it's still better practice to catch specific exceptions or at least use except Exception: to avoid masking system-level exceptions that should propagate.

Copilot uses AI. Check for mistakes.
import Webcam from 'react-webcam';
import { detectorsApi } from './api/detectors';

const TrafficSignDetector = () => {
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The component doesn't accept or use the onBack prop that is passed from App.jsx (line 314), but other detector components like NoiseDetector and WasteDetector do. While not strictly necessary if users can navigate back via browser controls, this creates an inconsistency in the component API. Consider adding the prop to the component signature for consistency: const TrafficSignDetector = ({ onBack }) => { even if not actively used, or add a back button that calls it.

Suggested change
const TrafficSignDetector = () => {
const TrafficSignDetector = ({ onBack }) => {

Copilot uses AI. Check for mistakes.
import Webcam from 'react-webcam';
import { detectorsApi } from './api/detectors';

const AbandonedVehicleDetector = () => {
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The component doesn't accept or use the onBack prop that is passed from App.jsx (line 315), but other detector components like NoiseDetector and WasteDetector do. While not strictly necessary if users can navigate back via browser controls, this creates an inconsistency in the component API. Consider adding the prop to the component signature for consistency: const AbandonedVehicleDetector = ({ onBack }) => { even if not actively used, or add a back button that calls it.

Copilot uses AI. Check for mistakes.
- Move `vite`, `tailwindcss`, `postcss`, `autoprefixer`, `vite-plugin-pwa` from `devDependencies` to `dependencies` in `frontend/package.json`.
- This ensures these tools are installed in Netlify's production environment where `NODE_ENV=production` causes `devDependencies` to be skipped.
- Update `frontend/package-lock.json`.
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/package.json">

<violation number="1" location="frontend/package.json:35">
P2: Build-time packages (`vite`, `@vitejs/plugin-react`, `vite-plugin-pwa`, `tailwindcss`, `postcss`, `autoprefixer`) have been moved from `devDependencies` to `dependencies`. These are all build tools or build-time CSS processors and are not needed at runtime. This will unnecessarily bloat production installs (e.g., `npm install --production` or CI deployments that prune dev deps). Move them back to `devDependencies`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@@ -18,16 +18,22 @@
"dependencies": {
Copy link

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

Choose a reason for hiding this comment

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

P2: Build-time packages (vite, @vitejs/plugin-react, vite-plugin-pwa, tailwindcss, postcss, autoprefixer) have been moved from devDependencies to dependencies. These are all build tools or build-time CSS processors and are not needed at runtime. This will unnecessarily bloat production installs (e.g., npm install --production or CI deployments that prune dev deps). Move them back to devDependencies.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/package.json, line 35:

<comment>Build-time packages (`vite`, `@vitejs/plugin-react`, `vite-plugin-pwa`, `tailwindcss`, `postcss`, `autoprefixer`) have been moved from `devDependencies` to `dependencies`. These are all build tools or build-time CSS processors and are not needed at runtime. This will unnecessarily bloat production installs (e.g., `npm install --production` or CI deployments that prune dev deps). Move them back to `devDependencies`.</comment>

<file context>
@@ -18,16 +18,22 @@
-    "react-webcam": "^7.2.0"
+    "react-webcam": "^7.2.0",
+    "tailwindcss": "^3.4.1",
+    "vite": "^7.3.0",
+    "vite-plugin-pwa": "^1.2.0"
   },
</file context>
Fix with Cubic

- Set `base = "frontend"` in `netlify.toml` to correctly handle the monorepo structure.
- Update `command` to `npm install && npm run build` (executed within the `frontend` directory).
- Set `publish` to `dist` (relative to the `frontend` directory).
- Revert changes to `package.json` regarding `dependencies` vs `devDependencies` as moving them might not be strictly necessary if build context is correct, but keeping them if already committed is fine. (Wait, I kept them in dependencies in the previous step, so they are still there. This is safer).
- Update `package-lock.json` via `npm install` in `frontend` directory.
@github-actions
Copy link

🔍 Quality Reminder

Thanks for the updates! Please ensure:
- Your changes don't break existing functionality
- All tests still pass
- Code quality standards are maintained

*The maintainers will verify that the overall project flow remains intact.*

- Update `netlify.toml` to remove `base` property and instead use explicit `cd frontend && npm install && npm run build` command.
- Set `publish` to `frontend/dist`.
- Regenerate `frontend/package-lock.json` to ensure consistency with current environment.
- This approach avoids potential ambiguity with Netlify's base directory detection in monorepos.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments