Compare commits

..

No commits in common. "map" and "main" have entirely different histories.
map ... main

44 changed files with 35 additions and 719 deletions

114
package-lock.json generated
View File

@ -8,15 +8,11 @@
"name": "lts-stats-web", "name": "lts-stats-web",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "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", "@headlessui/vue": "^1.7.7",
"@heroicons/vue": "^2.0.13", "@heroicons/vue": "^2.0.13",
"@popperjs/core": "^2.11.6", "@popperjs/core": "^2.11.6",
"axios": "^1.2.2", "axios": "^1.2.2",
"bootstrap": "^5.2.3", "bootstrap": "^5.2.3",
"leaflet-rotatedmarker": "^0.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.29.4", "moment": "^2.29.4",
"pinia": "^2.0.29", "pinia": "^2.0.29",
@ -29,13 +25,11 @@
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.1.4", "@rushstack/eslint-patch": "^1.1.4",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue-leaflet/vue-leaflet": "^0.8.0",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"eslint": "^8.22.0", "eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"leaflet": "^1.9.3",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"sass": "^1.57.1", "sass": "^1.57.1",
"vite": "^4.0.0" "vite": "^4.0.0"
@ -427,48 +421,6 @@
"url": "https://opencollective.com/eslint" "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": { "node_modules/@headlessui/vue": {
"version": "1.7.7", "version": "1.7.7",
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.7.tgz", "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.7.tgz",
@ -715,16 +667,6 @@
"vue": "^3.2.25" "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": { "node_modules/@vue/compiler-core": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
@ -2017,17 +1959,6 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true "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": { "node_modules/levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -3292,33 +3223,6 @@
"strip-json-comments": "^3.1.1" "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": { "@headlessui/vue": {
"version": "1.7.7", "version": "1.7.7",
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.7.tgz", "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.7.tgz",
@ -3507,13 +3411,6 @@
"dev": true, "dev": true,
"requires": {} "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": { "@vue/compiler-core": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
@ -4489,17 +4386,6 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true "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": { "levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",

View File

@ -11,15 +11,11 @@
"docker-build": "docker build -t arhuako/ltsweb ." "docker-build": "docker build -t arhuako/ltsweb ."
}, },
"dependencies": { "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", "@headlessui/vue": "^1.7.7",
"@heroicons/vue": "^2.0.13", "@heroicons/vue": "^2.0.13",
"@popperjs/core": "^2.11.6", "@popperjs/core": "^2.11.6",
"axios": "^1.2.2", "axios": "^1.2.2",
"bootstrap": "^5.2.3", "bootstrap": "^5.2.3",
"leaflet-rotatedmarker": "^0.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.29.4", "moment": "^2.29.4",
"pinia": "^2.0.29", "pinia": "^2.0.29",
@ -32,13 +28,11 @@
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.1.4", "@rushstack/eslint-patch": "^1.1.4",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue-leaflet/vue-leaflet": "^0.8.0",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"eslint": "^8.22.0", "eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"leaflet": "^1.9.3",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"sass": "^1.57.1", "sass": "^1.57.1",
"vite": "^4.0.0" "vite": "^4.0.0"

View File

@ -46,9 +46,9 @@ export default {
} }
const user = await authenticate(this.username, this.password); const user = await authenticate(this.username, this.password);
this.setUser(user); this.setUser(user);
console.log('user :>> ', user);
this.username = ''; this.username = '';
this.password = ''; this.password = '';
window.location.reload();
}, },
async logout() { async logout() {
await logout(); await logout();
@ -81,9 +81,8 @@ export default {
<div class="navbar-start"> <div class="navbar-start">
<router-link class="navbar-item" to="/">ICAO</router-link> <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="/acars">Acars</router-link>
<router-link class="navbar-item" to="/map">Live Map</router-link>
<router-link v-if="hasRoles(['admin', 'cabal'])" class="navbar-item" to="/cabal">Capt Cabal</router-link> <router-link v-if="hasRoles(['cabal'])" class="navbar-item" to="/cabal">Capt Cabal</router-link>
<router-link v-if="hasRoles(['admin'])" class="navbar-item" to="/admin">Admin</router-link> <router-link v-if="hasRoles(['admin'])" class="navbar-item" to="/admin">Admin</router-link>
</div> </div>
</div> </div>
@ -130,13 +129,10 @@ export default {
.logo { .logo {
margin-right: 0.2rem; margin-right: 0.2rem;
} }
.tag {
font-size: 60% !important;
}
.input-login { .input-login {
width: 100px; width: 100px;
} }
.loading-icon {
margin-top: 24px;
color: #ddd;
width: 100%;
text-align: center;;
}
</style> </style>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1023 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -1,7 +1,7 @@
<template> <template>
<section class=""> <section class="">
<div class="solari-container"> <div class="solari-container">
<SolariBoard :headers="headers" :loading="isLoading" :config="colsConfig" :data="list" :length="18" /> <SolariBoard :headers="headers" :loading="isLoading" :config="colsConfig" :data="list" :size="18" />
</div> </div>
</section> </section>
</template> </template>
@ -31,15 +31,15 @@
colsConfig: [ colsConfig: [
{ {
align: 'left', align: 'left',
length: 8 size: 8
}, },
{ {
align: 'right', align: 'right',
length: 9 size: 9
}, },
{ {
align: 'right', align: 'right',
length: 5 size: 5
}, },
], ],
} }

View File

@ -10,7 +10,7 @@
<SolariBoardRow :ref="`line_${j - 1}`" <SolariBoardRow :ref="`line_${j - 1}`"
:textToShow="getValue(j - 1)" :textToShow="getValue(j - 1)"
:loops="getConfigValue(j - 1, 'loop')" :loops="getConfigValue(j - 1, 'loop')"
:length="getConfigValue(j - 1, 'length')" :size="getConfigValue(j - 1, 'size')"
:delay="getConfigValue(j - 1, 'delay')" :delay="getConfigValue(j - 1, 'delay')"
:align="getConfigValue(j - 1, 'align')"></SolariBoardRow> :align="getConfigValue(j - 1, 'align')"></SolariBoardRow>
</div> </div>
@ -36,7 +36,7 @@ export default {
type: Number, type: Number,
default: () => 200 default: () => 200
}, },
length: { size: {
type: Number, type: Number,
default: () => 25 default: () => 25
}, },
@ -107,7 +107,7 @@ export default {
mapRow(r, i) { mapRow(r, i) {
r.loops = (_isNil(r.loops)) ? this.loops : r.loops; r.loops = (_isNil(r.loops)) ? this.loops : r.loops;
r.delay = ((_isNil(r.delay)) ? this.delay : r.delay) * i; r.delay = ((_isNil(r.delay)) ? this.delay : r.delay) * i;
r.length = (_isNil(r.length)) ? this.length : r.length; r.size = (_isNil(r.size)) ? this.size : r.size;
r.align = (_isNil(r.align)) ? this.align : r.align; r.align = (_isNil(r.align)) ? this.align : r.align;
return r; return r;
}, },

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="board-line" :class="size"> <div class="board-line">
<div class="board-letter" :ref="'letter' + $index" :class="{'animating': isAnimating[$index]}" v-for="(char, $index) in charsToShow" :key="$index">{{ char }}</div> <div class="board-letter" :ref="'letter' + $index" :class="{'animating': isAnimating[$index]}" v-for="(char, $index) in charsToShow" :key="$index">{{ char }}</div>
</div> </div>
</template> </template>
@ -29,12 +29,6 @@
position: relative; position: relative;
line-height: 1; line-height: 1;
.big & {
font-size: 32px;
height: 38px;
width: 24px;
}
&::after { &::after {
content:' '; content:' ';
display: inline-block; display: inline-block;
@ -66,10 +60,6 @@
export default { export default {
props: { props: {
size: {
type: String,
default: () => ''
},
textToShow: { textToShow: {
type: String, type: String,
default: () => '' default: () => ''
@ -78,7 +68,7 @@
type: Number, type: Number,
default: () => 0 default: () => 0
}, },
length: { size: {
type: Number, type: Number,
}, },
loops: { loops: {
@ -91,10 +81,10 @@
} }
}, },
data() { data() {
const newArray = Array.apply(null, Array(this.length)).map(() => ' '); const newArray = Array.apply(null, Array(this.size)).map(() => ' ');
const newArrayAnimating = Array.apply(null, Array(this.length)).map(() => false); const newArrayAnimating = Array.apply(null, Array(this.size)).map(() => false);
return { return {
charsAll: ' ABCDEFGHIJKLMNÑOPQRSTUVWXYZ0123456789-:.>*¡!¿?@#', charsAll: ' ABCDEFGHIJKLMNÑOPQRSTUVWXYZ0123456789-:.>*',
charsReference: _clone(newArray), charsReference: _clone(newArray),
charsToShow: _clone(newArray), charsToShow: _clone(newArray),
isAnimating: newArrayAnimating, isAnimating: newArrayAnimating,
@ -103,7 +93,7 @@
computed: { computed: {
chars() { chars() {
const text = this.getText(this.textToShow); const text = this.getText(this.textToShow);
const chars = Array.apply(null, Array(this.length)).map(() => ' '); const chars = Array.apply(null, Array(this.size)).map(() => ' ');
const offset = (this.align === 'left') ? 0 : chars.length - text.length; const offset = (this.align === 'left') ? 0 : chars.length - text.length;
for (let index = 0; index < text.length; index++) { for (let index = 0; index < text.length; index++) {
@ -132,7 +122,7 @@
return this.charsAll.indexOf(letter); return this.charsAll.indexOf(letter);
}, },
getText(text) { getText(text) {
return (text.length > this.length) ? text.substring(0, this.length) : text; return (text.length > this.size) ? text.substring(0, this.size) : text;
}, },
async startAnimation() { async startAnimation() {
const cts = _clone(this.charsReference); const cts = _clone(this.charsReference);

View File

@ -1,4 +1,7 @@
<template> <template>
<loading v-model:active="isLoading"
:can-cancel="false"
:is-full-page="fullPage"/>
<section class="section"> <section class="section">
<h1 class="title">Pilotos activos con mas de 200 horas en ACARS</h1> <h1 class="title">Pilotos activos con mas de 200 horas en ACARS</h1>
<table class="table is-striped is-hoverable is-fullwidth"> <table class="table is-striped is-hoverable is-fullwidth">
@ -10,15 +13,6 @@
<th class="has-text-centered">Horas totales</th> <th class="has-text-centered">Horas totales</th>
</tr> </tr>
</thead> </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> <tbody>
<tr v-for="item in whitelist" :key="item.vid"> <tr v-for="item in whitelist" :key="item.vid">
<td class="has-text-centered"> <td class="has-text-centered">
@ -47,6 +41,7 @@
import moment from 'moment'; import moment from 'moment';
import { getWhitelist } from '../data/http/listRequest.js'; import { getWhitelist } from '../data/http/listRequest.js';
import FormatTime from './FormatTime.vue'; import FormatTime from './FormatTime.vue';
import Loading from 'vue-loading-overlay';
import 'vue-loading-overlay/dist/css/index.css'; import 'vue-loading-overlay/dist/css/index.css';
export default { export default {
@ -74,6 +69,7 @@
}, },
components: { components: {
FormatTime, FormatTime,
Loading,
} }
} }
</script> </script>

View File

@ -1,7 +1,8 @@
<template> <template>
<!-- <loading v-model:active="isLoading"
:can-cancel="false"
:is-full-page="fullPage"/> -->
<section class="section"> <section class="section">
<!-- <loading class="mt-6" v-model:active="isLoading"
:can-cancel="false"/> -->
<h1 class="title">Status mensual IVAO</h1> <h1 class="title">Status mensual IVAO</h1>
<table class="table is-striped is-hoverable is-fullwidth" v-if="!$isMobile()"> <table class="table is-striped is-hoverable is-fullwidth" v-if="!$isMobile()">
<thead> <thead>
@ -13,15 +14,6 @@
<th class="has-text-centered">Último vuelo</th> <th class="has-text-centered">Último vuelo</th>
</tr> </tr>
</thead> </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> <tbody>
<tr v-for="item in list" :key="item.vid"> <tr v-for="item in list" :key="item.vid">
<td class="has-text-centered"> <td class="has-text-centered">
@ -89,11 +81,7 @@
</section> </section>
</template> </template>
<style lang="scss" scoped> <style lang="scss">
.tag {
font-size: 60% !important;
}
.box { .box {
border: 1px solid hsl(0, 0%, 86%); border: 1px solid hsl(0, 0%, 86%);
} }
@ -103,6 +91,7 @@
import moment from 'moment'; import moment from 'moment';
import { getMonthlyList } from '../data/http/listRequest.js'; import { getMonthlyList } from '../data/http/listRequest.js';
import FormatTime from './FormatTime.vue'; import FormatTime from './FormatTime.vue';
import Loading from 'vue-loading-overlay';
import 'vue-loading-overlay/dist/css/index.css'; import 'vue-loading-overlay/dist/css/index.css';
export default { export default {

View File

@ -24,9 +24,3 @@ export async function getFlightplansNow() {
const response = await get(`${VITE_API_BASE}${VITE_API_PATH_NOW_FLIGHTPLANS}`); const response = await get(`${VITE_API_BASE}${VITE_API_PATH_NOW_FLIGHTPLANS}`);
return response; return response;
} }
export async function getWazzup() {
const { VITE_API_BASE } =
import.meta.env;
const response = await get(`${VITE_API_BASE}/ivao/wazzup`);
return response;
}

View File

@ -1,82 +0,0 @@
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],
}),
};

View File

@ -6,22 +6,12 @@ import VueMobileDetection from 'vue-mobile-detection';
import './assets/css/main.css'; 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 pinia = createPinia();
const app = createApp(App); const app = createApp(App);
app.use(pinia); app.use(pinia);
app.use(router); app.use(router);
app.use(VueMobileDetection); app.use(VueMobileDetection);
app.component('font-awesome-icon', FontAwesomeIcon);
app.mount('#app'); app.mount('#app');

View File

@ -1,21 +1,11 @@
import { createRouter, createWebHashHistory } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router';
import { useSessionStore } from '../stores/session-store';
import TableAcars from '../components/TableAcars.vue'; import TableAcars from '../components/TableAcars.vue';
import IvaoView from '../views/IvaoView.vue'; import IvaoView from '../views/IvaoView.vue';
import CabalView from '../views/CabalView.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({ const router = createRouter({
history: createWebHashHistory(), history: createWebHistory(
import.meta.env.BASE_URL),
routes: [{ routes: [{
path: '/', path: '/',
name: 'home', name: 'home',
@ -26,23 +16,11 @@ const router = createRouter({
name: 'acars', name: 'acars',
component: TableAcars, component: TableAcars,
}, },
{
path: '/admin',
name: 'admin',
component: AdminView,
beforeEnter: checkRolesFn(['admin']),
},
{ {
path: '/cabal', path: '/cabal',
name: 'cabal', name: 'cabal',
component: CabalView, component: CabalView,
beforeEnter: checkRolesFn(['admin', 'cabal']),
}, },
{
path: '/map',
name: 'map',
component: MapView
}
// { // {
// path: '/about', // path: '/about',
// name: 'about', // name: 'about',

View File

@ -1,3 +0,0 @@
<template>
<h1>TODO: Admin page</h1>
</template>

View File

@ -1,286 +1,8 @@
<script> <script setup>
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> </script>
<template> <template>
<main class="section"> <main class="section">
<h1 class="title is-4"> Capitán Cabal Hub</h1> <h1 class="title is-2"> 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> </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>

View File

@ -1,8 +0,0 @@
<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>

View File

@ -1,14 +0,0 @@
h1 {
color: green
}
.livemap-wrapper {
// position: absolute;
// bottom: 0;
width: 100%;
height: 90vh;
}
img.leaflet-marker-icon.plane {
opacity: 0.5 !important;
}

View File

@ -1,97 +0,0 @@
<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: '&copy; <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: '&copy; <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>