0% found this document useful (0 votes)
5 views13 pages

Pasted Text 1741005155187

The document outlines the structure and design of a web application called RoadWatch Pro, which allows users to report road issues and view an interactive map. It includes a header with navigation links, a report issue form, and sections for statistics and user reports. The application utilizes various libraries for styling and mapping functionalities, and features a modal for user authentication.

Uploaded by

xCharzD
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views13 pages

Pasted Text 1741005155187

The document outlines the structure and design of a web application called RoadWatch Pro, which allows users to report road issues and view an interactive map. It includes a header with navigation links, a report issue form, and sections for statistics and user reports. The application utilizes various libraries for styling and mapping functionalities, and features a modal for user authentication.

Uploaded by

xCharzD
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 13

<!

DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RoadWatch Pro</title>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-
awesome/6.4.0/css/all.min.css" />
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/
MarkerCluster.css" />
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/
MarkerCluster.Default.css" />
<style>
/* Original CSS styles */
:root {
--primary: #2563eb;
--primary-dark: #1e40af;
--success: #10b981;
--warning: #f59e0b;
--danger: #dc2626;
--glass: rgba(255, 255, 255, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, sans-serif;
line-height: 1.6;
background: #0f172a;
color: #f8fafc;
min-height: 100vh;
direction: ltr;
}
header {
background: rgba(15, 23, 42, 0.95);
backdrop-filter: blur(10px);
color: white;
padding: 1rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 1000;
}
.header-content {
max-width: 1400px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1.5rem;
font-weight: bold;
}
.logo i {
color: var(--primary);
}
nav {
display: flex;
gap: 1rem;
}
nav a {
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
nav a:hover {
background: var(--glass);
transform: translateY(-1px);
}
main {
max-width: 1400px;
margin: 2rem auto;
padding: 0 1rem;
display: grid;
grid-template-columns: 300px 1fr;
gap: 2rem;
}
.card {
background: rgba(30, 41, 59, 0.9);
border-radius: 1rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
margin-bottom: 1rem;
}
.card-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.card-content {
padding: 1.5rem;
}
#mapContainer {
height: 700px;
border-radius: 1rem;
overflow: hidden;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #e2e8f0;
}
input, select, textarea {
width: 100%;
padding: 0.75rem;
background: rgba(30, 41, 59, 0.9);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 0.375rem;
font-size: 1rem;
color: white;
transition: all 0.2s;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
}
button {
background: var(--primary);
color: white;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.375rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
button:hover {
background: var(--primary-dark);
transform: translateY(-1px);
}
.custom-marker {
border-radius: 50%;
width: 30px;
height: 30px;
border: 2px solid white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 14px;
}
.severity-high {
background: var(--danger);
animation: pulse 2s infinite;
}
.severity-medium {
background: var(--warning);
}
.severity-low {
background: var(--success);
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.stat-card {
background: rgba(30, 41, 59, 0.9);
padding: 1rem;
border-radius: 0.5rem;
text-align: center;
}
.stat-value {
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.stat-label {
color: #94a3b8;
font-size: 0.875rem;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.leaflet-popup-content-wrapper {
background: rgba(30, 41, 59, 0.95);
color: white;
border-radius: 0.5rem;
backdrop-filter: blur(10px);
}
.leaflet-popup-tip {
background: rgba(30, 41, 59, 0.95);
}
.issue-popup {
padding: 0.5rem;
}
.issue-popup h3 {
color: var(--primary);
margin-bottom: 0.5rem;
}
.issue-meta {
display: flex;
gap: 1rem;
font-size: 0.875rem;
color: #94a3b8;
}
.admin-only {
display: none;
}
.user-only {
display: none;
}
.logged-in {
display: none;
}
.logged-out {
display: block;
}
.modal {
display: none;
position: fixed;
z-index: 1001;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
}
.modal-content {
background: #1e293b;
margin: 15% auto;
padding: 20px;
width: 300px;
border-radius: 8px;
}
</style>
</head>
<body>
<header>
<div class="header-content">
<div class="logo">
<i class="fas fa-road"></i>
RoadWatch Pro
</div>
<nav>
<a href="#report"><i class="fas fa-plus-circle"></i> Report an Issue</a>
<a href="#map"><i class="fas fa-map-marked-alt"></i> View Map</a>
<div id="authSection" class="logged-out">
<button onclick="showAuthModal()">Login</button>
</div>
<div id="userSection" class="logged-in">
<span id="usernameDisplay"></span>
<button onclick="logout()">Logout</button>
</div>
</nav>
</div>
</header>

<div id="authModal" class="modal">


<div class="modal-content">
<h3>Login/Register</h3>
<input type="email" id="authEmail" placeholder="Email">
<input type="password" id="authPassword" placeholder="Password">
<button onclick="handleLogin()">Login as User</button>
<button onclick="handleAdminLogin()">Login as Admin</button>
</div>
</div>

<main>
<aside>
<!-- Report Issue Card -->
<section id="report" class="card">
<div class="card-header">
<h2><i class="fas fa-exclamation-triangle"></i> Report an Issue</h2>
</div>
<div class="card-content">
<form id="reportForm">
<div class="form-group">
<label for="location">Location (Click on the map)</label>
<input type="text" id="location" required readonly>
</div>
<div class="form-group">
<label for="type">Issue Type</label>
<select id="type" required>
<option value="">Select Type...</option>
<option value="pothole">Pothole</option>
<option value="construction">Construction</option>
<option value="flooding">Flooding</option>
<option value="debris">Debris</option>
<option value="accident">Accident</option>
<option value="signal">Traffic Signal Issue</option>
</select>
</div>
<div class="form-group">
<label for="severity">Severity</label>
<select id="severity" required>
<option value="">Select Severity...</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea id="description" required rows="3"></textarea>
</div>
<div class="form-group">
<label for="photo">Upload Photo</label>
<input type="file" id="photo" accept="image/*" capture="environment">
<div id="photoPreview" class="photo-preview"></div>
</div>
<button type="submit">
<i class="fas fa-paper-plane"></i>
Submit Report
</button>
</form>
</div>
</section>

<!-- Admin Statistics Card with Chart -->


<section class="card admin-only">
<div class="card-header">
<h2><i class="fas fa-chart-pie"></i> Statistics</h2>
</div>
<div class="card-content">
<div class="stats">
<div class="stat-card">
<div class="stat-value" id="totalIssues">0</div>
<div class="stat-label">Total Issues</div>
</div>
<div class="stat-card">
<div class="stat-value" id="pendingCount">0</div>
<div class="stat-label">Pending</div>
</div>
<div class="stat-card">
<div class="stat-value" id="inProgressCount">0</div>
<div class="stat-label">In Progress</div>
</div>
<div class="stat-card">
<div class="stat-value" id="resolvedCount">0</div>
<div class="stat-label">Resolved</div>
</div>
</div>
<!-- Chart container -->
<canvas id="statusChart" style="max-width: 100%; margin-top:
1rem;"></canvas>
</div>
</section>

<!-- My Reports Card for Regular Users -->


<section class="card user-only">
<div class="card-header">
<h2><i class="fas fa-list"></i> My Reports</h2>
</div>
<div class="card-content" id="myReportsList">
<!-- Dynamically filled -->
</div>
</section>
</aside>

<!-- Map Section -->


<section id="map" class="card">
<div class="card-header">
<h2><i class="fas fa-map-marked-alt"></i> Interactive Map</h2>
<div class="filters admin-only">
<select id="filterType">
<option value="">All Types</option>
<option value="pothole">Pothole</option>
<option value="construction">Construction</option>
<option value="flooding">Flooding</option>
<option value="debris">Debris</option>
<option value="accident">Accident</option>
<option value="signal">Signal Issue</option>
</select>
<select id="filterSeverity">
<option value="">All Severities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
</div>
<div id="mapContainer"></div>
</section>
</main>

<!-- Load Chart.js from CDN -->


<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></
script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/
leaflet.markercluster.js"></script>
<script>
// Initialize the map
const map = L.map('mapContainer').setView([40.7128, -74.0060], 13);
const baseLayer =
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
const markers = L.markerClusterGroup();
map.addLayer(markers);

let reports = [];


let tempMarker = null;
let currentUser = null;
let isAdmin = false;
let statusChart; // Chart.js instance

// Create custom icon


function createCustomIcon(severity, type) {
const iconHtml = `
<div class="custom-marker severity-${severity}">
<i class="fas ${getIconForType(type)}"></i>
</div>`;
return L.divIcon({
html: iconHtml,
className: 'custom-marker-wrapper',
iconSize: [30, 30]
});
}

function getIconForType(type) {
const icons = {
pothole: 'fa-road',
construction: 'fa-traffic-light',
flooding: 'fa-water',
debris: 'fa-trash',
accident: 'fa-car-crash',
signal: 'fa-traffic-light'
};
return icons[type] || 'fa-exclamation-circle';
}

// Handle map click


map.on('click', function(e) {
const { lat, lng } = e.latlng;
document.getElementById('location').value = `${lat.toFixed(5)}, $
{lng.toFixed(5)}`;
if (tempMarker) map.removeLayer(tempMarker);
tempMarker = L.marker([lat, lng], {
icon: createCustomIcon('low', 'pothole'),
opacity: 0.5
}).addTo(map);
});

// Handle form submission


document.getElementById('reportForm').addEventListener('submit', function(e) {
e.preventDefault();
const locationInput = document.getElementById('location').value.split(',');
const photoInput = document.getElementById('photo');

if (locationInput.length !== 2 || isNaN(locationInput[0]) ||


isNaN(locationInput[1])) {
alert("‫;)"الرجاء تحديد موقع صحيح على الخريطة‬
return;
}

const reader = new FileReader();


reader.onload = function(e) {
// New reports start with status "pending"
const newReport = {
id: Date.now(),
location: {
lat: parseFloat(locationInput[0]),
lng: parseFloat(locationInput[1])
},
type: document.getElementById('type').value,
severity: document.getElementById('severity').value,
description: document.getElementById('description').value,
timestamp: new Date().toISOString(),
photo: e.target.result,
status: "pending", // "pending", "in-progress", "resolved"
owner: currentUser || "anonymous"
};
reports.push(newReport);
renderMarkers();
updateStatistics();
renderMyReports();
document.getElementById('reportForm').reset();
document.getElementById('photoPreview').innerHTML = '';
if (tempMarker) map.removeLayer(tempMarker);
alert("‫;)"!تم إرسال التقرير بنجاح‬
};

if (photoInput.files[0]) {
reader.readAsDataURL(photoInput.files[0]);
} else {
reader.onload({ target: { result: null } });
}
});

// Render markers with admin controls


function renderMarkers() {
markers.clearLayers();
filterReports().forEach(report => {
let popupContent = `
<div class="issue-popup">
<h3>${report.type}</h3>
<p>${report.description}</p>
${report.photo ? `<img src="${report.photo}" style="max-width:200px;">`
: ''}
<div class="issue-meta">
<span><i class="fas fa-exclamation-circle"></i>
${report.severity}</span>
<span><i class="fas fa-clock"></i> ${new
Date(report.timestamp).toLocaleString()}</span>
<span>Status: <strong>${report.status}</strong></span>
</div>
`;
// Admin controls: update status and delete
if (isAdmin) {
popupContent += `<div style="margin-top:0.5rem;">`;
if (report.status === "pending") {
popupContent += `<button onclick="markInProgress(${report.id})"
style="margin-right:5px;">Mark In Progress</button>`;
} else if (report.status === "in-progress") {
popupContent += `<button onclick="markResolved(${report.id})"
style="margin-right:5px;">Resolve</button>`;
}
popupContent += `<button
onclick="deleteReport(${report.id})">Delete</button>`;
popupContent += `</div>`;
}
popupContent += `</div>`;
const marker = L.marker(
[report.location.lat, report.location.lng],
{ icon: createCustomIcon(report.severity, report.type) }
).bindPopup(popupContent);
markers.addLayer(marker);
});
}

// Filter reports by type and severity


function filterReports() {
return reports.filter(report => {
const typeFilter = document.getElementById('filterType')?.value || '';
const severityFilter = document.getElementById('filterSeverity')?.value ||
'';
return (!typeFilter || report.type === typeFilter) &&
(!severityFilter || report.severity === severityFilter);
});
}

// Admin function: Mark report as In Progress


function markInProgress(reportId) {
const report = reports.find(r => r.id === reportId);
if (report && report.status === "pending") {
report.status = "in-progress";
updateStatistics();
renderMarkers();
renderMyReports();
}
}

// Admin function: Mark report as Resolved


function markResolved(reportId) {
const report = reports.find(r => r.id === reportId);
if (report && report.status === "in-progress") {
report.status = "resolved";
report.resolvedTimestamp = new Date().toISOString();
updateStatistics();
renderMarkers();
renderMyReports();
}
}

// Delete report (admin and user)


function deleteReport(reportId) {
reports = reports.filter(r => r.id !== reportId);
updateStatistics();
renderMarkers();
renderMyReports();
}

// User function: Edit own report description


function editMyReport(reportId) {
const report = reports.find(r => r.id === reportId);
if (report && report.owner === currentUser) {
const newDesc = prompt("Edit your report description:",
report.description);
if (newDesc !== null) {
report.description = newDesc;
renderMarkers();
renderMyReports();
}
} else {
alert("You can only edit your own reports.");
}
}

// Render My Reports for logged in non-admin user


function renderMyReports() {
const myReportsContainer = document.getElementById('myReportsList');
if (!myReportsContainer) return;
if (!currentUser || isAdmin) {
myReportsContainer.innerHTML = `<p>No personal reports to show.</p>`;
return;
}
const myReports = reports.filter(r => r.owner === currentUser);
if (myReports.length === 0) {
myReportsContainer.innerHTML = `<p>No reports submitted by you.</p>`;
} else {
myReportsContainer.innerHTML = myReports.map(report => `
<div style="border-bottom: 1px solid #ccc; margin-bottom: 10px; padding-
bottom: 10px;">
<strong>${report.type}</strong> (${report.severity})<br>
<small>${new Date(report.timestamp).toLocaleString()}</small><br>
<p>${report.description}</p>
<p>Status: <strong>${report.status}</strong></p>
<div style="margin-top:5px;">
<button onclick="editMyReport(${report.id})" style="margin-
right:5px;">Edit</button>
<button onclick="deleteReport(${report.id})">Delete</button>
</div>
</div>
`).join('');
}
}

// Auth functions
function showAuthModal() {
document.getElementById('authModal').style.display = 'block';
}
function handleLogin() {
currentUser = document.getElementById('authEmail').value;
isAdmin = false;
updateUI();
document.getElementById('authModal').style.display = 'none';
}
function handleAdminLogin() {
currentUser = document.getElementById('authEmail').value;
isAdmin = true;
updateUI();
document.getElementById('authModal').style.display = 'none';
}
function logout() {
currentUser = null;
isAdmin = false;
updateUI();
}

// Update statistics and update chart data if admin is logged in


function updateStatistics() {
const total = reports.length;
const pending = reports.filter(r => r.status === "pending").length;
const inProgress = reports.filter(r => r.status === "in-progress").length;
const resolved = reports.filter(r => r.status === "resolved").length;

document.getElementById('totalIssues').textContent = total;
document.getElementById('pendingCount').textContent = pending;
document.getElementById('inProgressCount').textContent = inProgress;
document.getElementById('resolvedCount').textContent = resolved;

// Update Chart if it exists (for admin)


if (isAdmin && statusChart) {
statusChart.data.datasets[0].data = [pending, inProgress, resolved];
statusChart.update();
}
}

// Initialize Chart.js for the admin status chart


function initChart() {
const ctx = document.getElementById('statusChart').getContext('2d');
statusChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ['Pending', 'In Progress', 'Resolved'],
datasets: [{
data: [0, 0, 0],
backgroundColor: ['#f59e0b', '#2563eb', '#10b981']
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}

function updateUI() {
const loggedIn = currentUser !== null;
document.querySelectorAll('.logged-in').forEach(el => el.style.display =
loggedIn ? 'block' : 'none');
document.querySelectorAll('.logged-out').forEach(el => el.style.display =
loggedIn ? 'none' : 'block');
document.querySelectorAll('.admin-only').forEach(el => el.style.display =
isAdmin ? 'block' : 'none');
document.querySelectorAll('.user-only').forEach(el => el.style.display =
(loggedIn && !isAdmin) ? 'block' : 'none');
if (loggedIn) {
document.getElementById('usernameDisplay').textContent = currentUser;
}
renderMarkers();
renderMyReports();
updateStatistics();
// Initialize chart if admin and chart hasn't been created
if (isAdmin && !statusChart) {
initChart();
}
}

// Event listeners for filters


document.getElementById('filterType').addEventListener('change',
renderMarkers);
document.getElementById('filterSeverity').addEventListener('change',
renderMarkers);

// Initial UI setup
updateUI();
</script>
</body>
</html>

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy