{"id":1945,"date":"2026-07-30T18:42:24","date_gmt":"2026-07-30T22:42:24","guid":{"rendered":"https:\/\/ectransfers.com\/nhmadison\/?page_id=1945"},"modified":"2026-07-31T06:51:09","modified_gmt":"2026-07-31T10:51:09","slug":"dispatcher-track","status":"publish","type":"page","link":"https:\/\/ectransfers.com\/nhmadison\/dispatcher-track\/","title":{"rendered":"Dispatcher Track"},"content":{"rendered":"\t<div class=\"ect3-dash\" style=\"font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\">\n\t\t<div class=\"ect3-board-header\" style=\"display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap;\">\n\t\t\t<h2 style=\"margin:0;color:#0B0C0F;letter-spacing:.2px;\">Active Drivers<\/h2>\n\t\t\t<button onclick=\"location.reload()\" style=\"background:#0B0C0F;color:#C8A35E;border:none;padding:12px 22px;border-radius:999px;font-weight:600;cursor:pointer;font-size:15px;box-shadow:0 2px 8px rgba(11,12,15,.18);-webkit-tap-highlight-color:transparent;\">\u21bb Refresh<\/button>\n\t\t<\/div>\n\n\t\t\t\t\t<div class=\"ect3-empty\" style=\"text-align:center;padding:40px 20px;background:#FBF9F5;border:1px solid #EEE6D6;border-radius:16px;margin-bottom:20px;\">\n\t\t\t\t<div style=\"font-size:32px;margin-bottom:8px;\">\ud83d\ude98<\/div>\n\t\t\t\t<p style=\"color:#8a8478;font-size:15px;margin:0;\">No active trips in the last 24 hours.<\/p>\n\t\t\t<\/div>\n\t\t\n\t\t<div class=\"ect3-map-card\"><div id=\"ect3-board-map\"><\/div><\/div>\n\t<\/div>\n\n\t<style>\n\t\t.ect3-sticky-col { position: sticky; left: 0; background: #f4f6f9; z-index: 1; }\n\t\t.ect3-card-view { display: none; }\n\t\t.ect3-map-card { border-radius:16px; overflow:hidden; box-shadow:0 2px 14px rgba(11,12,15,.08); border:1px solid #eee; }\n\t\t#ect3-board-map { width:100%; height:60vh; min-height:380px; }\n\t\t.ect3-card { background:#fff; border:1px solid #eee; border-radius:16px; padding:16px 18px; margin-bottom:12px; box-shadow:0 1px 6px rgba(11,12,15,.06); transition: box-shadow .15s, transform .1s; cursor:pointer; -webkit-tap-highlight-color:transparent; }\n\t\t.ect3-card:active { transform: scale(0.99); }\n\t\t.ect3-card.ect3-card-selected { border-color:#C8A35E; box-shadow:0 2px 12px rgba(200,163,94,.28); }\n\t\t.ect3-card-top { display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:10px; }\n\t\t.ect3-card-booking { font-weight:700; font-size:16px; color:#0B0C0F; }\n\t\t.ect3-badge { color:#fff; padding:5px 12px; border-radius:20px; font-size:12px; font-weight:600; white-space:nowrap; }\n\t\t.ect3-card-body { display:flex; flex-direction:column; gap:4px; margin-bottom:12px; padding-bottom:12px; border-bottom:1px dashed #eee; }\n\t\t.ect3-card-line { font-size:14px; color:#333; }\n\t\t.ect3-card-label { color:#9a9488; width:52px; display:inline-block; font-size:12px; text-transform:uppercase; letter-spacing:.4px; }\n\t\t.ect3-card-footer { display:flex; justify-content:space-between; align-items:center; }\n\t\t.ect3-card-updated { font-size:12.5px; color:#9a9488; }\n\t\t.ect3-card .ect3-archive-btn { background:#0B0C0F; color:#C8A35E; border:none; padding:8px 14px; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; -webkit-tap-highlight-color:transparent; margin-right:6px; }\n\t\t.ect3-card .ect3-delete-btn { background:#fff; color:#b3261e; border:1.5px solid #b3261e; padding:8px 14px; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; -webkit-tap-highlight-color:transparent; }\n\t\t.ect3-driver-select { width:100%; padding:6px 8px; border-radius:6px; border:1px solid #ccc; font-size:13px; background:#fff; color:#222; }\n\t\t.ect3-sendinfo-btn { display:inline-block; background:#fff; color:#0B0C0F; border:1.5px solid #25D366; padding:8px 14px; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; -webkit-tap-highlight-color:transparent; text-decoration:none; margin-right:6px; }\n\t\t.ect3-card .ect3-sendinfo-btn { padding:8px 14px; }\n\t\t@media (max-width: 640px) {\n\t\t\t.ect3-board-swipe-hint, .ect3-table-view { display:none !important; }\n\t\t\t.ect3-card-view { display:block; }\n\t\t\t.ect3-board-header h2 { font-size:20px; }\n\t\t\t.ect3-board-header button { flex:1 1 auto; }\n\t\t\t#ect3-board-map { height:42vh; min-height:280px; }\n\t\t}\n\t<\/style>\n\n\t<script>\n\t\twindow.ect3BoardData = [];\n\t\twindow.ect3RestBase = \"https:\\\/\\\/ectransfers.com\\\/nhmadison\\\/wp-json\\\/ec3\\\/v1\\\/track\";\n\t\twindow.ect3ApiBase = \"https:\\\/\\\/ectransfers.com\\\/nhmadison\\\/wp-json\\\/ec3\\\/v1\";\n\t\twindow.ect3MarkersByBooking = {};\n\t\twindow.ect3Map = null;\n\n\t\twindow.ect3CreateOrUpdateMarker = function (bookingId, lat, lng, driverName, status) {\n\t\t\tif (!window.ect3Map) return;\n\t\t\tvar key = String(bookingId);\n\t\t\tvar pos = { lat: parseFloat(lat), lng: parseFloat(lng) };\n\t\t\tif (window.ect3MarkersByBooking[key]) {\n\t\t\t\twindow.ect3MarkersByBooking[key].setPosition(pos);\n\t\t\t} else {\n\t\t\t\twindow.ect3MarkersByBooking[key] = new google.maps.Marker({\n\t\t\t\t\tposition: pos, map: window.ect3Map, label: String(bookingId),\n\t\t\t\t\ttitle: 'Booking #' + bookingId + ' \u2014 ' + (driverName || '') + ' (' + status + ')'\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (window.ect3FollowBookingId === key) window.ect3Map.panTo(pos);\n\t\t};\n\n\t\twindow.ect3InitBoardMap = function () {\n\t\t\twindow.ect3Map = new google.maps.Map(document.getElementById('ect3-board-map'), { center: { lat: 40.7128, lng: -74.0060 }, zoom: 11 });\n\t\t\twireRowClicks();\n\t\t\t(window.ect3BoardData || []).forEach(function (r) {\n\t\t\t\tif (r.lat === null || r.lng === null) return;\n\t\t\t\twindow.ect3CreateOrUpdateMarker(r.booking_id, r.lat, r.lng, r.driver_name, r.status);\n\t\t\t});\n\t\t\tgoogle.maps.event.addListenerOnce(window.ect3Map, 'idle', function () {\n\t\t\t\tsetTimeout(function () { pollLocations(true); }, 500);\n\t\t\t});\n\t\t};\n\n\t\tfunction wireRowClicks() {\n\t\t\tdocument.querySelectorAll('[data-row-id]').forEach(function (el) {\n\t\t\t\tel.addEventListener('click', function (e) {\n\t\t\t\t\tif (e.target.closest('.ect3-archive-btn') || e.target.closest('.ect3-delete-btn') || e.target.closest('.ect3-driver-cell') || e.target.closest('.ect3-sendinfo-btn')) return;\n\t\t\t\t\tvar bookingId = el.getAttribute('data-booking-id');\n\t\t\t\t\tdocument.querySelectorAll('tr[data-row-id]').forEach(function (r) {\n\t\t\t\t\t\tr.style.background = '';\n\t\t\t\t\t\tvar sticky = r.querySelector('.ect3-sticky-col');\n\t\t\t\t\t\tif (sticky) sticky.style.background = '#fff';\n\t\t\t\t\t});\n\t\t\t\t\tdocument.querySelectorAll('.ect3-card').forEach(function (c) { c.classList.remove('ect3-card-selected'); });\n\t\t\t\t\tvar nowFollowing = setFollowMode(bookingId);\n\t\t\t\t\tif (nowFollowing) {\n\t\t\t\t\t\tif (el.tagName === 'TR') {\n\t\t\t\t\t\t\tel.style.background = '#fdf6e8';\n\t\t\t\t\t\t\tvar stickyCell = el.querySelector('.ect3-sticky-col');\n\t\t\t\t\t\t\tif (stickyCell) stickyCell.style.background = '#fdf6e8';\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tel.classList.add('ect3-card-selected');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\n\t\tdocument.querySelectorAll('.ect3-archive-btn').forEach(function (btn) {\n\t\t\tbtn.addEventListener('click', function (e) {\n\t\t\t\te.stopPropagation();\n\t\t\t\tvar id = btn.getAttribute('data-id');\n\t\t\t\tif (!confirm('Archive this booking? It will move to Bookings Archived and disappear from this board.')) return;\n\t\t\t\tbtn.disabled = true;\n\t\t\t\tbtn.textContent = 'Archiving\u2026';\n\t\t\t\tfetch(window.ect3RestBase + '\/archive\/' + id, { method: 'POST' })\n\t\t\t\t\t.then(function (r) { return r.json(); })\n\t\t\t\t\t.then(function (res) {\n\t\t\t\t\t\tif (res && res.ok) {\n\t\t\t\t\t\t\tvar bookingId = null;\n\t\t\t\t\t\t\tdocument.querySelectorAll('[data-row-id=\"' + id + '\"]').forEach(function (el) {\n\t\t\t\t\t\t\t\tbookingId = String(el.getAttribute('data-booking-id'));\n\t\t\t\t\t\t\t\tel.remove();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (bookingId && window.ect3MarkersByBooking[bookingId]) {\n\t\t\t\t\t\t\t\twindow.ect3MarkersByBooking[bookingId].setMap(null);\n\t\t\t\t\t\t\t\tdelete window.ect3MarkersByBooking[bookingId];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\talert('Archive failed.'); btn.disabled = false; btn.textContent = '\ud83d\udce6 Archive';\n\t\t\t\t\t\t}\n\t\t\t\t\t}).catch(function () { alert('Archive failed.'); btn.disabled = false; btn.textContent = '\ud83d\udce6 Archive'; });\n\t\t\t});\n\t\t});\n\n\t\tdocument.querySelectorAll('.ect3-delete-btn').forEach(function (btn) {\n\t\t\tbtn.addEventListener('click', function (e) {\n\t\t\t\te.stopPropagation();\n\t\t\t\tvar id = btn.getAttribute('data-id');\n\t\t\t\tif (!confirm('Permanently delete this booking\\'s tracking record? It will NOT be saved to Bookings Archived \u2014 this cannot be undone.')) return;\n\t\t\t\tbtn.disabled = true;\n\t\t\t\tbtn.textContent = 'Deleting\u2026';\n\t\t\t\tfetch(window.ect3RestBase + '\/delete\/' + id, { method: 'POST' })\n\t\t\t\t\t.then(function (r) { return r.json(); })\n\t\t\t\t\t.then(function (res) {\n\t\t\t\t\t\tif (res && res.ok) {\n\t\t\t\t\t\t\tvar bookingId = null;\n\t\t\t\t\t\t\tdocument.querySelectorAll('[data-row-id=\"' + id + '\"]').forEach(function (el) {\n\t\t\t\t\t\t\t\tbookingId = String(el.getAttribute('data-booking-id'));\n\t\t\t\t\t\t\t\tel.remove();\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (bookingId && window.ect3MarkersByBooking[bookingId]) {\n\t\t\t\t\t\t\t\twindow.ect3MarkersByBooking[bookingId].setMap(null);\n\t\t\t\t\t\t\t\tdelete window.ect3MarkersByBooking[bookingId];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\talert('Delete failed.'); btn.disabled = false; btn.textContent = '\ud83d\uddd1\ufe0f Delete';\n\t\t\t\t\t\t}\n\t\t\t\t\t}).catch(function () { alert('Delete failed.'); btn.disabled = false; btn.textContent = '\ud83d\uddd1\ufe0f Delete'; });\n\t\t\t});\n\t\t});\n\n\n\t\t\/\/ \u2500\u2500 Editable Driver column \u2500\u2500 reassign straight from the board \u2500\u2500\n\t\tdocument.querySelectorAll('.ect3-driver-cell').forEach(function (cell) {\n\t\t\tvar select = cell.querySelector('.ect3-driver-select');\n\t\t\tselect.addEventListener('change', function () {\n\t\t\t\tvar booking = cell.getAttribute('data-booking');\n\t\t\t\tselect.disabled = true;\n\t\t\t\tfetch(window.ect3ApiBase + '\/drivers\/assign', {\n\t\t\t\t\tmethod: 'POST', headers: { 'Content-Type': 'application\/json' },\n\t\t\t\t\tbody: JSON.stringify({ booking_id: booking, driver_id: select.value || null })\n\t\t\t\t}).then(function (r) { return r.json(); }).then(function (res) {\n\t\t\t\t\tif (!res || !res.ok) { select.disabled = false; alert('Could not reassign driver.'); return; }\n\t\t\t\t\t\/\/ Reload so the Send Info button rebuilds its WhatsApp text\/\n\t\t\t\t\t\/\/ recipient with the newly-assigned driver \u2014 patching that\n\t\t\t\t\t\/\/ live would risk sending the old driver's info by mistake.\n\t\t\t\t\tlocation.reload();\n\t\t\t\t}).catch(function () { select.disabled = false; alert('Could not reassign driver.'); });\n\t\t\t});\n\t\t});\n\n\t\t\/\/ \u2500\u2500 Send Info \u2500\u2500 mark the booking as sent so it shows on the board\n\t\t\/\/ right away, independent of driver acceptance. Doesn't block the\n\t\t\/\/ WhatsApp navigation itself.\n\t\tdocument.querySelectorAll('.ect3-sendinfo-btn').forEach(function (btn) {\n\t\t\tbtn.addEventListener('click', function () {\n\t\t\t\tvar booking = btn.getAttribute('data-booking');\n\t\t\t\tfetch(window.ect3RestBase + '\/mark-sent', {\n\t\t\t\t\tmethod: 'POST', headers: { 'Content-Type': 'application\/json' },\n\t\t\t\t\tbody: JSON.stringify({ booking_id: booking })\n\t\t\t\t}).catch(function () {});\n\t\t\t});\n\t\t});\n\n\t\twindow.ect3FollowBookingId = null;\n\t\tfunction setFollowMode(bookingId) {\n\t\t\tvar key = String(bookingId);\n\t\t\tif (window.ect3FollowBookingId === key) {\n\t\t\t\twindow.ect3FollowBookingId = null;\n\t\t\t\tdocument.getElementById('ect3-follow-badge') && document.getElementById('ect3-follow-badge').remove();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\twindow.ect3FollowBookingId = key;\n\t\t\tvar existing = document.getElementById('ect3-follow-badge');\n\t\t\tif (existing) existing.remove();\n\t\t\tvar badge = document.createElement('div');\n\t\t\tbadge.id = 'ect3-follow-badge';\n\t\t\tbadge.textContent = '\ud83d\udccd Following #' + bookingId + ' \u2014 tap card to stop';\n\t\t\tbadge.style.cssText = 'position:absolute;top:10px;left:50%;transform:translateX(-50%);background:#0B0C0F;color:#C8A35E;font-size:12px;font-weight:600;padding:6px 14px;border-radius:999px;z-index:10;white-space:nowrap;pointer-events:none;font-family:-apple-system,sans-serif;';\n\t\t\tdocument.getElementById('ect3-board-map').style.position = 'relative';\n\t\t\tdocument.getElementById('ect3-board-map').appendChild(badge);\n\t\t\tvar marker = window.ect3MarkersByBooking[key];\n\t\t\tif (marker && window.ect3Map) { window.ect3Map.panTo(marker.getPosition()); window.ect3Map.setZoom(15); }\n\t\t\treturn true;\n\t\t}\n\n\t\tfunction pollLocations(forceRun) {\n\t\t\tif (!forceRun && document.visibilityState !== 'visible') return;\n\t\t\tif (!window.ect3Map) { setTimeout(function () { pollLocations(forceRun); }, 1000); return; }\n\t\t\tfetch(window.ect3RestBase + '\/board').then(function (r) { return r.json(); }).then(function (rows) {\n\t\t\t\tif (!window.ect3Map || !Array.isArray(rows)) return;\n\t\t\t\tvar missingRow = false;\n\t\t\t\trows.forEach(function (r) {\n\t\t\t\t\tif (r.lat === null || r.lng === null) return;\n\t\t\t\t\tvar key = String(r.booking_id);\n\t\t\t\t\t\/\/ The initial forced poll (right after map load) is expected\n\t\t\t\t\t\/\/ to place markers for drivers who opened their link after\n\t\t\t\t\t\/\/ the page rendered \u2014 that's not a mismatch. Only routine\n\t\t\t\t\t\/\/ 15s polls finding a booking with no matching card\/row mean\n\t\t\t\t\t\/\/ it became active after page load and the list is stale.\n\t\t\t\t\tif (!forceRun && !document.querySelector('[data-row-id][data-booking-id=\"' + key + '\"]')) {\n\t\t\t\t\t\tmissingRow = true;\n\t\t\t\t\t}\n\t\t\t\t\twindow.ect3CreateOrUpdateMarker(r.booking_id, r.lat, r.lng, r.driver_name, r.status);\n\t\t\t\t});\n\t\t\t\tif (missingRow) location.reload();\n\t\t\t}).catch(function () {});\n\t\t}\n\t\tsetInterval(pollLocations, 15000);\n\t\tsetInterval(function () { if (document.visibilityState === 'visible') location.reload(); }, 5 * 60 * 1000);\n\t\tdocument.addEventListener('visibilitychange', function () {\n\t\t\tif (document.visibilityState === 'visible') { pollLocations(); location.reload(); }\n\t\t});\n\t<\/script>\n\t<script async src=\"https:\/\/maps.googleapis.com\/maps\/api\/js?key=AIzaSyD6Zs7JSeVxNFhKvDCONFVICttJewmLBnM&callback=ect3InitBoardMap\"><\/script>\n\t\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"normal-width-container","site-content-style":"boxed","site-sidebar-style":"default","ast-global-header-display":"disabled","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-1945","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/pages\/1945","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/comments?post=1945"}],"version-history":[{"count":6,"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/pages\/1945\/revisions"}],"predecessor-version":[{"id":1972,"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/pages\/1945\/revisions\/1972"}],"wp:attachment":[{"href":"https:\/\/ectransfers.com\/nhmadison\/wp-json\/wp\/v2\/media?parent=1945"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}