wip
114
package-lock.json
generated
@ -8,11 +8,15 @@
|
||||
"name": "lts-stats-web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.2",
|
||||
"@headlessui/vue": "^1.7.7",
|
||||
"@heroicons/vue": "^2.0.13",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"axios": "^1.2.2",
|
||||
"bootstrap": "^5.2.3",
|
||||
"leaflet-rotatedmarker": "^0.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"pinia": "^2.0.29",
|
||||
@ -25,11 +29,13 @@
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue-leaflet/vue-leaflet": "^0.8.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"bulma": "^0.9.4",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"http-server": "^14.1.1",
|
||||
"leaflet": "^1.9.3",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.57.1",
|
||||
"vite": "^4.0.0"
|
||||
@ -421,6 +427,48 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz",
|
||||
"integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz",
|
||||
"integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.1.tgz",
|
||||
"integrity": "sha512-oKuqrP5jbfEPJWTij4sM+/RvgX+RMFwx3QZCZcK9PrBDgxC35zuc7AOFsyMjMd/PIFPeB2JxyqDr5zs/DZFPPw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/vue-fontawesome": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.2.tgz",
|
||||
"integrity": "sha512-xHVtVY8ASUeEvgcA/7vULUesENhD+pi/EirRHdMBqooHlXBqK+yrV6d8tUye1m5UKQKVgYAHMhUBfOnoiwvc8Q==",
|
||||
"peerDependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
|
||||
"vue": ">= 3.0.0 < 4"
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/vue": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.7.tgz",
|
||||
@ -667,6 +715,16 @@
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue-leaflet/vue-leaflet": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.8.0.tgz",
|
||||
"integrity": "sha512-oHPdD4zq243NvK98T+HiRT9qf1zvRys5KO0eDdRkZKd09i9Epj9ALPoRqF2NFUPcS8G2yEO1qCLiKAgFQ26ARQ==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"leaflet": "^1.6.0",
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||
@ -1959,6 +2017,17 @@
|
||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/leaflet": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
|
||||
"integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/leaflet-rotatedmarker": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz",
|
||||
"integrity": "sha512-yc97gxLXwbZa+Gk9VCcqI0CkvIBC9oNTTjFsHqq4EQvANrvaboib4UdeQLyTnEqDpaXHCqzwwVIDHtvz2mUiDg=="
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
@ -3223,6 +3292,33 @@
|
||||
"strip-json-comments": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz",
|
||||
"integrity": "sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ=="
|
||||
},
|
||||
"@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz",
|
||||
"integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-common-types": "6.2.1"
|
||||
}
|
||||
},
|
||||
"@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.1.tgz",
|
||||
"integrity": "sha512-oKuqrP5jbfEPJWTij4sM+/RvgX+RMFwx3QZCZcK9PrBDgxC35zuc7AOFsyMjMd/PIFPeB2JxyqDr5zs/DZFPPw==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-common-types": "6.2.1"
|
||||
}
|
||||
},
|
||||
"@fortawesome/vue-fontawesome": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.2.tgz",
|
||||
"integrity": "sha512-xHVtVY8ASUeEvgcA/7vULUesENhD+pi/EirRHdMBqooHlXBqK+yrV6d8tUye1m5UKQKVgYAHMhUBfOnoiwvc8Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"@headlessui/vue": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.7.tgz",
|
||||
@ -3411,6 +3507,13 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@vue-leaflet/vue-leaflet": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.8.0.tgz",
|
||||
"integrity": "sha512-oHPdD4zq243NvK98T+HiRT9qf1zvRys5KO0eDdRkZKd09i9Epj9ALPoRqF2NFUPcS8G2yEO1qCLiKAgFQ26ARQ==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@vue/compiler-core": {
|
||||
"version": "3.2.45",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
|
||||
@ -4386,6 +4489,17 @@
|
||||
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"leaflet": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
|
||||
"integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"leaflet-rotatedmarker": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz",
|
||||
"integrity": "sha512-yc97gxLXwbZa+Gk9VCcqI0CkvIBC9oNTTjFsHqq4EQvANrvaboib4UdeQLyTnEqDpaXHCqzwwVIDHtvz2mUiDg=="
|
||||
},
|
||||
"levn": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
|
||||
|
@ -11,11 +11,15 @@
|
||||
"docker-build": "docker build -t arhuako/ltsweb ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.2.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.2",
|
||||
"@headlessui/vue": "^1.7.7",
|
||||
"@heroicons/vue": "^2.0.13",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"axios": "^1.2.2",
|
||||
"bootstrap": "^5.2.3",
|
||||
"leaflet-rotatedmarker": "^0.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"pinia": "^2.0.29",
|
||||
@ -28,11 +32,13 @@
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue-leaflet/vue-leaflet": "^0.8.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"bulma": "^0.9.4",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"http-server": "^14.1.1",
|
||||
"leaflet": "^1.9.3",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.57.1",
|
||||
"vite": "^4.0.0"
|
||||
|
14
src/App.vue
@ -46,9 +46,9 @@ export default {
|
||||
}
|
||||
const user = await authenticate(this.username, this.password);
|
||||
this.setUser(user);
|
||||
console.log('user :>> ', user);
|
||||
this.username = '';
|
||||
this.password = '';
|
||||
window.location.reload();
|
||||
},
|
||||
async logout() {
|
||||
await logout();
|
||||
@ -81,8 +81,9 @@ export default {
|
||||
<div class="navbar-start">
|
||||
<router-link class="navbar-item" to="/">ICAO</router-link>
|
||||
<router-link class="navbar-item" to="/acars">Acars</router-link>
|
||||
<router-link class="navbar-item" to="/map">Live Map</router-link>
|
||||
|
||||
<router-link v-if="hasRoles(['cabal'])" class="navbar-item" to="/cabal">Capt Cabal</router-link>
|
||||
<router-link v-if="hasRoles(['admin', 'cabal'])" class="navbar-item" to="/cabal">Capt Cabal</router-link>
|
||||
<router-link v-if="hasRoles(['admin'])" class="navbar-item" to="/admin">Admin</router-link>
|
||||
</div>
|
||||
</div>
|
||||
@ -129,10 +130,13 @@ export default {
|
||||
.logo {
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
.tag {
|
||||
font-size: 60% !important;
|
||||
}
|
||||
.input-login {
|
||||
width: 100px;
|
||||
}
|
||||
.loading-icon {
|
||||
margin-top: 24px;
|
||||
color: #ddd;
|
||||
width: 100%;
|
||||
text-align: center;;
|
||||
}
|
||||
</style>
|
||||
|
15
src/assets/images/coloring.svg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/images/map/location-blue.png
Normal file
After Width: | Height: | Size: 653 B |
BIN
src/assets/images/map/location-gray.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
src/assets/images/map/location-green.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
src/assets/images/map/location-lightblue.png
Normal file
After Width: | Height: | Size: 589 B |
BIN
src/assets/images/map/location-red.png
Normal file
After Width: | Height: | Size: 1023 B |
BIN
src/assets/images/map/location-yellow.png
Normal file
After Width: | Height: | Size: 701 B |
BIN
src/assets/images/map/marker-here.png
Normal file
After Width: | Height: | Size: 987 B |
BIN
src/assets/images/map/marker-landing.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/images/map/marker-takeoff.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/images/map/plane.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
src/assets/images/ranks/01.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/images/ranks/02.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/assets/images/ranks/03.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
src/assets/images/ranks/04.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
src/assets/images/ranks/05.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
src/assets/images/ranks/06.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
src/assets/images/ranks/07.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
src/assets/images/ranks/08.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/images/ranks/09.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/images/ranks/10.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/images/ranks/11.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
src/assets/images/ranks/12.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/images/ranks/13.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/images/ranks/14.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/images/ranks/15.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/images/ranks/16.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<section class="">
|
||||
<div class="solari-container">
|
||||
<SolariBoard :headers="headers" :loading="isLoading" :config="colsConfig" :data="list" :size="18" />
|
||||
<SolariBoard :headers="headers" :loading="isLoading" :config="colsConfig" :data="list" :length="18" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@ -31,15 +31,15 @@
|
||||
colsConfig: [
|
||||
{
|
||||
align: 'left',
|
||||
size: 8
|
||||
length: 8
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
size: 9
|
||||
length: 9
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
size: 5
|
||||
length: 5
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
<SolariBoardRow :ref="`line_${j - 1}`"
|
||||
:textToShow="getValue(j - 1)"
|
||||
:loops="getConfigValue(j - 1, 'loop')"
|
||||
:size="getConfigValue(j - 1, 'size')"
|
||||
:length="getConfigValue(j - 1, 'length')"
|
||||
:delay="getConfigValue(j - 1, 'delay')"
|
||||
:align="getConfigValue(j - 1, 'align')"></SolariBoardRow>
|
||||
</div>
|
||||
@ -36,7 +36,7 @@ export default {
|
||||
type: Number,
|
||||
default: () => 200
|
||||
},
|
||||
size: {
|
||||
length: {
|
||||
type: Number,
|
||||
default: () => 25
|
||||
},
|
||||
@ -107,7 +107,7 @@ export default {
|
||||
mapRow(r, i) {
|
||||
r.loops = (_isNil(r.loops)) ? this.loops : r.loops;
|
||||
r.delay = ((_isNil(r.delay)) ? this.delay : r.delay) * i;
|
||||
r.size = (_isNil(r.size)) ? this.size : r.size;
|
||||
r.length = (_isNil(r.length)) ? this.length : r.length;
|
||||
r.align = (_isNil(r.align)) ? this.align : r.align;
|
||||
return r;
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="board-line">
|
||||
<div class="board-line" :class="size">
|
||||
<div class="board-letter" :ref="'letter' + $index" :class="{'animating': isAnimating[$index]}" v-for="(char, $index) in charsToShow" :key="$index">{{ char }}</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -29,6 +29,12 @@
|
||||
position: relative;
|
||||
line-height: 1;
|
||||
|
||||
.big & {
|
||||
font-size: 32px;
|
||||
height: 38px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content:' ';
|
||||
display: inline-block;
|
||||
@ -60,6 +66,10 @@
|
||||
|
||||
export default {
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
textToShow: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
@ -68,7 +78,7 @@
|
||||
type: Number,
|
||||
default: () => 0
|
||||
},
|
||||
size: {
|
||||
length: {
|
||||
type: Number,
|
||||
},
|
||||
loops: {
|
||||
@ -81,10 +91,10 @@
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const newArray = Array.apply(null, Array(this.size)).map(() => ' ');
|
||||
const newArrayAnimating = Array.apply(null, Array(this.size)).map(() => false);
|
||||
const newArray = Array.apply(null, Array(this.length)).map(() => ' ');
|
||||
const newArrayAnimating = Array.apply(null, Array(this.length)).map(() => false);
|
||||
return {
|
||||
charsAll: ' ABCDEFGHIJKLMNÑOPQRSTUVWXYZ0123456789-:.>*',
|
||||
charsAll: ' ABCDEFGHIJKLMNÑOPQRSTUVWXYZ0123456789-:.>*¡!¿?@#',
|
||||
charsReference: _clone(newArray),
|
||||
charsToShow: _clone(newArray),
|
||||
isAnimating: newArrayAnimating,
|
||||
@ -93,7 +103,7 @@
|
||||
computed: {
|
||||
chars() {
|
||||
const text = this.getText(this.textToShow);
|
||||
const chars = Array.apply(null, Array(this.size)).map(() => ' ');
|
||||
const chars = Array.apply(null, Array(this.length)).map(() => ' ');
|
||||
const offset = (this.align === 'left') ? 0 : chars.length - text.length;
|
||||
|
||||
for (let index = 0; index < text.length; index++) {
|
||||
@ -122,7 +132,7 @@
|
||||
return this.charsAll.indexOf(letter);
|
||||
},
|
||||
getText(text) {
|
||||
return (text.length > this.size) ? text.substring(0, this.size) : text;
|
||||
return (text.length > this.length) ? text.substring(0, this.length) : text;
|
||||
},
|
||||
async startAnimation() {
|
||||
const cts = _clone(this.charsReference);
|
||||
|
@ -1,7 +1,4 @@
|
||||
<template>
|
||||
<loading v-model:active="isLoading"
|
||||
:can-cancel="false"
|
||||
:is-full-page="fullPage"/>
|
||||
<section class="section">
|
||||
<h1 class="title">Pilotos activos con mas de 200 horas en ACARS</h1>
|
||||
<table class="table is-striped is-hoverable is-fullwidth">
|
||||
@ -13,6 +10,15 @@
|
||||
<th class="has-text-centered">Horas totales</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-show="isLoading">
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="loading-icon">
|
||||
<font-awesome-icon spin icon="fa-solid fa-spinner" size="2x" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr v-for="item in whitelist" :key="item.vid">
|
||||
<td class="has-text-centered">
|
||||
@ -41,7 +47,6 @@
|
||||
import moment from 'moment';
|
||||
import { getWhitelist } from '../data/http/listRequest.js';
|
||||
import FormatTime from './FormatTime.vue';
|
||||
import Loading from 'vue-loading-overlay';
|
||||
import 'vue-loading-overlay/dist/css/index.css';
|
||||
|
||||
export default {
|
||||
@ -69,7 +74,6 @@
|
||||
},
|
||||
components: {
|
||||
FormatTime,
|
||||
Loading,
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<!-- <loading v-model:active="isLoading"
|
||||
:can-cancel="false"
|
||||
:is-full-page="fullPage"/> -->
|
||||
<section class="section">
|
||||
<!-- <loading class="mt-6" v-model:active="isLoading"
|
||||
:can-cancel="false"/> -->
|
||||
<h1 class="title">Status mensual IVAO</h1>
|
||||
<table class="table is-striped is-hoverable is-fullwidth" v-if="!$isMobile()">
|
||||
<thead>
|
||||
@ -14,6 +13,15 @@
|
||||
<th class="has-text-centered">Último vuelo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-show="isLoading">
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<div class="loading-icon">
|
||||
<font-awesome-icon spin icon="fa-solid fa-spinner" size="2x" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr v-for="item in list" :key="item.vid">
|
||||
<td class="has-text-centered">
|
||||
@ -81,7 +89,11 @@
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.tag {
|
||||
font-size: 60% !important;
|
||||
}
|
||||
.box {
|
||||
border: 1px solid hsl(0, 0%, 86%);
|
||||
}
|
||||
@ -91,7 +103,6 @@
|
||||
import moment from 'moment';
|
||||
import { getMonthlyList } from '../data/http/listRequest.js';
|
||||
import FormatTime from './FormatTime.vue';
|
||||
import Loading from 'vue-loading-overlay';
|
||||
import 'vue-loading-overlay/dist/css/index.css';
|
||||
|
||||
export default {
|
||||
|
@ -23,4 +23,10 @@ export async function getFlightplansNow() {
|
||||
import.meta.env;
|
||||
const response = await get(`${VITE_API_BASE}${VITE_API_PATH_NOW_FLIGHTPLANS}`);
|
||||
return response;
|
||||
}
|
||||
export async function getWazzup() {
|
||||
const { VITE_API_BASE } =
|
||||
import.meta.env;
|
||||
const response = await get(`${VITE_API_BASE}/ivao/wazzup`);
|
||||
return response;
|
||||
}
|
82
src/helpers/MapHelper.js
Normal file
@ -0,0 +1,82 @@
|
||||
import { icon } from 'leaflet';
|
||||
const popupAnchor = [0, -30];
|
||||
export const customIcons = {
|
||||
iconHere: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/marker-here.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [21, 32],
|
||||
iconAnchor: [10, 32],
|
||||
popupAnchor,
|
||||
}),
|
||||
iconLanding: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/marker-landing.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [21, 32],
|
||||
iconAnchor: [10, 32],
|
||||
popupAnchor,
|
||||
}),
|
||||
iconTakeoff: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/marker-takeoff.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [21, 32],
|
||||
iconAnchor: [10, 32],
|
||||
popupAnchor,
|
||||
}),
|
||||
iconPlane: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/plane.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [15, 15],
|
||||
iconAnchor: [15, 15],
|
||||
popupAnchor: [0, -20],
|
||||
className: 'plane'
|
||||
}),
|
||||
ownPlane: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/plane.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [15, 15],
|
||||
iconAnchor: [15, 15],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
iconLocationRed: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/location-red.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [12, 16],
|
||||
iconAnchor: [6, 16],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
iconLocationYellow: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/location-yellow.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [12, 16],
|
||||
iconAnchor: [6, 16],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
iconLocationBlue: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/location-blue.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [12, 16],
|
||||
iconAnchor: [6, 16],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
iconLocationLightBlue: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/location-lightblue.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [12, 16],
|
||||
iconAnchor: [6, 16],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
iconLocationGreen: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/location-green.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [12, 16],
|
||||
iconAnchor: [6, 16],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
iconLocationGray: icon({
|
||||
iconUrl: new URL('/src/assets/images/map/location-gray.png',
|
||||
import.meta.url).href,
|
||||
iconSize: [12, 16],
|
||||
iconAnchor: [6, 16],
|
||||
popupAnchor: [0, -20],
|
||||
}),
|
||||
};
|
10
src/main.js
@ -6,12 +6,22 @@ import VueMobileDetection from 'vue-mobile-detection';
|
||||
|
||||
import './assets/css/main.css';
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
import { faSpinner, faPaperPlane, faDeleteLeft, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
library.add(faPaperPlane);
|
||||
library.add(faDeleteLeft);
|
||||
library.add(faXmark);
|
||||
library.add(faSpinner);
|
||||
|
||||
const pinia = createPinia();
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
app.use(VueMobileDetection);
|
||||
app.component('font-awesome-icon', FontAwesomeIcon);
|
||||
|
||||
|
||||
app.mount('#app');
|
@ -1,11 +1,21 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
|
||||
import { useSessionStore } from '../stores/session-store';
|
||||
import TableAcars from '../components/TableAcars.vue';
|
||||
import IvaoView from '../views/IvaoView.vue';
|
||||
import CabalView from '../views/CabalView.vue';
|
||||
import AdminView from '../views/AdminView.vue';
|
||||
import MapView from '../views/MapView/MapView.vue';
|
||||
|
||||
const checkRolesFn = (roles) => {
|
||||
return () => {
|
||||
const store = useSessionStore();
|
||||
return store.isAuthenticated && store.hasRoles(roles);
|
||||
}
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(
|
||||
import.meta.env.BASE_URL),
|
||||
history: createWebHashHistory(),
|
||||
routes: [{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
@ -16,11 +26,23 @@ const router = createRouter({
|
||||
name: 'acars',
|
||||
component: TableAcars,
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'admin',
|
||||
component: AdminView,
|
||||
beforeEnter: checkRolesFn(['admin']),
|
||||
},
|
||||
{
|
||||
path: '/cabal',
|
||||
name: 'cabal',
|
||||
component: CabalView,
|
||||
beforeEnter: checkRolesFn(['admin', 'cabal']),
|
||||
},
|
||||
{
|
||||
path: '/map',
|
||||
name: 'map',
|
||||
component: MapView
|
||||
}
|
||||
// {
|
||||
// path: '/about',
|
||||
// name: 'about',
|
||||
|
3
src/views/AdminView.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<h1>TODO: Admin page</h1>
|
||||
</template>
|
@ -1,8 +1,286 @@
|
||||
<script setup>
|
||||
<script>
|
||||
import SolariBoardRow from '../components/SolariBoardRow.vue';
|
||||
export default {
|
||||
mounted() {
|
||||
const sts = localStorage.getItem('defined_statuses');
|
||||
if (sts !== null) {
|
||||
this.addedStatuses = JSON.parse(sts);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
route: '',
|
||||
status: '',
|
||||
routeAlign: 'left',
|
||||
statusAlign: 'left',
|
||||
routeToSend: '',
|
||||
routeError: '',
|
||||
statusError: '',
|
||||
addingError: '',
|
||||
statusToSend: '',
|
||||
statusToAdd: '',
|
||||
charRegexp: /[a-zA-Z0-9Ññ\-:.>*¡!¿?@#]/g,
|
||||
defaultStatuses: [
|
||||
'BIENVENIDOS!',
|
||||
'BOARDING',
|
||||
'TAXI OUT',
|
||||
'TAKEOFF',
|
||||
'CLIMB',
|
||||
'DESCENT',
|
||||
'APPROACH',
|
||||
'LANDING',
|
||||
'TAXI TO GATE'
|
||||
],
|
||||
addedStatuses: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setRoute(val = '') {
|
||||
const newVal = val.trim();
|
||||
if (newVal && !this.charRegexp.test(newVal)) {
|
||||
this.routeError = 'Hay carácteres inválidos';
|
||||
return;
|
||||
}
|
||||
this.route = newVal.toUpperCase();
|
||||
this.routeError = '';
|
||||
},
|
||||
setStatus(val = '') {
|
||||
const newVal = val.trim();
|
||||
if (newVal && !this.charRegexp.test(newVal)) {
|
||||
this.statusError = 'Hay carácteres inválidos';
|
||||
return;
|
||||
}
|
||||
this.status = newVal.toUpperCase();
|
||||
this.statusError = '';
|
||||
},
|
||||
deleteStatus() {
|
||||
this.setStatus(this.statusToSend = '');
|
||||
},
|
||||
deleteRoute() {
|
||||
this.setRoute(this.routeToSend = '');
|
||||
},
|
||||
setQuickStatus(value) {
|
||||
this.setStatus(this.statusToSend = value);
|
||||
},
|
||||
addQuickStatus() {
|
||||
const newStatus = this.statusToAdd.trim();
|
||||
if (!newStatus) {
|
||||
return;
|
||||
}
|
||||
if (newStatus && !this.charRegexp.test(newStatus)) {
|
||||
this.addingError = 'Hay carácteres inválidos';
|
||||
return;
|
||||
}
|
||||
if (this.defaultStatuses.indexOf(newStatus) === -1 && this.addedStatuses.indexOf(newStatus) === -1) {
|
||||
this.addedStatuses.push(newStatus.toUpperCase());
|
||||
this.updateStatuses();
|
||||
this.statusToAdd = '';
|
||||
this.addingError = '';
|
||||
}
|
||||
},
|
||||
deleteQuickStatus(val) {
|
||||
const index = this.addedStatuses.indexOf(val);
|
||||
this.addedStatuses.splice(index, 1);
|
||||
this.updateStatuses();
|
||||
},
|
||||
updateStatuses() {
|
||||
localStorage.setItem('defined_statuses', JSON.stringify(this.addedStatuses));
|
||||
}
|
||||
},
|
||||
components: {
|
||||
SolariBoardRow
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="section">
|
||||
<h1 class="title is-2"> Capitán Cabal Hub</h1>
|
||||
<h1 class="title is-4"> Capitán Cabal Hub</h1>
|
||||
<div class="board">
|
||||
<div class="board-content">
|
||||
<h2>Ruta</h2>
|
||||
<h2>Estado</h2>
|
||||
<div class="solari-row">
|
||||
<SolariBoardRow ref="solari" :length="9" size="big" :align="routeAlign"
|
||||
:textToShow="route"></SolariBoardRow>
|
||||
</div>
|
||||
<div class="solari-row">
|
||||
<SolariBoardRow ref="solari" :length="15" :align="statusAlign" size="big"
|
||||
:textToShow="status"></SolariBoardRow>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-board">
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<p class="title is-5">Quick Statuses</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="tags">
|
||||
<span v-for="s in defaultStatuses" @click="setQuickStatus(s)" :key="s" class="tag is-rounded is-medium is-info is-light is-tag-clickable">{{ s }}</span>
|
||||
<span v-for="s in addedStatuses" @click="setQuickStatus(s)" :key="s" class="tag is-rounded is-medium is-info is-tag-clickable">{{ s }}
|
||||
<button @click.prevent.stop="deleteQuickStatus(s)" class="delete is-small"></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal mt-6">
|
||||
<div class="field-label">
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<p class="title is-6">Carácteres aceptados: (espacio)ABCDEFGHIJKLMNÑOPQRSTUVWXYZ0123456789-:.>*¡!¿?@#</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="field">Ruta</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" @keydown.enter="setRoute(routeToSend)" maxlength="9" v-model="routeToSend" type="text" placeholder="Route">
|
||||
</div>
|
||||
<p v-if="routeError" class="help is-danger">{{ routeError }}</p>
|
||||
<p v-if="!routeError" class="help">9 carácteres</p>
|
||||
</div>
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select v-model="routeAlign">
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<div class="buttons">
|
||||
<button class="button is-info" @click="setRoute(routeToSend)"><span class="icon"><font-awesome-icon :icon="['fas', 'paper-plane']" /></span></button>
|
||||
<button class="button is-danger" @click="deleteRoute"><span class="icon"><font-awesome-icon :icon="['fas', 'delete-left']" /></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="field">Estado</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" @keydown.enter="setStatus(statusToSend)" maxlength="15" v-model="statusToSend" type="text" placeholder="Status">
|
||||
</div>
|
||||
<p v-if="statusError" class="help is-danger">{{ statusError }}</p>
|
||||
<p v-if="!statusError" class="help">15 carácteres</p>
|
||||
</div>
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select v-model="statusAlign">
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<div class="buttons">
|
||||
<button class="button is-info" @click="setStatus(statusToSend)"><span class="icon"><font-awesome-icon :icon="['fas', 'paper-plane']" /></span></button>
|
||||
<button class="button is-danger" @click="deleteStatus"><span class="icon"><font-awesome-icon :icon="['fas', 'delete-left']" /></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal mt-6">
|
||||
<div class="field-label"></div>
|
||||
<div class="field-body">
|
||||
|
||||
<div class="field is-narrow">
|
||||
<div class="control">
|
||||
<input @keydown.enter="addQuickStatus" :class="{ 'is-danger': addingError }" class="input" maxlength="15" v-model="statusToAdd" type="text" placeholder="Add a Quick Status">
|
||||
</div>
|
||||
<p v-if="addingError" class="help is-danger">{{ addingError }}</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button class="button is-light is-info" @click="addQuickStatus">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-board{
|
||||
border: 1px solid #dedede;
|
||||
margin-top: 130px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.board {
|
||||
margin: 16px auto;
|
||||
width: 100%;
|
||||
background-color: #333;
|
||||
padding: 32px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.board-content{
|
||||
display: inline-grid;
|
||||
grid-template-columns: auto auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.is-tag-clickable {
|
||||
cursor: pointer;
|
||||
|
||||
&.is-info:hover {
|
||||
background-color: #3488ce;
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
&.is-light.is-info:hover {
|
||||
background-color: #e4eff9;
|
||||
border-color: transparent;
|
||||
color: #296fa8;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: #eff1ed;
|
||||
text-transform: uppercase;
|
||||
font-size: 60%;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.solari-row {
|
||||
margin-right: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
8
src/views/MapView/MapView.html
Normal file
@ -0,0 +1,8 @@
|
||||
<h1>Map</h1>
|
||||
<section class="hero">
|
||||
<div class="livemap-wrapper">
|
||||
<l-map ref="map" :zoom="zoom" :center="[51, -0.09]">
|
||||
<l-tile-layer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" layer-type="base" name="OpenStreetMap"></l-tile-layer>
|
||||
</l-map>
|
||||
</div>
|
||||
</section>
|
14
src/views/MapView/MapView.scss
Normal file
@ -0,0 +1,14 @@
|
||||
h1 {
|
||||
color: green
|
||||
}
|
||||
|
||||
.livemap-wrapper {
|
||||
// position: absolute;
|
||||
// bottom: 0;
|
||||
width: 100%;
|
||||
height: 90vh;
|
||||
}
|
||||
|
||||
img.leaflet-marker-icon.plane {
|
||||
opacity: 0.5 !important;
|
||||
}
|
97
src/views/MapView/MapView.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Map</h1>
|
||||
<div id="map" class="livemap-wrapper" ref="map"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- <template src="./MapView.html">
|
||||
</template> -->
|
||||
|
||||
<style src="./MapView.scss" lang="scss" scoped>
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import leaflet from 'leaflet'
|
||||
import 'leaflet-rotatedmarker';
|
||||
// import { LatLng, latLngBounds, latLng, point } from 'leaflet';
|
||||
import { customIcons } from '../../helpers/MapHelper';
|
||||
import { getWazzup } from '../../data/http/listRequest';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
tilesUrl: {
|
||||
type: String,
|
||||
default: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
// default: 'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
|
||||
},
|
||||
attribution: {
|
||||
type: String,
|
||||
default: '© <a href=\'https://www.openstreetmap.org/copyright\'>OpenStreetMap</a> contributors',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
zoom: 13.5,
|
||||
map: null,
|
||||
tileLayer: null,
|
||||
markers: {},
|
||||
testPilot: null,
|
||||
updateInterval: null
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.start();
|
||||
},
|
||||
unmounted() {
|
||||
clearInterval(this.updateInterval);
|
||||
},
|
||||
methods: {
|
||||
createMap() {
|
||||
this.map = leaflet.map(this.$refs.map).setView([0,0], 4);
|
||||
this.tileLayer = leaflet.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(this.map);
|
||||
},
|
||||
async start() {
|
||||
this.createMap();
|
||||
const wazzup = await getWazzup();
|
||||
const pilots= wazzup.clients.pilots;
|
||||
|
||||
const latitudes = [];
|
||||
const longitudes = [];
|
||||
|
||||
pilots.forEach(pilot => {
|
||||
if (pilot.lastTrack) {
|
||||
const { latitude, longitude, heading } = pilot.lastTrack;
|
||||
this.markers[pilot.userId] = leaflet.marker([latitude, longitude], {
|
||||
icon: customIcons.iconPlane
|
||||
}).addTo(this.map).setRotationAngle(heading);
|
||||
latitudes.push(latitude);
|
||||
longitudes.push(longitude);
|
||||
}
|
||||
});
|
||||
this.updateInterval = setInterval(this.update, 5000);
|
||||
},
|
||||
async update() {
|
||||
console.log('update');
|
||||
const wazzup = await getWazzup();
|
||||
const pilots= wazzup.clients.pilots;
|
||||
pilots.forEach(pilot => {
|
||||
if (pilot.lastTrack) {
|
||||
const { latitude, longitude, heading } = pilot.lastTrack;
|
||||
let marker = this.markers[pilot.userId];
|
||||
if (marker) {
|
||||
marker.setLatLng([latitude, longitude]).setRotationAngle(heading);
|
||||
} else {
|
||||
this.markers[pilot.userId] = leaflet.marker([latitude, longitude], {
|
||||
icon: customIcons.iconPlane
|
||||
}).addTo(this.map).setRotationAngle(heading);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|