diff --git a/admin/travel/coordinatorDashboard.html b/admin/travel/coordinatorDashboard.html
new file mode 100644
index 0000000..63a42fb
--- /dev/null
+++ b/admin/travel/coordinatorDashboard.html
@@ -0,0 +1,349 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Coordinator Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Coordinator Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Assigned Bus
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | # |
+
+ Name |
+
+ Mobile |
+ Pickup |
+
+ Stop Time |
+
+ Drop |
+ Luggage |
+ Comments |
+
+ Contact |
+
+ Status |
+ Action |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin/travel/coordinatorDashboard.js b/admin/travel/coordinatorDashboard.js
new file mode 100644
index 0000000..8c5b29f
--- /dev/null
+++ b/admin/travel/coordinatorDashboard.js
@@ -0,0 +1,804 @@
+let allPassengers = [];
+let allBuses = [];
+let selectedBusIndex = 0;
+let qrScanner = null;
+
+document.addEventListener(
+ 'DOMContentLoaded',
+ fetchDashboard
+);
+
+document.addEventListener(
+ 'DOMContentLoaded',
+ () => {
+
+ document
+ .getElementById(
+ 'passengerSearch'
+ )
+ .addEventListener(
+ 'input',
+ applyPassengerFilters
+ );
+
+ document
+ .getElementById(
+ 'boardingFilter'
+ )
+ .addEventListener(
+ 'change',
+ applyPassengerFilters
+ );
+ }
+);
+
+async function fetchDashboard() {
+
+ try {
+
+ const token =
+ sessionStorage.getItem(
+ 'coordinatorToken'
+ );
+
+ const response = await fetch(
+
+ `${CONFIG.baseUrl}/coordinator/dashboard`,
+
+ {
+
+ headers: {
+
+ Authorization:
+ `Bearer ${token}`,
+ },
+ }
+ );
+
+ if (
+ response.status === 401 ||
+ response.status === 403
+ ) {
+
+ coordinatorLogout();
+ return;
+ }
+
+ const data =
+ await response.json();
+
+ if (!response.ok) {
+
+ throw new Error(
+ data.message
+ );
+ }
+ renderCoordinatorInfo(
+ data.coordinator
+ );
+ allBuses =
+ data.buses || [];
+
+ renderBusDropdown();
+
+ if (allBuses.length) {
+
+ if (
+ selectedBusIndex >=
+ allBuses.length
+ ) {
+
+ selectedBusIndex = 0;
+ }
+
+ loadBusData(
+ selectedBusIndex
+ );
+
+ document
+ .getElementById(
+ 'busSelector'
+ )
+ .value =
+ selectedBusIndex;
+ }
+ } catch (error) {
+
+ alert(error.message);
+ }
+}
+
+function renderCoordinatorInfo(
+ coordinator
+) {
+
+ document
+ .getElementById(
+ 'coordinatorInfo'
+ )
+ .innerHTML = `
+
+
+
+
+ Name
+
+
+
+ ${coordinator.name || ''}
+
+
+
+
+
+
+
+ Mobile
+
+
+
+ ${coordinator.mobno || ''}
+
+
+
+
+
+
+
+ Card No
+
+
+
+ ${coordinator.cardno || ''}
+
+
+
+
+
+
+
+ Center
+
+
+
+ ${coordinator.center || ''}
+
+
+
+ `;
+}
+
+function renderBusDropdown() {
+
+ const selector =
+ document.getElementById(
+ 'busSelector'
+ );
+
+ selector.innerHTML = '';
+
+ allBuses.forEach(
+ (
+ item,
+ index
+ ) => {
+
+ const option =
+ document.createElement(
+ 'option'
+ );
+
+ option.value =
+ index;
+
+ option.textContent = `
+
+${item.bus.bus_name}
+- ${item.bus.event_date}
+
+ `;
+
+ selector.appendChild(
+ option
+ );
+ }
+ );
+
+ selector.onchange =
+ event => {
+
+ selectedBusIndex =
+ Number(
+ event.target.value
+ );
+
+ loadBusData(
+ selectedBusIndex
+ );
+ };
+}
+
+function loadBusData(
+ index
+) {
+
+ const selectedBus =
+ allBuses[index];
+
+ if (!selectedBus) {
+
+ return;
+ }
+
+ renderBusInfo(
+ selectedBus.bus
+ );
+
+ renderBusSummary(
+
+ selectedBus.bus,
+
+ selectedBus.passengers
+ );
+
+ allPassengers =
+ selectedBus.passengers;
+
+ renderPassengers(
+ allPassengers
+ );
+}
+
+function renderBusInfo(bus) {
+
+ const sortedStops =
+
+ (bus.stops || [])
+ .sort(
+ (a, b) =>
+ a.stop_order -
+ b.stop_order
+ );
+
+ document
+ .getElementById(
+ 'busInfo'
+ )
+ .innerHTML = `
+
+
+
+
+ Bus
+
+
+
+ ${bus.bus_name || ''}
+
+
+
+
+
+
+
+ Date
+
+
+
+ ${bus.event_date || ''}
+
+
+
+
+
+
+
+ Route
+
+
+
+
+ ${sortedStops
+ .map(
+ (
+ stop,
+ index
+ ) => `
+
+
+
+ ${stop.stop_name}
+
+ (${stop.timing || '-'})
+
+
+ `
+ )
+ .join('')
+ }
+
+
+
+
+
+ `;
+}
+
+function renderBusSummary(
+ bus,
+ passengers
+) {
+
+ const boardedCount =
+ passengers.filter(
+ p => p.boarded
+ ).length;
+
+ const pendingCount =
+ passengers.filter(
+ p => !p.boarded
+ ).length;
+
+ document
+ .getElementById(
+ 'busSummary'
+ )
+ .innerHTML = `
+
+
+
+
+
+
+ Total Capacity
+
+
+
+ ${bus.capacity || 0}
+
+
+
+
+
+
+
+ Remaining Seats
+
+
+
+ ${bus.remaining_seats || 0}
+
+
+
+
+
+
+
+ Boarded
+
+
+
+ ${boardedCount}
+
+
+
+
+
+
+
+ Pending
+
+
+
+ ${pendingCount}
+
+
+
+
+
+ `;
+}
+
+function renderPassengers(
+ passengers
+) {
+
+ const tbody =
+ document.querySelector(
+ '#passengerTable tbody'
+ );
+
+ tbody.innerHTML = '';
+
+ if (!passengers.length) {
+
+ tbody.innerHTML = `
+
+
+
+ |
+
+ No passengers assigned
+
+ |
+
+
+ `;
+
+ return;
+ }
+
+ passengers.forEach(
+ (
+ passenger,
+ index
+ ) => {
+ const selectedBus =
+ allBuses[selectedBusIndex];
+
+ const stops =
+ selectedBus?.bus?.stops || [];
+
+ let stopTime = '';
+
+ if (
+ passenger.pickup_point ===
+ 'Research Centre'
+ ) {
+
+ // RC → Mumbai
+ stopTime =
+ stops.find(
+ stop =>
+ stop.stop_name ===
+ passenger.drop_point
+ )?.timing || '';
+
+ } else {
+
+ // Mumbai → RC
+ stopTime =
+ stops.find(
+ stop =>
+ stop.stop_name ===
+ passenger.pickup_point
+ )?.timing || '';
+ }
+
+ const row =
+ document.createElement(
+ 'tr'
+ );
+
+ row.innerHTML = `
+
+
+ ${index + 1}
+ |
+
+
+ ${passenger.name || ''}
+ |
+
+
+ ${passenger.mobno || ''}
+ |
+
+
+ ${passenger.pickup_point || ''}
+ |
+
+
+ ${stopTime || '-'}
+ |
+
+
+ ${passenger.drop_point || ''}
+ |
+
+
+ ${passenger.luggage || '-'
+ }
+ |
+
+
+ ${passenger.comments || '-'
+ }
+ |
+
+
+
+
+
+ |
+
+
+
+ ${passenger.boarded
+ ? '🟢 Boarded'
+ : '🔴 Pending'
+ }
+
+ |
+
+
+
+
+
+ |
+`;
+ tbody.appendChild(row);
+ }
+ );
+}
+
+function applyPassengerFilters() {
+
+ const search =
+ document
+ .getElementById(
+ 'passengerSearch'
+ )
+ .value
+ .toLowerCase();
+
+ const filter =
+ document
+ .getElementById(
+ 'boardingFilter'
+ )
+ .value;
+
+ let filtered =
+ [...allPassengers];
+
+ // SEARCH
+
+ filtered =
+ filtered.filter(
+ passenger => {
+
+ return (
+
+ passenger.name
+ ?.toLowerCase()
+ .includes(search)
+
+ ||
+
+ String(
+ passenger.mobno || ''
+ ).includes(search)
+
+ ||
+
+ passenger.pickup_point
+ ?.toLowerCase()
+ .includes(search)
+ );
+ }
+ );
+
+ // FILTER
+
+ if (filter === 'boarded') {
+
+ filtered =
+ filtered.filter(
+ p => p.boarded
+ );
+ }
+
+ if (filter === 'pending') {
+
+ filtered =
+ filtered.filter(
+ p => !p.boarded
+ );
+ }
+
+
+
+ renderPassengers(
+ filtered
+ );
+}
+
+async function
+ toggleBoardingStatus(
+ passengerId,
+ boarded
+ ) {
+
+ try {
+
+ const token =
+ sessionStorage.getItem(
+ 'coordinatorToken'
+ );
+
+ const response =
+ await fetch(
+
+ `${CONFIG.baseUrl}/coordinator/boarding-status`,
+
+ {
+
+ method: 'PUT',
+
+ headers: {
+
+ 'Content-Type':
+ 'application/json',
+
+ Authorization:
+ `Bearer ${token}`,
+ },
+
+ body: JSON.stringify({
+
+ passenger_id:
+ passengerId,
+
+ boarded,
+ }),
+ }
+ );
+
+ const data =
+ await response.json();
+
+ if (!response.ok) {
+
+ throw new Error(
+ data.message
+ );
+ }
+
+ fetchDashboard();
+
+ } catch (error) {
+
+ alert(error.message);
+ }
+}
+
+async function
+ startQrScanner() {
+
+ const qrDiv =
+ document.getElementById(
+ 'qr-reader'
+ );
+
+ qrDiv.style.display =
+ 'block';
+
+ qrScanner =
+ new Html5Qrcode(
+ 'qr-reader'
+ );
+
+ await qrScanner.start(
+
+ {
+ facingMode: 'environment',
+ },
+
+ {
+ fps: 10,
+ qrbox: 250,
+ },
+
+ async decodedText => {
+
+ await handleQrScan(
+ decodedText
+ );
+ }
+ );
+}
+
+async function
+ handleQrScan(
+ qrValue
+ ) {
+
+ const passenger =
+ allPassengers.find(p =>
+
+ String(
+ p.cardno || ''
+ ) === String(qrValue)
+ );
+
+ if (!passenger) {
+
+ alert(
+ 'Passenger not found'
+ );
+
+ return;
+ }
+
+ if (passenger.boarded) {
+
+ alert(
+ 'Already boarded'
+ );
+
+ return;
+ }
+
+ await toggleBoardingStatus(
+
+ passenger.passenger_id,
+
+ true
+ );
+
+ alert(
+ `${passenger.name} boarded`
+ );
+
+ if (qrScanner) {
+
+ await qrScanner.stop();
+ }
+
+ document
+ .getElementById(
+ 'qr-reader'
+ )
+ .style.display = 'none';
+}
\ No newline at end of file
diff --git a/admin/travel/coordinatorLogin.html b/admin/travel/coordinatorLogin.html
new file mode 100644
index 0000000..0e36836
--- /dev/null
+++ b/admin/travel/coordinatorLogin.html
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+ Coordinator Login
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Coordinator Login
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/admin/travel/coordinatorLogin.js b/admin/travel/coordinatorLogin.js
new file mode 100644
index 0000000..9e962df
--- /dev/null
+++ b/admin/travel/coordinatorLogin.js
@@ -0,0 +1,151 @@
+document.addEventListener(
+ 'DOMContentLoaded',
+ () => {
+
+ document
+ .getElementById('sendOtpBtn')
+ .addEventListener(
+ 'click',
+ sendOtp
+ );
+
+ document
+ .getElementById('verifyOtpBtn')
+ .addEventListener(
+ 'click',
+ verifyOtp
+ );
+ }
+);
+
+async function sendOtp() {
+
+ try {
+
+ const mobno =
+ document
+ .getElementById('mobno')
+ .value
+ .trim();
+
+ if (!mobno) {
+ alert('Enter mobile number');
+ return;
+ }
+
+ const response = await fetch(
+
+ `${CONFIG.baseUrl}/coordinator/send-otp`,
+
+ {
+ method: 'POST',
+
+ headers: {
+ 'Content-Type':
+ 'application/json',
+ },
+
+ body: JSON.stringify({
+ mobno,
+ }),
+ }
+ );
+
+ const data =
+ await response.json();
+
+ if (!response.ok) {
+ throw new Error(
+ data.message
+ );
+ }
+
+ alert(
+ 'OTP sent successfully'
+ );
+
+ document
+ .getElementById(
+ 'otpSection'
+ )
+ .style.display = 'block';
+
+ } catch (error) {
+
+ alert(error.message);
+ }
+}
+
+async function verifyOtp() {
+
+ try {
+
+ const mobno =
+ document
+ .getElementById('mobno')
+ .value
+ .trim();
+
+ const otp =
+ document
+ .getElementById('otp')
+ .value
+ .trim();
+
+ if (!otp) {
+ alert('Enter OTP');
+ return;
+ }
+
+ const response = await fetch(
+
+ `${CONFIG.baseUrl}/coordinator/verify-otp`,
+
+ {
+ method: 'POST',
+
+ headers: {
+ 'Content-Type':
+ 'application/json',
+ },
+
+ body: JSON.stringify({
+ mobno,
+ otp,
+ }),
+ }
+ );
+
+ const data =
+ await response.json();
+
+ if (!response.ok) {
+ throw new Error(
+ data.message
+ );
+ }
+
+ // STORE SESSION
+
+ sessionStorage.setItem(
+ 'coordinatorToken',
+ data.token
+ );
+
+ sessionStorage.setItem(
+ 'coordinatorUser',
+ JSON.stringify(data.user)
+ );
+
+ alert(
+ 'Login successful'
+ );
+
+ window.location.href =
+ 'coordinatorDashboard.html';
+
+ } catch (error) {
+
+ alert(error.message);
+ }
+}
\ No newline at end of file
diff --git a/admin/travel/fetchUpcomingBookings.html b/admin/travel/fetchUpcomingBookings.html
index 0f6f5da..2e7cafa 100644
--- a/admin/travel/fetchUpcomingBookings.html
+++ b/admin/travel/fetchUpcomingBookings.html
@@ -5,176 +5,190 @@
-
-
-
- Upcoming Travel Bookings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-