push to gitea

This commit is contained in:
lucas bamberg 2023-02-19 01:20:55 +01:00
parent 29fd802cad
commit fb256fd284
18 changed files with 33456 additions and 0 deletions

52
frontend/gulpfile.mjs Normal file
View File

@ -0,0 +1,52 @@
import gulp from 'gulp';
const {src, parallel, dest, watch} = gulp
import dartSass from 'sass';
import gulpSass from 'gulp-sass';
const sass = gulpSass(dartSass);
import concat from 'gulp-concat';
import minify from 'gulp-minify';
import rename from 'gulp-rename';
const BUILD_TARGET_DIR = "../public/assets/";
export const css = () => {
return src('./src/scss/global.scss')
.pipe(sass())
.pipe(dest(BUILD_TARGET_DIR + 'css/'));
};
export const js = () => {
return src(['./src/js/**/*.js'])
.pipe(concat('app.js'))
.pipe(minify())
.pipe(rename("app.js"))
.pipe(dest(BUILD_TARGET_DIR + 'js/'));
};
export const libs = () => {
return src([
'./node_modules/bootstrap/dist/js/bootstrap.bundle.js',
'./node_modules/jquery/dist/jquery.js',
'./node_modules/leaflet/dist/leaflet.js'
])
.pipe(concat('libs.js'))
.pipe(minify())
.pipe(rename("libs.js"))
.pipe(dest(BUILD_TARGET_DIR + 'js/'));
};
export const w = () => {
watch(["./src/scss/**/*.scss", './public/**/*.html'], css);
watch("./src/js/**/*.js", js);
};
export const images = () => {
return src([
'./node_modules/leaflet/dist/images/*.png'
])
.pipe(dest(BUILD_TARGET_DIR + 'css/images/'))
}
export default parallel(css, js, libs);

3411
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
frontend/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "frontend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "./node_modules/.bin/gulp w",
"build": "./node_modules/.bin/gulp",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bootstrap": "^5.1.0",
"jquery": "^3.6.0",
"leaflet": "^1.7.1"
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-cli": "^2.3.0",
"gulp-concat": "^2.6.1",
"gulp-minify": "^3.1.0",
"gulp-rename": "^2.0.0",
"gulp-sass": "^5.0.0",
"sass": "^1.39.0"
}
}

301
frontend/src/js/main.js Normal file
View File

@ -0,0 +1,301 @@
const api = {
url: 'https://verkehr.autobahn.de/o/autobahn/',
roadworks: '/services/roadworks',
webcams: '/services/webcam',
charging: '/services/electric_charging_station',
mapbox: {
pubToken: 'enter your key here'
}
}
$(async function onload() {
let $roadSelection = $('#roadSelection');
/** Register list sections and hide on load */
let $roadworks = $('#roadworks');
$roadworks.addClass('d-none');
let $roadworksList = $('#roadworksList');
let $webcams = $('#webcams');
$webcams.addClass('d-none');
let $webcamList = $('#webcamList');
let $cStations = $('#chargingStations');
$cStations.addClass('d-none');
let $cStationList = $('#cStationList');
/** Basic Buttons */
let $btnRoadworks = $('#btn-roadworks');
let $btnWebcams = $('#btn-webcams');
let $btnCStations = $('#btn-cStations');
$btnRoadworks.addClass('disabled').attr('disabled', 'disabled');
$btnWebcams.addClass('disabled').attr('disabled', 'disabled');
$btnCStations.addClass('disabled').attr('disabled', 'disabled');
/** register vars for different lists.. */
let actualRoadworks = null;
let actualWebcams = null;
let actualChargingStations = null;
/** request road list from server */
let road = await $.get(api.url);
/** create select options for each road
* enable / disable Buttons
*/
road.roads.forEach(road => {
$roadSelection.append($('<option />').val(road).text(road));
});
$roadSelection.click(() => {
if ($roadSelection.val() === null) {
$btnRoadworks.addClass('disabled').attr('disabled', 'disabled');
$btnWebcams.addClass('disabled').attr('disabled', 'disabled');
$btnCStations.addClass('disabled').attr('disabled', 'disabled');
} else {
$btnRoadworks.removeClass('disabled').removeAttr('disabled');
$btnWebcams.removeClass('disabled').removeAttr('disabled');
$btnCStations.removeClass('disabled').removeAttr('disabled');
}
});
$btnRoadworks.click(async function () {
$btnWebcams.removeClass('active');
$btnCStations.removeClass('active');
$btnRoadworks.addClass('active');
$webcams.addClass('d-none');
$cStations.addClass('d-none');
if ($roadSelection.val() === null) {
$roadworks.addClass('d-none');
$roadworksList.empty();
} else {
actualRoadworks = await $.get(api.url + $roadSelection.val() + api.roadworks);
$roadworks.removeClass('d-none');
$roadworksList.empty();
if (actualRoadworks.roadworks.length === 0) {
$roadworksList.append($('<div class="fw-bold" />').text('Keine Einträge zu aktuellen Baustellen auf der ' + $roadSelection.val() + ' gefunden.'));
} else {
actualRoadworks.roadworks.forEach((roadwork, key) => {
$roadworksList.append($('<a href="#" class="list-group-item list-group-item-action btn-roadwork" data-key="' + key + '"/>').append($('<div/>').text(roadwork.title)).append($('<div />').addClass('fw-bold').text(roadwork.subtitle)));
})
}
}
});
$roadworksList.off('click', '.btn-roadwork').on('click', '.btn-roadwork', async (e) => {
e.preventDefault();
let target = $(e.currentTarget);
let roadwork = actualRoadworks.roadworks[target.data('key')];
let body = $('<div/>').append($('<div />').text(roadwork.subtitle).append($('<hr/>')));
roadwork.description.forEach(e => {
body.append($('<div />').text(e));
});
await buildModal({
modalDialog: 'modal-xl',
modalTitle: roadwork.title,
modalBody: body
})
});
$btnWebcams.click(async function () {
$btnWebcams.addClass('active');
$btnRoadworks.removeClass('active');
$btnCStations.removeClass('active');
$roadworks.addClass('d-none');
$cStations.addClass('d-none');
if ($roadSelection.val() === null) {
$webcams.addClass('d-none');
$webcamList.empty();
} else {
actualWebcams = await $.get(api.url + $roadSelection.val() + api.webcams);
$webcams.removeClass('d-none');
$webcamList.empty();
if (actualWebcams.webcam.length === 0) {
$webcamList.append($('<div class="fw-bold"/>').text('Keine Webcams auf der ' + $roadSelection.val() + ' gefunden.'));
} else {
actualWebcams.webcam.forEach((webcam, key) => {
$webcamList.append($('<a href="#" class="list-group-item list-group-item-action btn-webcam" data-key="' + key + '"/>').append($('<div/>').text(webcam.title)).append($('<div />').addClass('fw-bold').text(webcam.subtitle)));
})
}
}
});
$webcamList.off('click', '.btn-webcam').on('click', '.btn-webcam', async (e) => {
e.preventDefault();
let target = $(e.currentTarget);
let webcam = actualWebcams.webcam[target.data('key')];
let body = $('<div class="modalBody"/>').append(
$('<div/>')
.append($('<div class="row"/>')
.append($('<div class="col-6"/>')
.append($('<ul class="list-group webcamMetaList"/>')
.append($('<li class="list-group-item"/>').text('Bundesland: ' + webcam.operator))
.append($('<li class="list-group-item"/>').text('Position Lat: ' + webcam.coordinate.lat + ', Long: ' + webcam.coordinate.long))
.append($('<li class="list-group-item"/>').text(webcam.subtitle))
)
)
.append($('<div class="col-6"/>')
.append($('<div class="img-wrap"/>').append($('<img src="' + webcam.imageurl + '" alt="webcam Image"/>')))
)
)
);
if (webcam.isBlocked !== false && webcam.linkurl !== '' && webcam.linkurl !== 'https://#') {
body.append($('<div class="d-grid gap-2"/>')
.append($('<button class="btn btn-primary btn-sm btnWebcamFrame" data-link="' + target.data('key') + '" />').text('zeige Video')
)
);
}
await buildModal({
modalTitle: "Kamera " + webcam.title,
modalBody: body,
type: 'webcam',
additionalData: webcam.linkurl
});
});
$btnCStations.click(async function () {
$btnCStations.addClass('active');
$btnRoadworks.removeClass('active');
$btnWebcams.removeClass('active');
$roadworks.addClass('d-none');
$webcams.addClass('d-none');
if ($roadSelection.val() === null) {
$cStations.addClass('d-none');
$cStationList.empty();
} else {
actualChargingStations = await $.get(api.url + $roadSelection.val() + api.charging);
$cStations.removeClass('d-none');
$cStationList.empty();
if (actualChargingStations.electric_charging_station.length === 0) {
$cStationList.append($('<div class="fw-bold"/>').text('Keine elektrischen Ladestationen auf der ' + $roadSelection.val() + ' gefunden.'));
} else {
actualChargingStations.electric_charging_station.forEach((station, key) => {
$cStationList.append($('<a href="#" class="list-group-item list-group-item-action btn-cStation" data-key="' + key + '"/>').append($('<div/>').text(station.title)).append($('<div />').addClass('fw-bold').text(station.subtitle)));
})
}
}
})
$cStationList.off('click', '.btn-cStation').on('click', '.btn-cStation', async (e) => {
e.preventDefault();
let target = $(e.currentTarget);
let cStation = actualChargingStations.electric_charging_station[target.data('key')];
let body = $('<div class="modalBody" />').append(
$('<div />')
.append($('<div class="row"/>')
.append($('<div class="col-6"/>')
.append($('<ul class="list-group cStationMetaList"/>')
.append($('<li class="list-group-item"/>').text(cStation.subtitle))
.append($('<li class="list-group-item"/>').text('Position Lat: ' + cStation.coordinate.lat + ', Long: ' + cStation.coordinate.long))
)
)
.append($('<div class="col-6" id="cStationPos"/>')
)
)
)
cStation.description.forEach((descr) => {
if (descr !== '') {
if (descr.match(/(Ladepunkt)/g)) {
body.find('.cStationMetaList').append($('<li class="list-group-item fw-bold" />').text(descr));
} else {
body.find('.cStationMetaList').append($('<li class="list-group-item" />').text(descr));
}
}
});
await buildModal({
modalTitle: "Ladestation" + cStation.title,
modalBody: body,
type: 'electric_charging_station',
additionalData: cStation.coordinate
});
});
});
async function buildModal(options) {
const modalTemplate = $(await $.get('/templates/modal.standard.html'));
let section = $('#modalSection');
section.empty();
if (options.modalDialog) {
modalTemplate.find('.modal-dialog').addClass(options.modalDialog);
}
if (options.modalTitle) {
modalTemplate.find('.modal-title').text(options.modalTitle);
}
if (options.modalBody) {
modalTemplate.find('.modal-body').append(options.modalBody);
}
section.append(modalTemplate);
let htmlModal = document.getElementById('myModal');
let Modal = new bootstrap.Modal(htmlModal);
Modal.show();
switch (options.type){
case 'webcam':
await webcamFrame(options.additionalData);
break;
case 'electric_charging_station':
await leafletDiv(options.additionalData)
break;
default:
console.debug('default');
}
}
async function webcamFrame(additionalData) {
let btnWebcamFrame = $('.btnWebcamFrame');
btnWebcamFrame.click((e) => {
$('.modalBody').append($('<iframe src="' + additionalData + '" width="100%" height="500px" class="mt-3"/>'));
btnWebcamFrame.addClass('d-none');
$('.modal-dialog').addClass('modal-xl');
});
}
async function leafletDiv (coordinates) {
$('.modal-dialog').addClass('modal-xl');
console.debug(coordinates);
let place = L.map('cStationPos').setView([
coordinates.lat,
coordinates.long
],17);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
maxZoom: 18,
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1,
accessToken: api.mapbox.pubToken
}).addTo(place);
L.marker([
coordinates.lat,
coordinates.long
]).addTo(place);
}

View File

@ -0,0 +1,18 @@
.img-wrap {
width: 200px;
height: 150px;
position: relative;
display: inline-block;
overflow: hidden;
margin: 0;
}
div > img {
display: block;
position: absolute;
top: 50%;
left: 50%;
min-height: 100%;
min-width: 100%;
transform: translate(-50%, -50%);
}

View File

@ -0,0 +1,24 @@
body {
background-color: $dark;
}
//jumbotron
$jumbotron-padding: 2rem !default;
$jumbotron-bg: $gray-200 !default;
.jumbotron {
padding: $jumbotron-padding ($jumbotron-padding / 2);
margin-bottom: $jumbotron-padding;
background-color: $jumbotron-bg;
@include border-radius($border-radius-lg);
@include media-breakpoint-up(sm) {
padding: ($jumbotron-padding * 2) $jumbotron-padding;
}
}
.jumbotron-fluid {
padding-right: 0;
padding-left: 0;
@include border-radius(0);
}

View File

@ -0,0 +1,3 @@
#cStationPos {
height: 400px;
}

View File

@ -0,0 +1,9 @@
@import "../../node_modules/bootstrap/scss/functions";
@import "../../node_modules/bootstrap/scss/variables";
@import "../../node_modules/bootstrap/scss/mixins";
@import "bootstrap_overrides";
@import "../../node_modules/bootstrap/scss/bootstrap";
@import "../../node_modules/leaflet/dist/leaflet";
@import "_main";
@import "_cams";
@import "_map";

11889
public/assets/css/global.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

17669
public/assets/js/libs.js Normal file

File diff suppressed because one or more lines are too long

36
public/index.html Normal file
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
<link rel="stylesheet" href="assets/css/global.css">
</head>
<body>
<div class="mt-5 container jumbotron">
<select name="road" id="roadSelection" class="form-select" aria-label="Auswahl der Fernstraße">
<option selected disabled>Bitte wählen</option>
</select>
<div class="btn-group" role="group">
<button type="button" class="mt-3 btn btn-sm btn-primary" id="btn-roadworks">Baustellen</button>
<button type="button" class="mt-3 btn btn-sm btn-primary" id="btn-webcams">Webcams</button>
<button type="button" class="mt-3 btn btn-sm btn-primary" id="btn-cStations">Ladestationen</button>
</div>
<div id="roadworks" class="mt-5">
<div class="list-group" id="roadworksList"></div>
</div>
<div id="webcams" class="mt-5">
<div class="list-group" id="webcamList"></div>
</div>
<div id="chargingStations" class="mt-5">
<div class="list-group" id="cStationList"></div>
</div>
</div>
<section id="modalSection"></section>
<script src="/assets/js/libs.js"></script>
<script src="/assets/js/app.js"></script>
</body>
</html>

View File

@ -0,0 +1,16 @@
<div class="modal" tabindex="-1" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>