feat: Add lightweight issue tracking UI, HF OD model, and fix camera unmount bug#703
feat: Add lightweight issue tracking UI, HF OD model, and fix camera unmount bug#703RohanExploit wants to merge 4 commits intomainfrom
Conversation
- Added 'Track Your Issue' component in frontend Home.jsx - Integrated facebook/detr-resnet-50 Hugging Face model for /api/detect-objects endpoint - Fixed camera not fully shutting off in CameraCheckModal on unmount - Cleaned up temp scripts and corrected dev dependencies for tests
|
👋 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 New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
🙏 Thank you for your contribution, @RohanExploit!PR Details:
Quality Checklist:
Review Process:
Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken. |
❌ Deploy Preview for fixmybharat failed. Why did it fail? →
|
📝 WalkthroughWalkthroughThis PR introduces object detection capabilities by adding a new Hugging Face-based detector function and corresponding FastAPI endpoint to the backend, alongside frontend updates for issue tracking functionality and dependency management adjustments. Changes
Sequence DiagramsequenceDiagram
participant Client as Client/Frontend
participant Endpoint as FastAPI Endpoint
participant Service as HF Service
participant HFApi as Hugging Face API
Client->>Endpoint: POST /detect-objects (image file)
Endpoint->>Endpoint: validate_uploaded_file()
Note over Endpoint: Image validation
Endpoint->>Endpoint: process_uploaded_image()
Note over Endpoint: Convert to bytes
Endpoint->>Service: detect_objects_hf(image_bytes)
Service->>HFApi: POST with auth headers
Note over HFApi: Object detection processing
HFApi-->>Service: JSON response (detections or error)
alt List response
Service-->>Endpoint: detections list
else Non-list success
Service-->>Endpoint: empty list
else Error response
Service-->>Endpoint: {error: "..."}
end
Endpoint-->>Client: Detection results or 500 error
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
backend/hf_api_service.py (1)
530-559: LGTM — consistent with existing HF detector pattern.
detect_objects_hfmirrorsdetect_nsfw_content/detect_facial_emotion: prepares bytes, optionally reuses the shared client, returns{"detections": [...]}on success and a generic{"error": ...}on failure with details only logged server-side. The DETR response shape ([{"score","label","box":{xmin,ymin,xmax,ymax}}, ...]) is preserved verbatim underdetections, which downstream consumers can render directly.Minor optional nit:
headers_bin(line 537) is identical to the module-levelheaders; you could drop the local rebinding to reduce duplication, but the existing functions in this file all do the same thing — so leaving it consistent is also fine.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/hf_api_service.py` around lines 530 - 559, The function detect_objects_hf creates a local headers_bin identical to the module-level headers; remove the local headers_bin and use the existing headers variable when calling OBJECT_DETECTION_API_URL (update references inside the nested do_post and any calls that pass headers_bin), or if you prefer to keep symmetry with other functions, add a short comment explaining why the local copy is retained; locate and edit detect_objects_hf and the nested do_post to replace headers_bin with headers (or add the explanatory comment) so duplication is eliminated or justified.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/routers/detection.py`:
- Around line 522-528: The endpoint currently treats validate_uploaded_file's
return as a dict and wrongly wraps process_uploaded_image in run_in_threadpool;
instead call process_uploaded_image(image) directly (it is async), let
validate_uploaded_file raise HTTPException if needed, unpack its return as (_,
image_bytes), remove the "error" dict checks and the run_in_threadpool call,
then pass image_bytes to detect_objects_hf with the client from get_http_client;
apply the same fix pattern used in detect_nsfw_endpoint/detect_emotion_endpoint
to match other HF endpoints.
In `@frontend/src/views/Home.jsx`:
- Around line 161-176: Add accessible labels and live region announcements for
tracking UI: give the input an id and either add a visible <label htmlFor="...">
or set aria-label on the input tied to trackingId used in the form that calls
handleTrackIssue; mark the submit state by setting aria-busy={isTracking} on the
<form> (or on the input) and wrap the tracking result panel (the element
rendering trackingResult) with role="status" and aria-live="polite" so
success/error updates are announced to assistive tech.
- Around line 153-194: The new tracker UI in Home.jsx uses hardcoded strings
(header "Track Your Issue", input placeholder, button labels when isTracking is
true/false, "Status:", and the trackingResult error messages) which bypass i18n;
update the JSX to call t(...) for each user-facing string (e.g.
t('home.tracker.title'), t('home.tracker.placeholder'), t('home.tracker.track'),
t('home.tracker.tracking'), t('home.tracker.status'),
t('home.tracker.notFound'), t('home.tracker.serverError')), add those keys into
the i18n resource files under home.tracker.*, and localize the date display
(replace new Date(...).toLocaleDateString() with a localized formatter using the
current locale or Intl.DateTimeFormat with i18n.language) so all visible text in
the Track Issue form and trackingResult flows uses translations.
- Around line 75-86: In handleTrackIssue, ensure trackingId is sanitized and
encoded by using encodeURIComponent(trackingId.trim()) when building the fetch
URL (replace the raw interpolation), improve error messaging by branching on
response.status (or reading the response error body) instead of treating every
non-OK as "Issue not found" and map 401/403/500 to appropriate messages when
calling setTrackingResult, and guarantee the UI unlock by moving
setIsTracking(false) into a finally block so it always runs regardless of
success or exceptions; update references accordingly for trackingId,
handleTrackIssue, setTrackingResult, and setIsTracking.
- Line 76: The fetch URL construction in Home.jsx (const response = await
fetch(...)) and the same pattern in ChatWidget.jsx uses the || fallback, which
causes an empty string VITE_API_URL to resolve to 'http://localhost:8000' in
production; change the fallback to use the nullish coalescing operator (??) so
import.meta.env.VITE_API_URL ?? 'http://localhost:8000' is used instead,
ensuring the hardcoded localhost is only used when VITE_API_URL is actually
undefined or null; alternatively ensure VITE_API_URL is set per-environment
(e.g., .env.production) if you prefer configuring at build time rather than
using a JS-side fallback.
In `@package.json`:
- Line 36: Remove the redundant devDependency "playwright" from package.json's
devDependencies (it duplicates functionality already provided by
"@playwright/test"), delete the "playwright" entry, then run your package
manager (npm/yarn/pnpm) to update the lockfile and node_modules so the change is
reflected; finally run the test suite/CI to ensure nothing breaks after removing
"playwright".
---
Nitpick comments:
In `@backend/hf_api_service.py`:
- Around line 530-559: The function detect_objects_hf creates a local
headers_bin identical to the module-level headers; remove the local headers_bin
and use the existing headers variable when calling OBJECT_DETECTION_API_URL
(update references inside the nested do_post and any calls that pass
headers_bin), or if you prefer to keep symmetry with other functions, add a
short comment explaining why the local copy is retained; locate and edit
detect_objects_hf and the nested do_post to replace headers_bin with headers (or
add the explanatory comment) so duplication is eliminated or justified.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 609c563e-27b5-4420-96f7-a1a5a1a7bb7a
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
backend/hf_api_service.pybackend/routers/detection.pyfrontend/src/views/Home.jsxpackage.jsonpatch_home.py
💤 Files with no reviewable changes (1)
- patch_home.py
| img_data = await validate_uploaded_file(image) | ||
| if "error" in img_data: | ||
| raise HTTPException(status_code=400, detail=img_data["error"]) | ||
|
|
||
| processed_bytes = await run_in_threadpool(process_uploaded_image, img_data["bytes"]) | ||
| client = get_http_client(request) | ||
| result = await detect_objects_hf(processed_bytes, client) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the actual signatures/return types of validate_uploaded_file and process_uploaded_image
# to make sure the endpoint really is misusing them.
fd -t f 'utils.py' backend | xargs -I {} sh -c 'echo "=== {} ==="; cat {}'Repository: RohanExploit/VishwaGuru
Length of output: 27535
🏁 Script executed:
#!/bin/bash
# Show how other endpoints in this router consume these helpers, for the correct pattern.
rg -nP -C2 '\b(validate_uploaded_file|process_uploaded_image)\s*\(' --type=pyRepository: RohanExploit/VishwaGuru
Length of output: 12658
Endpoint will fail at runtime — validate_uploaded_file returns a PIL Image, not a dict, and process_uploaded_image is already async.
validate_uploaded_file(file: UploadFile) -> Optional[Image.Image] (per backend/utils.py line 150) and process_uploaded_image(file: UploadFile) -> tuple[Image.Image, bytes] is already async (line 225). The new endpoint code instead:
if "error" in img_data:against a PIL.Image object → raises TypeErrorimg_data["bytes"]— Image is not subscriptable → raises TypeErrorawait run_in_threadpool(process_uploaded_image, ...)— wrapping an already-async function schedules the coroutine creation in a thread but never awaits it
This broken pattern is already present in detect_nsfw_endpoint (lines 478–480) and detect_emotion_endpoint (lines 500–502). Follow the working pattern used by 15+ other HF endpoints: call process_uploaded_image directly on the UploadFile, unpack the tuple (_, image_bytes), and let validation HTTPExceptions bubble up (e.g. detect_illegal_parking_endpoint at line 160, detect_street_light_endpoint at line 173).
🔧 Proposed fix
`@router.post`("/detect-objects")
async def detect_objects_endpoint(
request: Request,
image: UploadFile = File(...)
):
"""
Detects general objects in the image using Hugging Face object detection model.
"""
- img_data = await validate_uploaded_file(image)
- if "error" in img_data:
- raise HTTPException(status_code=400, detail=img_data["error"])
-
- processed_bytes = await run_in_threadpool(process_uploaded_image, img_data["bytes"])
- client = get_http_client(request)
- result = await detect_objects_hf(processed_bytes, client)
-
- if "error" in result:
- raise HTTPException(status_code=500, detail="Internal server error")
-
- return result
+ # process_uploaded_image validates, resizes and strips EXIF; raises HTTPException on bad input.
+ _, image_bytes = await process_uploaded_image(image)
+
+ try:
+ client = get_http_client(request)
+ result = await detect_objects_hf(image_bytes, client=client)
+ except HTTPException:
+ raise
+ except Exception as e:
+ logger.error(f"Object detection error: {e}", exc_info=True)
+ raise HTTPException(status_code=500, detail="Internal server error")
+
+ if "error" in result:
+ raise HTTPException(status_code=500, detail="Internal server error")
+
+ return result🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/routers/detection.py` around lines 522 - 528, The endpoint currently
treats validate_uploaded_file's return as a dict and wrongly wraps
process_uploaded_image in run_in_threadpool; instead call
process_uploaded_image(image) directly (it is async), let validate_uploaded_file
raise HTTPException if needed, unpack its return as (_, image_bytes), remove the
"error" dict checks and the run_in_threadpool call, then pass image_bytes to
detect_objects_hf with the client from get_http_client; apply the same fix
pattern used in detect_nsfw_endpoint/detect_emotion_endpoint to match other HF
endpoints.
| try { | ||
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| setTrackingResult({ success: true, data }); | ||
| } else { | ||
| setTrackingResult({ success: false, error: 'Issue not found. Please check the ID.' }); | ||
| } | ||
| } catch (error) { | ||
| setTrackingResult({ success: false, error: 'Error connecting to server.' }); | ||
| } | ||
| setIsTracking(false); |
There was a problem hiding this comment.
Robustness nits in handleTrackIssue.
trackingIdis interpolated raw into the URL path. If a user pastes anything containing/,?,#, or whitespace, the request goes to the wrong endpoint. Wrap withencodeURIComponent(trackingId.trim()).- All non-OK responses surface as
"Issue not found.", but a 401/403/500 is not the same as a 404. Branch onresponse.status(or read the server error body) so users get a meaningful message. setIsTracking(false)runs after thetry/catch; move it into afinallyblock so future early-returns or rethrows can't accidentally leave the button stuck in the disabled state.
🔧 Proposed fix
const handleTrackIssue = async (e) => {
e.preventDefault();
- if (!trackingId.trim()) return;
+ const id = trackingId.trim();
+ if (!id) return;
setIsTracking(true);
setTrackingResult(null);
try {
- const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`);
+ const apiBase = import.meta.env.VITE_API_URL ?? '';
+ const response = await fetch(`${apiBase}/api/issues/${encodeURIComponent(id)}`);
if (response.ok) {
const data = await response.json();
setTrackingResult({ success: true, data });
+ } else if (response.status === 404) {
+ setTrackingResult({ success: false, error: t('home.tracker.errors.notFound') });
} else {
- setTrackingResult({ success: false, error: 'Issue not found. Please check the ID.' });
+ setTrackingResult({ success: false, error: t('home.tracker.errors.server', { status: response.status }) });
}
} catch (error) {
- setTrackingResult({ success: false, error: 'Error connecting to server.' });
+ setTrackingResult({ success: false, error: t('home.tracker.errors.network') });
+ } finally {
+ setIsTracking(false);
}
- setIsTracking(false);
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/views/Home.jsx` around lines 75 - 86, In handleTrackIssue,
ensure trackingId is sanitized and encoded by using
encodeURIComponent(trackingId.trim()) when building the fetch URL (replace the
raw interpolation), improve error messaging by branching on response.status (or
reading the response error body) instead of treating every non-OK as "Issue not
found" and map 401/403/500 to appropriate messages when calling
setTrackingResult, and guarantee the UI unlock by moving setIsTracking(false)
into a finally block so it always runs regardless of success or exceptions;
update references accordingly for trackingId, handleTrackIssue,
setTrackingResult, and setIsTracking.
| setIsTracking(true); | ||
| setTrackingResult(null); | ||
| try { | ||
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm how VITE_API_URL is resolved at build time and whether other call sites use the same pattern.
fd -t f 'vite.config.*' | xargs -I{} sh -c 'echo "=== {} ==="; cat "{}"'
echo "--- other VITE_API_URL usages ---"
rg -nP --type=js --type=jsx --type=ts --type=tsx -C2 'VITE_API_URL'Repository: RohanExploit/VishwaGuru
Length of output: 1985
🏁 Script executed:
#!/bin/bash
# Search for VITE_API_URL usages with broader search to work around file type issue
echo "=== All VITE_API_URL usages ==="
rg 'VITE_API_URL' -C 3
echo ""
echo "=== Line count and location in vite.config.js ==="
cat -n frontend/vite.config.js | grep -A 5 -B 5 'VITE_API_URL'
echo ""
echo "=== Environment files ==="
fd -t f '\.env' | head -20
echo ""
echo "=== Home.jsx around line 76 ==="
sed -n '70,85p' frontend/src/views/Home.jsx | cat -nRepository: RohanExploit/VishwaGuru
Length of output: 34779
Production builds will call http://localhost:8000 — issue tracking will be broken for end users.
Per frontend/vite.config.js line 74, import.meta.env.VITE_API_URL is replaced with "" at build time. Since an empty string is falsy, import.meta.env.VITE_API_URL || 'http://localhost:8000' always resolves to the hardcoded localhost URL in shipped bundles. This affects:
frontend/src/views/Home.jsx(line 76)frontend/src/components/ChatWidget.jsx
User browsers will attempt requests to http://localhost:8000/api/... and fail.
Use the nullish coalescing operator to only fall back to localhost when VITE_API_URL is genuinely undefined (not empty):
🔧 Proposed fix
- const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`);
+ const apiBase = import.meta.env.VITE_API_URL ?? 'http://localhost:8000';
+ const response = await fetch(`${apiBase}/api/issues/${encodeURIComponent(trackingId)}`);Or configure VITE_API_URL per-environment (.env.production) instead of relying on the JS-side fallback.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/views/Home.jsx` at line 76, The fetch URL construction in
Home.jsx (const response = await fetch(...)) and the same pattern in
ChatWidget.jsx uses the || fallback, which causes an empty string VITE_API_URL
to resolve to 'http://localhost:8000' in production; change the fallback to use
the nullish coalescing operator (??) so import.meta.env.VITE_API_URL ??
'http://localhost:8000' is used instead, ensuring the hardcoded localhost is
only used when VITE_API_URL is actually undefined or null; alternatively ensure
VITE_API_URL is set per-environment (e.g., .env.production) if you prefer
configuring at build time rather than using a JS-side fallback.
| {/* Issue Tracker Component */} | ||
| <div className="bg-white rounded-3xl p-6 shadow-sm border border-slate-100 mb-8 mt-8"> | ||
| <div className="flex items-center gap-3 mb-4"> | ||
| <div className="bg-indigo-100 p-2 rounded-xl text-indigo-600"> | ||
| <Search size={24} /> | ||
| </div> | ||
| <h3 className="text-xl font-bold">Track Your Issue</h3> | ||
| </div> | ||
| <form onSubmit={handleTrackIssue} className="flex gap-2"> | ||
| <input | ||
| type="text" | ||
| placeholder="Enter Tracking ID (e.g., 1)" | ||
| className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" | ||
| value={trackingId} | ||
| onChange={(e) => setTrackingId(e.target.value)} | ||
| /> | ||
| <button | ||
| type="submit" | ||
| disabled={isTracking} | ||
| className="bg-indigo-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-indigo-700 transition disabled:opacity-70" | ||
| > | ||
| {isTracking ? 'Tracking...' : 'Track'} | ||
| </button> | ||
| </form> | ||
|
|
||
| {trackingResult && trackingResult.success && ( | ||
| <div className="mt-4 p-4 bg-green-50 rounded-xl border border-green-100"> | ||
| <div className="flex items-center justify-between mb-2"> | ||
| <span className="font-bold text-green-800 uppercase text-sm">Status: {trackingResult.data.status}</span> | ||
| <span className="text-xs text-green-600">{new Date(trackingResult.data.created_at).toLocaleDateString()}</span> | ||
| </div> | ||
| <p className="text-green-900 font-medium">{trackingResult.data.category}</p> | ||
| <p className="text-sm text-green-700 mt-1 line-clamp-2">{trackingResult.data.description}</p> | ||
| </div> | ||
| )} | ||
|
|
||
| {trackingResult && !trackingResult.success && ( | ||
| <div className="mt-4 p-3 bg-red-50 text-red-600 rounded-xl text-sm font-medium"> | ||
| {trackingResult.error} | ||
| </div> | ||
| )} | ||
| </div> |
There was a problem hiding this comment.
New UI strings bypass i18n — inconsistent with the rest of Home.jsx.
Every other label in this view goes through t(...) (e.g. t('home.privacyActive'), t('home.tools.cameraCheck')), but the new tracker hardcodes "Track Your Issue", "Enter Tracking ID (e.g., 1)", "Tracking...", "Track", "Status:", "Issue not found. Please check the ID.", and "Error connecting to server.". This breaks localization for every non-English user.
Move these strings into the i18n resources (e.g. home.tracker.*) and reference them via t(). Also consider localizing the date formatter:
🔧 Suggested skeleton
- <h3 className="text-xl font-bold">Track Your Issue</h3>
+ <h3 className="text-xl font-bold">{t('home.tracker.title')}</h3>
@@
- placeholder="Enter Tracking ID (e.g., 1)"
+ placeholder={t('home.tracker.placeholder')}
@@
- {isTracking ? 'Tracking...' : 'Track'}
+ {isTracking ? t('home.tracker.loading') : t('home.tracker.submit')}
@@
- <span className="font-bold text-green-800 uppercase text-sm">Status: {trackingResult.data.status}</span>
- <span className="text-xs text-green-600">{new Date(trackingResult.data.created_at).toLocaleDateString()}</span>
+ <span className="font-bold text-green-800 uppercase text-sm">
+ {t('home.tracker.statusLabel', { status: trackingResult.data.status })}
+ </span>
+ <span className="text-xs text-green-600">
+ {new Date(trackingResult.data.created_at).toLocaleDateString(i18n.language)}
+ </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.
| {/* Issue Tracker Component */} | |
| <div className="bg-white rounded-3xl p-6 shadow-sm border border-slate-100 mb-8 mt-8"> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <div className="bg-indigo-100 p-2 rounded-xl text-indigo-600"> | |
| <Search size={24} /> | |
| </div> | |
| <h3 className="text-xl font-bold">Track Your Issue</h3> | |
| </div> | |
| <form onSubmit={handleTrackIssue} className="flex gap-2"> | |
| <input | |
| type="text" | |
| placeholder="Enter Tracking ID (e.g., 1)" | |
| className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| value={trackingId} | |
| onChange={(e) => setTrackingId(e.target.value)} | |
| /> | |
| <button | |
| type="submit" | |
| disabled={isTracking} | |
| className="bg-indigo-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-indigo-700 transition disabled:opacity-70" | |
| > | |
| {isTracking ? 'Tracking...' : 'Track'} | |
| </button> | |
| </form> | |
| {trackingResult && trackingResult.success && ( | |
| <div className="mt-4 p-4 bg-green-50 rounded-xl border border-green-100"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="font-bold text-green-800 uppercase text-sm">Status: {trackingResult.data.status}</span> | |
| <span className="text-xs text-green-600">{new Date(trackingResult.data.created_at).toLocaleDateString()}</span> | |
| </div> | |
| <p className="text-green-900 font-medium">{trackingResult.data.category}</p> | |
| <p className="text-sm text-green-700 mt-1 line-clamp-2">{trackingResult.data.description}</p> | |
| </div> | |
| )} | |
| {trackingResult && !trackingResult.success && ( | |
| <div className="mt-4 p-3 bg-red-50 text-red-600 rounded-xl text-sm font-medium"> | |
| {trackingResult.error} | |
| </div> | |
| )} | |
| </div> | |
| {/* Issue Tracker Component */} | |
| <div className="bg-white rounded-3xl p-6 shadow-sm border border-slate-100 mb-8 mt-8"> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <div className="bg-indigo-100 p-2 rounded-xl text-indigo-600"> | |
| <Search size={24} /> | |
| </div> | |
| <h3 className="text-xl font-bold">{t('home.tracker.title')}</h3> | |
| </div> | |
| <form onSubmit={handleTrackIssue} className="flex gap-2"> | |
| <input | |
| type="text" | |
| placeholder={t('home.tracker.placeholder')} | |
| className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| value={trackingId} | |
| onChange={(e) => setTrackingId(e.target.value)} | |
| /> | |
| <button | |
| type="submit" | |
| disabled={isTracking} | |
| className="bg-indigo-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-indigo-700 transition disabled:opacity-70" | |
| > | |
| {isTracking ? t('home.tracker.loading') : t('home.tracker.submit')} | |
| </button> | |
| </form> | |
| {trackingResult && trackingResult.success && ( | |
| <div className="mt-4 p-4 bg-green-50 rounded-xl border border-green-100"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="font-bold text-green-800 uppercase text-sm"> | |
| {t('home.tracker.statusLabel', { status: trackingResult.data.status })} | |
| </span> | |
| <span className="text-xs text-green-600"> | |
| {new Date(trackingResult.data.created_at).toLocaleDateString(i18n.language)} | |
| </span> | |
| </div> | |
| <p className="text-green-900 font-medium">{trackingResult.data.category}</p> | |
| <p className="text-sm text-green-700 mt-1 line-clamp-2">{trackingResult.data.description}</p> | |
| </div> | |
| )} | |
| {trackingResult && !trackingResult.success && ( | |
| <div className="mt-4 p-3 bg-red-50 text-red-600 rounded-xl text-sm font-medium"> | |
| {trackingResult.error} | |
| </div> | |
| )} | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/views/Home.jsx` around lines 153 - 194, The new tracker UI in
Home.jsx uses hardcoded strings (header "Track Your Issue", input placeholder,
button labels when isTracking is true/false, "Status:", and the trackingResult
error messages) which bypass i18n; update the JSX to call t(...) for each
user-facing string (e.g. t('home.tracker.title'), t('home.tracker.placeholder'),
t('home.tracker.track'), t('home.tracker.tracking'), t('home.tracker.status'),
t('home.tracker.notFound'), t('home.tracker.serverError')), add those keys into
the i18n resource files under home.tracker.*, and localize the date display
(replace new Date(...).toLocaleDateString() with a localized formatter using the
current locale or Intl.DateTimeFormat with i18n.language) so all visible text in
the Track Issue form and trackingResult flows uses translations.
| <form onSubmit={handleTrackIssue} className="flex gap-2"> | ||
| <input | ||
| type="text" | ||
| placeholder="Enter Tracking ID (e.g., 1)" | ||
| className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" | ||
| value={trackingId} | ||
| onChange={(e) => setTrackingId(e.target.value)} | ||
| /> | ||
| <button | ||
| type="submit" | ||
| disabled={isTracking} | ||
| className="bg-indigo-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-indigo-700 transition disabled:opacity-70" | ||
| > | ||
| {isTracking ? 'Tracking...' : 'Track'} | ||
| </button> | ||
| </form> |
There was a problem hiding this comment.
Accessibility: form input lacks an associated label.
The <input> only has a placeholder, which screen readers and assistive tech don't treat as a label. Add a visible <label htmlFor=...> (or at minimum aria-label) and an id on the input. Also consider aria-busy={isTracking} on the form, or aria-live region for trackingResult so announcements reach AT users.
🔧 Proposed fix
- <form onSubmit={handleTrackIssue} className="flex gap-2">
+ <form onSubmit={handleTrackIssue} className="flex gap-2" aria-busy={isTracking}>
+ <label htmlFor="tracking-id" className="sr-only">
+ {t('home.tracker.placeholder')}
+ </label>
<input
+ id="tracking-id"
type="text"
placeholder="Enter Tracking ID (e.g., 1)"
+ aria-label={t('home.tracker.placeholder')}
className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500"
value={trackingId}
onChange={(e) => setTrackingId(e.target.value)}
/>Wrap the result panels (lines 178-193) in role="status" aria-live="polite" so success/error states are announced.
📝 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.
| <form onSubmit={handleTrackIssue} className="flex gap-2"> | |
| <input | |
| type="text" | |
| placeholder="Enter Tracking ID (e.g., 1)" | |
| className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| value={trackingId} | |
| onChange={(e) => setTrackingId(e.target.value)} | |
| /> | |
| <button | |
| type="submit" | |
| disabled={isTracking} | |
| className="bg-indigo-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-indigo-700 transition disabled:opacity-70" | |
| > | |
| {isTracking ? 'Tracking...' : 'Track'} | |
| </button> | |
| </form> | |
| <form onSubmit={handleTrackIssue} className="flex gap-2" aria-busy={isTracking}> | |
| <label htmlFor="tracking-id" className="sr-only"> | |
| {t('home.tracker.placeholder')} | |
| </label> | |
| <input | |
| id="tracking-id" | |
| type="text" | |
| placeholder="Enter Tracking ID (e.g., 1)" | |
| aria-label={t('home.tracker.placeholder')} | |
| className="flex-1 border border-gray-200 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| value={trackingId} | |
| onChange={(e) => setTrackingId(e.target.value)} | |
| /> | |
| <button | |
| type="submit" | |
| disabled={isTracking} | |
| className="bg-indigo-600 text-white px-6 py-3 rounded-xl font-bold hover:bg-indigo-700 transition disabled:opacity-70" | |
| > | |
| {isTracking ? 'Tracking...' : 'Track'} | |
| </button> | |
| </form> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/views/Home.jsx` around lines 161 - 176, Add accessible labels
and live region announcements for tracking UI: give the input an id and either
add a visible <label htmlFor="..."> or set aria-label on the input tied to
trackingId used in the form that calls handleTrackIssue; mark the submit state
by setting aria-busy={isTracking} on the <form> (or on the input) and wrap the
tracking result panel (the element rendering trackingResult) with role="status"
and aria-live="polite" so success/error updates are announced to assistive tech.
| "jest": "^29.7.0", | ||
| "jest-environment-jsdom": "^30.3.0", | ||
| "jest-util": "^30.3.0", | ||
| "playwright": "^1.59.1", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if the base 'playwright' package is imported or used anywhere
# Search for direct imports of 'playwright' (excluding `@playwright/test`)
rg -n --type=js --type=ts -C3 "(?:require\(['\"]playwright['\"]\)|from ['\"]playwright['\"])" --pcre2
# Also check for dynamic imports
rg -n --type=js --type=ts -C3 "import\(['\"]playwright['\"]\)" --pcre2Repository: RohanExploit/VishwaGuru
Length of output: 49
Remove the redundant playwright package from devDependencies.
The @playwright/test package (line 28) already includes the full Playwright library. The base playwright package at line 36 is unused in the codebase and only adds unnecessary installation time, disk usage, and maintenance overhead.
Fix
- "playwright": "^1.59.1",📝 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.
| "playwright": "^1.59.1", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 36, Remove the redundant devDependency "playwright"
from package.json's devDependencies (it duplicates functionality already
provided by "@playwright/test"), delete the "playwright" entry, then run your
package manager (npm/yarn/pnpm) to update the lockfile and node_modules so the
change is reflected; finally run the test suite/CI to ensure nothing breaks
after removing "playwright".
There was a problem hiding this comment.
Pull request overview
Adds a small “Track Your Issue” UI to the Home page, introduces a new Hugging Face-backed object detection endpoint, fixes camera cleanup on modal close, and updates root-level test tooling dependencies.
Changes:
- Add “Track Your Issue” form + results panel to
Home.jsxand improve camera stream teardown. - Add
/api/detect-objectsendpoint wired to HFfacebook/detr-resnet-50viadetect_objects_hf. - Add Playwright dependency updates in root
package.json/ lockfile (to support existing Playwright spec usage).
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| patch_home.py | Removes a one-off patch script previously used to modify Home.jsx. |
| package.json | Adds Playwright dependency entries for root tooling. |
| package-lock.json | Locks Playwright-related packages/versions. |
| frontend/src/views/Home.jsx | Adds issue-tracking UI and clears video.srcObject on unmount. |
| backend/routers/detection.py | Adds /detect-objects API endpoint and imports detect_objects_hf. |
| backend/hf_api_service.py | Adds detect_objects_hf() and the DETR model URL constant. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!trackingId.trim()) return; | ||
|
|
||
| setIsTracking(true); | ||
| setTrackingResult(null); | ||
| try { | ||
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| setTrackingResult({ success: true, data }); |
There was a problem hiding this comment.
handleTrackIssue calls /api/issues/${trackingId}, but the backend router mounted at /api does not currently expose a GET /issues/{id} endpoint (see backend/routers/issues.py, which only has /issues/recent, /issues/nearby, /issues/user, etc.). As-is, the new “Track Your Issue” UI will always get a 404. Either add the missing backend GET-by-id endpoint, or update this UI to call an endpoint that actually exists.
| if (!trackingId.trim()) return; | |
| setIsTracking(true); | |
| setTrackingResult(null); | |
| try { | |
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| setTrackingResult({ success: true, data }); | |
| const normalizedTrackingId = trackingId.trim(); | |
| if (!normalizedTrackingId) return; | |
| setIsTracking(true); | |
| setTrackingResult(null); | |
| try { | |
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/recent`); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| const issues = Array.isArray(data) | |
| ? data | |
| : Array.isArray(data?.issues) | |
| ? data.issues | |
| : Array.isArray(data?.data) | |
| ? data.data | |
| : []; | |
| const matchedIssue = issues.find((issue) => { | |
| const candidateIds = [issue?.id, issue?._id, issue?.tracking_id] | |
| .filter((value) => value !== undefined && value !== null) | |
| .map((value) => String(value).trim()); | |
| return candidateIds.includes(normalizedTrackingId); | |
| }); | |
| if (matchedIssue) { | |
| setTrackingResult({ success: true, data: matchedIssue }); | |
| } else { | |
| setTrackingResult({ success: false, error: 'Issue not found. Please check the ID.' }); | |
| } |
| if (!trackingId.trim()) return; | ||
|
|
||
| setIsTracking(true); | ||
| setTrackingResult(null); | ||
| try { | ||
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); |
There was a problem hiding this comment.
The tracking request URL interpolates user input directly into the path. If the user enters characters like /, ?, or #, this can produce an unintended request path. Encode the ID segment (and/or validate it as an integer if that’s what the backend expects) before building the URL.
| if (!trackingId.trim()) return; | |
| setIsTracking(true); | |
| setTrackingResult(null); | |
| try { | |
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); | |
| const trimmedTrackingId = trackingId.trim(); | |
| if (!trimmedTrackingId) return; | |
| setIsTracking(true); | |
| setTrackingResult(null); | |
| try { | |
| const encodedTrackingId = encodeURIComponent(trimmedTrackingId); | |
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${encodedTrackingId}`); |
| <span className="font-bold text-green-800 uppercase text-sm">Status: {trackingResult.data.status}</span> | ||
| <span className="text-xs text-green-600">{new Date(trackingResult.data.created_at).toLocaleDateString()}</span> | ||
| </div> |
There was a problem hiding this comment.
new Date(trackingResult.data.created_at).toLocaleDateString() can throw (RangeError: Invalid time value) if created_at is missing/null/not parseable. Several backend issue responses in this repo serialize created_at conditionally (... if row.created_at else None), so this UI should guard and render a fallback (or omit the date) when created_at isn’t present.
| img_data = await validate_uploaded_file(image) | ||
| if "error" in img_data: | ||
| raise HTTPException(status_code=400, detail=img_data["error"]) | ||
|
|
||
| processed_bytes = await run_in_threadpool(process_uploaded_image, img_data["bytes"]) | ||
| client = get_http_client(request) |
There was a problem hiding this comment.
This endpoint treats validate_uploaded_file() as if it returns a dict containing bytes/error, but in backend/utils.py it returns a PIL Image.Image (or raises an HTTPException). As written, "error" in img_data will raise a TypeError, and img_data["bytes"] will also fail. Align this with the other detection endpoints that use process_uploaded_image(image) to obtain validated image bytes before calling the HF service.
| "jest": "^29.7.0", | ||
| "jest-environment-jsdom": "^30.3.0", | ||
| "jest-util": "^30.3.0", | ||
| "playwright": "^1.59.1", |
There was a problem hiding this comment.
@playwright/test already pulls in playwright (and exposes the Playwright CLI via npx playwright). Adding playwright separately is typically redundant and increases install size/time. If you don’t have a specific need for playwright as a direct devDependency, consider removing it and keeping only @playwright/test.
| "playwright": "^1.59.1", |
There was a problem hiding this comment.
3 issues found across 6 files
Prompt for AI agents (unresolved 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/views/Home.jsx">
<violation number="1" location="frontend/src/views/Home.jsx:76">
P1: In production builds, `VITE_API_URL` is statically replaced with `""` (empty string) by Vite. Since empty string is falsy, the `||` operator causes the fallback to `'http://localhost:8000'`, making all API requests go to localhost in production. Use nullish coalescing (`??`) instead so empty string is treated as a valid value (relative URL), or set `VITE_API_URL` properly in `.env.production`.</violation>
<violation number="2" location="frontend/src/views/Home.jsx:159">
P2: All other labels in this view use `t(...)` for i18n, but the new tracker section hardcodes English strings (`"Track Your Issue"`, `"Tracking..."`, `"Issue not found. Please check the ID."`, etc.). This breaks localization for non-English users. Move these strings to i18n resources and reference them via `t('home.tracker.*')`.</violation>
</file>
<file name="backend/routers/detection.py">
<violation number="1" location="backend/routers/detection.py:522">
P1: The new object-detection endpoint misuses `validate_uploaded_file` as a dict result, causing runtime errors before detection runs.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| setIsTracking(true); | ||
| setTrackingResult(null); | ||
| try { | ||
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); |
There was a problem hiding this comment.
P1: In production builds, VITE_API_URL is statically replaced with "" (empty string) by Vite. Since empty string is falsy, the || operator causes the fallback to 'http://localhost:8000', making all API requests go to localhost in production. Use nullish coalescing (??) instead so empty string is treated as a valid value (relative URL), or set VITE_API_URL properly in .env.production.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/views/Home.jsx, line 76:
<comment>In production builds, `VITE_API_URL` is statically replaced with `""` (empty string) by Vite. Since empty string is falsy, the `||` operator causes the fallback to `'http://localhost:8000'`, making all API requests go to localhost in production. Use nullish coalescing (`??`) instead so empty string is treated as a valid value (relative URL), or set `VITE_API_URL` properly in `.env.production`.</comment>
<file context>
@@ -58,6 +61,31 @@ const Home = ({ setView, fetchResponsibilityMap, recentIssues, handleUpvote, loa
+ setIsTracking(true);
+ setTrackingResult(null);
+ try {
+ const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`);
+ if (response.ok) {
+ const data = await response.json();
</file context>
| const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api/issues/${trackingId}`); | |
| const apiBase = import.meta.env.VITE_API_URL ?? 'http://localhost:8000'; | |
| const response = await fetch(`${apiBase}/api/issues/${encodeURIComponent(trackingId.trim())}`); |
| img_data = await validate_uploaded_file(image) | ||
| if "error" in img_data: | ||
| raise HTTPException(status_code=400, detail=img_data["error"]) | ||
|
|
||
| processed_bytes = await run_in_threadpool(process_uploaded_image, img_data["bytes"]) |
There was a problem hiding this comment.
P1: The new object-detection endpoint misuses validate_uploaded_file as a dict result, causing runtime errors before detection runs.
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 522:
<comment>The new object-detection endpoint misuses `validate_uploaded_file` as a dict result, causing runtime errors before detection runs.</comment>
<file context>
@@ -510,3 +510,24 @@ async def detect_emotion_endpoint(
+ """
+ Detects general objects in the image using Hugging Face object detection model.
+ """
+ img_data = await validate_uploaded_file(image)
+ if "error" in img_data:
+ raise HTTPException(status_code=400, detail=img_data["error"])
</file context>
| img_data = await validate_uploaded_file(image) | |
| if "error" in img_data: | |
| raise HTTPException(status_code=400, detail=img_data["error"]) | |
| processed_bytes = await run_in_threadpool(process_uploaded_image, img_data["bytes"]) | |
| _, processed_bytes = await process_uploaded_image(image) |
| <div className="bg-indigo-100 p-2 rounded-xl text-indigo-600"> | ||
| <Search size={24} /> | ||
| </div> | ||
| <h3 className="text-xl font-bold">Track Your Issue</h3> |
There was a problem hiding this comment.
P2: All other labels in this view use t(...) for i18n, but the new tracker section hardcodes English strings ("Track Your Issue", "Tracking...", "Issue not found. Please check the ID.", etc.). This breaks localization for non-English users. Move these strings to i18n resources and reference them via t('home.tracker.*').
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/views/Home.jsx, line 159:
<comment>All other labels in this view use `t(...)` for i18n, but the new tracker section hardcodes English strings (`"Track Your Issue"`, `"Tracking..."`, `"Issue not found. Please check the ID."`, etc.). This breaks localization for non-English users. Move these strings to i18n resources and reference them via `t('home.tracker.*')`.</comment>
<file context>
@@ -122,6 +150,50 @@ const Home = ({ setView, fetchResponsibilityMap, recentIssues, handleUpvote, loa
+ <div className="bg-indigo-100 p-2 rounded-xl text-indigo-600">
+ <Search size={24} />
+ </div>
+ <h3 className="text-xl font-bold">Track Your Issue</h3>
+ </div>
+ <form onSubmit={handleTrackIssue} className="flex gap-2">
</file context>
- Moved netlify.toml to frontend/ directory where the Vite build is actually configured - Removed the `base = "frontend"` directive since the toml is now inside the frontend folder - This ensures Netlify correctly picks up the Vite build command and dist folder
- Restored `netlify.toml` in the repository root with `base = "frontend"` directive. - This configuration is required for Netlify to properly execute `npm install && npm run build` inside the `frontend` subdirectory, allowing the `_redirects` and `_headers` routing rules to correctly deploy for React Router.
🔍 Quality Reminder |
- Moved React routing redirects and security headers from `frontend/public/_redirects` and `_headers` into the root `netlify.toml` file. - When `base = "frontend"` is used in `netlify.toml`, Netlify often expects the routing and header rules to be defined in the toml file rather than physical files in the dist directory to pass CI validation checks for those rules.
This PR adds several features:
/api/issues/{id}endpoint.facebook/detr-resnet-50serverless inference.CameraCheckModalclose._redirectsand_headersare properly placed for Netlify deployments.PR created automatically by Jules for task 1984330708984751353 started by @RohanExploit
Summary by cubic
Adds a lightweight issue tracking UI and a Hugging Face object detection API. Fixes camera unmount and moves Netlify redirects/headers into root config to resolve CI.
New Features
/api/detect-objectsusing Hugging Facefacebook/detr-resnet-50; returns{ detections: [{ score, label, box }, ...] }./api/issues/{id}and shows status, category, description, and created date.Bug Fixes
videoRef.srcObjecton unmount.netlify.tomlwithbase = "frontend"now includes React Router redirect and security headers; removedfrontend/public/_redirectsand_headersto fix CI.playwrightto dev dependencies to unblock TS/React tests.Written for commit e6b8a02. Summary will update on new commits.
Summary by CodeRabbit