Erweiterungen
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,59 @@
|
|||||||
|
package de.assecutor.votianlt.controller;
|
||||||
|
|
||||||
|
import de.assecutor.votianlt.model.LocationPosition;
|
||||||
|
import de.assecutor.votianlt.service.LocationService;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST-Controller für Location-bezogene API-Endpunkte.
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/location")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class LocationApiController {
|
||||||
|
|
||||||
|
private final LocationService locationService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die aktuelle Position eines App-Nutzers zurück.
|
||||||
|
*
|
||||||
|
* @param appUserId die ID des App-Nutzers
|
||||||
|
* @return die aktuelle Position oder 404 wenn keine vorhanden
|
||||||
|
*/
|
||||||
|
@GetMapping("/{appUserId}")
|
||||||
|
public ResponseEntity<LocationResponse> getCurrentPosition(@PathVariable String appUserId) {
|
||||||
|
LocationPosition position = locationService.getLatestPosition(appUserId);
|
||||||
|
|
||||||
|
if (position == null || position.getLatitude() == null || position.getLongitude() == null) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationResponse response = new LocationResponse();
|
||||||
|
response.setLatitude(position.getLatitude());
|
||||||
|
response.setLongitude(position.getLongitude());
|
||||||
|
response.setAccuracy(position.getAccuracy());
|
||||||
|
response.setSpeed(position.getSpeed());
|
||||||
|
response.setTimestamp(position.getTimestamp());
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class LocationResponse {
|
||||||
|
private Double latitude;
|
||||||
|
private Double longitude;
|
||||||
|
private Double accuracy;
|
||||||
|
private Double speed;
|
||||||
|
private Instant timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ public class LocationPosition {
|
|||||||
* Timestamp when the position was received by the server
|
* Timestamp when the position was received by the server
|
||||||
*/
|
*/
|
||||||
@Field("received_at")
|
@Field("received_at")
|
||||||
@Indexed(expireAfterSeconds = 3600) // TTL index: auto-delete after 60 minutes
|
@Indexed(expireAfter = "3600s") // TTL index: auto-delete after 60 minutes
|
||||||
private Instant receivedAt;
|
private Instant receivedAt;
|
||||||
|
|
||||||
public LocationPosition(String appUserId, Double latitude, Double longitude, Double accuracy,
|
public LocationPosition(String appUserId, Double latitude, Double longitude, Double accuracy,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.vaadin.flow.router.Route;
|
|||||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
import de.assecutor.votianlt.model.CargoItem;
|
import de.assecutor.votianlt.model.CargoItem;
|
||||||
import de.assecutor.votianlt.model.Job;
|
import de.assecutor.votianlt.model.Job;
|
||||||
|
import de.assecutor.votianlt.model.LocationPosition;
|
||||||
import de.assecutor.votianlt.model.task.BaseTask;
|
import de.assecutor.votianlt.model.task.BaseTask;
|
||||||
import de.assecutor.votianlt.model.task.TodoListTask;
|
import de.assecutor.votianlt.model.task.TodoListTask;
|
||||||
import de.assecutor.votianlt.model.task.PhotoTask;
|
import de.assecutor.votianlt.model.task.PhotoTask;
|
||||||
@@ -45,6 +46,7 @@ import de.assecutor.votianlt.model.Comment;
|
|||||||
import de.assecutor.votianlt.model.JobStatus;
|
import de.assecutor.votianlt.model.JobStatus;
|
||||||
import de.assecutor.votianlt.pages.service.AppUserService;
|
import de.assecutor.votianlt.pages.service.AppUserService;
|
||||||
import de.assecutor.votianlt.service.JobHistoryService;
|
import de.assecutor.votianlt.service.JobHistoryService;
|
||||||
|
import de.assecutor.votianlt.service.LocationService;
|
||||||
import de.assecutor.votianlt.service.MessageService;
|
import de.assecutor.votianlt.service.MessageService;
|
||||||
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
import de.assecutor.votianlt.util.DateTimeFormatUtil;
|
||||||
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
|
||||||
@@ -73,6 +75,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
|||||||
private final CommentRepository commentRepository;
|
private final CommentRepository commentRepository;
|
||||||
private final AppUserService appUserService;
|
private final AppUserService appUserService;
|
||||||
private final JobHistoryService jobHistoryService;
|
private final JobHistoryService jobHistoryService;
|
||||||
|
private final LocationService locationService;
|
||||||
|
|
||||||
@Value("${app.google.maps.api-key}")
|
@Value("${app.google.maps.api-key}")
|
||||||
private String googleMapsApiKey;
|
private String googleMapsApiKey;
|
||||||
@@ -83,7 +86,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
|||||||
public JobSummaryView(JobRepository jobRepository, CargoItemRepository cargoItemRepository,
|
public JobSummaryView(JobRepository jobRepository, CargoItemRepository cargoItemRepository,
|
||||||
TaskRepository taskRepository, SignatureRepository signatureRepository, BarcodeRepository barcodeRepository,
|
TaskRepository taskRepository, SignatureRepository signatureRepository, BarcodeRepository barcodeRepository,
|
||||||
PhotoRepository photoRepository, CommentRepository commentRepository, AppUserService appUserService,
|
PhotoRepository photoRepository, CommentRepository commentRepository, AppUserService appUserService,
|
||||||
MessageService messageService, JobHistoryService jobHistoryService) {
|
MessageService messageService, JobHistoryService jobHistoryService, LocationService locationService) {
|
||||||
this.jobRepository = jobRepository;
|
this.jobRepository = jobRepository;
|
||||||
this.cargoItemRepository = cargoItemRepository;
|
this.cargoItemRepository = cargoItemRepository;
|
||||||
this.taskRepository = taskRepository;
|
this.taskRepository = taskRepository;
|
||||||
@@ -93,6 +96,7 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
|||||||
this.commentRepository = commentRepository;
|
this.commentRepository = commentRepository;
|
||||||
this.appUserService = appUserService;
|
this.appUserService = appUserService;
|
||||||
this.jobHistoryService = jobHistoryService;
|
this.jobHistoryService = jobHistoryService;
|
||||||
|
this.locationService = locationService;
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
|
||||||
@@ -421,17 +425,27 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
|||||||
+ concatZipCity(job.getDeliveryZip(), job.getDeliveryCity())).trim();
|
+ concatZipCity(job.getDeliveryZip(), job.getDeliveryCity())).trim();
|
||||||
|
|
||||||
if (origin.isBlank() || destination.isBlank()) {
|
if (origin.isBlank() || destination.isBlank()) {
|
||||||
// Wenn nicht genug Daten vorhanden sind, Karte nicht anzeigen
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe ob App-Tracking aktiviert ist und Job nicht erledigt/storniert
|
||||||
|
LocationPosition appUserPosition = null;
|
||||||
|
boolean showAppUserPosition = job.isDigitalProcessing()
|
||||||
|
&& job.getStatus() != JobStatus.COMPLETED
|
||||||
|
&& job.getStatus() != JobStatus.CANCELLED
|
||||||
|
&& job.getAppUser() != null
|
||||||
|
&& !job.getAppUser().isBlank();
|
||||||
|
|
||||||
|
if (showAppUserPosition) {
|
||||||
|
appUserPosition = locationService.getLatestPosition(job.getAppUser());
|
||||||
|
}
|
||||||
|
|
||||||
Div map = new Div();
|
Div map = new Div();
|
||||||
map.setWidthFull();
|
map.setWidthFull();
|
||||||
map.setHeight("520px");
|
map.setHeight("520px");
|
||||||
map.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
map.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||||
map.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
map.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
||||||
|
|
||||||
// Box für Fahrzeit/Alternativen
|
|
||||||
Div routeInfo = new Div();
|
Div routeInfo = new Div();
|
||||||
routeInfo.setWidthFull();
|
routeInfo.setWidthFull();
|
||||||
routeInfo.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
routeInfo.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
|
||||||
@@ -441,45 +455,218 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
|||||||
|
|
||||||
content.add(map, routeInfo);
|
content.add(map, routeInfo);
|
||||||
|
|
||||||
String js = ("(function(){" + " var host = $0; var infoEl = $1;" + " function init(){"
|
// Position für JavaScript vorbereiten
|
||||||
+ " var map = new google.maps.Map(host, {center: {lat: 51.163, lng: 10.447}, zoom: 6, mapTypeControl: false});"
|
final LocationPosition position = appUserPosition;
|
||||||
+ " var trafficLayer = new google.maps.TrafficLayer(); trafficLayer.setMap(map);"
|
final boolean hasPosition = position != null && position.getLatitude() != null && position.getLongitude() != null;
|
||||||
+ " var ds = new google.maps.DirectionsService();" + " ds.route({" + " origin: '"
|
final String appUserId = showAppUserPosition ? job.getAppUser() : "";
|
||||||
+ escapeJs(origin) + "'," + " destination: '" + escapeJs(destination) + "',"
|
final boolean shouldUpdate = showAppUserPosition;
|
||||||
+ " travelMode: google.maps.TravelMode.DRIVING," + " provideRouteAlternatives: true,"
|
|
||||||
+ " drivingOptions: { departureTime: new Date(), trafficModel: google.maps.TrafficModel.BEST_GUESS }"
|
String js = buildMapJs(origin, destination, hasPosition, position, appUserId, shouldUpdate);
|
||||||
+ " }, function(res, status){ if(status==='OK'){ " + " infoEl.innerHTML='';"
|
|
||||||
+ " var bounds = new google.maps.LatLngBounds();"
|
|
||||||
+ " var renderers = []; var polylines = [];" + " res.routes.forEach(function(route, idx){"
|
|
||||||
+ " var dr = new google.maps.DirectionsRenderer({map: map, preserveViewport: idx>0, suppressMarkers:false, suppressPolylines:true});"
|
|
||||||
+ " dr.setRouteIndex(idx); dr.setDirections(res);" + " renderers.push(dr);"
|
|
||||||
+ " var path = route.overview_path || [];"
|
|
||||||
+ " var poly = new google.maps.Polyline({path: path, strokeColor: idx===0?'#1976d2':'#90caf9', strokeOpacity: 0.95, strokeWeight: idx===0?6:4});"
|
|
||||||
+ " poly.setMap(map); polylines.push(poly);"
|
|
||||||
+ " var leg = route.legs && route.legs[0];" + " if (leg) {"
|
|
||||||
+ " var dur = leg.duration ? leg.duration.text : '';"
|
|
||||||
+ " var durT = leg.duration_in_traffic ? leg.duration_in_traffic.text : '';"
|
|
||||||
+ " var dist = leg.distance ? leg.distance.text : '';"
|
|
||||||
+ " var alt = (idx===0?'Schnellste Route':'Alternative '+idx);"
|
|
||||||
+ " var row = document.createElement('div'); row.style.margin='4px 0'; row.style.cursor='pointer';"
|
|
||||||
+ " row.textContent = alt + ': ' + dist + ' • Dauer: ' + dur + (durT?(' (mit Verkehr: '+durT+')'):'');"
|
|
||||||
+ " row.onmouseenter = function(){"
|
|
||||||
+ " polylines.forEach(function(p,i){ p.setOptions({strokeColor: i===0?'#90caf9':'#e3f2fd', strokeOpacity: 0.6, strokeWeight: 3}); });"
|
|
||||||
+ " poly.setOptions({strokeColor:'#0d47a1', strokeOpacity:1, strokeWeight:7});"
|
|
||||||
+ " };" + " row.onmouseleave = function(){"
|
|
||||||
+ " polylines.forEach(function(p,i){ p.setOptions({strokeColor: i===0?'#1976d2':'#90caf9', strokeOpacity:0.95, strokeWeight: i===0?6:4}); });"
|
|
||||||
+ " };" + " infoEl.appendChild(row);"
|
|
||||||
+ " if (path && path.length){ path.forEach(function(pt){ bounds.extend(pt); }); }"
|
|
||||||
+ " }" + " });" + " if (!bounds.isEmpty()) { map.fitBounds(bounds); }"
|
|
||||||
+ " }});" + " }" + " if (!(window.google && window.google.maps)) {"
|
|
||||||
+ " var s=document.createElement('script');"
|
|
||||||
+ " s.src='https://maps.googleapis.com/maps/api/js?key=" + getGoogleMapsApiKey()
|
|
||||||
+ "&libraries=places';" + " s.onload=init; document.head.appendChild(s);" + " } else { init(); }"
|
|
||||||
+ "})();");
|
|
||||||
|
|
||||||
map.getElement().executeJs(js, map.getElement(), routeInfo.getElement());
|
map.getElement().executeJs(js, map.getElement(), routeInfo.getElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildMapJs(String origin, String destination, boolean hasPosition, LocationPosition position, String appUserId, boolean shouldUpdate) {
|
||||||
|
String apiKey = getGoogleMapsApiKey();
|
||||||
|
// Explizit mit Punkt als Dezimaltrennzeichen formatieren
|
||||||
|
String lat = hasPosition ? String.format(java.util.Locale.US, "%.6f", position.getLatitude()) : "0";
|
||||||
|
String lng = hasPosition ? String.format(java.util.Locale.US, "%.6f", position.getLongitude()) : "0";
|
||||||
|
|
||||||
|
return """
|
||||||
|
(function(){
|
||||||
|
var host = $0;
|
||||||
|
var infoEl = $1;
|
||||||
|
var origin = '%s';
|
||||||
|
var destination = '%s';
|
||||||
|
var apiKey = '%s';
|
||||||
|
var appUserLat = %s;
|
||||||
|
var appUserLng = %s;
|
||||||
|
var hasAppUserPos = %s;
|
||||||
|
var appUserId = '%s';
|
||||||
|
var shouldUpdate = %s;
|
||||||
|
|
||||||
|
var appUserMarker = null;
|
||||||
|
var updateInterval = null;
|
||||||
|
var map = null;
|
||||||
|
|
||||||
|
function init(){
|
||||||
|
map = new google.maps.Map(host, {
|
||||||
|
center: {lat: 51.163, lng: 10.447},
|
||||||
|
zoom: 6,
|
||||||
|
mapTypeControl: false
|
||||||
|
});
|
||||||
|
|
||||||
|
var trafficLayer = new google.maps.TrafficLayer();
|
||||||
|
trafficLayer.setMap(map);
|
||||||
|
|
||||||
|
var ds = new google.maps.DirectionsService();
|
||||||
|
ds.route({
|
||||||
|
origin: origin,
|
||||||
|
destination: destination,
|
||||||
|
travelMode: google.maps.TravelMode.DRIVING,
|
||||||
|
provideRouteAlternatives: true,
|
||||||
|
drivingOptions: {
|
||||||
|
departureTime: new Date(),
|
||||||
|
trafficModel: google.maps.TrafficModel.BEST_GUESS
|
||||||
|
}
|
||||||
|
}, function(res, status){
|
||||||
|
if(status === 'OK'){
|
||||||
|
infoEl.innerHTML = '';
|
||||||
|
var bounds = new google.maps.LatLngBounds();
|
||||||
|
var renderers = [];
|
||||||
|
var polylines = [];
|
||||||
|
|
||||||
|
res.routes.forEach(function(route, idx){
|
||||||
|
var dr = new google.maps.DirectionsRenderer({
|
||||||
|
map: map,
|
||||||
|
preserveViewport: idx > 0,
|
||||||
|
suppressMarkers: false,
|
||||||
|
suppressPolylines: true
|
||||||
|
});
|
||||||
|
dr.setRouteIndex(idx);
|
||||||
|
dr.setDirections(res);
|
||||||
|
renderers.push(dr);
|
||||||
|
|
||||||
|
var path = route.overview_path || [];
|
||||||
|
var poly = new google.maps.Polyline({
|
||||||
|
path: path,
|
||||||
|
strokeColor: idx === 0 ? '#1976d2' : '#90caf9',
|
||||||
|
strokeOpacity: 0.95,
|
||||||
|
strokeWeight: idx === 0 ? 6 : 4
|
||||||
|
});
|
||||||
|
poly.setMap(map);
|
||||||
|
polylines.push(poly);
|
||||||
|
|
||||||
|
var leg = route.legs && route.legs[0];
|
||||||
|
if(leg){
|
||||||
|
var dur = leg.duration ? leg.duration.text : '';
|
||||||
|
var durT = leg.duration_in_traffic ? leg.duration_in_traffic.text : '';
|
||||||
|
var dist = leg.distance ? leg.distance.text : '';
|
||||||
|
var alt = (idx === 0 ? 'Schnellste Route' : 'Alternative ' + idx);
|
||||||
|
|
||||||
|
var row = document.createElement('div');
|
||||||
|
row.style.margin = '4px 0';
|
||||||
|
row.style.cursor = 'pointer';
|
||||||
|
row.textContent = alt + ': ' + dist + ' • Dauer: ' + dur + (durT ? ' (mit Verkehr: ' + durT + ')' : '');
|
||||||
|
|
||||||
|
row.onmouseenter = function(){
|
||||||
|
polylines.forEach(function(p, i){
|
||||||
|
p.setOptions({
|
||||||
|
strokeColor: i === 0 ? '#90caf9' : '#e3f2fd',
|
||||||
|
strokeOpacity: 0.6,
|
||||||
|
strokeWeight: 3
|
||||||
|
});
|
||||||
|
});
|
||||||
|
poly.setOptions({
|
||||||
|
strokeColor: '#0d47a1',
|
||||||
|
strokeOpacity: 1,
|
||||||
|
strokeWeight: 7
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
row.onmouseleave = function(){
|
||||||
|
polylines.forEach(function(p, i){
|
||||||
|
p.setOptions({
|
||||||
|
strokeColor: i === 0 ? '#1976d2' : '#90caf9',
|
||||||
|
strokeOpacity: 0.95,
|
||||||
|
strokeWeight: i === 0 ? 6 : 4
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
infoEl.appendChild(row);
|
||||||
|
|
||||||
|
if(path && path.length){
|
||||||
|
path.forEach(function(pt){ bounds.extend(pt); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// App-Nutzer Position Marker
|
||||||
|
if(hasAppUserPos){
|
||||||
|
createOrUpdateAppUserMarker(appUserLat, appUserLng);
|
||||||
|
bounds.extend({lat: appUserLat, lng: appUserLng});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!bounds.isEmpty()){
|
||||||
|
map.fitBounds(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle 30 Sekunden aktualisieren
|
||||||
|
if(shouldUpdate && appUserId){
|
||||||
|
startPositionUpdates(appUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOrUpdateAppUserMarker(lat, lng){
|
||||||
|
if(appUserMarker){
|
||||||
|
appUserMarker.setPosition({lat: lat, lng: lng});
|
||||||
|
} else {
|
||||||
|
appUserMarker = new google.maps.Marker({
|
||||||
|
position: {lat: lat, lng: lng},
|
||||||
|
map: map,
|
||||||
|
title: 'Position App-Nutzer',
|
||||||
|
icon: {
|
||||||
|
path: google.maps.SymbolPath.CIRCLE,
|
||||||
|
scale: 10,
|
||||||
|
fillColor: '#4caf50',
|
||||||
|
fillOpacity: 1,
|
||||||
|
strokeColor: '#ffffff',
|
||||||
|
strokeWeight: 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var infoWindow = new google.maps.InfoWindow({
|
||||||
|
content: '<div style="font-weight:bold;">Position App-Nutzer</div>'
|
||||||
|
});
|
||||||
|
|
||||||
|
appUserMarker.addListener('click', function(){
|
||||||
|
infoWindow.open(map, appUserMarker);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPositionUpdates(userId){
|
||||||
|
updateInterval = setInterval(function(){
|
||||||
|
fetch('/api/location/' + encodeURIComponent(userId))
|
||||||
|
.then(function(response){
|
||||||
|
if(!response.ok) throw new Error('No position');
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(data){
|
||||||
|
if(data && data.latitude && data.longitude){
|
||||||
|
createOrUpdateAppUserMarker(data.latitude, data.longitude);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(err){
|
||||||
|
console.log('Location update failed:', err);
|
||||||
|
});
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(window.google && window.google.maps)){
|
||||||
|
var s = document.createElement('script');
|
||||||
|
s.src = 'https://maps.googleapis.com/maps/api/js?key=' + apiKey + '&libraries=places';
|
||||||
|
s.onload = init;
|
||||||
|
document.head.appendChild(s);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
""".formatted(
|
||||||
|
escapeJs(origin),
|
||||||
|
escapeJs(destination),
|
||||||
|
escapeJs(apiKey),
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
|
Boolean.toString(hasPosition),
|
||||||
|
escapeJs(appUserId),
|
||||||
|
Boolean.toString(shouldUpdate)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Hilfsfunktion zum einfachen Escapen von JS-Zeichen in Strings
|
// Hilfsfunktion zum einfachen Escapen von JS-Zeichen in Strings
|
||||||
private String escapeJs(String s) {
|
private String escapeJs(String s) {
|
||||||
if (s == null)
|
if (s == null)
|
||||||
@@ -999,17 +1186,14 @@ public class JobSummaryView extends Main implements HasUrlParameter<String> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getGoogleMapsApiKey() {
|
|
||||||
return googleMapsApiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetAllTaskCardHoverStates() {
|
private void resetAllTaskCardHoverStates() {
|
||||||
// Reset hover state for all task cards
|
|
||||||
for (Div taskCard : taskCards) {
|
for (Div taskCard : taskCards) {
|
||||||
if (taskCard != null) {
|
|
||||||
taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
|
taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
|
||||||
.set("border-color", "var(--lumo-contrast-20pct)");
|
.set("border-color", "var(--lumo-contrast-20pct)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getGoogleMapsApiKey() {
|
||||||
|
return googleMapsApiKey != null ? googleMapsApiKey : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user