Skip to content

resolved the issue about Emergency and High-Severity Grievance.#339

Open
Arman-op wants to merge 4 commits intoRohanExploit:mainfrom
Arman-op:Emergency-and-High-Severity-#290
Open

resolved the issue about Emergency and High-Severity Grievance.#339
Arman-op wants to merge 4 commits intoRohanExploit:mainfrom
Arman-op:Emergency-and-High-Severity-#290

Conversation

@Arman-op
Copy link

@Arman-op Arman-op commented Feb 5, 2026

Here is a summary of the changes implemented for the Priority Grievance Handling system:

  1. Database & Backend Logic
  • Database Schema: Added a severity column to the
    Issue table in models.py

  • API Updates: Updated
    routers/issues.py
    and schemas.py
    to accept and return severity levels (low, medium, high, critical) via the API.

  • Priority Queues: Modified
    routers/grievances.py
    to sort grievances by Severity first (Critical → Low) and then by Date, creating a true priority queue for officials.

  • Dynamic SLAs: Updated
    sla_config_service.py
    to enforce stricter deadlines based on urgency (e.g., Critical = 6 hours, Low = 72 hours).

  1. Frontend (User Interface)
    Report Form (ReportForm.jsx):
  • Added a Severity Dropdown allowing users to manually flag issues as Urgent/Critical.
  • Linked this to the AI analysis—if the AI detects a fire or major hazard, it automatically sets the severity to "Critical" (which the user can adjust).

Home Feed (Home.jsx):

  • Added a pulsing "URGENT" badge for High and Critical issues.
  • Applied a Red Highlight style to Critical issues to make them stand out immediately in the public feed.
  • Fixed
    nesting and syntax errors in the file.

Summary by CodeRabbit

  • New Features

    • Issues now include a severity field (Critical, High, Medium, Low) visible in creation and listings.
  • Behavior Changes

    • Grievances/issues are ordered by severity then date; SLA defaults and task handling now consider severity and trigger immediate escalation for critical items.
  • AI / Analysis

    • AI-powered urgency/severity analysis integrated into reporting and stored with submissions.
  • UI/UX Improvements

    • Severity badges/urgent indicators, multilingual labels, expanded quick-actions, 3D depth-map photo view, and enhanced nearby-issues flow.

@netlify
Copy link

netlify bot commented Feb 5, 2026

👷 Deploy request for fixmybharat pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit a1a1ea9

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

🙏 Thank you for your contribution, @Arman-op!

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 5, 2026

📝 Walkthrough

Walkthrough

Adds an indexed Issue.severity enum (default MEDIUM) and propagates severity through schemas, issue creation, grievance ordering, SLA lookup, background tasks/escalation, and frontend UI/analysis flows (ReportForm, Home, i18n).

Changes

Cohort / File(s) Summary
Data model
backend/models.py
Add Issue.severity: Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True) (plus two stray lines inserted in the diff).
Schemas
backend/schemas.py
Add severity fields to request/response models (IssueCreateRequest, IssueSummaryResponse, NearbyIssueResponse, IssueResponse), include validation/defaults and a model_config; stray tokens present in diff.
Issue endpoints
backend/routers/issues.py
Expose severity input on create_issue, persist severity on Issue creation, and include severity in recent-issues response (handles enum/value extraction).
Grievance listing
backend/routers/grievances.py
Import case and SeverityLevel; apply CASE-based ordering (CRITICAL→1, HIGH→2, MEDIUM→3, LOW→4) then by created_at.
SLA resolution
backend/sla_config_service.py
When no exact SLAConfig, return severity-aware default SLA hours (CRITICAL:6, HIGH:24, MEDIUM:48, LOW:72) before falling back to generic default.
Background tasks / escalation
backend/tasks.py
Prefer issue's severity when selecting SLA; for critical severity, log a warning with issue ID and a 12-char hash of description and trigger immediate escalation placeholder.
Frontend — ReportForm
frontend/src/views/ReportForm.jsx
Add severity state/UI, AI-driven severity display and propagation, set severity on submission (online/offline), integrate severity with image/urgency analysis and depth map UI.
Frontend — Home & i18n
frontend/src/views/Home.jsx, frontend/src/locales/en.json
Home refactor: expanded Quick Actions, use labelKey translations, added urgent/high-severity indicators/badges, accept loadMore props; add home.urgent and new issue labels in en.json.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant FE as Frontend (ReportForm / Home)
    participant Det as Detectors (AI)
    participant API as Backend API (routers)
    participant DB as Database
    participant Svc as Services (SLA / Tasks)

    User->>FE: request urgency analysis or submit report
    FE->>Det: analyzeUrgency / analyzeImage
    Det-->>FE: severity, confidence
    FE->>API: POST /create_issue (payload includes severity)
    API->>DB: insert Issue (with severity)
    DB-->>API: created
    API->>Svc: schedule background processing (issue_id)
    Svc->>DB: read Issue (severity)
    Svc->>Svc: determine SLA / escalate (severity-based)
    Svc-->>DB: create/update Grievance (use severity)
    User->>FE: view recent issues
    FE->>API: GET /recent_issues
    API->>DB: query ORDER BY severity_case, created_at
    DB-->>API: ordered issues (include severity)
    API-->>FE: recent issues
    FE->>User: render with severity badges/urgent indicators
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ECWoC26, ECWoC26-L3

Poem

🐰 I sniffed the levels, low to critical,
I hop and tag the urgent, prompt and lyrical.
A badge, a hash, a careful little nudge,
I sort the queue — no time to trudge.
Hop on — severity leads the judge.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly addresses the main objective of implementing emergency and high-severity grievance handling, which is the primary focus of the changeset across multiple backend and frontend components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

👋 Hello @Arman-op! Thank you for your first Pull Request to VishwaGuru! 🎉

We appreciate your contribution to this open source platform empowering India's youth to engage with democracy.

What happens next?

  • A maintainer will review your PR soon
  • Please ensure all tests pass and the code follows our style guidelines
  • Be ready to address any feedback or requested changes

Before your PR is merged:

  • Ensure your code follows the project's coding standards
  • All tests should pass
  • Update documentation if needed
  • Make sure your commits are clear and descriptive

Resources:

Thank you for contributing! 🚀

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
backend/schemas.py (1)

102-106: ⚠️ Potential issue | 🟡 Minor

Duplicate class definition - DetectionResponse is defined twice.

Lines 102-106 and 105-106 contain identical DetectionResponse class definitions. The second one overwrites the first. This appears to be a copy-paste error.

🧹 Proposed fix - remove duplicate
 class DetectionResponse(BaseModel):
     detections: List[Dict[str, Any]] = Field(..., description="List of detected objects/items")

-class DetectionResponse(BaseModel):
-    detections: List[Dict[str, Any]] = Field(..., description="List of detected objects/items")
-
 class UrgencyAnalysisRequest(BaseModel):
backend/routers/issues.py (2)

113-126: ⚠️ Potential issue | 🟡 Minor

Missing severity field in NearbyIssueResponse construction.

The NearbyIssueResponse schema includes a severity field (with default "medium"), but the actual issue severity is not being passed here. This causes all nearby issues to appear as "medium" severity regardless of their actual severity, which could mislead users during deduplication.

🐛 Proposed fix
                 nearby_responses = [
                     NearbyIssueResponse(
                         id=issue.id,
                         description=issue.description[:100] + "..." if len(issue.description) > 100 else issue.description,
                         category=issue.category,
                         latitude=issue.latitude,
                         longitude=issue.longitude,
                         distance_meters=distance,
                         upvotes=issue.upvotes or 0,
                         created_at=issue.created_at,
-                        status=issue.status
+                        status=issue.status,
+                        severity=issue.severity.value if hasattr(issue.severity, 'value') else (issue.severity or "medium")
                     )
                     for issue, distance in nearby_issues_with_distance[:3]  # Limit to top 3 closest
                 ]

273-286: ⚠️ Potential issue | 🟡 Minor

Missing severity field in NearbyIssueResponse construction (same issue).

The get_nearby_issues endpoint also constructs NearbyIssueResponse without passing the actual issue severity.

🐛 Proposed fix
         nearby_responses = [
             NearbyIssueResponse(
                 id=issue.id,
                 description=issue.description[:100] + "..." if len(issue.description) > 100 else issue.description,
                 category=issue.category,
                 latitude=issue.latitude,
                 longitude=issue.longitude,
                 distance_meters=distance,
                 upvotes=issue.upvotes or 0,
                 created_at=issue.created_at,
-                status=issue.status
+                status=issue.status,
+                severity=issue.severity.value if hasattr(issue.severity, 'value') else (issue.severity or "medium")
             )
             for issue, distance in nearby_issues_with_distance[:limit]
         ]
frontend/src/views/ReportForm.jsx (2)

199-232: ⚠️ Potential issue | 🟡 Minor

Memory leak: URL.createObjectURL is never revoked.

The blob URL created on line 230 persists until page unload. This can accumulate if users upload multiple images during a session.

🔧 Proposed fix to revoke the object URL
       img.onload = () => {
+        URL.revokeObjectURL(img.src);
         // Calculate new dimensions
         let { width, height } = img;

249-259: ⚠️ Potential issue | 🟡 Minor

Stale closure risk: use functional state updates in async handlers.

Lines 249 and 259 use { ...formData, image: ... } inside an async function. If the user modifies other form fields (e.g., description) while image processing is in progress, those changes will be overwritten when setFormData executes with the stale formData reference.

🔧 Proposed fix using functional updates
-        setFormData({ ...formData, image: processedFile });
+        setFormData(prev => ({ ...prev, image: processedFile }));
         
         // Analyze in parallel but with error handling
         await Promise.allSettled([
           analyzeImage(processedFile),
           analyzeSmartScan(processedFile)
         ]);
       } catch (error) {
         console.error('Image processing failed:', error);
         // Fallback to original file
-        setFormData({ ...formData, image: file });
+        setFormData(prev => ({ ...prev, image: file }));
frontend/src/views/Home.jsx (1)

153-325: ⚠️ Potential issue | 🟡 Minor

Localize new Quick Actions labels.
Several newly added button labels are hard-coded (e.g., “Report Issue”, “Pothole”, “Garbage”, etc.), which bypasses i18n. Please route these through t(...) and add the corresponding keys to locale files.

Suggested pattern
- <span className="font-semibold text-blue-800 text-sm">Report Issue</span>
+ <span className="font-semibold text-blue-800 text-sm">{t('home.quickActions.reportIssue')}</span>
🤖 Fix all issues with AI agents
In `@backend/tasks.py`:
- Around line 68-71: Avoid logging raw user-submitted text in the critical-path
logging: in the severity == 'critical' branch where logger.warning currently
includes issue.description[:50], remove any direct usage of issue.description
and instead log only non-PII identifiers such as issue.id and a fixed safe
marker (or a hashed/anonymized fingerprint) plus context (e.g., "CRITICAL ISSUE
REPORTED"). Update the logger.warning call in that block (the severity check for
'critical') to emit no user content and, if you need traceability, include an
anonymized token derived from issue.description rather than the plaintext.

In `@frontend/src/views/Home.jsx`:
- Around line 394-407: Replace the hard-coded "Urgent" text inside the severity
badge span with a localized string via the translation function (e.g.,
t('urgent') or t('severity.urgent')) and ensure the component imports/uses the
translation hook (useTranslation) to get t if it doesn't already; also add the
corresponding translation key ("urgent" or "severity.urgent") to your i18n
resource files so the badge displays the translated label. Reference the span
rendering the badge (the JSX that currently contains "Urgent") and the
component-level translation helper (useTranslation / t) when making the change.
- Around line 329-349: The category item labels are hard-coded strings; switch
them to i18n keys and use the translation function when rendering. Import and
use the translation hook (e.g., const { t } = useTranslation()) in Home.jsx,
update the categories data so each item has a label key (e.g., labelKey:
"categories.noise") instead of a plain label, and replace usages of item.label
in the map with t(item.labelKey). Ensure the new translation keys are added to
your locale files for "Noise", "Crowd", "Water Leak", "Waste Sorter", "Civic
Eye" so translations are available.

In `@frontend/src/views/ReportForm.jsx`:
- Around line 545-647: The UI is missing a user-adjustable Severity selector:
add a Severity <select> element in ReportForm.jsx (place it after the Category
control) bound to formData.severity and update via setFormData(prev => ({
...prev, severity: e.target.value })); ensure option values match how you store
severity (e.g., "Low"/"Medium"/"High"/"Critical"), and display the AI suggestion
using the existing severity object (severity.level and severity.confidence)
beneath the select so the AI can auto-set formData.severity but users can
override it.
🧹 Nitpick comments (7)
backend/tasks.py (2)

62-66: Simplify severity extraction - hasattr check is now redundant.

Since the Issue model now always has a severity column (with default MEDIUM), the hasattr check is unnecessary. However, handling None values and extracting .value from the enum is still needed.

♻️ Suggested simplification
-        # Prefer issue's own severity if set, otherwise fallback to mapping
-        if hasattr(issue, 'severity') and issue.severity:
-            severity = issue.severity.value if hasattr(issue.severity, 'value') else issue.severity
-        else:
-            severity = severity_mapping.get(issue.category.lower(), 'medium')
+        # Prefer issue's own severity if set, otherwise fallback to mapping
+        if issue.severity:
+            severity = issue.severity.value if hasattr(issue.severity, 'value') else issue.severity
+        else:
+            severity = severity_mapping.get(issue.category.lower(), 'medium')

71-71: Reminder: Address the TODO for critical issue notifications.

The comment indicates SMS/Call alerts or specialized notifications should be triggered for critical issues. This is important for the emergency grievance handling feature.

Would you like me to help design the notification service interface or open an issue to track this work?

backend/sla_config_service.py (1)

86-97: Consider moving severity_defaults to a class constant.

The dictionary is recreated on every call. Moving it to a class-level constant improves readability and avoids repeated allocation.

♻️ Suggested refactor
 class SLAConfigService:
     """
     Service for managing SLA configurations and calculating deadlines.
     """
+    
+    SEVERITY_DEFAULTS = {
+        SeverityLevel.CRITICAL: 6,
+        SeverityLevel.HIGH: 24,
+        SeverityLevel.MEDIUM: 48,
+        SeverityLevel.LOW: 72
+    }

     def __init__(self, default_sla_hours: int = 48):

Then in get_sla_hours:

-            # Return default based on severity if available
-            severity_defaults = {
-                SeverityLevel.CRITICAL: 6,
-                SeverityLevel.HIGH: 24,
-                SeverityLevel.MEDIUM: 48,
-                SeverityLevel.LOW: 72
-            }
-            
-            if severity in severity_defaults:
-                return severity_defaults[severity]
+            # Return default based on severity if available
+            if severity in self.SEVERITY_DEFAULTS:
+                return self.SEVERITY_DEFAULTS[severity]
frontend/src/views/ReportForm.jsx (4)

112-126: Consider using detectorsApi.severity for consistency.

This function uses fetch directly while analyzeDepth (line 144) and analyzeSmartScan (line 182) use detectorsApi. The detectorsApi.severity method exists in frontend/src/api/detectors.js.

♻️ Proposed refactor for consistency
-    try {
-      const response = await fetch(`${API_URL}/api/detect-severity`, {
-        method: 'POST',
-        body: uploadData
-      });
-      if (response.ok) {
-        const data = await response.json();
-        setSeverity(data);
-        if (data.level) {
-          setFormData(prev => ({ ...prev, severity: data.level.toLowerCase() }));
-        }
-      } else {
-        const errorData = await response.json().catch(() => ({ detail: 'Unknown error' }));
-        setAnalysisErrors(prev => ({ ...prev, severity: errorData.detail || 'Analysis failed' }));
-      }
+    try {
+      const data = await detectorsApi.severity(uploadData);
+      setSeverity(data);
+      if (data.level) {
+        setFormData(prev => ({ ...prev, severity: data.level.toLowerCase() }));
+      }
     } catch (e) {
       console.error("Severity analysis failed", e);
-      setAnalysisErrors(prev => ({ ...prev, severity: 'Network error - please try again' }));
+      setAnalysisErrors(prev => ({ ...prev, severity: e.message || 'Analysis failed - please try again' }));
     } finally {

321-321: Minor: Variable shadowing for clarity.

isOnline on line 321 shadows the state variable from line 36. While the fresh navigator.onLine check is correct for accurate status at submission time, consider renaming to currentlyOnline to avoid confusion.

♻️ Optional rename for clarity
-    const isOnline = navigator.onLine;
+    const currentlyOnline = navigator.onLine;
 
-    if (!isOnline) {
+    if (!currentlyOnline) {

480-485: Minor: Voice transcript may produce awkward spacing.

The transcript is appended with a leading space (line 482). If the description is empty, this creates a leading space; if it already ends with a space, this creates a double space.

♻️ Optional fix for cleaner spacing
-                onTranscript={(transcript) => setFormData(prev => ({ ...prev, description: prev.description + ' ' + transcript }))}
+                onTranscript={(transcript) => setFormData(prev => ({ 
+                  ...prev, 
+                  description: prev.description ? `${prev.description.trimEnd()} ${transcript}` : transcript 
+                }))}

684-686: Consider using a dedicated close icon.

Using CheckCircle2 rotated 45° to create an X is semantically confusing. Consider using the X icon from lucide-react for clarity.

♻️ Optional fix using X icon

Add to imports:

import { Camera, Image as ImageIcon, CheckCircle2, AlertTriangle, Loader2, Layers, X } from 'lucide-react';

Then replace:

-                <CheckCircle2 size={24} className="rotate-45" />
+                <X size={24} />

@Arman-op
Copy link
Author

Arman-op commented Feb 6, 2026

hey @RohanExploit i will resolve this issue i just need some more time

…ncluding frontend UI for home and reporting, and backend background tasks for AI action plans, grievance creation, and push notifications.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
frontend/src/views/ReportForm.jsx (3)

249-259: ⚠️ Potential issue | 🟠 Major

Stale closure: setFormData({ ...formData, image: ... }) may overwrite concurrent state changes.

handleImageChange is async. By the time line 249 or 259 executes, formData may be stale (e.g., if the user typed in description while the image was compressing). Use the functional updater form consistently.

Proposed fix
-        setFormData({ ...formData, image: processedFile });
+        setFormData(prev => ({ ...prev, image: processedFile }));
-        setFormData({ ...formData, image: file });
+        setFormData(prev => ({ ...prev, image: file }));

The same pattern applies to other spread-based setFormData calls at lines 415, 438, and 509 — consider using the functional updater consistently to avoid similar races.


199-232: ⚠️ Potential issue | 🟡 Minor

URL.createObjectURL is never revoked in compressImage, causing a memory leak.

Each call creates a blob URL that persists until the page is unloaded. Revoke it after the image loads.

Proposed fix
       img.onload = () => {
+        URL.revokeObjectURL(img.src);
         // Calculate new dimensions
         let { width, height } = img;

323-348: ⚠️ Potential issue | 🟠 Major

Offline path omits formData.severity, relying solely on AI-derived severity.

If the user manually sets severity (e.g., to "critical") but AI analysis didn't run (no image uploaded), severity?.level will be undefined and the offline report will lose the user's severity selection. Include formData.severity in the offline payload.

Proposed fix
       const reportData = {
           category: formData.category,
           description: formData.description,
           latitude: formData.latitude,
           longitude: formData.longitude,
           location: formData.location,
           imageBlob: formData.image,
+          severity: formData.severity,
           severity_level: severity?.level,
           severity_score: severity?.confidence
         };
backend/tasks.py (1)

162-166: ⚠️ Potential issue | 🟠 Major

e.response can be None, causing AttributeError and aborting remaining notifications.

If the WebPushException is raised due to a connection failure (no HTTP response), e.response will be None and accessing .status_code throws AttributeError. This escapes the inner try/except WebPushException block, halts the loop, and skips db.commit() — leaving already-queued subscription deletions uncommitted.

Proposed fix
             except WebPushException as e:
                 logger.error(f"Failed to send push notification: {e}")
                 # Remove invalid subscriptions
-                if e.response.status_code in [400, 404, 410]:
-                    db.delete(subscription)
+                if e.response is not None and e.response.status_code in [400, 404, 410]:
+                    db.delete(subscription)
🤖 Fix all issues with AI agents
In `@backend/tasks.py`:
- Around line 68-74: The critical-severity logging block currently calls
issue.description.encode(...) which will raise if description is None and also
imports hashlib inside the function; move the hashlib import to the top-level
imports and change the hash computation in the critical branch to guard against
a null description (e.g., use an empty string fallback or conditional) before
encoding, then compute the SHA-256 and use logger.warning exactly as before
(reference symbols: issue.description, severity == 'critical', logger.warning,
hashlib).

In `@frontend/src/views/Home.jsx`:
- Line 479: The scroll-to-top button uses a fragile magic number bottom-[447px]
in the className on the fixed button element (the scroll-to-top button in
Home.jsx); replace that hard-coded pixel value with a standard Tailwind bottom
utility (e.g., bottom-8 or bottom-24) and add responsive variants or safe-area
handling (e.g., sm:bottom-24, lg:bottom-16 or use safe-area-inset-bottom) so the
button is consistently anchored across screen sizes; update the className string
on the fixed element to remove bottom-[447px] and use the chosen utility
classes.
🧹 Nitpick comments (2)
frontend/src/views/Home.jsx (1)

325-325: Extraneous space in self-closing </div > tags.

Lines 325 and 465 have </div > with a trailing space before >. While browsers tolerate this, it's a typo worth fixing.

Fix
-        </div >
+        </div>

Also applies to: 465-465

frontend/src/views/ReportForm.jsx (1)

315-321: const isOnline on line 321 shadows the state variable from line 36.

This works (it re-checks live connectivity at submit time, which is arguably better than stale state), but shadowing makes it confusing. Consider renaming for clarity.

Suggested rename
-    const isOnline = navigator.onLine;
+    const currentlyOnline = navigator.onLine;

-    if (!isOnline) {
+    if (!currentlyOnline) {

…ance creation, and push notifications, alongside a new Home view for issue reporting and display.
@github-actions
Copy link

github-actions bot commented Feb 7, 2026

🔍 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.*

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
backend/tasks.py (1)

163-167: ⚠️ Potential issue | 🟡 Minor

e.response may be None, causing AttributeError on .status_code access.

This is pre-existing code, but WebPushException can have a None response (e.g., connection failures). The AttributeError would be caught by the outer except, silently skipping stale subscription cleanup.

Proposed guard
             except WebPushException as e:
                 logger.error(f"Failed to send push notification: {e}")
                 # Remove invalid subscriptions
-                if e.response.status_code in [400, 404, 410]:
+                if e.response and e.response.status_code in [400, 404, 410]:
                     db.delete(subscription)
frontend/src/views/Home.jsx (2)

356-377: ⚠️ Potential issue | 🟡 Minor

Additional Tools labels are also hardcoded English strings.

"Who is Responsible?", "Top Reporters", and "Camera Check" should use t(...) for consistency with the rest of the i18n migration.


440-443: ⚠️ Potential issue | 🟡 Minor

"No recent activity to show." is also a hardcoded English string.

Same i18n gap as the Quick Actions and Additional Tools sections. Route through t(...).

🤖 Fix all issues with AI agents
In `@frontend/src/views/Home.jsx`:
- Line 392: The conditional rendering uses unguarded recentIssues.length which
can be null/undefined; update the checks that use recentIssues.length (the
conditional around the JSX at the first diff and the other occurrence near the
second diff) to safely handle nullish values by using a null-safe check (e.g.,
recentIssues?.length > 0 or (recentIssues && recentIssues.length > 0)) or
validate with Array.isArray(recentIssues) before accessing .length so the
component won't throw when recentIssues is null/undefined.
- Around line 154-305: Buttons in the Quick Actions grid use hardcoded English
labels (e.g., the span text like "Report Issue", "Pothole", "Garbage", etc.)
which breaks i18n; replace each hardcoded label inside the span elements with
t(...) calls (e.g., t('quickActions.reportIssue')) matching the setView('...')
action so the UI shows localized text, ensure you import/use the same t hook
used elsewhere in Home.jsx, and add the corresponding keys (quickActions.*) to
the translation files for all affected buttons.
🧹 Nitpick comments (3)
backend/tasks.py (1)

63-67: hasattr(issue, 'severity') is unnecessary on a SQLAlchemy model column.

Since severity is a mapped column on the Issue model, the attribute always exists (it's None when unset, never absent). The hasattr guard adds indirection without benefit. issue.severity truthiness check alone suffices.

Also note: if the migration sets default=MEDIUM, every new row will satisfy issue.severity and the category-based fallback (lines 48–61) becomes unreachable for post-migration data.

Simplified severity resolution
-        # Prefer issue's own severity if set, otherwise fallback to mapping
-        if hasattr(issue, 'severity') and issue.severity:
-            severity = issue.severity.value if hasattr(issue.severity, 'value') else issue.severity
-        else:
-            severity = severity_mapping.get(issue.category.lower(), 'medium')
+        # Prefer issue's own severity if set, otherwise fallback to category mapping
+        if issue.severity:
+            severity = issue.severity.value if hasattr(issue.severity, 'value') else str(issue.severity)
+        else:
+            severity = severity_mapping.get(issue.category.lower(), 'medium')
frontend/src/views/Home.jsx (2)

154-305: Extract Quick Actions into a data-driven array to eliminate ~150 lines of duplicated JSX.

Each button follows an identical template (icon, label, color, onClick view). This can be reduced to a compact array + .map(), matching the pattern already used for categories. This would also make i18n easier to apply.

Suggested refactor pattern
const quickActions = [
  { id: 'report', labelKey: 'home.quickActions.reportIssue', icon: <AlertTriangle size={24} />, color: 'blue' },
  { id: 'pothole', labelKey: 'home.quickActions.pothole', icon: <Camera size={24} />, color: 'red' },
  // ... remaining items
];

// Then in JSX:
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
  {quickActions.map((action) => (
    <button
      key={action.id}
      onClick={() => setView(action.id)}
      className={`flex flex-col items-center justify-center bg-${action.color}-50 border-2 border-${action.color}-100 p-4 rounded-xl hover:bg-${action.color}-100 transition shadow-sm h-32`}
    >
      <div className={`bg-${action.color}-500 text-white p-3 rounded-full mb-2`}>
        {action.icon}
      </div>
      <span className={`font-semibold text-${action.color}-800 text-sm`}>{t(action.labelKey)}</span>
    </button>
  ))}
</div>

Note: Tailwind CSS purges dynamic class names. You'd need to either safelist the color variants or use a style map with full class strings (e.g., { bg: 'bg-blue-50', border: 'border-blue-100', ... }), similar to how the categories items already define color and bg.


60-60: Magic number 1240 for totalImpact is unexplained.

This appears to be a fabricated baseline count. If intentional, extract it to a named constant with a comment explaining its origin. If it's meant to come from the backend, it should be fetched rather than hardcoded.

Comment on lines +154 to +305
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
<button
onClick={() => setView('report')}
className="flex flex-col items-center justify-center bg-blue-50 border-2 border-blue-100 p-4 rounded-xl hover:bg-blue-100 transition shadow-sm h-32"
>
<div className="bg-blue-500 text-white p-3 rounded-full mb-2">
<AlertTriangle size={24} />
</div>
<span className="font-semibold text-blue-800 text-sm">Report Issue</span>
</button>

<button
onClick={() => setView('pothole')}
className="flex flex-col items-center justify-center bg-red-50 border-2 border-red-100 p-4 rounded-xl hover:bg-red-100 transition shadow-sm h-32"
>
<div className="bg-red-500 text-white p-3 rounded-full mb-2">
<Camera size={24} />
</div>
<span className="font-semibold text-red-800 text-sm">Pothole</span>
</button>

<button
onClick={() => setView('garbage')}
className="flex flex-col items-center justify-center bg-orange-50 border-2 border-orange-100 p-4 rounded-xl hover:bg-orange-100 transition shadow-sm h-32"
>
<div className="bg-orange-500 text-white p-3 rounded-full mb-2">
<Trash2 size={24} />
</div>
<span className="font-semibold text-orange-800 text-sm">Garbage</span>
</button>

<button
onClick={() => setView('mh-rep')}
className="flex flex-col items-center justify-center bg-purple-50 border-2 border-purple-100 p-4 rounded-xl hover:bg-purple-100 transition shadow-sm h-32"
>
<div className="bg-purple-500 text-white p-3 rounded-full mb-2">
<Search size={24} />
</div>
<span className="font-semibold text-purple-800 text-sm">Find MLA</span>
</button>

<button
onClick={() => setView('vandalism')}
className="flex flex-col items-center justify-center bg-indigo-50 border-2 border-indigo-100 p-4 rounded-xl hover:bg-indigo-100 transition shadow-sm h-32"
>
<div className="bg-indigo-500 text-white p-3 rounded-full mb-2">
<Brush size={24} />
</div>
<span className="font-semibold text-indigo-800 text-sm">Graffiti</span>
</button>

<button
onClick={() => setView('flood')}
className="flex flex-col items-center justify-center bg-cyan-50 border-2 border-cyan-100 p-4 rounded-xl hover:bg-cyan-100 transition shadow-sm h-32"
>
<div className="bg-cyan-500 text-white p-3 rounded-full mb-2">
<Droplets size={24} />
</div>
<span className="font-semibold text-cyan-800 text-sm">Flood</span>
</button>

<button
onClick={() => setView('infrastructure')}
className="flex flex-col items-center justify-center bg-yellow-50 border-2 border-yellow-100 p-4 rounded-xl hover:bg-yellow-100 transition shadow-sm h-32"
>
<div className="bg-yellow-500 text-white p-3 rounded-full mb-2">
<Zap size={24} />
</div>
<span className="font-semibold text-yellow-800 text-sm">Broken Infra</span>
</button>

{/* New Western Style Features */}
<button
onClick={() => setView('parking')}
className="flex flex-col items-center justify-center bg-rose-50 border-2 border-rose-100 p-4 rounded-xl hover:bg-rose-100 transition shadow-sm h-32"
>
<div className="bg-rose-500 text-white p-3 rounded-full mb-2">
<Truck size={24} />
</div>
<span className="font-semibold text-rose-800 text-sm">Illegal Parking</span>
</button>

<button
onClick={() => setView('streetlight')}
className="flex flex-col items-center justify-center bg-slate-50 border-2 border-slate-100 p-4 rounded-xl hover:bg-slate-100 transition shadow-sm h-32"
>
<div className="bg-slate-700 text-white p-3 rounded-full mb-2">
<Lightbulb size={24} />
</div>
<span className="font-semibold text-slate-800 text-sm">Dark Street</span>
</button>

<button
onClick={() => setView('fire')}
className="flex flex-col items-center justify-center bg-red-100 border-2 border-red-200 p-4 rounded-xl hover:bg-red-200 transition shadow-sm h-32"
>
<div className="bg-red-600 text-white p-3 rounded-full mb-2">
<Flame size={24} />
</div>
<span className="font-semibold text-red-900 text-sm">Fire/Smoke</span>
</button>

<button
onClick={() => setView('animal')}
className="flex flex-col items-center justify-center bg-amber-50 border-2 border-amber-100 p-4 rounded-xl hover:bg-amber-100 transition shadow-sm h-32"
>
<div className="bg-amber-600 text-white p-3 rounded-full mb-2">
<Dog size={24} />
</div>
<span className="font-semibold text-amber-900 text-sm">Stray Animal</span>
</button>

<button
onClick={() => setView('blocked')}
className="flex flex-col items-center justify-center bg-gray-50 border-2 border-gray-100 p-4 rounded-xl hover:bg-gray-100 transition shadow-sm h-32"
>
<div className="bg-gray-600 text-white p-3 rounded-full mb-2">
<XCircle size={24} />
</div>
<span className="font-semibold text-gray-800 text-sm">Blocked Road</span>
</button>

<button
onClick={() => setView('tree')}
className="flex flex-col items-center justify-center bg-green-50 border-2 border-green-100 p-4 rounded-xl hover:bg-green-100 transition shadow-sm h-32"
>
<div className="bg-green-600 text-white p-3 rounded-full mb-2">
<TreeDeciduous size={24} />
</div>
<span className="font-semibold text-green-800 text-sm">Tree Hazard</span>
</button>

<button
onClick={() => setView('pest')}
className="flex flex-col items-center justify-center bg-amber-50 border-2 border-amber-100 p-4 rounded-xl hover:bg-amber-100 transition shadow-sm h-32"
>
<div className="bg-amber-800 text-white p-3 rounded-full mb-2">
<Bug size={24} />
</div>
<span className="font-semibold text-amber-900 text-sm">Pest Control</span>
</button>

<button
onClick={() => setView('grievance-analysis')}
className="flex flex-col items-center justify-center bg-teal-50 border-2 border-teal-100 p-4 rounded-xl hover:bg-teal-100 transition shadow-sm h-32"
>
<div className="bg-teal-600 text-white p-3 rounded-full mb-2">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path><path d="M13 8H7"></path><path d="M17 12H7"></path></svg>
</div>
<span className="font-semibold text-teal-800 text-sm">Analyze Grievance</span>
</button>
</div>
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

Hardcoded English strings in Quick Actions break i18n consistency.

The categories section was properly migrated to use t(labelKey), but every Quick Actions button still uses hardcoded English text (e.g., "Report Issue", "Pothole", "Garbage", "Find MLA", etc.). This defeats the localization effort for non-English users.

These should be routed through t(...) with corresponding translation keys, mirroring the approach used for the category items above.

Example fix pattern (apply to all buttons in this grid)
-              <span className="font-semibold text-blue-800 text-sm">Report Issue</span>
+              <span className="font-semibold text-blue-800 text-sm">{t('home.quickActions.reportIssue')}</span>
📝 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
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
<button
onClick={() => setView('report')}
className="flex flex-col items-center justify-center bg-blue-50 border-2 border-blue-100 p-4 rounded-xl hover:bg-blue-100 transition shadow-sm h-32"
>
<div className="bg-blue-500 text-white p-3 rounded-full mb-2">
<AlertTriangle size={24} />
</div>
<span className="font-semibold text-blue-800 text-sm">Report Issue</span>
</button>
<button
onClick={() => setView('pothole')}
className="flex flex-col items-center justify-center bg-red-50 border-2 border-red-100 p-4 rounded-xl hover:bg-red-100 transition shadow-sm h-32"
>
<div className="bg-red-500 text-white p-3 rounded-full mb-2">
<Camera size={24} />
</div>
<span className="font-semibold text-red-800 text-sm">Pothole</span>
</button>
<button
onClick={() => setView('garbage')}
className="flex flex-col items-center justify-center bg-orange-50 border-2 border-orange-100 p-4 rounded-xl hover:bg-orange-100 transition shadow-sm h-32"
>
<div className="bg-orange-500 text-white p-3 rounded-full mb-2">
<Trash2 size={24} />
</div>
<span className="font-semibold text-orange-800 text-sm">Garbage</span>
</button>
<button
onClick={() => setView('mh-rep')}
className="flex flex-col items-center justify-center bg-purple-50 border-2 border-purple-100 p-4 rounded-xl hover:bg-purple-100 transition shadow-sm h-32"
>
<div className="bg-purple-500 text-white p-3 rounded-full mb-2">
<Search size={24} />
</div>
<span className="font-semibold text-purple-800 text-sm">Find MLA</span>
</button>
<button
onClick={() => setView('vandalism')}
className="flex flex-col items-center justify-center bg-indigo-50 border-2 border-indigo-100 p-4 rounded-xl hover:bg-indigo-100 transition shadow-sm h-32"
>
<div className="bg-indigo-500 text-white p-3 rounded-full mb-2">
<Brush size={24} />
</div>
<span className="font-semibold text-indigo-800 text-sm">Graffiti</span>
</button>
<button
onClick={() => setView('flood')}
className="flex flex-col items-center justify-center bg-cyan-50 border-2 border-cyan-100 p-4 rounded-xl hover:bg-cyan-100 transition shadow-sm h-32"
>
<div className="bg-cyan-500 text-white p-3 rounded-full mb-2">
<Droplets size={24} />
</div>
<span className="font-semibold text-cyan-800 text-sm">Flood</span>
</button>
<button
onClick={() => setView('infrastructure')}
className="flex flex-col items-center justify-center bg-yellow-50 border-2 border-yellow-100 p-4 rounded-xl hover:bg-yellow-100 transition shadow-sm h-32"
>
<div className="bg-yellow-500 text-white p-3 rounded-full mb-2">
<Zap size={24} />
</div>
<span className="font-semibold text-yellow-800 text-sm">Broken Infra</span>
</button>
{/* New Western Style Features */}
<button
onClick={() => setView('parking')}
className="flex flex-col items-center justify-center bg-rose-50 border-2 border-rose-100 p-4 rounded-xl hover:bg-rose-100 transition shadow-sm h-32"
>
<div className="bg-rose-500 text-white p-3 rounded-full mb-2">
<Truck size={24} />
</div>
<span className="font-semibold text-rose-800 text-sm">Illegal Parking</span>
</button>
<button
onClick={() => setView('streetlight')}
className="flex flex-col items-center justify-center bg-slate-50 border-2 border-slate-100 p-4 rounded-xl hover:bg-slate-100 transition shadow-sm h-32"
>
<div className="bg-slate-700 text-white p-3 rounded-full mb-2">
<Lightbulb size={24} />
</div>
<span className="font-semibold text-slate-800 text-sm">Dark Street</span>
</button>
<button
onClick={() => setView('fire')}
className="flex flex-col items-center justify-center bg-red-100 border-2 border-red-200 p-4 rounded-xl hover:bg-red-200 transition shadow-sm h-32"
>
<div className="bg-red-600 text-white p-3 rounded-full mb-2">
<Flame size={24} />
</div>
<span className="font-semibold text-red-900 text-sm">Fire/Smoke</span>
</button>
<button
onClick={() => setView('animal')}
className="flex flex-col items-center justify-center bg-amber-50 border-2 border-amber-100 p-4 rounded-xl hover:bg-amber-100 transition shadow-sm h-32"
>
<div className="bg-amber-600 text-white p-3 rounded-full mb-2">
<Dog size={24} />
</div>
<span className="font-semibold text-amber-900 text-sm">Stray Animal</span>
</button>
<button
onClick={() => setView('blocked')}
className="flex flex-col items-center justify-center bg-gray-50 border-2 border-gray-100 p-4 rounded-xl hover:bg-gray-100 transition shadow-sm h-32"
>
<div className="bg-gray-600 text-white p-3 rounded-full mb-2">
<XCircle size={24} />
</div>
<span className="font-semibold text-gray-800 text-sm">Blocked Road</span>
</button>
<button
onClick={() => setView('tree')}
className="flex flex-col items-center justify-center bg-green-50 border-2 border-green-100 p-4 rounded-xl hover:bg-green-100 transition shadow-sm h-32"
>
<div className="bg-green-600 text-white p-3 rounded-full mb-2">
<TreeDeciduous size={24} />
</div>
<span className="font-semibold text-green-800 text-sm">Tree Hazard</span>
</button>
<button
onClick={() => setView('pest')}
className="flex flex-col items-center justify-center bg-amber-50 border-2 border-amber-100 p-4 rounded-xl hover:bg-amber-100 transition shadow-sm h-32"
>
<div className="bg-amber-800 text-white p-3 rounded-full mb-2">
<Bug size={24} />
</div>
<span className="font-semibold text-amber-900 text-sm">Pest Control</span>
</button>
<button
onClick={() => setView('grievance-analysis')}
className="flex flex-col items-center justify-center bg-teal-50 border-2 border-teal-100 p-4 rounded-xl hover:bg-teal-100 transition shadow-sm h-32"
>
<div className="bg-teal-600 text-white p-3 rounded-full mb-2">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path><path d="M13 8H7"></path><path d="M17 12H7"></path></svg>
</div>
<span className="font-semibold text-teal-800 text-sm">Analyze Grievance</span>
</button>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
<button
onClick={() => setView('report')}
className="flex flex-col items-center justify-center bg-blue-50 border-2 border-blue-100 p-4 rounded-xl hover:bg-blue-100 transition shadow-sm h-32"
>
<div className="bg-blue-500 text-white p-3 rounded-full mb-2">
<AlertTriangle size={24} />
</div>
<span className="font-semibold text-blue-800 text-sm">{t('home.quickActions.reportIssue')}</span>
</button>
<button
onClick={() => setView('pothole')}
className="flex flex-col items-center justify-center bg-red-50 border-2 border-red-100 p-4 rounded-xl hover:bg-red-100 transition shadow-sm h-32"
>
<div className="bg-red-500 text-white p-3 rounded-full mb-2">
<Camera size={24} />
</div>
<span className="font-semibold text-red-800 text-sm">{t('home.quickActions.pothole')}</span>
</button>
<button
onClick={() => setView('garbage')}
className="flex flex-col items-center justify-center bg-orange-50 border-2 border-orange-100 p-4 rounded-xl hover:bg-orange-100 transition shadow-sm h-32"
>
<div className="bg-orange-500 text-white p-3 rounded-full mb-2">
<Trash2 size={24} />
</div>
<span className="font-semibold text-orange-800 text-sm">{t('home.quickActions.garbage')}</span>
</button>
<button
onClick={() => setView('mh-rep')}
className="flex flex-col items-center justify-center bg-purple-50 border-2 border-purple-100 p-4 rounded-xl hover:bg-purple-100 transition shadow-sm h-32"
>
<div className="bg-purple-500 text-white p-3 rounded-full mb-2">
<Search size={24} />
</div>
<span className="font-semibold text-purple-800 text-sm">{t('home.quickActions.findMLA')}</span>
</button>
<button
onClick={() => setView('vandalism')}
className="flex flex-col items-center justify-center bg-indigo-50 border-2 border-indigo-100 p-4 rounded-xl hover:bg-indigo-100 transition shadow-sm h-32"
>
<div className="bg-indigo-500 text-white p-3 rounded-full mb-2">
<Brush size={24} />
</div>
<span className="font-semibold text-indigo-800 text-sm">{t('home.quickActions.graffiti')}</span>
</button>
<button
onClick={() => setView('flood')}
className="flex flex-col items-center justify-center bg-cyan-50 border-2 border-cyan-100 p-4 rounded-xl hover:bg-cyan-100 transition shadow-sm h-32"
>
<div className="bg-cyan-500 text-white p-3 rounded-full mb-2">
<Droplets size={24} />
</div>
<span className="font-semibold text-cyan-800 text-sm">{t('home.quickActions.flood')}</span>
</button>
<button
onClick={() => setView('infrastructure')}
className="flex flex-col items-center justify-center bg-yellow-50 border-2 border-yellow-100 p-4 rounded-xl hover:bg-yellow-100 transition shadow-sm h-32"
>
<div className="bg-yellow-500 text-white p-3 rounded-full mb-2">
<Zap size={24} />
</div>
<span className="font-semibold text-yellow-800 text-sm">{t('home.quickActions.brokenInfra')}</span>
</button>
{/* New Western Style Features */}
<button
onClick={() => setView('parking')}
className="flex flex-col items-center justify-center bg-rose-50 border-2 border-rose-100 p-4 rounded-xl hover:bg-rose-100 transition shadow-sm h-32"
>
<div className="bg-rose-500 text-white p-3 rounded-full mb-2">
<Truck size={24} />
</div>
<span className="font-semibold text-rose-800 text-sm">{t('home.quickActions.illegalParking')}</span>
</button>
<button
onClick={() => setView('streetlight')}
className="flex flex-col items-center justify-center bg-slate-50 border-2 border-slate-100 p-4 rounded-xl hover:bg-slate-100 transition shadow-sm h-32"
>
<div className="bg-slate-700 text-white p-3 rounded-full mb-2">
<Lightbulb size={24} />
</div>
<span className="font-semibold text-slate-800 text-sm">{t('home.quickActions.darkStreet')}</span>
</button>
<button
onClick={() => setView('fire')}
className="flex flex-col items-center justify-center bg-red-100 border-2 border-red-200 p-4 rounded-xl hover:bg-red-200 transition shadow-sm h-32"
>
<div className="bg-red-600 text-white p-3 rounded-full mb-2">
<Flame size={24} />
</div>
<span className="font-semibold text-red-900 text-sm">{t('home.quickActions.fireSmoke')}</span>
</button>
<button
onClick={() => setView('animal')}
className="flex flex-col items-center justify-center bg-amber-50 border-2 border-amber-100 p-4 rounded-xl hover:bg-amber-100 transition shadow-sm h-32"
>
<div className="bg-amber-600 text-white p-3 rounded-full mb-2">
<Dog size={24} />
</div>
<span className="font-semibold text-amber-900 text-sm">{t('home.quickActions.strayAnimal')}</span>
</button>
<button
onClick={() => setView('blocked')}
className="flex flex-col items-center justify-center bg-gray-50 border-2 border-gray-100 p-4 rounded-xl hover:bg-gray-100 transition shadow-sm h-32"
>
<div className="bg-gray-600 text-white p-3 rounded-full mb-2">
<XCircle size={24} />
</div>
<span className="font-semibold text-gray-800 text-sm">{t('home.quickActions.blockedRoad')}</span>
</button>
<button
onClick={() => setView('tree')}
className="flex flex-col items-center justify-center bg-green-50 border-2 border-green-100 p-4 rounded-xl hover:bg-green-100 transition shadow-sm h-32"
>
<div className="bg-green-600 text-white p-3 rounded-full mb-2">
<TreeDeciduous size={24} />
</div>
<span className="font-semibold text-green-800 text-sm">{t('home.quickActions.treeHazard')}</span>
</button>
<button
onClick={() => setView('pest')}
className="flex flex-col items-center justify-center bg-amber-50 border-2 border-amber-100 p-4 rounded-xl hover:bg-amber-100 transition shadow-sm h-32"
>
<div className="bg-amber-800 text-white p-3 rounded-full mb-2">
<Bug size={24} />
</div>
<span className="font-semibold text-amber-900 text-sm">{t('home.quickActions.pestControl')}</span>
</button>
<button
onClick={() => setView('grievance-analysis')}
className="flex flex-col items-center justify-center bg-teal-50 border-2 border-teal-100 p-4 rounded-xl hover:bg-teal-100 transition shadow-sm h-32"
>
<div className="bg-teal-600 text-white p-3 rounded-full mb-2">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path><path d="M13 8H7"></path><path d="M17 12H7"></path></svg>
</div>
<span className="font-semibold text-teal-800 text-sm">{t('home.quickActions.analyzeGrievance')}</span>
</button>
</div>
🤖 Prompt for AI Agents
In `@frontend/src/views/Home.jsx` around lines 154 - 305, Buttons in the Quick
Actions grid use hardcoded English labels (e.g., the span text like "Report
Issue", "Pothole", "Garbage", etc.) which breaks i18n; replace each hardcoded
label inside the span elements with t(...) calls (e.g.,
t('quickActions.reportIssue')) matching the setView('...') action so the UI
shows localized text, ensure you import/use the same t hook used elsewhere in
Home.jsx, and add the corresponding keys (quickActions.*) to the translation
files for all affected buttons.

@@ -390,14 +391,20 @@ const Home = ({ setView, fetchResponsibilityMap, recentIssues, handleUpvote, loa
<div className="divide-y divide-gray-50 max-h-80 overflow-y-auto custom-scrollbar">
{recentIssues.length > 0 ? (
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

recentIssues can be null/undefined — unguarded .length access will crash.

Line 60 already guards against this (recentIssues ? recentIssues.length : 0), confirming the prop can be nullish. But lines 392 and 446 access recentIssues.length without a guard, which will throw a TypeError.

Suggested fix
-            {recentIssues.length > 0 ? (
+            {recentIssues?.length > 0 ? (

And on line 446:

-          {recentIssues.length > 0 && hasMore && (
+          {recentIssues?.length > 0 && hasMore && (
📝 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
{recentIssues.length > 0 ? (
{recentIssues?.length > 0 ? (
🤖 Prompt for AI Agents
In `@frontend/src/views/Home.jsx` at line 392, The conditional rendering uses
unguarded recentIssues.length which can be null/undefined; update the checks
that use recentIssues.length (the conditional around the JSX at the first diff
and the other occurrence near the second diff) to safely handle nullish values
by using a null-safe check (e.g., recentIssues?.length > 0 or (recentIssues &&
recentIssues.length > 0)) or validate with Array.isArray(recentIssues) before
accessing .length so the component won't throw when recentIssues is
null/undefined.

@Arman-op
Copy link
Author

Arman-op commented Feb 7, 2026

Hey @RohanExploit i have resolved the issue can you have a look into this and still any changes required you can give the description about the changes required.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
frontend/src/views/Home.jsx (1)

315-470: ⚠️ Potential issue | 🔴 Critical

Entire Quick Actions grid is duplicated.

Lines 163–314 and 315–470 contain two identical copies of the Quick Actions grid — an artifact of the unresolved merge conflict. Remove one copy entirely. Both copies also use hardcoded English strings (already noted in a prior review).

frontend/src/views/ReportForm.jsx (2)

548-586: ⚠️ Potential issue | 🔴 Critical

Unresolved git merge conflict — component will not compile.

Lines 548 and 582 contain raw branch-name text (Emergency-and-High-Severity-#290 and main) that appear to be leftover merge-conflict markers. The surrounding JSX is structurally broken:

  1. The <button> opened at Line 559 is never closed — Line 564 starts a new <div> (the Email block) mid-attribute.
  2. The Email field (Lines 564-574) and Language selector (Lines 577-582) are duplicates of sections already rendered earlier (Language at Lines 490-501), and are spliced into the middle of the Location section.
  3. Biome confirms: "Expected corresponding JSX closing tag for 'div'" at Lines 551-553.

This needs to be resolved by keeping only one side of the conflict. Based on the rest of the file, the intended structure for the Location block should look something like:

Proposed fix (keep the new-branch version of Location, remove conflict debris)
-         Emergency-and-High-Severity-#290
         <div>
           <label className="block text-sm font-medium text-gray-700">Location (Optional)</label>
           <div className="flex gap-2 mt-1">
             <input
               type="text"
               readOnly
               placeholder="Location not set"
               className="block w-full rounded-md border-gray-300 shadow-sm p-2 border bg-gray-50"
               value={formData.location || ''}
             />
             <button
               type="button"
               onClick={getLocation}
               disabled={gettingLocation}
               className="bg-gray-200 hover:bg-gray-300 text-gray-800 font-semibold py-2 px-4 rounded inline-flex items-center"
-          <div>
-            <label className="block text-sm font-medium text-gray-700">Email (Optional)</label>
-            <input
-              type="email"
-              className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 border"
-              placeholder="Enter your email to track your reports"
-              value={formData.email}
-              onChange={(e) => setFormData({...formData, email: e.target.value})}
-            />
-            <p className="text-xs text-gray-500 mt-1">We'll use this to group your reports under "My Reports".</p>
-          </div>
-
-          <div>
-            <label className="block text-sm font-medium text-gray-700">Language</label>
-            <select
-              className="mt-1 block w-full rounded-md border-gray-300 shadow-sm p-2 border"
-              value={i18n.language}
-              onChange={(e) => i18n.changeLanguage(e.target.value)}
-         main
             >
               {gettingLocation ? '...' : '📍'}
             </button>
           </div>

Note: The Email field is being removed from this location — verify whether it should be added back elsewhere in the form (it existed in the main branch version). If so, place it as a separate <div> block outside the Location section.


326-336: ⚠️ Potential issue | 🟡 Minor

Offline payload omits user-selected severity.

The online path sends formData.severity (Line 363), which reflects the user's dropdown selection. The offline path instead uses severity?.level (Line 334) from the AI analysis state, which is null when no image was analyzed. This means offline reports may lose the user-chosen severity.

Proposed fix
         const reportData = {
           category: formData.category,
           description: formData.description,
           latitude: formData.latitude,
           longitude: formData.longitude,
           location: formData.location,
           imageBlob: formData.image,
+          severity: formData.severity,
           severity_level: severity?.level,
           severity_score: severity?.confidence
         };
backend/routers/issues.py (3)

584-632: ⚠️ Potential issue | 🔴 Critical

Unresolved merge conflict left both old and new code blocks — duplicated logic, NameError, and missing severity in projection.

Multiple problems here after the botched merge:

  1. Conflict markers on lines 599 and 632 cause a SyntaxError.
  2. Undefined variable: The new block (line 600) iterates over issues, but the query result is stored in results (line 584).
  3. Duplicate appending: Both the new block (lines 600-614) and the old block (lines 615-631) append to data, so if somehow both ran, every issue would appear twice.
  4. Missing column: Issue.severity is not included in the column projection (lines 584-594), so accessing i.severity (line 612) would fail on the named tuple.

You need to keep only one block, add Issue.severity to the projection, and remove the conflict markers.

🐛 Proposed fix — keep the old dict-based approach, adding severity
     results = db.query(
         Issue.id,
         Issue.category,
         Issue.description,
         Issue.created_at,
         Issue.image_path,
         Issue.status,
         Issue.upvotes,
         Issue.location,
         Issue.latitude,
-        Issue.longitude
+        Issue.longitude,
+        Issue.severity
     ).order_by(Issue.created_at.desc()).offset(offset).limit(limit).all()
 
     # Convert to Pydantic models for validation and serialization
     data = []
-     Emergency-and-High-Severity-#290
-    for i in issues:
-        data.append(IssueSummaryResponse(
-            id=i.id,
-            category=i.category,
-            description=i.description[:100] + "..." if len(i.description) > 100 else i.description,
-            created_at=i.created_at,
-            image_path=i.image_path,
-            status=i.status,
-            upvotes=i.upvotes if i.upvotes is not None else 0,
-            location=i.location,
-            latitude=i.latitude,
-            longitude=i.longitude,
-            severity=i.severity.value if hasattr(i.severity, 'value') else i.severity
-            # action_plan is deferred and excluded
-        ).model_dump(mode='json'))
     for row in results:
-        # Manually construct dict from named tuple row to avoid full object overhead
         desc = row.description or ""
         short_desc = desc[:100] + "..." if len(desc) > 100 else desc
 
         data.append({
             "id": row.id,
             "category": row.category,
             "description": short_desc,
             "created_at": row.created_at.isoformat() if row.created_at else None,
             "image_path": row.image_path,
             "status": row.status,
             "upvotes": row.upvotes if row.upvotes is not None else 0,
             "location": row.location,
             "latitude": row.latitude,
-            "longitude": row.longitude
+            "longitude": row.longitude,
+            "severity": row.severity.value if hasattr(row.severity, 'value') else (row.severity or "medium")
         })
-     main

524-569: ⚠️ Potential issue | 🟡 Minor

get_user_issues does not include severity in its column projection or response.

The query (lines 535-545) and the response dict (lines 556-567) omit severity, so IssueSummaryResponse will always use the default "medium" regardless of the actual stored value. This should be consistent with get_recent_issues.

Proposed fix

Add Issue.severity to the query projection and include it in the response dict:

     results = db.query(
         Issue.id,
         Issue.category,
         Issue.description,
         Issue.created_at,
         Issue.image_path,
         Issue.status,
         Issue.upvotes,
         Issue.location,
         Issue.latitude,
-        Issue.longitude
+        Issue.longitude,
+        Issue.severity
     ).filter(Issue.user_email == user_email)\
         data.append({
             ...
             "latitude": row.latitude,
-            "longitude": row.longitude
+            "longitude": row.longitude,
+            "severity": row.severity.value if hasattr(row.severity, 'value') else (row.severity or "medium")
         })

124-136: ⚠️ Potential issue | 🟡 Minor

NearbyIssueResponse in deduplication omits severity field.

The nearby-issue responses constructed here (and similarly at lines 314-325) don't include severity, and Issue.severity is also missing from the column projection at lines 99-108. The response will always show "medium" regardless of actual severity.

🤖 Fix all issues with AI agents
In `@backend/models.py`:
- Around line 147-150: There are leftover merge markers
"Emergency-and-High-Severity-#290" and "main" breaking parsing; open the section
around the Column definitions for severity and integrity_hash and remove the
stray branch-name tokens so only the intended declarations remain (e.g., keep
severity = Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True)
and integrity_hash = Column(String, nullable=True) and delete the merge artifact
strings); ensure no other conflict markers remain in models.py and run a quick
import to verify syntax.

In `@backend/routers/grievances.py`:
- Around line 11-14: Remove the unresolved merge conflict markers and duplicate
import lines; replace the two separate imports and the conflict text with a
single cleaned import from backend.models that includes Grievance,
EscalationAudit, GrievanceFollower, ClosureConfirmation, and SeverityLevel so
there are no duplicate imports or leftover conflict strings in the module (look
for the existing import occurrences referencing Grievance, EscalationAudit,
SeverityLevel, GrievanceFollower, ClosureConfirmation and the conflict markers
like "Emergency-and-High-Severity-#290" / "main" and remove them).

In `@backend/routers/issues.py`:
- Around line 191-194: The Issue constructor call contains leftover merge
conflict markers and a missing comma causing a SyntaxError: remove the conflict
marker lines ("Emergency-and-High-Severity-#290" and "main") so the call is
clean, and add the missing comma after the severity=severity argument so the
arguments read ..., severity=severity, integrity_hash=integrity_hash ...; update
the code where the Issue is constructed to ensure only valid keyword arguments
remain and the commas separate them correctly.

In `@backend/schemas.py`:
- Around line 47-57: Remove the leftover merge markers
("Emergency-and-High-Severity-#290" and "main") and the stray token so the file
parses; then eliminate the duplicate model_config declaration leaving a single
model_config = ConfigDict(from_attributes=True) in the class (or module) scope,
and ensure IssueResponse (which subclasses IssueSummaryResponse) has only the
intended field declarations (action_plan) with correct indentation and no
extraneous tokens.

In `@frontend/src/views/Home.jsx`:
- Around line 109-121: Remove the unresolved merge markers and duplicate entries
in the management array: delete the raw branch markers
"Emergency-and-High-Severity-#290" and "main" and the second block of duplicated
items (the ones using label instead of labelKey), leaving only a single set of
items that use labelKey; also ensure the missing safety-check (id:
'safety-check') and my-reports (id: 'my-reports') entries are present and use
labelKey (e.g., t keys like 'home.issues.civicEye', 'home.issues.myReports' or
similar) so that the render code that calls t(item.labelKey) (see usage around
line where t(item.labelKey) is used) will not receive undefined for any item.
- Around line 158-160: Remove the leftover merge marker string
"Emergency-and-High-Severity-#290" that is embedded inside the JSX (next to the
<ChevronRight> element) and also remove its corresponding "main" merge marker
elsewhere in the file; ensure you delete both conflict markers and any
surrounding stray characters so the JSX for the button/child elements remains
valid and well-formed (no extra text nodes or broken tags), then run the
linter/build to confirm no JSX syntax errors remain.
🧹 Nitpick comments (2)
frontend/src/views/ReportForm.jsx (2)

250-260: Stale closure: setFormData spreads a potentially outdated formData snapshot.

handleImageChange is async — by the time Line 250 or 260 executes (after await compressImage), the closed-over formData may be stale if the user changed another field in the meantime. This silently reverts those changes. Use the functional updater form consistently:

Proposed fix
-        setFormData({ ...formData, image: processedFile });
+        setFormData(prev => ({ ...prev, image: processedFile }));
         ...
-        setFormData({ ...formData, image: file });
+        setFormData(prev => ({ ...prev, image: file }));

The same pattern appears in several synchronous onChange handlers (Lines 417, 440, 511, 571) — less risky but worth aligning for consistency, since other handlers already use the functional form.


200-233: Object URL is never revoked in compressImage, causing a minor memory leak.

URL.createObjectURL(file) at Line 231 allocates a blob URL that persists until the page is unloaded or explicitly revoked. Since this runs on every large-image upload, consider revoking it after the image loads:

Proposed fix
       img.onload = () => {
+        URL.revokeObjectURL(img.src);
         // Calculate new dimensions
         let { width, height } = img;

Comment on lines +147 to +150
Emergency-and-High-Severity-#290
severity = Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True)
integrity_hash = Column(String, nullable=True) # Blockchain integrity seal
main
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

Unresolved git merge conflict markers — this file will not parse.

Lines 147 and 150 are leftover branch-name markers from an unresolved merge/rebase (Emergency-and-High-Severity-#290 and main). Python will raise a SyntaxError on import, preventing the entire backend from starting.

Remove the conflict markers and keep only the intended code.

🐛 Proposed fix
     action_plan = Column(JSONEncodedDict, nullable=True)
-    Emergency-and-High-Severity-#290
     severity = Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True)
     integrity_hash = Column(String, nullable=True)  # Blockchain integrity seal
-    main
📝 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
Emergency-and-High-Severity-#290
severity = Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True)
integrity_hash = Column(String, nullable=True) # Blockchain integrity seal
main
action_plan = Column(JSONEncodedDict, nullable=True)
severity = Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True)
integrity_hash = Column(String, nullable=True) # Blockchain integrity seal
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 147-147: Expected an identifier, but found a keyword and that cannot be used here

(invalid-syntax)


[warning] 147-148: Expected an expression

(invalid-syntax)

🤖 Prompt for AI Agents
In `@backend/models.py` around lines 147 - 150, There are leftover merge markers
"Emergency-and-High-Severity-#290" and "main" breaking parsing; open the section
around the Column definitions for severity and integrity_hash and remove the
stray branch-name tokens so only the intended declarations remain (e.g., keep
severity = Column(Enum(SeverityLevel), default=SeverityLevel.MEDIUM, index=True)
and integrity_hash = Column(String, nullable=True) and delete the merge artifact
strings); ensure no other conflict markers remain in models.py and run a quick
import to verify syntax.

Comment on lines +11 to +14
Emergency-and-High-Severity-#290
from backend.models import Grievance, EscalationAudit, SeverityLevel
from backend.models import Grievance, EscalationAudit, GrievanceFollower, ClosureConfirmation
main
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

Unresolved merge conflict markers and duplicate imports — SyntaxError at startup.

Lines 11 and 14 are conflict remnants. Lines 12 and 13 are two separate imports from backend.models that should be merged into one after resolving the conflict.

🐛 Proposed fix
-  Emergency-and-High-Severity-#290
- from backend.models import Grievance, EscalationAudit, SeverityLevel
- from backend.models import Grievance, EscalationAudit, GrievanceFollower, ClosureConfirmation
-  main
+ from backend.models import Grievance, EscalationAudit, SeverityLevel, GrievanceFollower, ClosureConfirmation
📝 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
Emergency-and-High-Severity-#290
from backend.models import Grievance, EscalationAudit, SeverityLevel
from backend.models import Grievance, EscalationAudit, GrievanceFollower, ClosureConfirmation
main
from backend.models import Grievance, EscalationAudit, SeverityLevel, GrievanceFollower, ClosureConfirmation
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 11-11: Unexpected indentation

(invalid-syntax)


[warning] 11-11: Expected an identifier, but found a keyword and that cannot be used here

(invalid-syntax)


[warning] 11-12: Expected an expression

(invalid-syntax)


[warning] 12-12: Expected a statement

(invalid-syntax)


[warning] 14-14: Unexpected indentation

(invalid-syntax)

🤖 Prompt for AI Agents
In `@backend/routers/grievances.py` around lines 11 - 14, Remove the unresolved
merge conflict markers and duplicate import lines; replace the two separate
imports and the conflict text with a single cleaned import from backend.models
that includes Grievance, EscalationAudit, GrievanceFollower,
ClosureConfirmation, and SeverityLevel so there are no duplicate imports or
leftover conflict strings in the module (look for the existing import
occurrences referencing Grievance, EscalationAudit, SeverityLevel,
GrievanceFollower, ClosureConfirmation and the conflict markers like
"Emergency-and-High-Severity-#290" / "main" and remove them).

Comment on lines +191 to +194
Emergency-and-High-Severity-#290
severity=severity
integrity_hash=integrity_hash
main
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

Unresolved merge conflict markers in Issue constructor — SyntaxError.

Lines 191 and 194 are conflict remnants. Additionally, line 192 (severity=severity) is missing a trailing comma before integrity_hash=integrity_hash on line 193.

🐛 Proposed fix
             action_plan=None,
-        Emergency-and-High-Severity-#290
-                severity=severity
-                integrity_hash=integrity_hash
-         main
+                severity=severity,
+                integrity_hash=integrity_hash,
📝 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
Emergency-and-High-Severity-#290
severity=severity
integrity_hash=integrity_hash
main
action_plan=None,
severity=severity,
integrity_hash=integrity_hash,
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 191-192: Expected a parameter name

(invalid-syntax)


[warning] 191-191: Expected an identifier, but found a keyword and that cannot be used here

(invalid-syntax)


[warning] 193-193: Expected ,, found name

(invalid-syntax)


[warning] 194-194: Expected ,, found name

(invalid-syntax)

🤖 Prompt for AI Agents
In `@backend/routers/issues.py` around lines 191 - 194, The Issue constructor call
contains leftover merge conflict markers and a missing comma causing a
SyntaxError: remove the conflict marker lines
("Emergency-and-High-Severity-#290" and "main") so the call is clean, and add
the missing comma after the severity=severity argument so the arguments read
..., severity=severity, integrity_hash=integrity_hash ...; update the code where
the Issue is constructed to ensure only valid keyword arguments remain and the
commas separate them correctly.

Comment on lines +47 to +57
Emergency-and-High-Severity-#290
severity: Optional[str] = "medium"
action_plan: Optional[Any] = None

model_config = ConfigDict(from_attributes=True)

model_config = ConfigDict(from_attributes=True)

class IssueResponse(IssueSummaryResponse):
action_plan: Optional[Union[Dict[str, Any], Any]] = Field(None, description="Generated action plan")
main
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

Unresolved merge conflict markers and duplicate model_config — will cause SyntaxError.

Lines 47 and 57 are leftover branch markers. Additionally, after removing them, model_config would be declared twice (lines 51 and 53). Keep only one.

🐛 Proposed fix
     latitude: Optional[float] = None
     longitude: Optional[float] = None
-      Emergency-and-High-Severity-#290
     severity: Optional[str] = "medium"
     action_plan: Optional[Any] = None
 
     model_config = ConfigDict(from_attributes=True)
 
-    model_config = ConfigDict(from_attributes=True)
-
 class IssueResponse(IssueSummaryResponse):
     action_plan: Optional[Union[Dict[str, Any], Any]] = Field(None, description="Generated action plan")
-     main
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 47-47: Unexpected indentation

(invalid-syntax)


[warning] 47-47: Expected an identifier, but found a keyword and that cannot be used here

(invalid-syntax)


[warning] 47-48: Expected an expression

(invalid-syntax)


[warning] 55-55: Expected a statement

(invalid-syntax)


[warning] 57-57: Unexpected indentation

(invalid-syntax)

🤖 Prompt for AI Agents
In `@backend/schemas.py` around lines 47 - 57, Remove the leftover merge markers
("Emergency-and-High-Severity-#290" and "main") and the stray token so the file
parses; then eliminate the duplicate model_config declaration leaving a single
model_config = ConfigDict(from_attributes=True) in the class (or module) scope,
and ensure IssueResponse (which subclasses IssueSummaryResponse) has only the
intended field declarations (action_plan) with correct indentation and no
extraneous tokens.

Comment on lines +109 to +121
Emergency-and-High-Severity-#290
{ id: 'civic-eye', labelKey: 'home.issues.civicEye', icon: <Eye size={24} />, color: 'text-blue-600', bg: 'bg-blue-50' },
{ id: 'grievance', labelKey: 'home.issues.grievanceManagement', icon: <AlertTriangle size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
{ id: 'stats', labelKey: 'home.issues.viewStats', icon: <Activity size={24} />, color: 'text-indigo-600', bg: 'bg-indigo-50' },
{ id: 'leaderboard', labelKey: 'home.issues.leaderboard', icon: <Trophy size={24} />, color: 'text-yellow-600', bg: 'bg-yellow-50' },
{ id: 'map', labelKey: 'home.issues.responsibilityMap', icon: <MapPin size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
{ id: 'safety-check', label: "Civic Eye", icon: <Eye size={24} />, color: 'text-blue-600', bg: 'bg-blue-50' },
{ id: 'my-reports', label: "My Reports", icon: <CheckCircle size={24} />, color: 'text-teal-600', bg: 'bg-teal-50' },
{ id: 'grievance', label: t('home.issues.grievanceManagement'), icon: <AlertTriangle size={24} />, color: 'text-orange-600', bg: 'bg-orange-50' },
{ id: 'stats', label: t('home.issues.viewStats'), icon: <Activity size={24} />, color: 'text-indigo-600', bg: 'bg-indigo-50' },
{ id: 'leaderboard', label: t('home.issues.leaderboard'), icon: <Trophy size={24} />, color: 'text-yellow-600', bg: 'bg-yellow-50' },
{ id: 'map', label: t('home.issues.responsibilityMap'), icon: <MapPin size={24} />, color: 'text-green-600', bg: 'bg-green-50' },
main
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

Unresolved merge conflict markers break the build.

Lines 109 and 121 contain raw git branch names (Emergency-and-High-Severity-#290 and main) that are not valid JavaScript. The static analyzer confirms parse errors here. Additionally, this leaves duplicate category items in the management array — lines 110–114 use labelKey while lines 115–120 use label, so half the items will fail at render time when t(item.labelKey) is called on line 512 (since labelKey is undefined for those entries).

Remove the conflict markers and the duplicate set of items, keeping only the labelKey-based entries (and adding the missing my-reports and safety-check items with labelKey too).

🧰 Tools
🪛 Biome (2.3.13)

[error] 109-109: Expected an identifier but instead found '290'.

Expected an identifier here.

(parse)


[error] 109-109: Private names are only allowed on the left side of a 'in' expression

(parse)


[error] 109-109: expected , but instead found 290

Remove 290

(parse)


[error] 110-110: expected , but instead found {

Remove {

(parse)

🤖 Prompt for AI Agents
In `@frontend/src/views/Home.jsx` around lines 109 - 121, Remove the unresolved
merge markers and duplicate entries in the management array: delete the raw
branch markers "Emergency-and-High-Severity-#290" and "main" and the second
block of duplicated items (the ones using label instead of labelKey), leaving
only a single set of items that use labelKey; also ensure the missing
safety-check (id: 'safety-check') and my-reports (id: 'my-reports') entries are
present and use labelKey (e.g., t keys like 'home.issues.civicEye',
'home.issues.myReports' or similar) so that the render code that calls
t(item.labelKey) (see usage around line where t(item.labelKey) is used) will not
receive undefined for any item.

Comment on lines +158 to +160
Emergency-and-High-Severity-#290
<ChevronRight size={24} />
</button>
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

Second merge conflict marker — same root cause.

Line 158 contains another Emergency-and-High-Severity-#290 branch-name marker inside JSX, producing invalid output. This and the corresponding main marker on line 471 must be removed as part of resolving the merge conflict.

🤖 Prompt for AI Agents
In `@frontend/src/views/Home.jsx` around lines 158 - 160, Remove the leftover
merge marker string "Emergency-and-High-Severity-#290" that is embedded inside
the JSX (next to the <ChevronRight> element) and also remove its corresponding
"main" merge marker elsewhere in the file; ensure you delete both conflict
markers and any surrounding stray characters so the JSX for the button/child
elements remains valid and well-formed (no extra text nodes or broken tags),
then run the linter/build to confirm no JSX syntax errors remain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant