Merge branch '299-cmb-realtime-map' into '270-final-cmb-fixes'
Resolve "CMB - realtime map" See merge request jankstudio/resq!348
This commit is contained in:
commit
0297d66dd5
15 changed files with 222 additions and 42 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,6 +7,7 @@
|
||||||
/sql/
|
/sql/
|
||||||
/pem/
|
/pem/
|
||||||
/migration/
|
/migration/
|
||||||
|
/kml/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
*.swp
|
*.swp
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,5 @@ twig:
|
||||||
strict_variables: '%kernel.debug%'
|
strict_variables: '%kernel.debug%'
|
||||||
globals:
|
globals:
|
||||||
gmaps_api_key: "%env(GMAPS_API_KEY)%"
|
gmaps_api_key: "%env(GMAPS_API_KEY)%"
|
||||||
|
mqtt_host: "%env(MQTT_WS_HOST)%"
|
||||||
|
mqtt_port: "%env(MQTT_WS_PORT)%"
|
||||||
|
|
|
||||||
|
|
@ -195,3 +195,9 @@ jo_onestep_edit_submit:
|
||||||
path: /job-order/onestep/{id}/edit
|
path: /job-order/onestep/{id}/edit
|
||||||
controller: App\Controller\JobOrderController::oneStepEditSubmit
|
controller: App\Controller\JobOrderController::oneStepEditSubmit
|
||||||
methods: [POST]
|
methods: [POST]
|
||||||
|
|
||||||
|
jo_ajax_popup:
|
||||||
|
path: /job-order/{id}/popup
|
||||||
|
controller: App\Controller\JobOrderController::popupInfo
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,8 @@ rider_delete:
|
||||||
path: /riders/{id}
|
path: /riders/{id}
|
||||||
controller: App\Controller\RiderController::destroy
|
controller: App\Controller\RiderController::destroy
|
||||||
methods: [DELETE]
|
methods: [DELETE]
|
||||||
|
|
||||||
|
rider_ajax_popup:
|
||||||
|
path: /riders/{id}/popup
|
||||||
|
controller: App\Controller\RiderController::popupInfo
|
||||||
|
methods: [GET]
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,8 @@ services:
|
||||||
App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~
|
App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~
|
||||||
|
|
||||||
# invoice generator interface
|
# invoice generator interface
|
||||||
#App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\CMBInvoiceGenerator"
|
App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\CMBInvoiceGenerator"
|
||||||
App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator"
|
#App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator"
|
||||||
|
|
||||||
# job order generator
|
# job order generator
|
||||||
#App\Service\JobOrderHandler\ResqJobOrderHandler:
|
#App\Service\JobOrderHandler\ResqJobOrderHandler:
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ namespace App\Controller;
|
||||||
use App\Ramcar\CrudException;
|
use App\Ramcar\CrudException;
|
||||||
|
|
||||||
use App\Service\CustomerHandlerInterface;
|
use App\Service\CustomerHandlerInterface;
|
||||||
|
use App\Entity\Customer;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ class HomeController extends Controller
|
||||||
|
|
||||||
public function getRiderLocations(EntityManagerInterface $em, RiderTracker $rider_tracker)
|
public function getRiderLocations(EntityManagerInterface $em, RiderTracker $rider_tracker)
|
||||||
{
|
{
|
||||||
|
// TODO: get active riders from cache
|
||||||
|
// TODO: get active JOs from cache
|
||||||
// get all riders
|
// get all riders
|
||||||
$riders = $em->getRepository(Rider::class)->findAll();
|
$riders = $em->getRepository(Rider::class)->findAll();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use App\Ramcar\InvoiceCriteria;
|
||||||
use App\Entity\CustomerVehicle;
|
use App\Entity\CustomerVehicle;
|
||||||
use App\Entity\Promo;
|
use App\Entity\Promo;
|
||||||
use App\Entity\Battery;
|
use App\Entity\Battery;
|
||||||
|
use App\Entity\JobOrder;
|
||||||
|
|
||||||
use App\Service\InvoiceGeneratorInterface;
|
use App\Service\InvoiceGeneratorInterface;
|
||||||
use App\Service\JobOrderHandlerInterface;
|
use App\Service\JobOrderHandlerInterface;
|
||||||
|
|
@ -20,6 +21,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
|
|
||||||
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
|
|
||||||
use Catalyst\MenuBundle\Annotation\Menu;
|
use Catalyst\MenuBundle\Annotation\Menu;
|
||||||
|
|
||||||
class JobOrderController extends Controller
|
class JobOrderController extends Controller
|
||||||
|
|
@ -908,4 +911,15 @@ class JobOrderController extends Controller
|
||||||
'success' => 'Changes have been saved!'
|
'success' => 'Changes have been saved!'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ParamConverter("jo", class="App\Entity\JobOrder")
|
||||||
|
*/
|
||||||
|
public function popupInfo(JobOrder $jo)
|
||||||
|
{
|
||||||
|
if ($jo == null)
|
||||||
|
return new Response('No job order data');
|
||||||
|
|
||||||
|
return $this->render('job-order/popup.html.twig', [ 'jo' => $jo ]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,8 @@ class RAPIController extends Controller
|
||||||
|
|
||||||
// data
|
// data
|
||||||
$data = [
|
$data = [
|
||||||
'hub' => $hub_data
|
'hub' => $hub_data,
|
||||||
|
'rider_id' => $rider_id,
|
||||||
];
|
];
|
||||||
|
|
||||||
$res->setData($data);
|
$res->setData($data);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use App\Entity\User;
|
||||||
use App\Service\FileUploader;
|
use App\Service\FileUploader;
|
||||||
|
|
||||||
use Doctrine\ORM\Query;
|
use Doctrine\ORM\Query;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
|
||||||
|
|
@ -500,4 +501,13 @@ class RiderController extends Controller
|
||||||
->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%');
|
->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function popupInfo(EntityManagerInterface $em, $id)
|
||||||
|
{
|
||||||
|
$rider = $em->getRepository(Rider::class)->find($id);
|
||||||
|
if ($rider == null)
|
||||||
|
return new Response('No rider data');
|
||||||
|
|
||||||
|
return $this->render('rider/popup.html.twig', [ 'rider' => $rider ]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,114 @@
|
||||||
<!-- END: Subheader -->
|
<!-- END: Subheader -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{{ include('map/' ~ map_js_file) }}
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
{% if 'OpenStreet' in map_js_file %}
|
var map;
|
||||||
initMap();
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
function initMap() {
|
function initMap() {
|
||||||
var default_lat = {% trans %}default_lat{% endtrans %};
|
var default_lat = {% trans %}default_lat{% endtrans %};
|
||||||
var default_lng = {% trans %}default_long{% endtrans %};
|
var default_lng = {% trans %}default_long{% endtrans %};
|
||||||
|
|
||||||
var map = mapCreate('dashboard_map', default_lat, default_lng, 'road', 13);
|
var rider_popup_url = '/riders/[id]/popup';
|
||||||
|
var cust_popup_url = '/job-order/[id]/popup';
|
||||||
|
map = mapCreate('dashboard_map', default_lat, default_lng, 'road', 13, rider_popup_url, cust_popup_url);
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ include('map/' ~ map_js_file) }}
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// TODO: put this in .env
|
||||||
|
var mqtt;
|
||||||
|
var timeout = 2000;
|
||||||
|
var host = '{{ mqtt_host }}';
|
||||||
|
var port = {{ mqtt_port }};
|
||||||
|
|
||||||
|
var icon_rider_available = L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
});
|
||||||
|
|
||||||
|
function onConnect() {
|
||||||
|
console.log('connected!');
|
||||||
|
|
||||||
|
mqtt.subscribe('rider/+/location');
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMessage(msg) {
|
||||||
|
// console.log(msg);
|
||||||
|
console.log('received message');
|
||||||
|
|
||||||
|
var channel = msg.destinationName;
|
||||||
|
var chan_split = channel.split('/');
|
||||||
|
var payload = msg.payloadString;
|
||||||
|
|
||||||
|
// handle different channels
|
||||||
|
switch (chan_split[0]) {
|
||||||
|
case "rider":
|
||||||
|
handleRider(chan_split, payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleRider(chan_split, payload) {
|
||||||
|
console.log("rider message");
|
||||||
|
switch (chan_split[2]) {
|
||||||
|
case "location":
|
||||||
|
console.log("got location for rider " + chan_split[1] + " - " + payload);
|
||||||
|
pl_split = payload.split(':');
|
||||||
|
console.log(pl_split);
|
||||||
|
|
||||||
|
// check for correct format
|
||||||
|
if (pl_split.length != 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var lat = parseFloat(pl_split[0]);
|
||||||
|
var lng = parseFloat(pl_split[1]);
|
||||||
|
|
||||||
|
// move marker
|
||||||
|
console.log(rider_markers[chan_split[1]]);
|
||||||
|
|
||||||
|
// check if marker exists
|
||||||
|
if (rider_markers.hasOwnProperty(chan_split[1])) {
|
||||||
|
// marker's there, move it
|
||||||
|
rider_markers[chan_split[1]].setLatLng(L.latLng(lat, lng));
|
||||||
|
} else {
|
||||||
|
// no marker, make one
|
||||||
|
console.log('creating marker');
|
||||||
|
// TODO: make it add to the correct map layer
|
||||||
|
rider_markers[chan_split[1]]= L.marker([lat, lng], { icon: icon_rider_available }).addTo(map);;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mqttConnect() {
|
||||||
|
var d = new Date();
|
||||||
|
var client_id = "dash-{{ app.user.getID }}-" + d.getMonth() + "-" + d.getDate() + "-" + d.getHours() + "-" + d.getMinutes() + "-" + d.getSeconds() + "-" + d.getMilliseconds();
|
||||||
|
console.log(client_id);
|
||||||
|
|
||||||
|
mqtt = new Paho.MQTT.Client(host, port, client_id);
|
||||||
|
var options = {
|
||||||
|
useSSL: true,
|
||||||
|
timeout: 3,
|
||||||
|
onSuccess: onConnect,
|
||||||
|
};
|
||||||
|
|
||||||
|
mqtt.onMessageArrived = onMessage;
|
||||||
|
|
||||||
|
console.log('connecting to mqtt server...');
|
||||||
|
mqtt.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
mqttConnect();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
8
templates/job-order/popup.html.twig
Normal file
8
templates/job-order/popup.html.twig
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% set cust = jo.getCustomer %}
|
||||||
|
{% set cv = jo.getCustomerVehicle %}
|
||||||
|
<strong>{{ cust.getNameDisplay }}</strong><br>
|
||||||
|
{{ cv.getPlateNumber }}<br>
|
||||||
|
<a href="">Job Order #{{ jo.getID }}</a><br>
|
||||||
|
{{ jo.getServiceTypeName }}<br>
|
||||||
|
{{ jo.getStatusText }}
|
||||||
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
function mapCreate(div_id, center_lat, center_lng, map_type, zoom) {
|
var rider_markers = {};
|
||||||
|
var cust_markers = {};
|
||||||
|
|
||||||
|
function mapCreate(div_id, center_lat, center_lng, map_type, zoom, rider_popup_url, cust_popup_url) {
|
||||||
var map = L.map(div_id).setView(
|
var map = L.map(div_id).setView(
|
||||||
[center_lat, center_lng],
|
[center_lat, center_lng],
|
||||||
zoom
|
zoom
|
||||||
|
|
@ -79,28 +82,43 @@ function mapCreate(div_id, center_lat, center_lng, map_type, zoom) {
|
||||||
// create rider markers
|
// create rider markers
|
||||||
if (rider_data['has_jo']) {
|
if (rider_data['has_jo']) {
|
||||||
var jo_data = rider_data['jo'];
|
var jo_data = rider_data['jo'];
|
||||||
rider_popup += '<br>';
|
|
||||||
rider_popup += '<a href="' + jo_data['url'] + '">Job Order #' + jo_data['id'] + '</a><br>';
|
|
||||||
rider_popup += jo_data['stype'] + '<br>';
|
|
||||||
rider_popup += jo_data['status'] + '<br><br>';
|
|
||||||
rider_popup += jo_data['cname'] + '<br>';
|
|
||||||
rider_popup += jo_data['plate'];
|
|
||||||
|
|
||||||
var cust_popup = '<strong>' + jo_data['cname'] + '</strong><br>';
|
// rider_markers[rider_id] = L.marker([lat, long], { icon: icon_rider_active_jo }).bindPopup(rider_popup);
|
||||||
cust_popup += jo_data['plate'] + '<br>';
|
rider_markers[rider_id] = L.marker([lat, long], { icon: icon_rider_active_jo }).bindPopup('Loading...');
|
||||||
cust_popup += '<a href="' + jo_data['url'] + '">Job Order #' + jo_data['id'] + '</a><br>';
|
// var cust_marker = L.marker([clat, clong], { icon: icon_customer }).bindPopup('Loading...');
|
||||||
cust_popup += jo_data['stype'] + '<br>';
|
cust_markers[jo_data['id']] = L.marker([clat, clong], { icon: icon_customer }).bindPopup('Loading...');
|
||||||
cust_popup += jo_data['status'];
|
lg_cust.addLayer(cust_markers[jo_data['id']]);
|
||||||
|
lg_jo_rider.addLayer(rider_markers[rider_id]);
|
||||||
|
|
||||||
var rider_marker = L.marker([lat, long], { icon: icon_rider_active_jo }).bindPopup(rider_popup);
|
// customer popup ajax
|
||||||
var cust_marker = L.marker([clat, clong], { icon: icon_customer }).bindPopup(cust_popup);
|
cust_markers[jo_data['id']].on('click', function(e) {
|
||||||
lg_cust.addLayer(cust_marker);
|
var popup = e.target.getPopup();
|
||||||
lg_jo_rider.addLayer(rider_marker);
|
var url = cust_popup_url.replace('[id]', jo_data['id']);
|
||||||
|
console.log(url);
|
||||||
|
$.get(url).done(function(data) {
|
||||||
|
popup.setContent(data);
|
||||||
|
popup.update();
|
||||||
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
var rider_marker = L.marker([lat, long], { icon: icon_rider_available }).bindPopup(rider_popup);
|
// rider_markers[rider_id]= L.marker([lat, long], { icon: icon_rider_available }).bindPopup(rider_popup);
|
||||||
lg_avail_rider.addLayer(rider_marker);
|
rider_markers[rider_id]= L.marker([lat, long], { icon: icon_rider_available }).bindPopup('Loading...');
|
||||||
|
lg_avail_rider.addLayer(rider_markers[rider_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ajax loading of rider popup
|
||||||
|
rider_markers[rider_id].on('click', function(e) {
|
||||||
|
var popup = e.target.getPopup();
|
||||||
|
var url = rider_popup_url.replace('[id]', rider_id);
|
||||||
|
console.log(url);
|
||||||
|
$.get(url).done(function(data) {
|
||||||
|
popup.setContent(data);
|
||||||
|
popup.update();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// console.log(rider_markers);
|
||||||
});
|
});
|
||||||
|
|
||||||
// base layer
|
// base layer
|
||||||
|
|
@ -120,4 +138,6 @@ function mapCreate(div_id, center_lat, center_lng, map_type, zoom) {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initMap();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
13
templates/rider/popup.html.twig
Normal file
13
templates/rider/popup.html.twig
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<strong>{{ rider.getFullName }}</strong>
|
||||||
|
{% set jo = rider.getActiveJobOrder %}
|
||||||
|
{% if jo is not null %}
|
||||||
|
{% set cust = jo.getCustomer %}
|
||||||
|
{% set cv = jo.getCustomerVehicle %}
|
||||||
|
<br>
|
||||||
|
<a href="">Job Order #{{ jo.getID }}</a><br>
|
||||||
|
{{ jo.getServiceTypeName }}<br>
|
||||||
|
{{ jo.getStatusText }}<br><br>
|
||||||
|
{{ cust.getNameDisplay }}<br>
|
||||||
|
{{ cv.getPlateNumber }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
@ -1,27 +1,29 @@
|
||||||
# text
|
# text
|
||||||
title_login: Motolite Res-Q | Login
|
title_login: Res-Q for CMB | Login
|
||||||
block_title: Motolite Res-Q
|
block_title: Res-Q for CMB
|
||||||
control_panel_sign_in: Sign-in to Control Panel
|
control_panel_sign_in: Sign-in to Control Panel
|
||||||
alt_image_logo_login: Res-Q
|
alt_image_logo_login: Res-Q for CMB
|
||||||
alt_image_dashboard: Motolite
|
alt_image_dashboard: Res-Q for CMB
|
||||||
copyright: Motolite Res-Q
|
copyright: Res-Q for CMB
|
||||||
battery_size_tradein_brand: Trade-in Motolite
|
battery_size_tradein_brand: Trade-in Motolite
|
||||||
battery_size_tradein_premium: Trade-in Premium
|
battery_size_tradein_premium: Trade-in Premium
|
||||||
battery_size_tradein_other: Trade-in Other
|
battery_size_tradein_other: Trade-in Other
|
||||||
add_cust_vehicle_battery_info: This vehicle is using a Motolite battery
|
add_cust_vehicle_battery_info: This vehicle is using a Motolite battery
|
||||||
jo_title_pdf: Motolite Res-Q Job Order
|
jo_title_pdf: Res-Q for CMB Job Order
|
||||||
country_code_prefix: '+63'
|
country_code_prefix: '+60'
|
||||||
delivery_instructions_label: Delivery Instructions
|
delivery_instructions_label: 'Delivery Instructions - CarFix Job Order No.'
|
||||||
|
|
||||||
# images
|
# images
|
||||||
image_logo_login: /assets/images/logo-resq.png
|
image_logo_login: /assets/images/black-text-logo-01.png
|
||||||
icon_login: /assets/demo/default/media/img/logo/favicon.ico
|
icon_login: /assets/images/battery-assist-bm-logo-32x32.png
|
||||||
icon_base_32x32: /assets/images/favicon/favicon-32x32.png
|
icon_base_32x32: /assets/images/black-text-logo-01-32x32.png
|
||||||
icon_base_16x16: /assets/images/favicon/favicon-16x16.png
|
icon_base_16x16: /assets/images/black-text-logo-01-16x16.png
|
||||||
image_dashboard: /assets/images/logo-motolite.png
|
image_dashboard: /assets/images/century_logo.png
|
||||||
image_jo_pdf: /public/assets/images/logo-resq.png
|
image_jo_pdf: /public/assets/images/black-text-logo-01-115x115.png
|
||||||
|
|
||||||
# default point for maps
|
# default point for maps
|
||||||
default_lat: 14.6091
|
default_lat: 14.6091
|
||||||
default_long: 121.0223
|
default_long: 121.0223
|
||||||
default_region: ph
|
#default_lat: 3.084216
|
||||||
|
#default_long: 101.6129996
|
||||||
|
default_region: my
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue