Illegal Parking Map is an interactive web application that allows users to:
- View reported illegal parking spots on a city map.
- Submit new illegal parking reports without requiring a login.
- Administrators can review, approve, reject, or delete reports through a dedicated dashboard.
This project uses Leaflet for map rendering, Firebase (Firestore, Storage, Authentication) for backend services, and vanilla JavaScript/HTML/CSS for the frontend interface.
You can try out the application live at:
https://pakelcomedy.github.io/illegal-parking-map/
-
Interactive Map
- Displays markers for each reported illegal parking spot.
- Marker colors indicate status:
- Red = Unverified
- Green = Approved
- Gray = Rejected
- Clicking a marker opens a detail popup showing description, photo (if any), date, and status.
-
Submit a New Report
- Accessible on
pages/contribute.html
without login. - Click the map to select coordinates or use the βShow My Locationβ button to autofill.
- Upload a photo (JPEG/PNG, max 5 MB) and enter a description (10β500 characters).
- If the user is offline, the report is stored locally and automatically synced once back online.
- Accessible on
-
Admin Dashboard
- Accessible on
pages/admin.html
after logging in. - Real-time table displays all reports: ID, description, status, timestamp, photo thumbnail, and coordinates.
- Buttons to βApproveβ, βRejectβ, or βDeleteβ each report.
- Filter by status or search by keyword in the description.
- Accessible on
-
Authentication & Security
- Admin must log in (Firebase Authentication) to access
admin.html
. - Client-side route guard redirects non-authenticated users to
login.html
.
- Admin must log in (Firebase Authentication) to access
-
Toast Notifications
- All major interactions (success, error, offline, sync events) show a toast message in the top-right corner.
-
Responsive Design
- Sidebar legend and mobile navigation adapt to various screen sizes.
- Layout adjusts for both desktop and mobile.
illegal-parking-map/
βββ index.html # Main page (view reports on a map)
βββ pages/ # Separate pages
β βββ contribute.html # Report submission page (no login)
β βββ login.html # Admin login page
β βββ admin.html # Admin dashboard (requires login)
βββ assets/ # Static assets (CSS, JS, images, etc.)
β βββ css/
β β βββ admin.css # Styles specific to the admin dashboard
β β βββ style.css # Global styles (layout, toast, sidebar, etc.)
β βββ images/
β β βββ screenshot-home.png
β β βββ screenshot-contribute.png
β β βββ screenshot-admin.png
β βββ js/
β βββ firebase.js # Firebase initialization (API keys, project config)
β βββ main.js # Logic for the main page (index.html)
β βββ contribute.js # Logic for the contribute page (contribute.html)
β βββ admin.js # Logic for the admin dashboard (admin.html)
βββ .gitignore # Files/folders to ignore in Git
βββ README.md # Project documentation (this file)
βββ LICENSE # Open-source license (MIT)
βββ firebase.json # (Optional) Firebase Hosting configuration
- Clone the Repository
git clone https://github.com/pakelcomedy/illegal-parking-map.git
cd illegal-parking-map
-
Configure Firebase
- Create or use an existing Firebase project.
- In the Firebase Console, enable Authentication (Email/Password) under βSign-in method.β
- Create a Firestore database (mode: βStart in test modeβ or set rules as needed).
- Enable Storage (configure rules to allow authenticated uploads or adjust as needed).
- Copy your Firebase SDK configuration into
assets/js/firebase.js
:
// assets/js/firebase.js
// Replace with your Firebase projectβs settings
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_SENDER_ID",
appId: "YOUR_APP_ID"
};
firebase.initializeApp(firebaseConfig);
```
- Run a Local Web Server
Serve the files over HTTP (not directly via
file://
). For example, using Python:
python -m http.server 8000
Then open http://localhost:8000/index.html
in your browser.
-
Access Application Pages
- Home:
index.html
- Contribute:
pages/contribute.html
- Login:
pages/login.html
- Admin:
pages/admin.html
(login required)
- Home:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Anyone can read reports
match /reports/{reportId} {
allow read: if true;
// Anyone can create (no auth required)
allow create: if true;
// Only authenticated users (admins) can update or delete
allow update, delete: if request.auth != null;
}
}
}
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /report_photos/{reportId}/{allPaths=**} {
// Only authenticated users can upload or delete
allow write: if request.auth != null;
// Public can read
allow read: if true;
}
}
}
-
index.html
- Loads Leaflet CSS/JS and global styles (
assets/css/style.css
). - Contains
<div id="map"></div>
for the map, a filter form, a legend toggle, and a modal template for report details. - Navigation links to Contribute and Admin pages.
- Loads Leaflet CSS/JS and global styles (
-
main.js
- Initializes Leaflet map centered on Jakarta by default.
- Adds an OpenStreetMap tile layer.
- Sets up a real-time Firestore listener:
db.collection('reports')
.orderBy('createdAt', 'desc')
.onSnapshot(...)
-
Creates custom markers for each report, colored by status:
- Red marker for unverified reports
- Green marker for approved reports
- Gray marker for rejected reports
-
Adds a βShow My Locationβ button (using browser geolocation).
-
Implements filter controls for status and date range.
-
Binds click events on markers to open a detail modal.
-
Displays toast notifications.
-
contribute.html
-
Form fields:
- Hidden text inputs for latitude (
#location-lat
) and longitude (#location-lng
). - Textarea for description (
#report-description
). - File input for photo (
#report-photo
). - Photo preview container (
#photo-preview
with<img id="preview-img">
and<button id="remove-photo">Remove Photo</button>
).
- Hidden text inputs for latitude (
-
<div id="contribute-map"></div>
for the Leaflet map. -
Buttons and containers for toast notifications and sidebar legend.
-
-
contribute.js
-
Initializes Leaflet map in
#contribute-map
, centered on Jakarta by default. -
Popup instructs: βClick on the map to select the location of the illegal parking spot.β
-
Map click event:
- If no marker exists β create a draggable marker at clicked location.
- If marker exists β move it to the clicked location.
- Always update
latitude
andlongitude
form inputs to six decimal places.
-
Adds a βShow My Locationβ button in the top-left corner (custom Leaflet control):
- Calls
navigator.geolocation.getCurrentPosition(...)
. - Centers map on user location, adds a blue marker and an accuracy circle, and fills form inputs.
- Calls
-
Validates description length (10β500 chars).
-
Validates photo type (JPEG/PNG) and size (β€ 5 MB), then shows preview.
-
On form submit:
- If offline: save payload (description, lat, lng, timestamp, file) to localStorage.
- If online: upload photo (if any) to Firebase Storage, then write report document to Firestore.
- Shows appropriate toast notifications on success/failure.
- Resets form and removes markers/preview.
-
- A simple login form (Email/Password) using Firebase Authentication.
- Redirects to
admin.html
upon successful login.
-
admin.html
- Checks authentication status on page load: if not authenticated β redirect to
login.html
. - Contains a table (
<table class="admin-table"><tbody id="reports-table-body"></tbody></table>
) to list all reports. - Filter form (status & keyword) above the table.
- Logout button.
- Checks authentication status on page load: if not authenticated β redirect to
-
admin.js
- Sets up Firebase Auth listener:
firebase.auth().onAuthStateChanged(user => {
if (!user) window.location.href = 'login.html';
});
- Sets up a real-time Firestore listener:
firestore.collection('reports')
.orderBy('createdAt', 'desc')
.onSnapshot(snapshot => { β¦ });
-
Renders each report as a table row (
<tr>
), with columns:-
Report ID
-
Description
-
Status (capitalized)
-
Reported At (formatted date)
-
Photo (thumbnail or βββ if none)
-
Coordinates (latitude, longitude)
-
Actions:
- Approve (button β update
status
to"approved"
) - Reject (button β update
status
to"rejected"
) - Delete (button β delete Firestore document + delete photo in Storage if exists)
- Approve (button β update
-
-
Filter functionality:
- Status filter uses Firestore
.where('status', '==', β¦)
if not βallβ. - Keyword filter (client-side) matches description (lowercase substring).
- Status filter uses Firestore
-
Logout button calls
firebase.auth().signOut()
and redirects back tologin.html
.
- Fork this repository.
- Create a new branch (
git checkout -b feature/XYZ
). - Make your changes and commit (
git commit -m "Add awesome feature"
). - Push to your branch (
git push origin feature/XYZ
). - Open a Pull Request describing your changes.
Contributions of any kindβbug fixes, new features, documentation improvementsβare very welcome!
- Leaflet β version 1.9.x
- Firebase Web SDK β version 9.x compat libraries (App, Firestore, Auth, Storage)
- Vanilla JavaScript, HTML5, and CSS3 (no frontend framework)
This project is licensed under the LGPL-2.1 LICENSE. See the LICENSE file for details.
Thank you for using Illegal Parking Map. Feel free to open issues or submit pull requests for improvements!