+ {/* Hero Section */}
+
+
+
+
+ VighnView
+
+
+ Smart Civic Monitoring Platform
+
+
+ Transform civic issue reporting from reactive to proactive through AI-powered detection,
+ gamification, and multi-source data integration. Making cities smarter, one report at a time!
+
+
+
+ {isAuthenticated ? (
+ <>
+
+ Report an Issue
+
+
+ View Dashboard
+
+ >
+ ) : (
+ <>
+
+ Get Started
+
+
+ Sign In
+
+ >
+ )}
+
+
+
+
+
+ {/* Stats Section */}
+ {stats && (
+
+
+
+
+
+ {stats.total_issues || 0}
+
+
Issues Reported
+
+
+
+ {stats.resolved || 0}
+
+
Issues Resolved
+
+
+
+ {stats.potholes || 0}
+
+
Potholes Fixed
+
+
+
+ {stats.garbage || 0}
+
+
Garbage Cleaned
+
+
+
+
+ )}
+
+ {/* Features Section */}
+
+
+
+
+ Why Choose VighnView?
+
+
+ Our platform combines cutting-edge technology with community engagement
+ to create a comprehensive civic monitoring solution.
+
+
+
+
+ {features.map((feature, index) => (
+
+
+ {feature.icon}
+
+
+ {feature.title}
+
+
+ {feature.description}
+
+
+ ))}
+
+
+
+
+ {/* Recent Issues Section */}
+ {recentIssues.length > 0 && (
+
+
+
+
+ Recent Issues
+
+
+ See what your community is reporting
+
+
+
+
+ {recentIssues.map((issue, index) => (
+
+
+
+ {issue.status.replace('_', ' ')}
+
+
+ {new Date(issue.createdAt).toLocaleDateString()}
+
+
+
+
+ {issue.title}
+
+
+
+ {issue.description || 'No description provided'}
+
+
+
+
+ {issue.category.replace('_', ' ')}
+
+
+ View Details →
+
+
+
+ ))}
+
+
+
+
+ View All Issues
+
+
+
+
+ )}
+
+ {/* Roadmap Section */}
+
+
+
+
+ Development Roadmap
+
+
+ Our journey from basic reporting to full smart governance automation
+
+
+
+
+ {phases.map((phase, index) => (
+
+
+
+ {phase.icon}
+
+
+
{phase.phase}
+
{phase.title}
+
+
+
+ {phase.description}
+
+
+
+
+ {phase.features.map((feature, featureIndex) => (
+
+ ))}
+
+
+ ))}
+
+
+
+
+ {/* CTA Section */}
+
+
+
+
+ Ready to Make a Difference?
+
+
+ Join thousands of citizens who are already using VighnView to improve their communities.
+
+
+ {!isAuthenticated && (
+
+
+ Start Reporting Issues
+
+
+ View Leaderboard
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default Home;
\ No newline at end of file
diff --git a/frontend/src/pages/ReportIssue.js b/frontend/src/pages/ReportIssue.js
new file mode 100644
index 0000000..fdb2b9e
--- /dev/null
+++ b/frontend/src/pages/ReportIssue.js
@@ -0,0 +1,528 @@
+import React, { useState, useRef } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useForm } from 'react-hook-form';
+import { useDropzone } from 'react-dropzone';
+import { motion } from 'framer-motion';
+import {
+ FaCamera,
+ FaMapMarkerAlt,
+ FaUpload,
+ FaTrash,
+ FaSpinner,
+ FaCheck,
+ FaExclamationTriangle
+} from 'react-icons/fa';
+import toast from 'react-hot-toast';
+import { issuesAPI, getCurrentLocation, reverseGeocode } from '../services/api';
+import { useAuth } from '../contexts/AuthContext';
+
+const ReportIssue = () => {
+ const navigate = useNavigate();
+ const { user, isAuthenticated } = useAuth();
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [location, setLocation] = useState(null);
+ const [isGettingLocation, setIsGettingLocation] = useState(false);
+ const [uploadedFiles, setUploadedFiles] = useState([]);
+ const [aiAnalysis, setAiAnalysis] = useState(null);
+ const [isAnalyzing, setIsAnalyzing] = useState(false);
+
+ const fileInputRef = useRef(null);
+
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ setValue,
+ watch,
+ reset
+ } = useForm({
+ defaultValues: {
+ title: '',
+ description: '',
+ category: '',
+ priority: 'medium',
+ latitude: '',
+ longitude: '',
+ address: '',
+ city: '',
+ state: '',
+ pincode: ''
+ }
+ });
+
+ const categories = [
+ { value: 'pothole', label: 'Pothole', icon: '🕳️' },
+ { value: 'garbage', label: 'Garbage', icon: '🗑️' },
+ { value: 'sewage', label: 'Sewage', icon: '💧' },
+ { value: 'street_light', label: 'Street Light', icon: '💡' },
+ { value: 'traffic_signal', label: 'Traffic Signal', icon: '🚦' },
+ { value: 'road_damage', label: 'Road Damage', icon: '🛣️' },
+ { value: 'water_leak', label: 'Water Leak', icon: '💧' },
+ { value: 'illegal_dumping', label: 'Illegal Dumping', icon: '🚛' },
+ { value: 'other', label: 'Other', icon: '📋' }
+ ];
+
+ const priorities = [
+ { value: 'low', label: 'Low', color: 'text-green-600' },
+ { value: 'medium', label: 'Medium', color: 'text-yellow-600' },
+ { value: 'high', label: 'High', color: 'text-orange-600' },
+ { value: 'critical', label: 'Critical', color: 'text-red-600' }
+ ];
+
+ const getCurrentLocationHandler = async () => {
+ setIsGettingLocation(true);
+ try {
+ const position = await getCurrentLocation();
+ setLocation(position);
+
+ setValue('latitude', position.latitude);
+ setValue('longitude', position.longitude);
+
+ // Get address from coordinates
+ const addressData = await reverseGeocode(position.latitude, position.longitude);
+ setValue('address', addressData.address);
+ setValue('city', addressData.city);
+ setValue('state', addressData.state);
+ setValue('pincode', addressData.pincode);
+
+ toast.success('Location detected successfully!');
+ } catch (error) {
+ console.error('Location error:', error);
+ toast.error('Failed to get location. Please enter manually.');
+ } finally {
+ setIsGettingLocation(false);
+ }
+ };
+
+ const onDrop = (acceptedFiles) => {
+ const newFiles = acceptedFiles.map(file => ({
+ file,
+ id: Math.random().toString(36).substr(2, 9),
+ preview: URL.createObjectURL(file),
+ uploading: false,
+ uploaded: false
+ }));
+
+ setUploadedFiles(prev => [...prev, ...newFiles]);
+
+ // Analyze first image with AI
+ if (acceptedFiles.length > 0 && acceptedFiles[0].type.startsWith('image/')) {
+ analyzeImageWithAI(acceptedFiles[0]);
+ }
+ };
+
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
+ onDrop,
+ accept: {
+ 'image/*': ['.jpeg', '.jpg', '.png', '.gif'],
+ 'video/*': ['.mp4', '.mov', '.avi']
+ },
+ maxFiles: 5,
+ maxSize: 10 * 1024 * 1024 // 10MB
+ });
+
+ const removeFile = (fileId) => {
+ setUploadedFiles(prev => {
+ const file = prev.find(f => f.id === fileId);
+ if (file) {
+ URL.revokeObjectURL(file.preview);
+ }
+ return prev.filter(f => f.id !== fileId);
+ });
+ };
+
+ const analyzeImageWithAI = async (file) => {
+ setIsAnalyzing(true);
+ try {
+ const formData = new FormData();
+ formData.append('image', file);
+
+ // This would call your AI service
+ // const response = await fetch('/api/ai/analyze', {
+ // method: 'POST',
+ // body: formData
+ // });
+ // const result = await response.json();
+
+ // Simulate AI analysis for demo
+ setTimeout(() => {
+ const mockAnalysis = {
+ detected_issues: [
+ { type: 'pothole', confidence: 0.85 },
+ { type: 'garbage', confidence: 0.72 }
+ ],
+ confidence: 0.78,
+ processing_time: 1.2
+ };
+ setAiAnalysis(mockAnalysis);
+ setIsAnalyzing(false);
+ toast.success('AI analysis completed!');
+ }, 2000);
+
+ } catch (error) {
+ console.error('AI analysis error:', error);
+ setIsAnalyzing(false);
+ toast.error('AI analysis failed');
+ }
+ };
+
+ const onSubmit = async (data) => {
+ if (!isAuthenticated && !user?.isAnonymous) {
+ toast.error('Please login or use anonymous mode to report issues');
+ return;
+ }
+
+ if (uploadedFiles.length === 0) {
+ toast.error('Please upload at least one photo or video');
+ return;
+ }
+
+ setIsSubmitting(true);
+ try {
+ const issueData = {
+ ...data,
+ media: uploadedFiles.map(f => f.file)
+ };
+
+ const response = await issuesAPI.createIssue(issueData);
+
+ if (response.data.success) {
+ toast.success('Issue reported successfully!');
+ reset();
+ setUploadedFiles([]);
+ setAiAnalysis(null);
+ navigate(`/issues/${response.data.data.issue.id}`);
+ }
+ } catch (error) {
+ console.error('Report issue error:', error);
+ const message = error.response?.data?.message || 'Failed to report issue';
+ toast.error(message);
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+