Compare commits
344 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a4304d65f | |||
| edad5306f2 | |||
| 5b775dfec9 | |||
| a485652ca5 | |||
| ed308246d7 | |||
| 621bb1ad55 | |||
| 154ae2ddac | |||
| d9da49a867 | |||
| 826d51f66c | |||
| 1d7fc2955f | |||
| c550e7598a | |||
| d5168ce59d | |||
| 380c97655c | |||
| e4ed24de80 | |||
| 8de03b9210 | |||
| 12ece46089 | |||
| 085238fada | |||
| 45c1d83512 | |||
| af9073ab98 | |||
| 800fc35e63 | |||
| d5649d221b | |||
| 5b35fac512 | |||
| d5bc90f668 | |||
| 6d663886f0 | |||
| 85a1a0216e | |||
| 4ac054e947 | |||
| ba70fa1316 | |||
| 77e6b20d0c | |||
| f60263c923 | |||
| 6aec1a75c9 | |||
| d28d600833 | |||
| a353eb3185 | |||
| c5735a6953 | |||
| 7930f7fc8a | |||
| 68f4d54619 | |||
| c4f9738589 | |||
| dd916afd1d | |||
| ea5c9e0028 | |||
| eb7c2d7132 | |||
| ee7c50f59b | |||
| 439f59fedc | |||
| c47d839ce3 | |||
| f77c13cbcf | |||
| dbbbd33100 | |||
| 14d13360a8 | |||
| dc862252ba | |||
| e5fe727ccd | |||
| e836bbed0c | |||
| d4438fd215 | |||
| 1550849360 | |||
| 9d1dc4ffca | |||
| 0397fa788d | |||
| 6e5696b0a6 | |||
| 4537341a57 | |||
| c35c74bd4a | |||
| 25735c5e6e | |||
| 41e60bc69e | |||
| 933bdecb3c | |||
| 10e183d96b | |||
| 5429d39f5e | |||
| ff31e7f903 | |||
| 91f4c6bc57 | |||
| c133eb060b | |||
| 7ffc169d8a | |||
| 1b85cc5f58 | |||
| 72ff857fff | |||
| 96d64e77fc | |||
| 6ceae3f161 | |||
| 8e8e27658c | |||
| 9b6ace394a | |||
| 6cfeaa91bf | |||
| 08b208aeaa | |||
| a089b5275b | |||
| 8425cd4371 | |||
| dbdc517b87 | |||
| e271358a27 | |||
| 66262e3fcd | |||
| 5b2b6bdea2 | |||
| c8587de6d9 | |||
| 1f376085f2 | |||
| f28600a7fa | |||
| d59ead87e6 | |||
| 34d91bc800 | |||
| cf9991d8a0 | |||
| 4ffb79d62b | |||
| d9f5edb4fe | |||
| 1b2112430a | |||
| 0a972a23ef | |||
| 6d52724d06 | |||
| 99415c35d3 | |||
| c3f687d439 | |||
| 266edfd6e6 | |||
| d32d5ad91b | |||
| c3481470cb | |||
| 57e88b9abc | |||
| 44ebf53798 | |||
| 145dc72b6b | |||
| b7f3761940 | |||
| ea7c49dfb3 | |||
| 5d6785813a | |||
| a0054aed14 | |||
| 471e6f5216 | |||
| a617eef00e | |||
| 38e700ecd6 | |||
| da1be0e10a | |||
| f49bb12948 | |||
| 02673a3d70 | |||
| 4ddc7345df | |||
| 5d822684c0 | |||
| 69fa15c70a | |||
| 9192067388 | |||
| 2b41e5b857 | |||
| 674680ff14 | |||
| 475bd2ff10 | |||
| 074d1eb155 | |||
| 378393de89 | |||
| 03e61083a7 | |||
| 0b746fce8c | |||
| 5883e710be | |||
| 3d0695a17b | |||
| 4adb76eeb0 | |||
| 4c41076519 | |||
| 77f61d17fd | |||
| 032a84cbcf | |||
| de9851ebcc | |||
| ff78eba927 | |||
| e4c5f6a322 | |||
| 0a78761928 | |||
| 4843043c29 | |||
| 9e1df1fb61 | |||
| 021474cfb0 | |||
| 7d0e68862c | |||
| 653d45dfc6 | |||
| 4a4e1240a4 | |||
| 14ca48a90d | |||
| a02f9804b1 | |||
| c5efc6fbac | |||
| cacd0a1e4e | |||
| 50375099ab | |||
| 6af67ec741 | |||
| c64112c86a | |||
| 0434702d3b | |||
| dd7d1b0bb0 | |||
| 68934a89a4 | |||
| b88a240ec1 | |||
| eaa34f3359 | |||
| febb22e1bc | |||
| 500f3c1223 | |||
| 221e0c7e82 | |||
| ca19f7e397 | |||
| a71ccd3e1a | |||
| d496c70fa8 | |||
| b9868ba52e | |||
| 59bd3fa2ef | |||
| e14d328ed9 | |||
| 36d71292bc | |||
| 2f6e2e7402 | |||
| e959eac6c5 | |||
| 8bedc4dfc6 | |||
| 73563d5db7 | |||
| 3f818069cd | |||
| cdf0b2a426 | |||
| c29ddeb78c | |||
| b81d98cab7 | |||
| 0e45bca5da | |||
| 715e66879f | |||
| 1747e15dc8 | |||
| 6a923a8e1d | |||
| 25a248e95e | |||
| aa7a6b220e | |||
| deb7b68985 | |||
| 633f05f690 | |||
| 73828867da | |||
| 75685c1e0e | |||
| 496ff95236 | |||
| 7e25327832 | |||
| 272c9f50f8 | |||
| 255e07372e | |||
| 279bbfa4db | |||
| a5c829faf5 | |||
| 5fdfaeac5e | |||
| 9beb30e3d5 | |||
| 48582e2eea | |||
| 2e721fb8bf | |||
| f93c1fbfec | |||
| c06e7b6468 | |||
| 22a6d266cb | |||
| 5f8a16401b | |||
| c9be01aa29 | |||
| 4ec058b33c | |||
| 27a5d2a406 | |||
| 58169e26f6 | |||
| fee1f4bbd5 | |||
| 240817acc3 | |||
| db3be87dd8 | |||
| 1665134d6f | |||
| df289ab734 | |||
| f74440ba6f | |||
| a25dbe9fd5 | |||
| 4fff136d6b | |||
| d06f2d5d2e | |||
| 9f68d628d0 | |||
| d64b906dac | |||
| f3e193e68a | |||
| 5640ce9f2b | |||
| 50100eb2f9 | |||
| e478c510b2 | |||
| 7ea558642f | |||
| 493145f7f2 | |||
| 4f72535365 | |||
| 8e3bf80715 | |||
| 6da586d08a | |||
| be53b9c7fb | |||
| 94ed1160a1 | |||
| 859d8d2631 | |||
| 5f3abd73c5 | |||
| d71c8bb6f9 | |||
| a3db13d79c | |||
| 8cb3da66f2 | |||
| 6e07897ac0 | |||
| 726b859f5c | |||
| 651c60707a | |||
| d4fee84603 | |||
| 86539cdf23 | |||
| 69772460b8 | |||
| 6988a83355 | |||
| b6425564c8 | |||
| caf0a9b4c5 | |||
| bd5f433d6e | |||
| 8d9cc721d6 | |||
| cceeffe49d | |||
| fcb8357489 | |||
| ceffd8e675 | |||
| 5aa53521f7 | |||
| d8b559694b | |||
| c82ac04a91 | |||
| 284bdcbf2a | |||
| 7f4df98349 | |||
| aecbcf62df | |||
| 2a817365a6 | |||
| ecf3a00cab | |||
| beb2f3c0d4 | |||
| a65b09981b | |||
| 4ec544e8a9 | |||
| 7e108c5183 | |||
| 72361b157e | |||
| 1cc4d76e4d | |||
| 846d4d0547 | |||
| 751cadd218 | |||
| 3b44adff44 | |||
| 29a02dd98f | |||
| c5e68c4d03 | |||
| 95f7c2a4d9 | |||
| 84412822ff | |||
| 42bb056e66 | |||
| 053e9d2b6a | |||
| c729d75541 | |||
| a9b72d0b7a | |||
| 95a027f284 | |||
| dbba83b28b | |||
| 65abe550f5 | |||
| 531108c25a | |||
| bcf750d451 | |||
| 0a8bfe4c52 | |||
| 0f19bc767a | |||
| 8eb0266874 | |||
| ae5b5ff965 | |||
| 3a0c4bc151 | |||
| 4f5fcb3189 | |||
| 3a2978bbe3 | |||
| a81cc4559b | |||
| 065143c359 | |||
| 1661881127 | |||
| 93aa889414 | |||
| 2a131ab1fb | |||
| 387f42985a | |||
| 6c83ce90bf | |||
| 3d519e874f | |||
| 99cdb3442a | |||
| a6c0fe86c8 | |||
| 828421efe0 | |||
| 21bacb1c95 | |||
| 0d9a3f4b4f | |||
| 76b8534d63 | |||
| 0821fd708e | |||
| b0a9939446 | |||
| 2a64b8f10d | |||
| dc1c457ea4 | |||
| 1f95bc5230 | |||
| 5a06920e5b | |||
| ee0d9e7ed4 | |||
| 30ad3ad4f2 | |||
| c2bd5a8a1b | |||
| 7101d0972d | |||
| 82bbfcdf70 | |||
| b90ac6c09e | |||
| 76d0ff88f1 | |||
| 951afcedeb | |||
| 96de3f0dcc | |||
| 03950eef66 | |||
| 6dd8cb2dad | |||
| aae51d4139 | |||
| 9994a541b1 | |||
| bc3a603ba2 | |||
| 7857377cab | |||
| 0034f43be4 | |||
| c09fc81886 | |||
| 30f72d518d | |||
| 9b86e07152 | |||
| 4e0fb5dc01 | |||
| a392991030 | |||
| ff7ca27fe6 | |||
| 94cd7aaa60 | |||
| 843289d8d7 | |||
| 66cae68e19 | |||
| b38e50396a | |||
| 7888e59117 | |||
| 46e700583d | |||
| fc56c38c45 | |||
| 9594e2c21a | |||
| a8bab5283b | |||
| 1cc799706c | |||
| 5ee8f72652 | |||
| 942f883b91 | |||
| 54b47d44e5 | |||
| f9aaf21f7a | |||
| d79705ca5c | |||
| 55c64d5f0a | |||
| 4ca1c7bb9c | |||
| abc8fda98e | |||
| aaec23d210 | |||
| 0af7b68138 | |||
| ae24eaf8e4 | |||
| f73a07daee | |||
| 89f5bf2e95 | |||
| 8137c1ff95 | |||
| 4b0d9b887e | |||
| 506064cf9a | |||
| 825e25434a | |||
| 32c601d50a | |||
| b88a96237e | |||
| 6c724440d7 | |||
| 71016e63bb | |||
| fb85352ce3 |
@@ -0,0 +1,18 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
# This file was auto-generated by the Firebase CLI
|
||||
# https://github.com/firebase/firebase-tools
|
||||
|
||||
name: Deploy to Firebase Hosting on PR
|
||||
'on': pull_request
|
||||
jobs:
|
||||
@@ -11,4 +14,4 @@ jobs:
|
||||
with:
|
||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_STACJOWNIK_TD2 }}'
|
||||
projectId: stacjownik-td2
|
||||
projectId: stacjownik-td2
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dev-dist
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
@@ -30,6 +31,7 @@ node_modules
|
||||
.firebase
|
||||
.firebaserc
|
||||
|
||||
# Env
|
||||
.env
|
||||
|
||||
.fake
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"hosting": {
|
||||
"public": "dist",
|
||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||
"ignore": [
|
||||
"firebase.json",
|
||||
"**/.*",
|
||||
"**/node_modules/**"
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "**",
|
||||
@@ -10,4 +14,3 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
|
||||
<meta name="keywords" content="Stacjownik, TD2, Train Driver 2, stacjownik-td2, stacjownik, td2.info.pl" />
|
||||
<meta name="description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
|
||||
|
||||
<title>Stacjownik</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta name="theme-color" content="#222222" />
|
||||
|
||||
<link rel="icon" href="favicon-64.png" sizes="64x64" type="image/png" />
|
||||
<link rel="icon" href="favicon-62.png" sizes="62x62" type="image/png" />
|
||||
<link rel="icon" href="favicon-32.png" sizes="32x32" type="image/png" />
|
||||
<link rel="icon" href="favicon-16.png" sizes="16x16" type="image/png" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
|
||||
<!-- Static OpenGraph meta -->
|
||||
<meta name="description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
|
||||
<meta property="og:url" content="https://stacjownik-td2.web.app/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="Stacjownik" />
|
||||
<meta property="og:description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
|
||||
<meta property="og:image" content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:site_name" content="Stacjownik" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Stacjownik" />
|
||||
<meta name="twitter:description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
|
||||
<meta name="twitter:image" content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg" />
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,37 +1,44 @@
|
||||
{
|
||||
"name": "stacjownik",
|
||||
"version": "1.9.9",
|
||||
"version": "1.18.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"deploy-prod": "npm run build && firebase deploy --only hosting:prod",
|
||||
"deploy-dev": "npm run build && firebase deploy --only hosting:dev"
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"deploy": "yarn build && firebase deploy --only hosting",
|
||||
"preview": "yarn build && vite preview",
|
||||
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.12.1",
|
||||
"dotenv": "^8.6.0",
|
||||
"firebase": "^9.8.1",
|
||||
"howler": "^2.2.1",
|
||||
"pinia": "^2.0.14",
|
||||
"socket.io-client": "^4.4.1",
|
||||
"vue": "^3.2.34",
|
||||
"vue-i18n": "^9.1.6",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vuex": "^4.0.2"
|
||||
"core-js": "^3.32.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"firebase": "^10.4.0",
|
||||
"howler": "^2.2.4",
|
||||
"pinia": "^2.1.6",
|
||||
"sass": "^1.67.0",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.4.1",
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.35",
|
||||
"@vue/cli-plugin-babel": "^5.0.4",
|
||||
"@vue/cli-plugin-router": "^5.0.4",
|
||||
"@vue/cli-plugin-typescript": "^5.0.4",
|
||||
"@vue/cli-plugin-vuex": "^5.0.4",
|
||||
"@vue/cli-service": "^5.0.4",
|
||||
"@vue/compiler-sfc": "^3.1.0",
|
||||
"axios": "^0.21.1",
|
||||
"sass": "^1.32.13",
|
||||
"sass-loader": "^8.0.2",
|
||||
"typescript": "^4.7.3"
|
||||
"@types/node": "^20.6.2",
|
||||
"@vite-pwa/assets-generator": "^0.0.10",
|
||||
"@vitejs/plugin-vue": "^4.3.4",
|
||||
"axios": "^1.5.0",
|
||||
"prettier": "^3.0.3",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-pwa": "^0.16.5",
|
||||
"vue-tsc": "^1.8.11",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/tsconfig": "^0.4.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"@rushstack/eslint-patch": "^1.3.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
||||
|
After Width: | Height: | Size: 951 B |
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 363 B |
|
Before Width: | Height: | Size: 537 B After Width: | Height: | Size: 537 B |
|
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
|
Before Width: | Height: | Size: 271 B After Width: | Height: | Size: 271 B |
|
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
|
Before Width: | Height: | Size: 212 B After Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 932 B |
|
After Width: | Height: | Size: 953 B |
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
|
Before Width: | Height: | Size: 582 B After Width: | Height: | Size: 582 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 256 B After Width: | Height: | Size: 256 B |
|
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
@@ -0,0 +1,20 @@
|
||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="-0.00012207" width="60" height="60" fill="#898989"/>
|
||||
<path d="M29.0126 32.4897V10.2511V9.52028H30.4337V10.2511V57.234H29.0126V32.4897Z" fill="#BFBFBF"/>
|
||||
<path d="M26.955 29.3992V32.9949L29.7672 36.9105" stroke="black" stroke-width="0.61183"/>
|
||||
<rect x="29.0051" y="34.0686" width="1.42857" height="22.8196" fill="white"/>
|
||||
<rect x="29.0051" y="34.0686" width="1.42857" height="5.18627" fill="#FF0000"/>
|
||||
<rect x="29.0051" y="54.8137" width="1.42857" height="5.18627" fill="#FF0000"/>
|
||||
<rect x="29.0051" y="44.4412" width="1.42857" height="5.18627" fill="#FF0000"/>
|
||||
<rect x="27.8749" y="31.8649" width="3.75" height="2.17823" fill="white"/>
|
||||
<path d="M33.5 28.5111V8.61545V8.11176H26V28.5111H33.5Z" fill="black"/>
|
||||
<path d="M29.6364 5.00276C27.1289 5.09112 26.2239 7.044 26 8.11176H33.5C33.3134 6.97036 32.1438 4.91439 29.6364 5.00276Z" fill="black"/>
|
||||
<path d="M29.8636 31.6201C32.3711 31.5317 33.2761 29.5789 33.5 28.5111H26C26.1865 29.6525 27.3561 31.7085 29.8636 31.6201Z" fill="black"/>
|
||||
<ellipse cx="29.887" cy="11.8168" rx="1.38696" ry="1.28474" fill="#212121"/>
|
||||
<ellipse cx="29.887" cy="8.0135" rx="1.38696" ry="1.28474" fill="#212121"/>
|
||||
<ellipse cx="29.887" cy="15.6151" rx="1.38696" ry="1.28474" fill="#212121"/>
|
||||
<ellipse cx="29.887" cy="19.6834" rx="1.38696" ry="1.28474" fill="#212121"/>
|
||||
<ellipse cx="29.887" cy="23.7518" rx="1.38696" ry="1.28474" fill="#212121"/>
|
||||
<ellipse cx="29.887" cy="27.8201" rx="1.38696" ry="1.28474" fill="#00FF0A"/>
|
||||
<ellipse cx="29.887" cy="19.769" rx="1.38696" ry="1.28474" fill="#00FF0A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 632 B After Width: | Height: | Size: 632 B |
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 398 B |
|
Before Width: | Height: | Size: 938 B After Width: | Height: | Size: 938 B |
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 356 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1020 B |
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 369 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
|
Before Width: | Height: | Size: 199 B After Width: | Height: | Size: 199 B |
|
Before Width: | Height: | Size: 384 B After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 270 B |
@@ -0,0 +1,18 @@
|
||||
<svg width="144" height="147" viewBox="0 0 144 147" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_1343_19)">
|
||||
<path d="M115.039 101.247C116.397 98.6665 115.405 95.4739 112.824 94.1167C110.243 92.7594 107.05 93.7514 105.693 96.3323L115.039 101.247ZM89.4447 44.0402L94.1179 46.4977L99.0329 37.1513L94.3597 34.6938L89.4447 44.0402ZM105.693 96.3323C95.7398 115.259 72.3278 122.534 53.4008 112.581L48.4858 121.927C72.5746 134.595 102.372 125.336 115.039 101.247L105.693 96.3323ZM53.4008 112.581C34.4739 102.627 27.1993 79.2155 37.1525 60.2885L27.8061 55.3735C15.1383 79.4623 24.397 109.259 48.4858 121.927L53.4008 112.581ZM37.1525 60.2885C47.1057 41.3616 70.5177 34.087 89.4447 44.0402L94.3597 34.6938C70.2709 22.026 40.4738 31.2846 27.8061 55.3735L37.1525 60.2885Z" fill="white"/>
|
||||
<path d="M91.2258 38.7627L101.056 20.0698L116.15 51.8695L81.3956 57.4555L91.2258 38.7627Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_1343_19" x="18.1328" y="20.0698" width="102.017" height="115.531" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1343_19"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1343_19" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 476 B |
|
Before Width: | Height: | Size: 863 B After Width: | Height: | Size: 863 B |
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 304 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg enable-background="new 0 0 32 32" id="Glyph" version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M27.414,24.586l-5.077-5.077C23.386,17.928,24,16.035,24,14c0-5.514-4.486-10-10-10S4,8.486,4,14 s4.486,10,10,10c2.035,0,3.928-0.614,5.509-1.663l5.077,5.077c0.78,0.781,2.048,0.781,2.828,0 C28.195,26.633,28.195,25.367,27.414,24.586z M7,14c0-3.86,3.14-7,7-7s7,3.14,7,7s-3.14,7-7,7S7,17.86,7,14z" id="XMLID_223_" fill="white" /></svg>
|
||||
|
After Width: | Height: | Size: 546 B |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 199 B After Width: | Height: | Size: 199 B |
|
Before Width: | Height: | Size: 522 B After Width: | Height: | Size: 522 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 268 B |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 478 B After Width: | Height: | Size: 478 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#F2E147"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#66FF6C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#898989"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
|
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 285 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,54 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
|
||||
<meta name="keywords" content="Stacjownik, TD2, Train Driver 2, stacjownik-td2" />
|
||||
<meta name="description" content="Automatycznie odświeżana strona wyświetlająca stacje w Train Driver 2!" />
|
||||
|
||||
<title>Stacjownik</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="favicon-64.png" sizes="64x64" type="image/png" />
|
||||
<link rel="icon" href="favicon-32.png" sizes="32x32" type="image/png" />
|
||||
<link rel="icon" href="favicon-62.png" sizes="62x62" type="image/png" />
|
||||
<link rel="icon" href="favicon-16.png" sizes="16x16" type="image/png" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;700&display=swap" rel="stylesheet" />
|
||||
|
||||
<script src="https://www.gstatic.com/firebasejs/8.1.1/firebase-app.js"></script>
|
||||
|
||||
<script>
|
||||
const firebaseConfig = {
|
||||
apiKey: 'AIzaSyBI36X2-p7vU1flxoJdCEc0noByyTe1mpw',
|
||||
authDomain: 'stacjownik-td2.firebaseapp.com',
|
||||
databaseURL: 'https://stacjownik-td2.firebaseio.com',
|
||||
projectId: 'stacjownik-td2',
|
||||
storageBucket: 'stacjownik-td2.appspot.com',
|
||||
};
|
||||
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please
|
||||
enable it to continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"name": "Stacjownik TD2",
|
||||
"short_name": "Stacjownik",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
@@ -13,7 +13,8 @@
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
"theme_color": "#ffc014",
|
||||
"background_color": "#4d4d4d",
|
||||
"display": "standalone",
|
||||
"start_url": "."
|
||||
}
|
||||
|
||||
@@ -1,240 +1,106 @@
|
||||
@import './styles/responsive.scss';
|
||||
@import './styles/variables.scss';
|
||||
@import './styles/global.scss';
|
||||
@import './styles/scenery_status.scss';
|
||||
|
||||
// VUE ROUTE CHANGE ANIMATION
|
||||
.view-anim {
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0.02;
|
||||
}
|
||||
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all $animDuration $animType;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.route {
|
||||
margin: 0 0.2em;
|
||||
|
||||
&-active {
|
||||
color: $accentCol;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// APP
|
||||
.app {
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
|
||||
@include smallScreen() {
|
||||
font-size: calc(0.4rem + 1.4vw);
|
||||
}
|
||||
}
|
||||
|
||||
// CONTAINER
|
||||
.app_container {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100vh;
|
||||
min-height: 800px;
|
||||
|
||||
header {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1 1 auto;
|
||||
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
footer {
|
||||
flex: 0 1 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: firebrick;
|
||||
text-align: center;
|
||||
padding: 0.5em 0.4em;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
|
||||
border-radius: 0 0 1em 1em;
|
||||
}
|
||||
|
||||
// Error icon
|
||||
.wip-alert {
|
||||
padding: 0 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon-error {
|
||||
width: 13em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
// HEADER
|
||||
.app_header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
position: relative;
|
||||
background-color: $primaryCol;
|
||||
}
|
||||
|
||||
.header {
|
||||
&_body {
|
||||
max-width: 21em;
|
||||
}
|
||||
|
||||
&_container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
width: 1350px;
|
||||
padding: 0.5em 0.3em 0 0.3em;
|
||||
border-radius: 0 0 1em 1em;
|
||||
}
|
||||
|
||||
&_brand {
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&_info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
max-width: 100%;
|
||||
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
&_links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
border-radius: 0.7em;
|
||||
|
||||
font-size: 1.25em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
&_icons {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
padding: 0.5em 0.5em;
|
||||
|
||||
@include smallScreen() {
|
||||
right: auto;
|
||||
left: 0.75em;
|
||||
padding: 0;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ICONS
|
||||
.icons {
|
||||
position: relative;
|
||||
|
||||
&-top {
|
||||
img {
|
||||
width: 2.5em;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
&-bottom {
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
margin-left: 0.6em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1.9em;
|
||||
}
|
||||
|
||||
@include smallScreen() {
|
||||
flex-direction: column;
|
||||
|
||||
a {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// COUNTER
|
||||
.info_counter {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
margin: 0 0.15em;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1.35em;
|
||||
}
|
||||
}
|
||||
|
||||
// REGION SELECTION
|
||||
.info_region {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.select-box_content button {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
padding: 0.1em 0.5em;
|
||||
color: paleturquoise;
|
||||
|
||||
}
|
||||
|
||||
.options {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// FOOTER
|
||||
footer.app_footer {
|
||||
max-width: 100%;
|
||||
padding: 0.5em;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
background: #111;
|
||||
color: white;
|
||||
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@import './styles/responsive.scss';
|
||||
@import './styles/variables.scss';
|
||||
@import './styles/global.scss';
|
||||
|
||||
// VUE ROUTE CHANGE ANIMATION
|
||||
.view-anim {
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0.02;
|
||||
}
|
||||
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all $animDuration $animType;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all $animDuration $animType;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
transform: translateY(-25%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.route {
|
||||
margin: 0 0.2em;
|
||||
|
||||
&-active,
|
||||
&[data-active='true'] {
|
||||
color: $accentCol;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// APP
|
||||
#app {
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
overflow-x: hidden;
|
||||
|
||||
@include smallScreen() {
|
||||
font-size: calc(0.65rem + 0.8vw);
|
||||
}
|
||||
|
||||
@include screenLandscape() {
|
||||
font-size: calc(0.45rem + 0.8vw);
|
||||
}
|
||||
}
|
||||
|
||||
// CONTAINER
|
||||
.app_container {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
min-height: 100vh;
|
||||
|
||||
header {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1 1 auto;
|
||||
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
footer {
|
||||
flex: 0 1 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: firebrick;
|
||||
text-align: center;
|
||||
padding: 0.5em 0.4em;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
|
||||
border-radius: 0 0 1em 1em;
|
||||
}
|
||||
|
||||
// FOOTER
|
||||
footer.app_footer {
|
||||
max-width: 100%;
|
||||
padding: 0.5em;
|
||||
|
||||
img {
|
||||
width: 1.1em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
z-index: 10;
|
||||
|
||||
background: #111;
|
||||
color: white;
|
||||
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -1,227 +1,162 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<div class="app_container">
|
||||
<!-- <div class="wip-alert">
|
||||
<img class="icon-error" :src="iconError" alt="error" />
|
||||
<h2>Stacjownik tymczasowo nieaktywny!</h2>
|
||||
<p>Absolutny zakaz wjazdu!</p>
|
||||
</div> -->
|
||||
<header class="app_header">
|
||||
<div class="header_container">
|
||||
<div class="header_icons">
|
||||
<span class="icons-top">
|
||||
<img :src="icons.pl" alt="icon-pl" @click="changeLang('en')" v-if="currentLang == 'pl'" />
|
||||
<img :src="icons.en" alt="icon-en" @click="changeLang('pl')" v-else />
|
||||
</span>
|
||||
<span class="icons-bottom">
|
||||
<a href="https://www.paypal.com/paypalme/spythere" target="_blank">
|
||||
<img :src="icons.dollar" alt="icon paypal" />
|
||||
</a>
|
||||
|
||||
<a href="https://discord.gg/x2mpNN3svk" target="_blank">
|
||||
<img :src="icons.discord" alt="icon discord" />
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="header_body">
|
||||
<status-indicator />
|
||||
<span class="header_brand">
|
||||
<img :src="brand_logo" alt="Stacjownik" />
|
||||
</span>
|
||||
|
||||
<span class="header_info">
|
||||
<Clock />
|
||||
|
||||
<div class="info_counter">
|
||||
<img src="@/assets/icon-dispatcher.svg" alt="icon dispatcher" />
|
||||
<span class="text--primary">{{ onlineDispatchers.length }}</span>
|
||||
<span class="text--grayed"> / </span>
|
||||
<span class="text--primary">{{ trainList.length }}</span>
|
||||
<img src="@/assets/icon-train.svg" alt="icon train" />
|
||||
</div>
|
||||
|
||||
<span class="info_region">
|
||||
<SelectBox :itemList="computedRegions" :defaultItemIndex="0" @selected="changeRegion" />
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="header_links">
|
||||
<router-link class="route" active-class="route-active" to="/" exact>
|
||||
{{ $t('app.sceneries') }}
|
||||
</router-link>
|
||||
/
|
||||
<router-link class="route" active-class="route-active" to="/trains">{{ $t('app.trains') }}</router-link>
|
||||
/
|
||||
<router-link class="route" active-class="route-active" to="/journal">
|
||||
{{ $t('app.journal') }}
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="app_main">
|
||||
<router-view v-slot="{ Component }">
|
||||
<!-- <transition name="view-anim" mode="out-in"> -->
|
||||
<keep-alive>
|
||||
<component :is="Component" :key="$route.path" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</main>
|
||||
|
||||
<footer class="app_footer">
|
||||
©
|
||||
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
||||
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}
|
||||
|
||||
<div style="display: none">∫ ukryta taktyczna całka do programowania w HTMLu</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, provide, ref } from 'vue';
|
||||
|
||||
import Clock from '@/components/App/Clock.vue';
|
||||
import StorageManager from '@/scripts/managers/storageManager';
|
||||
|
||||
import packageInfo from '.././package.json';
|
||||
import options from '@/data/options.json';
|
||||
|
||||
import StatusIndicator from '@/components/App/StatusIndicator.vue';
|
||||
import SelectBox from '@/components/Global/SelectBox.vue';
|
||||
import { useStore } from './store/store';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Clock,
|
||||
StatusIndicator,
|
||||
SelectBox,
|
||||
},
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
store.connectToAPI();
|
||||
|
||||
const isFilterCardVisible = ref(false);
|
||||
|
||||
provide('isFilterCardVisible', isFilterCardVisible);
|
||||
|
||||
return {
|
||||
store,
|
||||
isFilterCardVisible,
|
||||
onlineDispatchers: computed(() =>
|
||||
store.stationList.filter((station) => station.onlineInfo && station.onlineInfo.region == store.region.id)
|
||||
),
|
||||
|
||||
dispatcherDataStatus: store.dataStatuses.dispatchers,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
trainList() {
|
||||
return this.store.trainList.filter((train) => train.online);
|
||||
},
|
||||
|
||||
computedRegions() {
|
||||
return this.options.regions.map((region) => {
|
||||
const regionStationCount =
|
||||
this.store.apiData.stations?.filter((station) => station.region == region.id && station.isOnline).length || 0;
|
||||
const regionTrainCount = this.store.apiData.trains?.filter((train) => train.region == region.id && train.online).length || 0;
|
||||
|
||||
return {
|
||||
id: region.id,
|
||||
value: `${region.value} <div class='text--grayed'>${regionStationCount} / ${regionTrainCount}</div>`,
|
||||
selectedValue: region.value,
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
VERSION: packageInfo.version,
|
||||
updateModalVisible: false,
|
||||
hasReleaseNotes: false,
|
||||
options,
|
||||
|
||||
currentLang: 'pl',
|
||||
|
||||
brand_logo: require('@/assets/stacjownik-header-logo.svg'),
|
||||
|
||||
icons: {
|
||||
en: require('@/assets/icon-en.jpg'),
|
||||
pl: require('@/assets/icon-pl.svg'),
|
||||
error: require('@/assets/icon-error.svg'),
|
||||
dollar: require('@/assets/icon-dollar.svg'),
|
||||
dispatcher: require('@/assets/icon-dispatcher.svg'),
|
||||
train: require('@/assets/icon-train.svg'),
|
||||
discord: require('@/assets/icon-discord.png'),
|
||||
},
|
||||
}),
|
||||
|
||||
created() {
|
||||
this.loadLang();
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
if (StorageManager.getStringValue('version') != this.VERSION) {
|
||||
StorageManager.setStringValue('version', this.VERSION);
|
||||
|
||||
if (this.hasReleaseNotes) StorageManager.setBooleanValue('version_notes_read', false);
|
||||
}
|
||||
|
||||
this.updateModalVisible = this.hasReleaseNotes && !StorageManager.getBooleanValue('version_notes_read');
|
||||
|
||||
this.updateToNewestVersion();
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleUpdateModal() {
|
||||
this.updateModalVisible = !this.updateModalVisible;
|
||||
StorageManager.setBooleanValue('version_notes_read', true);
|
||||
},
|
||||
|
||||
changeRegion(region: { id: string; value: string }) {
|
||||
this.store.changeRegion(region);
|
||||
},
|
||||
|
||||
changeLang(lang: string) {
|
||||
this.$i18n.locale = lang;
|
||||
this.currentLang = lang;
|
||||
|
||||
StorageManager.setStringValue('lang', lang);
|
||||
},
|
||||
|
||||
updateToNewestVersion() {
|
||||
if (!StorageManager.isRegistered('unavailable-status')) {
|
||||
StorageManager.setBooleanValue('unavailable-status', true);
|
||||
StorageManager.setBooleanValue('ending-status', true);
|
||||
StorageManager.setBooleanValue('no-space-status', true);
|
||||
StorageManager.setBooleanValue('afk-status', true);
|
||||
}
|
||||
},
|
||||
|
||||
loadLang() {
|
||||
const storageLang = StorageManager.getStringValue('lang');
|
||||
|
||||
if (storageLang) {
|
||||
this.changeLang(storageLang);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.navigator.language) return;
|
||||
|
||||
const naviLanguage = window.navigator.language.toString();
|
||||
|
||||
if (naviLanguage.includes('en')) {
|
||||
this.changeLang('en');
|
||||
return;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" src="./App.scss"></style>
|
||||
<template>
|
||||
<div class="app_container">
|
||||
<transition name="modal-anim">
|
||||
<keep-alive>
|
||||
<TrainModal v-if="store.chosenModalTrainId" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
|
||||
<AppHeader :current-lang="currentLang" @change-lang="changeLang" />
|
||||
|
||||
<main class="app_main">
|
||||
<router-view v-slot="{ Component }">
|
||||
<keep-alive exclude="JournalView">
|
||||
<component :is="Component" :key="$route.name" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</main>
|
||||
|
||||
<footer class="app_footer">
|
||||
©
|
||||
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
||||
{{ new Date().getUTCFullYear() }} |
|
||||
<a :href="releaseURL" target="_blank">v{{ VERSION }}{{ isOnProductionHost ? '' : 'dev' }}</a>
|
||||
<br />
|
||||
<a href="https://discord.gg/x2mpNN3svk">
|
||||
<img src="/images/icon-discord.png" alt="" /> <b>{{ $t('footer.discord') }}</b>
|
||||
</a>
|
||||
|
||||
<div style="display: none">∫ ukryta taktyczna całka do programowania w HTMLu</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch } from 'vue';
|
||||
|
||||
import Clock from './components/App/Clock.vue';
|
||||
|
||||
import packageInfo from '.././package.json';
|
||||
import { regions } from './data/options.json';
|
||||
|
||||
import { useStore } from './store/store';
|
||||
import StatusIndicator from './components/App/StatusIndicator.vue';
|
||||
import TrainModal from './components/Global/TrainModal.vue';
|
||||
import StorageManager from './scripts/managers/storageManager';
|
||||
import AppHeader from './components/App/AppHeader.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Clock,
|
||||
StatusIndicator,
|
||||
TrainModal,
|
||||
AppHeader
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
VERSION: packageInfo.version,
|
||||
store: useStore(),
|
||||
|
||||
currentLang: 'pl',
|
||||
releaseURL: '',
|
||||
isOnProductionHost: location.hostname == 'stacjownik-td2.web.app'
|
||||
}),
|
||||
|
||||
created() {
|
||||
this.loadLang();
|
||||
this.store.connectToAPI();
|
||||
|
||||
this.store.isOffline = !window.navigator.onLine;
|
||||
|
||||
window.addEventListener('offline', () => {
|
||||
this.store.isOffline = true;
|
||||
|
||||
this.store.apiData = {
|
||||
stations: [],
|
||||
dispatchers: [],
|
||||
trains: [],
|
||||
connectedSocketCount: 0
|
||||
};
|
||||
|
||||
this.store.setStatuses();
|
||||
});
|
||||
|
||||
window.addEventListener('online', () => {
|
||||
this.store.isOffline = false;
|
||||
});
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.setReleaseURL();
|
||||
|
||||
watch(
|
||||
() => this.store.blockScroll,
|
||||
(value) => {
|
||||
if (value) document.body.classList.add('no-scroll');
|
||||
else document.body.classList.remove('no-scroll');
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
watch: {
|
||||
'$route.query.region': {
|
||||
immediate: true,
|
||||
handler(regionQuery: string) {
|
||||
if (regionQuery) {
|
||||
this.store.region.id =
|
||||
regions.find(
|
||||
(reg) =>
|
||||
reg.id == regionQuery.toLocaleLowerCase() ||
|
||||
reg.value.toLocaleLowerCase() == regionQuery.toLocaleLowerCase()
|
||||
)?.id || 'eu';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeLang(lang: string) {
|
||||
this.$i18n.locale = lang;
|
||||
this.currentLang = lang;
|
||||
|
||||
StorageManager.setStringValue('lang', lang);
|
||||
},
|
||||
|
||||
async setReleaseURL() {
|
||||
try {
|
||||
const releaseData = await (
|
||||
await axios.get('https://api.github.com/repos/Spythere/stacjownik/releases/latest')
|
||||
).data;
|
||||
|
||||
if (!releaseData) return;
|
||||
|
||||
this.releaseURL = releaseData.html_url;
|
||||
} catch (error) {
|
||||
console.error(`Wystąpił błąd podczas pobierania danych z API GitHuba: ${error}`);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
loadLang() {
|
||||
const storageLang = StorageManager.getStringValue('lang');
|
||||
|
||||
if (storageLang) {
|
||||
this.changeLang(storageLang);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.navigator.language) return;
|
||||
|
||||
const naviLanguage = window.navigator.language.toString();
|
||||
|
||||
if (naviLanguage.includes('en')) {
|
||||
this.changeLang('en');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" src="./App.scss"></style>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="60" height="60" fill="#898989"/>
|
||||
<path d="M30.5 6.04878H35.2195" stroke="#BFBFBF"/>
|
||||
<path d="M27.9024 4.00303C25.2115 4.10008 24.2403 6.24494 24 7.41767H32.0488C31.8486 6.16406 30.5934 3.90598 27.9024 4.00303Z" fill="black"/>
|
||||
<path d="M33.0244 29.6688V5.47793V4.68292H34.4878V5.47793V56.5854H33.0244V32.5H27.5V28.5V28.0163L28.5 28V31.5L31.9268 31.5447H33.0244V29.6688Z" fill="#BFBFBF"/>
|
||||
<path d="M28.1463 29.2683C30.8373 29.1712 31.8085 27.0264 32.0488 25.8537H24C24.2002 27.1073 25.4554 29.3654 28.1463 29.2683Z" fill="black"/>
|
||||
<path d="M32.0488 25.8537V7.86993V7.41464H24V25.8537H32.0488Z" fill="black"/>
|
||||
<path d="M25 26V29.5L33.8781 44.9756" stroke="black"/>
|
||||
<rect x="33.0244" y="31.5447" width="1.46341" height="25.0407" fill="white"/>
|
||||
<rect x="33.0244" y="31.5447" width="1.46341" height="5.69106" fill="#FF0000"/>
|
||||
<rect x="33.0244" y="42.9268" width="1.46341" height="5.69106" fill="#FF0000"/>
|
||||
<rect x="33.0244" y="54.3089" width="1.46341" height="5.69106" fill="#FF0000"/>
|
||||
<ellipse cx="27.9024" cy="7.40022" rx="1.46341" ry="1.40022" fill="#212121"/>
|
||||
<ellipse cx="27.9024" cy="11.8343" rx="1.46341" ry="1.40022" fill="#212121"/>
|
||||
<ellipse cx="27.9024" cy="16.2683" rx="1.46341" ry="1.40022" fill="#FF0000"/>
|
||||
<ellipse cx="27.9024" cy="20.7023" rx="1.46341" ry="1.40022" fill="#212121"/>
|
||||
<ellipse cx="27.9024" cy="25.1364" rx="1.46341" ry="1.40022" fill="#212121"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<header class="app_header">
|
||||
<div class="header_container">
|
||||
<div class="header_icons">
|
||||
<span class="icons-top">
|
||||
<img
|
||||
src="/images/icon-pl.svg"
|
||||
alt="icon-pl"
|
||||
@click="changeLang('en')"
|
||||
v-if="currentLang == 'pl'"
|
||||
/>
|
||||
<img src="/images/icon-en.jpg" alt="icon-en" @click="changeLang('pl')" v-else />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="header_body">
|
||||
<StatusIndicator />
|
||||
|
||||
<span class="header_brand">
|
||||
<router-link to="/">
|
||||
<img src="/images/stacjownik-header-logo.svg" alt="Stacjownik" />
|
||||
</router-link>
|
||||
</span>
|
||||
|
||||
<span class="header_info">
|
||||
<Clock />
|
||||
|
||||
<div class="info_counter">
|
||||
<img src="/images/icon-dispatcher.svg" alt="icon dispatcher" />
|
||||
<span class="text--primary">{{ onlineDispatchersCount }}</span>
|
||||
|
||||
<!-- <span class="g-tooltip">
|
||||
<b class="text--primary">{{ factorU }}U</b>
|
||||
<div class="content">Test</div>
|
||||
</span> -->
|
||||
|
||||
<span class="text--grayed"> / </span>
|
||||
<span class="text--primary">{{ onlineTrainsCount }}</span>
|
||||
<img src="/images/icon-train.svg" alt="icon train" />
|
||||
</div>
|
||||
|
||||
<div class="info_region">
|
||||
<RegionDropdown />
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<span class="header_links">
|
||||
<router-link class="route" active-class="route-active" to="/" exact>
|
||||
{{ $t('app.sceneries') }}
|
||||
</router-link>
|
||||
/
|
||||
<router-link class="route" active-class="route-active" to="/trains">{{
|
||||
$t('app.trains')
|
||||
}}</router-link>
|
||||
/
|
||||
<router-link
|
||||
class="route"
|
||||
active-class="route-active"
|
||||
:data-active="$route.path.startsWith('/journal')"
|
||||
to="/journal"
|
||||
>
|
||||
{{ $t('app.journal') }}
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../../store/store';
|
||||
import StatusIndicator from './StatusIndicator.vue';
|
||||
import Clock from './Clock.vue';
|
||||
import RegionDropdown from '../Global/RegionDropdown.vue';
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['changeLang'],
|
||||
props: {
|
||||
currentLang: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeLang(lang: string) {
|
||||
this.$emit('changeLang', lang);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
onlineTrainsCount() {
|
||||
return this.store.trainList.filter((train) => train.online).length;
|
||||
},
|
||||
|
||||
onlineDispatchersCount() {
|
||||
return this.store.onlineSceneryList.length;
|
||||
},
|
||||
|
||||
factorU() {
|
||||
return this.onlineDispatchersCount == 0
|
||||
? '-'
|
||||
: (this.onlineTrainsCount / this.onlineDispatchersCount).toFixed(2);
|
||||
}
|
||||
},
|
||||
components: { StatusIndicator, Clock, RegionDropdown }
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/variables.scss';
|
||||
@import '../../styles/responsive.scss';
|
||||
|
||||
// HEADER
|
||||
.app_header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
position: relative;
|
||||
background-color: $primaryCol;
|
||||
}
|
||||
|
||||
.header {
|
||||
&_body {
|
||||
position: relative;
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
&_container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
border-radius: 0 0 1em 1em;
|
||||
|
||||
@include smallScreen {
|
||||
position: relative;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&_brand {
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&_info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
&_links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
border-radius: 0.7em;
|
||||
|
||||
font-size: 1.25em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
&_icons {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
padding: 0.5em;
|
||||
|
||||
@include smallScreen {
|
||||
transform: translateX(85%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ICONS
|
||||
.icons-top {
|
||||
img {
|
||||
width: 2.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// COUNTER
|
||||
.info_counter {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
margin: 0 0.15em;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1.35em;
|
||||
}
|
||||
}
|
||||
|
||||
.info_region {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -1,36 +1,37 @@
|
||||
<template>
|
||||
<div class="clock">{{ computedDate }}</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from "vue";
|
||||
export default defineComponent({
|
||||
name: "clock",
|
||||
data: () => ({
|
||||
timestamp: Date.now(),
|
||||
}),
|
||||
setup() {
|
||||
let timestamp = ref(Date.now());
|
||||
|
||||
const computedDate = computed(() => new Date(timestamp.value).toLocaleString("pl-PL", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}));
|
||||
|
||||
setInterval(() => (timestamp.value = Date.now()), 1000);
|
||||
|
||||
return { computedDate }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../styles/responsive.scss";
|
||||
|
||||
.clock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="clock">{{ computedDate }}</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'VueClock',
|
||||
data: () => ({
|
||||
timestamp: Date.now()
|
||||
}),
|
||||
setup() {
|
||||
let timestamp = ref(Date.now());
|
||||
|
||||
const computedDate = computed(() =>
|
||||
new Date(timestamp.value).toLocaleString('pl-PL', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
})
|
||||
);
|
||||
|
||||
setInterval(() => (timestamp.value = Date.now()), 1000);
|
||||
|
||||
return { computedDate };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/responsive.scss';
|
||||
|
||||
.clock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<div class="loading">{{message}}</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@vue/runtime-core";
|
||||
|
||||
export default defineComponent({
|
||||
props: ["message"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
min-height: 100%;
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
font-size: calc(0.75rem + 1vw);
|
||||
|
||||
color: #fdc62f;
|
||||
}
|
||||
</style>
|
||||
@@ -43,7 +43,13 @@
|
||||
<g v-if="greenBlinkLight" filter="url(#filter0_d_843_28)">
|
||||
<circle cx="15" cy="17" r="7" fill="#00FF0A" />
|
||||
|
||||
<animate attributeType="XML" attributeName="opacity" values="1;0;1" dur="1s" repeatCount="indefinite" />
|
||||
<animate
|
||||
attributeType="XML"
|
||||
attributeName="opacity"
|
||||
values="1;0;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</g>
|
||||
|
||||
<g v-if="redTopLight" filter="url(#filter1_d_843_28)">
|
||||
@@ -56,7 +62,13 @@
|
||||
<g v-if="redBottomLight" filter="url(#filter3_d_843_28)">
|
||||
<circle cx="15" cy="74" r="7" fill="#F40000" />
|
||||
|
||||
<animate attributeType="XML" attributeName="opacity" values="1;0;1" dur="1s" repeatCount="indefinite" />
|
||||
<animate
|
||||
attributeType="XML"
|
||||
attributeName="opacity"
|
||||
values="1;0;1"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
@@ -82,7 +94,12 @@
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 1 0 0 0 0 0.04 0 0 0 1 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_843_28"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1_d_843_28"
|
||||
@@ -104,7 +121,12 @@
|
||||
<feGaussianBlur stdDeviation="2.5" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.770833 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_843_28"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<filter
|
||||
id="filter2_d_843_28"
|
||||
@@ -126,7 +148,12 @@
|
||||
<feGaussianBlur stdDeviation="2.5" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.72 0 0 0 0 0 0 0 0 1 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_843_28"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<filter
|
||||
id="filter3_d_843_28"
|
||||
@@ -148,7 +175,12 @@
|
||||
<feGaussianBlur stdDeviation="2.5" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.770833 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_843_28"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -161,29 +193,26 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { DataStatus } from '@/scripts/enums/DataStatus';
|
||||
import { StoreData } from '@/scripts/interfaces/StoreData';
|
||||
import { useStore } from '@/store/store';
|
||||
import { StoreState } from '@/store/storeTypes';
|
||||
import { computed, defineComponent, watch } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||
import { useStore } from '../../store/store';
|
||||
import { StoreState } from '../../scripts/interfaces/store/storeTypes';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
icons: {
|
||||
statusIndicator: require('@/assets/signal-status-indicator.svg'),
|
||||
},
|
||||
tooltipActive: false,
|
||||
indicator: {
|
||||
offline: false,
|
||||
status: DataStatus.Loading,
|
||||
message: 'data-status.S3',
|
||||
message: 'data-status.S3'
|
||||
},
|
||||
|
||||
greenLight: false,
|
||||
greenBlinkLight: false,
|
||||
redTopLight: false,
|
||||
orangeLight: false,
|
||||
redBottomLight: false,
|
||||
redBottomLight: false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -196,6 +225,7 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
dataStatus: store.dataStatuses,
|
||||
store
|
||||
};
|
||||
},
|
||||
|
||||
@@ -209,6 +239,13 @@ export default defineComponent({
|
||||
const trainsDataStatus = statuses.trains;
|
||||
const dispatcherDataStatus = statuses.dispatchers;
|
||||
|
||||
if (this.store.isOffline) {
|
||||
this.setSignalStatus(DataStatus.Initialized);
|
||||
this.indicator.status = DataStatus.Initialized;
|
||||
this.indicator.message = 'data-status.S1-offline';
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionStatus == DataStatus.Error) {
|
||||
this.setSignalStatus(connectionStatus);
|
||||
this.indicator.status = connectionStatus;
|
||||
@@ -243,8 +280,8 @@ export default defineComponent({
|
||||
this.indicator.status = DataStatus.Loaded;
|
||||
this.indicator.message = 'data-status.S2';
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -255,6 +292,10 @@ export default defineComponent({
|
||||
this.orangeLight = false;
|
||||
this.redBottomLight = false;
|
||||
|
||||
if (status == DataStatus.Initialized) {
|
||||
this.redTopLight = true;
|
||||
}
|
||||
|
||||
if (status == DataStatus.Loaded) {
|
||||
this.greenLight = true;
|
||||
}
|
||||
@@ -271,8 +312,8 @@ export default defineComponent({
|
||||
if (status == DataStatus.Loading) {
|
||||
this.greenBlinkLight = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -294,10 +335,11 @@ export default defineComponent({
|
||||
|
||||
.status-indicator {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
transform: translateX(12em);
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
|
||||
transform: translateX(1.5em);
|
||||
}
|
||||
|
||||
.indicator {
|
||||
@@ -322,7 +364,7 @@ export default defineComponent({
|
||||
background-color: #171717;
|
||||
border-radius: 0.75em;
|
||||
|
||||
min-width: 13em;
|
||||
width: 13em;
|
||||
text-align: center;
|
||||
overflow: none;
|
||||
|
||||
@@ -346,22 +388,16 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
@include midScreen() {
|
||||
left: 50%;
|
||||
top: 100%;
|
||||
|
||||
transform: translate(-50%, 0);
|
||||
margin-left: 0;
|
||||
margin-top: 0.75em;
|
||||
left: auto;
|
||||
right: 200%;
|
||||
|
||||
&::before {
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid #171717;
|
||||
border-left: 12px solid #171717;
|
||||
right: 0;
|
||||
left: auto;
|
||||
|
||||
top: 0;
|
||||
left: 50%;
|
||||
|
||||
transform: translate(-50%, -100%);
|
||||
transform: translate(100%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<template>
|
||||
<section
|
||||
class="updates card"
|
||||
v-if="cardOpen"
|
||||
>
|
||||
<h2>Ostatnie aktualizacje w Stacjowniku</h2>
|
||||
<p>Tutaj będą pojawiać się informacje o kolejnych nowościach na stronie :)</p>
|
||||
|
||||
<ul>
|
||||
<li
|
||||
v-for="(update, i) in updates"
|
||||
:key="i"
|
||||
>
|
||||
<div>{{update.date}}</div>
|
||||
|
||||
<div>
|
||||
<span
|
||||
v-for="(line, l) in content"
|
||||
:key="l"
|
||||
>{{line}}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@vue/runtime-core";
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
updates: {
|
||||
date: "08/08/20",
|
||||
content: [
|
||||
"Lekko odświeżono wygląd strony, dodano nowy widok z pociągami online",
|
||||
"Dodano animacje zmieniania widoków (zakładek)",
|
||||
"Dodano przycisk zamykający kartę z filtrami",
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -1,65 +1,24 @@
|
||||
<template>
|
||||
<button class="action-btn">
|
||||
<div class="button_content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../styles/variables";
|
||||
@import "../../styles/responsive";
|
||||
|
||||
.action-btn {
|
||||
background: #333;
|
||||
border: none;
|
||||
|
||||
color: #bdbdbd;
|
||||
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
|
||||
padding: 0.35em 0.65em;
|
||||
cursor: pointer;
|
||||
|
||||
transition: all 0.3s;
|
||||
|
||||
&.outlined {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1.25em;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.35em;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.open {
|
||||
color: $accentCol;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $accentCol;
|
||||
background: #5c5c5c;
|
||||
}
|
||||
}
|
||||
|
||||
.button_content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<button class="action-btn btn--filled">
|
||||
<div class="button_content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../styles/variables';
|
||||
@import '../../styles/responsive';
|
||||
|
||||
.button_content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<button
|
||||
class="btn btn--option btn--load-data"
|
||||
v-if="!scrollNoMoreData && scrollDataLoaded && list.length >= 15"
|
||||
@click="addHistoryData"
|
||||
>
|
||||
{{ $t('journal.load-data') }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
scrollNoMoreData: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
|
||||
scrollDataLoaded: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
|
||||
list: {
|
||||
type: Array as PropType<any[]>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
emits: ['addHistoryData'],
|
||||
|
||||
methods: {
|
||||
addHistoryData() {
|
||||
this.$emit('addHistoryData');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -12,7 +12,7 @@ import { defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,7 @@ export default defineComponent({
|
||||
.loading {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="progress-bar">
|
||||
<span class="bar-bg"></span>
|
||||
<span
|
||||
class="bar-fg"
|
||||
:style="{ width: `${~~progressPercent}%`, backgroundColor: bgColor }"
|
||||
></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
progressPercent: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
progressType: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
bgColor() {
|
||||
switch (this.progressType) {
|
||||
case 'abandoned':
|
||||
return 'salmon';
|
||||
|
||||
default:
|
||||
return 'springgreen';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.progress-bar {
|
||||
position: relative;
|
||||
|
||||
width: 6em;
|
||||
height: 1em;
|
||||
margin: 0.5em 0;
|
||||
|
||||
.bar-fg,
|
||||
.bar-bg {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 100%;
|
||||
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.bar-fg {
|
||||
background-color: springgreen;
|
||||
}
|
||||
|
||||
.bar-bg {
|
||||
background-color: #5b5b5b;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="region-dropdown" v-click-outside="clickedOutside">
|
||||
<div class="content">
|
||||
<button class="selected-region" @click="toggleBox">
|
||||
<span>{{ selectedItem.name }}</span>
|
||||
|
||||
<img :src="`/images/icon-arrow-${listOpen ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
|
||||
</button>
|
||||
|
||||
<ul class="options">
|
||||
<li class="option" v-for="(item, i) in regionList" :key="item.id">
|
||||
<transition
|
||||
name="unfold"
|
||||
:style="`
|
||||
--delay-in: ${i * 55}ms;
|
||||
--delay-out: ${(regionList.length - 1 - i) * 55}ms`"
|
||||
>
|
||||
<label :for="item.id" v-if="listOpen">
|
||||
<input type="button" :id="item.id" name="select-box" @click="selectOption(item)" />
|
||||
<span :style="selectedItem.id == item.id ? 'color: gold;' : ''" v-html="item.value">
|
||||
</span>
|
||||
</label>
|
||||
</transition>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, Ref, ref } from 'vue';
|
||||
import { useStore } from '../../store/store';
|
||||
import { regions as regionsJSON } from '../../data/options.json';
|
||||
|
||||
interface Item {
|
||||
id: string;
|
||||
value: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
store: useStore(),
|
||||
selectedItemIndex: 0,
|
||||
listOpen: false
|
||||
};
|
||||
},
|
||||
|
||||
setup() {
|
||||
let buttonRef: Ref<HTMLButtonElement | null> = ref(null);
|
||||
|
||||
return {
|
||||
buttonRef
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
'store.region.id': {
|
||||
handler(regionId) {
|
||||
this.selectedItemIndex = this.regionList.findIndex((reg) => reg.id == regionId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
selectedItem() {
|
||||
return this.regionList[this.selectedItemIndex] || null;
|
||||
},
|
||||
regionList() {
|
||||
return regionsJSON.map((region) => {
|
||||
const regionStationCount =
|
||||
this.store.apiData.stations?.filter(
|
||||
(station) => station.region == region.id && station.isOnline
|
||||
).length || 0;
|
||||
|
||||
const regionTrainCount =
|
||||
this.store.apiData.trains?.filter((train) => train.region == region.id && train.online)
|
||||
.length || 0;
|
||||
|
||||
return {
|
||||
id: region.id,
|
||||
value: `${region.value} <div class='text--grayed'>${regionStationCount} / ${regionTrainCount}</div>`,
|
||||
name: region.name
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
selectOption(selectedRegion: Item) {
|
||||
this.store.region = selectedRegion;
|
||||
this.listOpen = false;
|
||||
},
|
||||
|
||||
toggleBox(e: Event) {
|
||||
this.listOpen = !this.listOpen;
|
||||
|
||||
if (!this.listOpen) (e.target as HTMLButtonElement).blur();
|
||||
},
|
||||
|
||||
clickedOutside() {
|
||||
this.listOpen = false;
|
||||
this.buttonRef?.blur();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
.region-dropdown {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
button img {
|
||||
vertical-align: middle;
|
||||
width: 1.35em;
|
||||
}
|
||||
|
||||
button.selected-region {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: paleturquoise;
|
||||
|
||||
font-weight: bold;
|
||||
padding: 0.1em 0.5em;
|
||||
|
||||
&:focus {
|
||||
background-color: #262626;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
font-weight: bold;
|
||||
|
||||
height: 100%;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.options {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
|
||||
height: auto;
|
||||
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
li.option {
|
||||
input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
|
||||
&:focus + span {
|
||||
color: $accentCol;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child label {
|
||||
border-radius: 0 0 1em 1em;
|
||||
}
|
||||
|
||||
label {
|
||||
position: relative;
|
||||
|
||||
display: inline-block;
|
||||
background-color: #262626f2;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: #333333f2;
|
||||
}
|
||||
|
||||
padding: 0.5em 0;
|
||||
|
||||
width: 100%;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.unfold {
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.85);
|
||||
}
|
||||
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 110ms ease-out;
|
||||
}
|
||||
|
||||
&-enter-active {
|
||||
transition-delay: var(--delay-in);
|
||||
}
|
||||
|
||||
&-leave-active {
|
||||
transition-delay: var(--delay-out);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,39 +7,31 @@
|
||||
@keypress="updateValue"
|
||||
/>
|
||||
|
||||
<img
|
||||
class="search-exit"
|
||||
:src="exitIcon"
|
||||
alt="exit-icon"
|
||||
@click="clearValue"
|
||||
/>
|
||||
<img class="search-exit" src="/images/icon-exit.svg" alt="exit-icon" @click="clearSearchValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from "vue";
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
data: () => ({
|
||||
exitIcon: require("@/assets/icon-exit.svg"),
|
||||
}),
|
||||
emits: ["update:searchedValue", "clearValue"],
|
||||
emits: ['update:searchedValue', 'clearValue'],
|
||||
props: {
|
||||
searchedValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
updateOnInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
titleToTranslate: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
clearValue: {
|
||||
type: Function,
|
||||
},
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
|
||||
setup(props, { emit }) {
|
||||
@@ -49,32 +41,32 @@ export default defineComponent({
|
||||
watch(
|
||||
() => compSearchedValue.value,
|
||||
(value) => {
|
||||
emit("update:searchedValue", value);
|
||||
emit('update:searchedValue', value);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const clearValue = () => {
|
||||
compSearchedValue.value = "";
|
||||
emit("clearValue");
|
||||
const clearSearchValue = () => {
|
||||
compSearchedValue.value = '';
|
||||
emit('clearValue');
|
||||
};
|
||||
|
||||
const updateValue = (e) => {
|
||||
const updateValue = (e: any) => {
|
||||
if (!props.updateOnInput && e.keyCode == 13)
|
||||
emit("update:searchedValue", compSearchedValue.value);
|
||||
emit('update:searchedValue', compSearchedValue.value);
|
||||
};
|
||||
|
||||
return {
|
||||
compSearchedValue,
|
||||
updateValue,
|
||||
clearValue,
|
||||
clearSearchValue
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../styles/responsive";
|
||||
@import '../../styles/responsive';
|
||||
|
||||
.search {
|
||||
&-box {
|
||||
@@ -109,4 +101,4 @@ export default defineComponent({
|
||||
width: 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
<template>
|
||||
<div class="select-box" >
|
||||
<div class="select-box_content">
|
||||
<button class="selected" @click="toggleBox">
|
||||
<span class="text--primary">{{ prefix }}</span>
|
||||
<span>{{ computedSelectedItem.selectedValue || computedSelectedItem.value }}</span>
|
||||
</button>
|
||||
|
||||
<ul class="options" :ref="(el) => (listRef = el as Element)">
|
||||
<li class="option" v-for="(item, i) in itemList" :key="item.id">
|
||||
<transition
|
||||
name="unfold"
|
||||
:style="`
|
||||
--delay-in: ${i * 55}ms;
|
||||
--delay-out: ${(itemList.length - 1 - i) * 55}ms`"
|
||||
>
|
||||
<label :for="item.id" v-if="listOpen">
|
||||
<input type="button" :id="item.id" name="select-box" @click="selectOption(item)" />
|
||||
<span :style="computedSelectedItem.id == item.id ? 'color: gold;' : ''" v-html="item.value"> </span>
|
||||
</label>
|
||||
</transition>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="arrow">
|
||||
<img :src="listOpen ? ascIcon : descIcon" alt="arrow-icon" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, Ref, ref } from '@vue/runtime-core';
|
||||
|
||||
interface Item {
|
||||
id: string;
|
||||
value: string;
|
||||
selectedValue?: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['selected'],
|
||||
|
||||
props: {
|
||||
itemList: {
|
||||
type: Array as () => Item[],
|
||||
required: true,
|
||||
},
|
||||
|
||||
defaultItemIndex: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
|
||||
prefix: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
ascIcon: require('@/assets/icon-arrow-asc.svg'),
|
||||
descIcon: require('@/assets/icon-arrow-desc.svg'),
|
||||
}),
|
||||
|
||||
setup(props) {
|
||||
let listRef: Ref<Element | null> = ref(null);
|
||||
let buttonRef: Ref<HTMLButtonElement | null> = ref(null);
|
||||
|
||||
let activeEl: Ref<Element | null> = ref(document.activeElement);
|
||||
|
||||
let listOpen = ref(false);
|
||||
let selectedItem: Ref<Item> = ref(props.itemList[props.defaultItemIndex]);
|
||||
|
||||
const computedSelectedItem = computed(() => {
|
||||
return props.itemList.find((item) => item.id === selectedItem.value.id) || props.itemList[props.defaultItemIndex];
|
||||
});
|
||||
|
||||
return {
|
||||
computedSelectedItem,
|
||||
listOpen,
|
||||
selectedItem,
|
||||
listRef,
|
||||
buttonRef,
|
||||
activeEl,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
selectOption(item: Item) {
|
||||
this.selectedItem = item;
|
||||
this.listOpen = false;
|
||||
|
||||
this.$emit('selected', item);
|
||||
},
|
||||
|
||||
toggleBox(e: Event) {
|
||||
this.listOpen = !this.listOpen;
|
||||
|
||||
if (!this.listOpen) (e.target as HTMLButtonElement).blur();
|
||||
},
|
||||
|
||||
clickedOutside() {
|
||||
this.listOpen = false;
|
||||
this.buttonRef?.blur();
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
.unfold {
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.85);
|
||||
}
|
||||
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 110ms ease-out;
|
||||
}
|
||||
|
||||
&-enter-active {
|
||||
transition-delay: var(--delay-in);
|
||||
}
|
||||
|
||||
&-leave-active {
|
||||
transition-delay: var(--delay-out);
|
||||
}
|
||||
}
|
||||
|
||||
.select-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
padding: 0.5em;
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
width: 1.35em;
|
||||
}
|
||||
|
||||
transform: translateY(-50%);
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
button.selected {
|
||||
background: #333;
|
||||
color: white;
|
||||
|
||||
font-size: 1em;
|
||||
|
||||
padding: 0.35em 0.5em;
|
||||
margin-right: 1.4em;
|
||||
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
text-align: left;
|
||||
|
||||
&:focus {
|
||||
background: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.select-box_content {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
|
||||
height: 100%;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.options {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
|
||||
height: auto;
|
||||
|
||||
z-index: 100;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li.option {
|
||||
input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&:focus + span {
|
||||
color: $accentCol;
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child label {
|
||||
border-radius: 0 0 1em 1em;
|
||||
}
|
||||
|
||||
label {
|
||||
position: relative;
|
||||
|
||||
display: inline-block;
|
||||
background-color: hsla(0, 0%, 15%, 0.95);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: hsla(0, 0%, 20%, 0.95);
|
||||
}
|
||||
|
||||
padding: 0.5em 0;
|
||||
|
||||
width: 100%;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,56 +1,90 @@
|
||||
$free: #8a8a8a;
|
||||
$ending: #e6c300;
|
||||
$no-limit: #117fc9;
|
||||
$unav: #ff3d5d;
|
||||
$brb: #e6a100;
|
||||
$no-space: #222;
|
||||
$taken: #09a116;
|
||||
$unknown: rgb(185, 60, 60);
|
||||
|
||||
.status-badge {
|
||||
border-radius: 1rem;
|
||||
font-weight: 500;
|
||||
|
||||
padding: 0.2em .55em;
|
||||
|
||||
background-color: $taken;
|
||||
|
||||
&.free {
|
||||
background-color: $free;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
&.ending {
|
||||
background-color: $ending;
|
||||
color: black;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&.no-limit {
|
||||
background-color: $no-limit;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&.not-signed,
|
||||
&.unavailable {
|
||||
background-color: $unav;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&.brb {
|
||||
background-color: $brb;
|
||||
color: black;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
&.no-space {
|
||||
background-color: $no-space;
|
||||
color: white;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&.unknown {
|
||||
background-color: $unknown;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
}
|
||||
<template>
|
||||
<span class="status-badge" :class="statusID" v-if="isOnline">
|
||||
{{ $t(`status.${statusID}`) }}
|
||||
{{ statusID == 'online' ? timestampToString(statusTimestamp!) : '' }}
|
||||
</span>
|
||||
|
||||
<span class="status-badge free" v-else>
|
||||
{{ $t('status.free') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import dateMixin from '../../mixins/dateMixin';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
statusID: {
|
||||
type: String
|
||||
},
|
||||
statusTimestamp: {
|
||||
type: Number
|
||||
},
|
||||
isOnline: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
mixins: [dateMixin]
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$free: #8a8a8a;
|
||||
$ending: #e6c300;
|
||||
$no-limit: #117fc9;
|
||||
$unav: #ff3d5d;
|
||||
$brb: #e6a100;
|
||||
$no-space: #222;
|
||||
$online: #09a116;
|
||||
$unknown: rgb(185, 60, 60);
|
||||
|
||||
.status-badge {
|
||||
border-radius: 1rem;
|
||||
font-weight: 500;
|
||||
|
||||
padding: 0.2em 0.55em;
|
||||
|
||||
background-color: $online;
|
||||
|
||||
&.free {
|
||||
background-color: $free;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
&.ending {
|
||||
background-color: $ending;
|
||||
color: black;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&.no-limit {
|
||||
background-color: $no-limit;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&.not-signed,
|
||||
&.unavailable {
|
||||
background-color: $unav;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&.brb {
|
||||
background-color: $brb;
|
||||
color: black;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
&.no-space {
|
||||
background-color: $no-space;
|
||||
border: 1px solid white;
|
||||
color: white;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
&.unknown {
|
||||
background-color: $unknown;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="stock-list">
|
||||
<ul>
|
||||
<li v-for="(stockName, i) in trainStockList" :key="i">
|
||||
<p>
|
||||
{{ stockName.split(':')[0].split('_').splice(0, 2).join(' ') }}
|
||||
{{ stockName.split(':')[1] }}
|
||||
</p>
|
||||
|
||||
<span>
|
||||
<img
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}${
|
||||
/^EN/.test(stockName) ? 'rb' : ''
|
||||
}.png`"
|
||||
@error="onImageError($event, stockName)"
|
||||
width="400"
|
||||
height="60"
|
||||
/>
|
||||
|
||||
<img
|
||||
v-if="/^(EN|2EN)/.test(stockName)"
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}s.png`"
|
||||
@error="
|
||||
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-s.png')
|
||||
"
|
||||
/>
|
||||
|
||||
<img
|
||||
class="train-thumbnail"
|
||||
v-if="/^EN71/.test(stockName)"
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}s.png`"
|
||||
@error="
|
||||
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-s.png')
|
||||
"
|
||||
/>
|
||||
|
||||
<img
|
||||
class="train-thumbnail"
|
||||
v-if="/^(EN|2EN)/.test(stockName)"
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}ra.png`"
|
||||
@error="
|
||||
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-ra.png')
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { PropType, defineComponent } from 'vue';
|
||||
import { useStore } from '../../store/store';
|
||||
import { RollingStockInfo } from '../../scripts/interfaces/github_api/StockInfoGithubData';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
trainStockList: {
|
||||
type: Array as PropType<string[]>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
onImageError(event: Event, stockName: string) {
|
||||
const fallbackName =
|
||||
Object.keys(this.store.rollingStockData!.info).find((type) => {
|
||||
return this.store.rollingStockData!.info[type as keyof RollingStockInfo].find(
|
||||
(v) => v[0] === stockName.split(':')[0]
|
||||
);
|
||||
}) || 'vehicle-unknown';
|
||||
|
||||
(event.target as HTMLImageElement).src = `/images/icon-${fallbackName}.png`;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stock-list {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stock-list ul {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
overflow: auto;
|
||||
margin: 0 auto;
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
ul > li > span {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 60px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
@@ -4,12 +4,12 @@
|
||||
class="date arrival"
|
||||
v-if="!stop.beginsHere"
|
||||
:class="{
|
||||
delayed: stop.arrivalDelay > 0 && stop.confirmed,
|
||||
preponed: stop.arrivalDelay < 0 && stop.confirmed,
|
||||
'on-time': stop.arrivalDelay == 0 && stop.confirmed,
|
||||
delayed: stop.arrivalDelay > 0 && (stop.confirmed || stop.stopped),
|
||||
preponed: stop.arrivalDelay < 0 && (stop.confirmed || stop.stopped),
|
||||
'on-time': stop.arrivalDelay == 0 && stop.confirmed
|
||||
}"
|
||||
>
|
||||
<span v-if="stop.arrivalDelay != 0 && stop.confirmed">
|
||||
<span v-if="stop.arrivalDelay != 0 && (stop.confirmed || stop.stopped)">
|
||||
<s>{{ timestampToString(stop.arrivalTimestamp) }}</s>
|
||||
{{ timestampToString(stop.arrivalRealTimestamp) }}
|
||||
({{ stop.arrivalDelay > 0 ? '+' : '' }}{{ stop.arrivalDelay }})
|
||||
@@ -20,16 +20,20 @@
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="date stop" v-if="stop.stopTime" :class="stop.stopType.replace(', ', '-')">
|
||||
<span
|
||||
class="date stop"
|
||||
v-if="stop.stopTime || stop.stopped"
|
||||
:class="stop.stopType.replace(', ', '-')"
|
||||
>
|
||||
{{ stop.stopTime }} {{ stop.stopType == '' ? 'pt' : stop.stopType }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="date departure"
|
||||
v-if="!stop.terminatesHere && stop.stopTime != 0"
|
||||
v-if="!stop.terminatesHere && (stop.stopTime != 0 || stop.stopped)"
|
||||
:class="{
|
||||
delayed: stop.departureDelay > 0 && stop.confirmed,
|
||||
preponed: stop.departureDelay < 0 && stop.confirmed,
|
||||
preponed: stop.departureDelay < 0 && stop.confirmed
|
||||
}"
|
||||
>
|
||||
<span v-if="stop.departureDelay != 0 && stop.confirmed">
|
||||
@@ -47,9 +51,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import dateMixin from '@/mixins/dateMixin';
|
||||
import TrainStop from '@/scripts/interfaces/TrainStop';
|
||||
import { defineComponent } from 'vue';
|
||||
import dateMixin from '../../mixins/dateMixin';
|
||||
import TrainStop from '../../scripts/interfaces/TrainStop';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [dateMixin],
|
||||
@@ -57,13 +61,13 @@ export default defineComponent({
|
||||
props: {
|
||||
stop: {
|
||||
type: Object as () => TrainStop,
|
||||
required: true,
|
||||
},
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div class="train-modal" v-if="chosenTrain" @keydown.esc="closeModal">
|
||||
<div class="modal_background" @click="closeModal"></div>
|
||||
<div class="modal_content" ref="content" tabindex="0">
|
||||
<button class="btn exit" @click="closeModal">
|
||||
<img src="/images/icon-exit.svg" alt="close card" />
|
||||
</button>
|
||||
|
||||
<TrainInfo :train="chosenTrain" :extended="false" ref="trainInfo" />
|
||||
<TrainSchedule :train="chosenTrain" tabindex="0" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||
import trainInfoMixin from '../../mixins/trainInfoMixin';
|
||||
import { useStore } from '../../store/store';
|
||||
import TrainInfo from '../TrainsView/TrainInfo.vue';
|
||||
import TrainSchedule from '../TrainsView/TrainSchedule.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: { TrainInfo, TrainSchedule },
|
||||
mixins: [trainInfoMixin, modalTrainMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isTopBarVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
||||
return {
|
||||
store
|
||||
};
|
||||
},
|
||||
|
||||
activated() {
|
||||
const contentEl = this.$refs['content'] as HTMLElement;
|
||||
|
||||
this.$nextTick(() => {
|
||||
contentEl.focus();
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleContentScroll(e: Event) {
|
||||
const trainInfoCompHeight: number = (
|
||||
this.$refs['trainInfo'] as any
|
||||
).$el.getBoundingClientRect().height;
|
||||
|
||||
const posTop = (e.target as HTMLElement).scrollTop;
|
||||
this.isTopBarVisible = posTop > trainInfoCompHeight;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/responsive.scss';
|
||||
@import '../../styles/card.scss';
|
||||
|
||||
.top-info-bar-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 150ms ease-in-out;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.exit {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
margin: 0.5em 1em;
|
||||
|
||||
padding: 0.25em;
|
||||
|
||||
z-index: 201;
|
||||
|
||||
img {
|
||||
width: 1.5rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.train-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
|
||||
color: white;
|
||||
z-index: 200;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.modal_background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.modal_content {
|
||||
position: relative;
|
||||
overflow-y: scroll;
|
||||
|
||||
margin-top: 1em;
|
||||
|
||||
width: 95vw;
|
||||
max-height: 96vh;
|
||||
|
||||
background-color: #1a1a1a;
|
||||
box-shadow: 0 0 15px 10px #0e0e0e;
|
||||
}
|
||||
|
||||
@include midScreen {
|
||||
.exit {
|
||||
margin: 0.5em;
|
||||
|
||||
img {
|
||||
width: 1.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include smallScreen {
|
||||
.modal_content {
|
||||
max-height: 85vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<img class="train-thumbnail" :src="placeholderUrl" v-if="isNotFound" />
|
||||
|
||||
<img
|
||||
class="train-thumbnail"
|
||||
v-else
|
||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${name.split(':')[0]}${
|
||||
stockType == 'loco-ezt' ? 'rb' : ''
|
||||
}.png`"
|
||||
@error="onImageError"
|
||||
@load="onImageLoad"
|
||||
width="220"
|
||||
height="60"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../../store/store';
|
||||
import { RollingStockInfo } from '../../scripts/interfaces/github_api/StockInfoGithubData';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
|
||||
onlyFirstSegment: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
store: useStore(),
|
||||
isNotFound: false,
|
||||
isLoaded: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
url() {
|
||||
return `https://rj.td2.info.pl/dist/img/thumbnails/${this.name.split(':')[0]}.png`;
|
||||
},
|
||||
|
||||
placeholderUrl() {
|
||||
return `/images/icon-${this.stockType}.png`;
|
||||
},
|
||||
|
||||
stockType() {
|
||||
if (!this.store.rollingStockData) return 'vehicle-unknown';
|
||||
|
||||
return (
|
||||
Object.keys(this.store.rollingStockData.info).find((type) => {
|
||||
return this.store.rollingStockData?.info[type as keyof RollingStockInfo].find(
|
||||
(v) => v[0] === this.name.split(':')[0]
|
||||
);
|
||||
}) || 'vehicle-unknown'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onImageError() {
|
||||
this.isNotFound = true;
|
||||
this.isLoaded = false;
|
||||
},
|
||||
|
||||
onImageLoad() {
|
||||
this.isNotFound = false;
|
||||
this.isLoaded = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.train-thumbnail {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-height: 60px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<section class="daily-stats">
|
||||
<span :data-active="statsStatus">
|
||||
<b v-if="statsStatus == DataStatus.Loading">
|
||||
{{ $t('app.loading') }}
|
||||
</b>
|
||||
|
||||
<b v-else-if="stats.distanceSum == null">
|
||||
{{ $t('journal.daily-stats-info') }}
|
||||
</b>
|
||||
|
||||
<span class="stats-list" v-else>
|
||||
<h3>
|
||||
{{ $t('journal.daily-stats-title') }}
|
||||
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
||||
</h3>
|
||||
<hr style="margin-bottom: 0.5em" />
|
||||
|
||||
<div v-if="stats.totalTimetables">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-total">
|
||||
<template #count>
|
||||
<b class="text--primary">
|
||||
{{ stats.totalTimetables }}
|
||||
{{ $t('journal.timetable-count', stats.totalTimetables) }}
|
||||
</b>
|
||||
</template>
|
||||
|
||||
<template #distance>
|
||||
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<div v-if="stats.timetableId">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-longest">
|
||||
<template #id>
|
||||
<router-link :to="`/journal/timetables?timetableId=${stats.timetableId}`">
|
||||
<b>{{ stats.timetableId }}</b>
|
||||
</router-link>
|
||||
</template>
|
||||
<template #author>
|
||||
<router-link :to="`/journal/dispatchers?dispatcherName=${stats.timetableAuthor}`">
|
||||
<b>{{ stats.timetableAuthor }}</b>
|
||||
</router-link>
|
||||
</template>
|
||||
<template #driver>
|
||||
<b class="text--primary">{{ stats.timetableDriver }}</b>
|
||||
</template>
|
||||
<template #distance>
|
||||
<b class="text--primary">{{ stats.timetableRouteDistance }} km</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<div v-if="topDispatchers.length == 1">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-most-active-dr">
|
||||
<template #dispatcher>
|
||||
<router-link :to="`/journal/dispatchers?dispatcherName=${topDispatchers[0].name}`">
|
||||
<b>{{ topDispatchers[0].name }}</b>
|
||||
</router-link>
|
||||
</template>
|
||||
<template #count>
|
||||
<b class="text--primary">
|
||||
{{ topDispatchers[0].count }}
|
||||
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
||||
</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<div v-if="topDispatchers.length > 1">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-most-active-dr-many">
|
||||
<template #dispatchers>
|
||||
<span v-for="(disp, i) in topDispatchers" :key="i">
|
||||
<span v-if="i == topDispatchers.length - 1"> {{ $t('general.and') }} </span>
|
||||
|
||||
<router-link :to="`/journal/dispatchers?dispatcherName=${disp.name}`">
|
||||
<b>{{ disp.name }}</b>
|
||||
</router-link>
|
||||
|
||||
<span v-if="i < topDispatchers.length - 2">, </span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #count>
|
||||
<b class="text--primary">
|
||||
{{ topDispatchers[0].count }}
|
||||
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
||||
</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<div v-if="stats.longestDuties.length > 0">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-longest-duties">
|
||||
<template #dispatcher>
|
||||
<router-link
|
||||
:to="`/journal/dispatchers?dispatcherName=${stats.longestDuties[0].name}`"
|
||||
>
|
||||
<b>{{ stats.longestDuties[0].name }}</b>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<template #station>{{ stats.longestDuties[0].station }}</template>
|
||||
|
||||
<template #duration>
|
||||
{{ calculateDuration(stats.longestDuties[0].duration) }}
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<div v-if="stats.mostActiveDrivers.length > 0">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-most-active-driver">
|
||||
<template #driver>
|
||||
<b class="text--primary">{{ stats.mostActiveDrivers[0].name }}</b>
|
||||
</template>
|
||||
<template #distance>
|
||||
<b class="text--primary">{{ stats.mostActiveDrivers[0].distance.toFixed(2) }} km</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import axios from 'axios';
|
||||
import { defineComponent } from 'vue';
|
||||
import dateMixin from '../../mixins/dateMixin';
|
||||
import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||
import {
|
||||
ITimetablesDailyStats,
|
||||
ITimetablesDailyStatsResponse
|
||||
} from '../../scripts/interfaces/api/StatsAPIData';
|
||||
import { URLs } from '../../scripts/utils/apiURLs';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [dateMixin],
|
||||
emits: ['toggleStatsOpen'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
DataStatus,
|
||||
statsStatus: DataStatus.Loading,
|
||||
intervalId: -1,
|
||||
|
||||
stats: {
|
||||
totalTimetables: 0,
|
||||
distanceSum: 0,
|
||||
distanceAvg: 0,
|
||||
timetableAuthor: '',
|
||||
timetableDriver: '',
|
||||
timetableId: 0,
|
||||
timetableRouteDistance: 0,
|
||||
longestDuties: [],
|
||||
mostActiveDrivers: [],
|
||||
mostActiveDispatchers: []
|
||||
} as ITimetablesDailyStats
|
||||
};
|
||||
},
|
||||
|
||||
activated() {
|
||||
this.startFetchingDailyStats();
|
||||
this.$emit('toggleStatsOpen', true);
|
||||
},
|
||||
|
||||
deactivated() {
|
||||
this.stopFetchingDailyStats();
|
||||
},
|
||||
|
||||
computed: {
|
||||
topDispatchers() {
|
||||
if (this.stats.mostActiveDispatchers.length == 0) return [];
|
||||
const maxCount = this.stats.mostActiveDispatchers[0].count;
|
||||
|
||||
return this.stats.mostActiveDispatchers.filter((disp) => disp.count === maxCount);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchDailyTimetableStats() {
|
||||
try {
|
||||
const res: ITimetablesDailyStatsResponse = await (
|
||||
await axios.get(`${URLs.stacjownikAPI}/api/getDailyTimetableStats`)
|
||||
).data;
|
||||
|
||||
this.stats = {
|
||||
totalTimetables: res.totalTimetables,
|
||||
distanceSum: res.distanceSum,
|
||||
distanceAvg: res.distanceAvg,
|
||||
timetableAuthor: res.maxTimetable?.authorName || '',
|
||||
timetableDriver: res.maxTimetable?.driverName || '',
|
||||
timetableId: res.maxTimetable?.id || 0,
|
||||
timetableRouteDistance: res.maxTimetable?.routeDistance || 0,
|
||||
|
||||
mostActiveDispatchers: res.mostActiveDispatchers,
|
||||
mostActiveDrivers: res.mostActiveDrivers,
|
||||
longestDuties: res.longestDuties
|
||||
};
|
||||
|
||||
this.statsStatus = DataStatus.Loaded;
|
||||
} catch (error) {
|
||||
console.error('Ups! Wystąpił błąd podczas pobierania statystyk rozkładów jazdy...');
|
||||
this.statsStatus = DataStatus.Error;
|
||||
}
|
||||
},
|
||||
|
||||
startFetchingDailyStats() {
|
||||
this.fetchDailyTimetableStats();
|
||||
|
||||
if (this.intervalId != -1) return;
|
||||
|
||||
this.intervalId = setInterval(this.fetchDailyTimetableStats, 60000);
|
||||
},
|
||||
|
||||
stopFetchingDailyStats() {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/responsive.scss';
|
||||
|
||||
.daily-stats {
|
||||
text-align: left;
|
||||
}
|
||||
.daily-stats > span[data-active='0'] {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.stats-list a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@include smallScreen {
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="stats_container" v-click-outside="() => (cardVisible = false)">
|
||||
<button class="stats_button btn btn--option" @click="toggleCard">
|
||||
<button class="stats_button" @click="toggleCard">
|
||||
Statystyki dyżurnego {{ store.dispatcherStatsName }}
|
||||
</button>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<div v-else>
|
||||
<h3>STATYSTYKI WYSTAWIONYCH ROZKŁADÓW</h3>
|
||||
|
||||
<div class="info-stats" v-if="store.dispatcherStatsData._count._all">
|
||||
<span class="stat-badge">
|
||||
<span>LICZBA</span>
|
||||
@@ -35,8 +36,9 @@
|
||||
|
||||
<h3>OSTATNIE WYSTAWIONE ROZKŁADY</h3>
|
||||
<div class="last-timetables">
|
||||
<div class="timetable-row" v-for="timetable in timetables">
|
||||
#{{ timetable.timetableId }} | <b>{{ timetable.trainCategoryCode }} {{ timetable.trainNo }}</b> |
|
||||
<div class="timetable-row" v-for="timetable in timetables" :key="timetable.id">
|
||||
#{{ timetable.timetableId }} |
|
||||
<b>{{ timetable.trainCategoryCode }} {{ timetable.trainNo }}</b> |
|
||||
{{ timetable.driverName }} ({{ timetable.routeDistance }}km)
|
||||
<div>{{ timetable.route.replace('|', ' > ') }}</div>
|
||||
</div>
|
||||
@@ -48,12 +50,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { DispatcherStatsAPIData } from '@/scripts/interfaces/api/DispatcherStatsAPIData';
|
||||
import { TimetableHistory } from '@/scripts/interfaces/api/TimetablesAPIData';
|
||||
import { URLs } from '@/scripts/utils/apiURLs';
|
||||
import { useStore } from '@/store/store';
|
||||
import axios from 'axios';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { DispatcherStatsAPIData } from '../../scripts/interfaces/api/DispatcherStatsAPIData';
|
||||
import { TimetableHistory } from '../../scripts/interfaces/api/TimetablesAPIData';
|
||||
import { URLs } from '../../scripts/utils/apiURLs';
|
||||
import { useStore } from '../../store/store';
|
||||
import Loading from '../Global/Loading.vue';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -62,15 +64,8 @@ export default defineComponent({
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
||||
const statsData2 = computed(async () => {
|
||||
return await (
|
||||
await axios.get(`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${store.dispatcherStatsName}`)
|
||||
).data;
|
||||
});
|
||||
|
||||
return {
|
||||
store,
|
||||
statsData2,
|
||||
store
|
||||
};
|
||||
},
|
||||
|
||||
@@ -78,7 +73,7 @@ export default defineComponent({
|
||||
return {
|
||||
cardVisible: false,
|
||||
lastDispatcherName: '',
|
||||
timetables: [] as TimetableHistory[],
|
||||
timetables: [] as TimetableHistory[]
|
||||
};
|
||||
},
|
||||
|
||||
@@ -96,18 +91,22 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const statsData: DispatcherStatsAPIData = await (
|
||||
await axios.get(`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`)
|
||||
await axios.get(
|
||||
`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`
|
||||
)
|
||||
).data;
|
||||
|
||||
const timetables: TimetableHistory[] = await (
|
||||
await axios.get(`${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}`)
|
||||
await axios.get(
|
||||
`${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}`
|
||||
)
|
||||
).data;
|
||||
|
||||
this.timetables = timetables;
|
||||
this.store.dispatcherStatsData = statsData;
|
||||
this.lastDispatcherName = this.store.dispatcherStatsName;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -161,42 +160,7 @@ h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-stats {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.last-timetables {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.stat-badge {
|
||||
margin-right: 0.5em;
|
||||
padding-bottom: 1em;
|
||||
|
||||
span {
|
||||
padding: 0.25em 0.3em;
|
||||
}
|
||||
|
||||
span:first-child {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
background-color: $accentCol;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallScreen() {
|
||||
.stats_card {
|
||||
text-align: center;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 0 0 1em 1em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||