mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 13:28:11 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c4c486643 | |||
| 755c729a9b | |||
| 3ac8d60c5c | |||
| dcff3b088f | |||
| 90b2099955 | |||
| fc0c04ec9d | |||
| 41b335555a | |||
| 60f7b3bbb5 | |||
| eaefe955a7 | |||
| edaa4f2684 | |||
| 30fce3787b | |||
| 4716f1c7a4 | |||
| bb7ccf98fd | |||
| c06d75b981 | |||
| c7da8477fa | |||
| e43f1e0819 | |||
| f130e6900b | |||
| db205915be | |||
| 05c38e10e3 | |||
| a8f683a585 | |||
| 68f6fc8a42 | |||
| 6d3b32cd7d | |||
| fadecc9d2c | |||
| 50602cb6db | |||
| 186ce81819 | |||
| f836a075b0 | |||
| 9acf3c740c | |||
| bc1c1bd3d2 | |||
| 2348277b95 | |||
| cd5f489df7 | |||
| f74962222b | |||
| e7f651d2b9 | |||
| 4862328090 | |||
| 87631d1f74 | |||
| 86bb9fcc2e | |||
| b85e3bfe1d | |||
| dd15072813 | |||
| 2f8376c996 | |||
| 514723cf74 | |||
| 0995ce15bc | |||
| 3b3c3bda31 | |||
| 2027b85450 | |||
| 0c6b55146f | |||
| 3c728e3cfa | |||
| adce339392 | |||
| 00a4a840b0 | |||
| 1e705ea496 | |||
| e8ed36df16 | |||
| f4be32aa39 | |||
| e0d3d2585d | |||
| ebfaf06a44 | |||
| 5a651aedf8 | |||
| b66af014b9 | |||
| 634c9e1514 | |||
| c4132a9be2 | |||
| 82a9a9165f | |||
| fcac03c0a4 | |||
| 39c3cf2329 | |||
| 59f4a0cb66 | |||
| e2b42d16a4 | |||
| e23663ed28 | |||
| dc7846c31e | |||
| d875433d56 | |||
| 71e5044cb4 | |||
| e83aa40f82 | |||
| d1c0e0b898 | |||
| 26a7c69886 | |||
| 0dc2c505db | |||
| 188857d335 | |||
| 3dbbb3b4f9 | |||
| 07a77c463b | |||
| 1a8e2231dd |
@@ -50,11 +50,6 @@
|
|||||||
name="twitter:image"
|
name="twitter:image"
|
||||||
content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg"
|
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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Generated
+35
-44
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.19.0",
|
"version": "1.20.3",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.19.0",
|
"version": "1.20.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^3.32.2",
|
"core-js": "^3.32.2",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
"sass": "^1.67.0",
|
"sass": "^1.67.0",
|
||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.4",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^9.4.1",
|
"vue-i18n": "^9.4.1",
|
||||||
"vue-router": "^4.2.4"
|
"vue-router": "^4.2.4"
|
||||||
@@ -2844,8 +2844,7 @@
|
|||||||
"node_modules/@socket.io/component-emitter": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
"license": "MIT"
|
|
||||||
},
|
},
|
||||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
@@ -3857,9 +3856,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001503",
|
"version": "1.0.30001565",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz",
|
||||||
"integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==",
|
"integrity": "sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3874,8 +3873,7 @@
|
|||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"license": "CC-BY-4.0"
|
|
||||||
},
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
@@ -4407,10 +4405,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/engine.io-client": {
|
"node_modules/engine.io-client": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||||
"integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==",
|
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@socket.io/component-emitter": "~3.1.0",
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
"debug": "~4.3.1",
|
"debug": "~4.3.1",
|
||||||
@@ -4423,7 +4420,6 @@
|
|||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
||||||
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
|
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
@@ -5090,9 +5086,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5100,7 +5096,6 @@
|
|||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
},
|
},
|
||||||
@@ -7567,10 +7562,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/socket.io-client": {
|
"node_modules/socket.io-client": {
|
||||||
"version": "4.7.2",
|
"version": "4.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz",
|
||||||
"integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==",
|
"integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@socket.io/component-emitter": "~3.1.0",
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
"debug": "~4.3.2",
|
"debug": "~4.3.2",
|
||||||
@@ -7585,7 +7579,6 @@
|
|||||||
"version": "4.2.4",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@socket.io/component-emitter": "~3.1.0",
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
"debug": "~4.3.1"
|
"debug": "~4.3.1"
|
||||||
@@ -8208,11 +8201,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.4.9",
|
"version": "4.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
|
||||||
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
|
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.27",
|
||||||
@@ -8788,7 +8780,6 @@
|
|||||||
"version": "8.11.0",
|
"version": "8.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
@@ -11513,9 +11504,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001503",
|
"version": "1.0.30001565",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz",
|
||||||
"integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==",
|
"integrity": "sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
@@ -11892,9 +11883,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"engine.io-client": {
|
"engine.io-client": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||||
"integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==",
|
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@socket.io/component-emitter": "~3.1.0",
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
"debug": "~4.3.1",
|
"debug": "~4.3.1",
|
||||||
@@ -12393,9 +12384,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
@@ -14052,9 +14043,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"socket.io-client": {
|
"socket.io-client": {
|
||||||
"version": "4.7.2",
|
"version": "4.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz",
|
||||||
"integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==",
|
"integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@socket.io/component-emitter": "~3.1.0",
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
"debug": "~4.3.2",
|
"debug": "~4.3.2",
|
||||||
@@ -14503,9 +14494,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"vite": {
|
"vite": {
|
||||||
"version": "4.4.9",
|
"version": "4.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
|
||||||
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
|
"integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
|
|||||||
+9
-9
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.19.0",
|
"version": "1.21.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -18,27 +18,27 @@
|
|||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
"sass": "^1.67.0",
|
"sass": "^1.67.0",
|
||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.4",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^9.4.1",
|
"vue-i18n": "^9.4.1",
|
||||||
"vue-router": "^4.2.4"
|
"vue-router": "^4.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rushstack/eslint-patch": "^1.3.3",
|
||||||
"@types/node": "^20.6.2",
|
"@types/node": "^20.6.2",
|
||||||
"@vite-pwa/assets-generator": "^0.0.10",
|
"@vite-pwa/assets-generator": "^0.0.10",
|
||||||
"@vitejs/plugin-vue": "^4.3.4",
|
"@vitejs/plugin-vue": "^4.3.4",
|
||||||
|
"@vue/eslint-config-prettier": "^8.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^12.0.0",
|
||||||
|
"@vue/tsconfig": "^0.4.0",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
|
"eslint": "^8.49.0",
|
||||||
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vite-plugin-pwa": "^0.16.5",
|
"vite-plugin-pwa": "^0.16.5",
|
||||||
"vue-tsc": "^1.8.11",
|
"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": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
<svg width="160" height="150" viewBox="0 0 160 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="160" height="150" viewBox="0 0 160 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M10.163 139L80 12.4204L149.837 139H80H10.163Z" stroke="white" stroke-width="12"/>
|
<path d="M10.163 139L80 12.4204L149.837 139H80H10.163Z" stroke="salmon" stroke-width="15"/>
|
||||||
<path d="M85.4488 50.3354V80.6619C85.4488 83.8784 85.2898 87.0418 84.9717 90.1522C84.6536 93.2273 84.2294 96.4968 83.6992 99.9606H74.8451C74.315 96.4968 73.8908 93.2273 73.5727 90.1522C73.2546 87.0418 73.0955 83.8784 73.0955 80.6619V50.3354H85.4488ZM71.0808 119.789C71.0808 118.694 71.2752 117.651 71.664 116.661C72.0882 115.672 72.6537 114.823 73.3606 114.117C74.1029 113.41 74.9689 112.844 75.9585 112.42C76.9482 111.996 78.0086 111.784 79.1396 111.784C80.2354 111.784 81.278 111.996 82.2677 112.42C83.2574 112.844 84.1057 113.41 84.8126 114.117C85.5195 114.823 86.085 115.672 86.5092 116.661C86.9333 117.651 87.1454 118.694 87.1454 119.789C87.1454 120.921 86.9333 121.981 86.5092 122.971C86.085 123.925 85.5195 124.756 84.8126 125.462C84.1057 126.169 83.2574 126.717 82.2677 127.106C81.278 127.53 80.2354 127.742 79.1396 127.742C78.0086 127.742 76.9482 127.53 75.9585 127.106C74.9689 126.717 74.1029 126.169 73.3606 125.462C72.6537 124.756 72.0882 123.925 71.664 122.971C71.2752 121.981 71.0808 120.921 71.0808 119.789Z" fill="#FFFBFB"/>
|
<path d="M85.4488 50.3354V80.6619C85.4488 83.8784 85.2898 87.0418 84.9717 90.1522C84.6536 93.2273 84.2294 96.4968 83.6992 99.9606H74.8451C74.315 96.4968 73.8908 93.2273 73.5727 90.1522C73.2546 87.0418 73.0955 83.8784 73.0955 80.6619V50.3354H85.4488ZM71.0808 119.789C71.0808 118.694 71.2752 117.651 71.664 116.661C72.0882 115.672 72.6537 114.823 73.3606 114.117C74.1029 113.41 74.9689 112.844 75.9585 112.42C76.9482 111.996 78.0086 111.784 79.1396 111.784C80.2354 111.784 81.278 111.996 82.2677 112.42C83.2574 112.844 84.1057 113.41 84.8126 114.117C85.5195 114.823 86.085 115.672 86.5092 116.661C86.9333 117.651 87.1454 118.694 87.1454 119.789C87.1454 120.921 86.9333 121.981 86.5092 122.971C86.085 123.925 85.5195 124.756 84.8126 125.462C84.1057 126.169 83.2574 126.717 82.2677 127.106C81.278 127.53 80.2354 127.742 79.1396 127.742C78.0086 127.742 76.9482 127.53 75.9585 127.106C74.9689 126.717 74.1029 126.169 73.3606 125.462C72.6537 124.756 72.0882 123.925 71.664 122.971C71.2752 121.981 71.0808 120.921 71.0808 119.789Z" fill="white"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
+43
-45
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<main class="app_main">
|
<main class="app_main">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<keep-alive exclude="JournalView,SceneryView">
|
<keep-alive exclude="SceneryView">
|
||||||
<component :is="Component" :key="$route.name" />
|
<component :is="Component" :key="$route.name" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</router-view>
|
</router-view>
|
||||||
@@ -33,30 +33,33 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, watch } from 'vue';
|
import { defineComponent, watch } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import packageInfo from '.././package.json';
|
||||||
|
|
||||||
import Clock from './components/App/Clock.vue';
|
import Clock from './components/App/Clock.vue';
|
||||||
|
|
||||||
import packageInfo from '.././package.json';
|
import { useMainStore } from './store/mainStore';
|
||||||
import { regions } from './data/options.json';
|
|
||||||
|
|
||||||
import { useStore } from './store/mainStore';
|
|
||||||
import StatusIndicator from './components/App/StatusIndicator.vue';
|
import StatusIndicator from './components/App/StatusIndicator.vue';
|
||||||
import TrainModal from './components/Global/TrainModal.vue';
|
|
||||||
import AppHeader from './components/App/AppHeader.vue';
|
import AppHeader from './components/App/AppHeader.vue';
|
||||||
import axios from 'axios';
|
import TrainModal from './components/TrainsView/TrainModal.vue';
|
||||||
|
|
||||||
import StorageManager from './managers/storageManager';
|
import StorageManager from './managers/storageManager';
|
||||||
|
import { useApiStore } from './store/apiStore';
|
||||||
|
import { Status } from './typings/common';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
Clock,
|
Clock,
|
||||||
StatusIndicator,
|
StatusIndicator,
|
||||||
TrainModal,
|
AppHeader,
|
||||||
AppHeader
|
TrainModal
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
VERSION: packageInfo.version,
|
VERSION: packageInfo.version,
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
|
apiStore: useApiStore(),
|
||||||
|
|
||||||
currentLang: 'pl',
|
currentLang: 'pl',
|
||||||
releaseURL: '',
|
releaseURL: '',
|
||||||
@@ -64,29 +67,10 @@ export default defineComponent({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.loadLang();
|
this.init();
|
||||||
this.store.connectToAPI();
|
|
||||||
|
|
||||||
this.store.isOffline = !window.navigator.onLine;
|
|
||||||
|
|
||||||
window.addEventListener('offline', () => {
|
|
||||||
this.store.isOffline = true;
|
|
||||||
|
|
||||||
this.store.activeData.activeSceneries = [];
|
|
||||||
this.store.activeData.trains = [];
|
|
||||||
this.store.activeData.connectedSocketCount = 0;
|
|
||||||
|
|
||||||
this.store.setStatuses();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('online', () => {
|
|
||||||
this.store.isOffline = false;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.setReleaseURL();
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => this.store.blockScroll,
|
() => this.store.blockScroll,
|
||||||
(value) => {
|
(value) => {
|
||||||
@@ -96,23 +80,37 @@ export default defineComponent({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
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: {
|
methods: {
|
||||||
|
init() {
|
||||||
|
this.loadLang();
|
||||||
|
this.setReleaseURL();
|
||||||
|
this.setupOfflineHandling();
|
||||||
|
|
||||||
|
this.apiStore.setupAPIData();
|
||||||
|
},
|
||||||
|
|
||||||
|
setupOfflineHandling() {
|
||||||
|
this.store.isOffline = !window.navigator.onLine;
|
||||||
|
|
||||||
|
if (this.store.isOffline) this.handleOfflineMode();
|
||||||
|
|
||||||
|
window.addEventListener('offline', this.handleOfflineMode);
|
||||||
|
window.addEventListener('online', this.handleOnlineMode);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOfflineMode() {
|
||||||
|
this.store.isOffline = true;
|
||||||
|
|
||||||
|
this.apiStore.activeData = undefined;
|
||||||
|
this.apiStore.dataStatuses.connection = Status.Data.Offline;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOnlineMode() {
|
||||||
|
this.store.isOffline = false;
|
||||||
|
|
||||||
|
this.apiStore.setupAPIData();
|
||||||
|
},
|
||||||
|
|
||||||
changeLang(lang: string) {
|
changeLang(lang: string) {
|
||||||
this.$i18n.locale = lang;
|
this.$i18n.locale = lang;
|
||||||
this.currentLang = lang;
|
this.currentLang = lang;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import StatusIndicator from './StatusIndicator.vue';
|
import StatusIndicator from './StatusIndicator.vue';
|
||||||
import Clock from './Clock.vue';
|
import Clock from './Clock.vue';
|
||||||
import RegionDropdown from '../Global/RegionDropdown.vue';
|
import RegionDropdown from '../Global/RegionDropdown.vue';
|
||||||
@@ -84,7 +84,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
store: useStore()
|
store: useMainStore()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -100,8 +100,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onlineDispatchersCount() {
|
onlineDispatchersCount() {
|
||||||
return this.store.onlineSceneryList.filter(
|
return this.store.activeSceneryList.filter(
|
||||||
(scenery) => scenery.region == this.store.region.id
|
(scenery) => scenery.region == this.store.region.id && scenery.dispatcherId != -1
|
||||||
).length;
|
).length;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,11 @@
|
|||||||
<circle id="Ellipse 18" cx="15" cy="17" r="7" fill="#393838" />
|
<circle id="Ellipse 18" cx="15" cy="17" r="7" fill="#393838" />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g v-if="greenLight" filter="url(#filter0_d_843_28)">
|
<g v-if="indicator.lights.greenLight" filter="url(#filter0_d_843_28)">
|
||||||
<circle cx="15" cy="17" r="7" fill="#00FF0A" />
|
<circle cx="15" cy="17" r="7" fill="#00FF0A" />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g v-if="greenBlinkLight" filter="url(#filter0_d_843_28)">
|
<g v-if="indicator.lights.greenBlinkLight" filter="url(#filter0_d_843_28)">
|
||||||
<circle cx="15" cy="17" r="7" fill="#00FF0A" />
|
<circle cx="15" cy="17" r="7" fill="#00FF0A" />
|
||||||
|
|
||||||
<animate
|
<animate
|
||||||
@@ -52,14 +52,14 @@
|
|||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g v-if="redTopLight" filter="url(#filter1_d_843_28)">
|
<g v-if="indicator.lights.redTopLight" filter="url(#filter1_d_843_28)">
|
||||||
<circle cx="15" cy="36" r="7" fill="#F40000" />
|
<circle cx="15" cy="36" r="7" fill="#F40000" />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g v-if="orangeLight" filter="url(#filter2_d_843_28)">
|
<g v-if="indicator.lights.orangeLight" filter="url(#filter2_d_843_28)">
|
||||||
<circle cx="15" cy="55" r="7" fill="#FFB800" />
|
<circle cx="15" cy="55" r="7" fill="#FFB800" />
|
||||||
</g>
|
</g>
|
||||||
<g v-if="redBottomLight" filter="url(#filter3_d_843_28)">
|
<g v-if="indicator.lights.redBottomLight" filter="url(#filter3_d_843_28)">
|
||||||
<circle cx="15" cy="74" r="7" fill="#F40000" />
|
<circle cx="15" cy="74" r="7" fill="#F40000" />
|
||||||
|
|
||||||
<animate
|
<animate
|
||||||
@@ -186,7 +186,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<transition name="tooltip-anim">
|
<transition name="tooltip-anim">
|
||||||
<div v-html="$t(indicator.message)" class="indicator-tooltip" v-if="tooltipActive"></div>
|
<div
|
||||||
|
v-html="$t('data-status.' + indicator.message)"
|
||||||
|
class="indicator-tooltip"
|
||||||
|
v-if="tooltipActive"
|
||||||
|
></div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -194,124 +198,112 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { StoreState } from '../../store/typings';
|
|
||||||
import { useStore } from '../../store/mainStore';
|
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
import { APIDataStatus } from '../../typings/api';
|
||||||
|
|
||||||
|
interface Indicator {
|
||||||
|
// status: Status.Data;
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
lights: {
|
||||||
|
greenLight: boolean;
|
||||||
|
greenBlinkLight: boolean;
|
||||||
|
redTopLight: boolean;
|
||||||
|
orangeLight: boolean;
|
||||||
|
redBottomLight: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tooltipActive: false,
|
tooltipActive: false,
|
||||||
indicator: {
|
apiStore: useApiStore()
|
||||||
offline: false,
|
|
||||||
status: Status.Data.Loading,
|
|
||||||
message: 'data-status.S3'
|
|
||||||
},
|
|
||||||
|
|
||||||
greenLight: false,
|
|
||||||
greenBlinkLight: false,
|
|
||||||
redTopLight: false,
|
|
||||||
orangeLight: false,
|
|
||||||
redBottomLight: false
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.setSignalStatus(Status.Data.Loading);
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const store = useStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
dataStatus: store.dataStatuses,
|
|
||||||
store
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
dataStatus: {
|
|
||||||
deep: true,
|
|
||||||
|
|
||||||
handler(statuses: StoreState['dataStatuses']) {
|
|
||||||
const connectionStatus = statuses.connection;
|
|
||||||
const sceneryDataStatus = statuses.sceneries;
|
|
||||||
const trainsDataStatus = statuses.trains;
|
|
||||||
const dispatcherDataStatus = statuses.dispatchers;
|
|
||||||
|
|
||||||
if (this.store.isOffline) {
|
|
||||||
this.setSignalStatus(Status.Data.Initialized);
|
|
||||||
this.indicator.status = Status.Data.Initialized;
|
|
||||||
this.indicator.message = 'data-status.S1-offline';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionStatus == Status.Data.Error) {
|
|
||||||
this.setSignalStatus(connectionStatus);
|
|
||||||
this.indicator.status = connectionStatus;
|
|
||||||
this.indicator.message = 'data-status.S1a-connection';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceneryDataStatus == Status.Data.Error) {
|
|
||||||
this.setSignalStatus(sceneryDataStatus);
|
|
||||||
this.indicator.status = sceneryDataStatus;
|
|
||||||
this.indicator.message = 'data-status.S1a-sceneries';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trainsDataStatus == Status.Data.Warning) {
|
|
||||||
this.setSignalStatus(trainsDataStatus);
|
|
||||||
this.indicator.status = trainsDataStatus;
|
|
||||||
this.indicator.message = 'data-status.S5-trains';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dispatcherDataStatus == Status.Data.Warning) {
|
|
||||||
this.setSignalStatus(dispatcherDataStatus);
|
|
||||||
this.indicator.status = dispatcherDataStatus;
|
|
||||||
this.indicator.message = 'data-status.S5-dispatchers';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceneryDataStatus == Status.Data.Loaded) {
|
|
||||||
this.setSignalStatus(Status.Data.Loaded);
|
|
||||||
|
|
||||||
this.indicator.status = Status.Data.Loaded;
|
|
||||||
this.indicator.message = 'data-status.S2';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setSignalStatus(status: Status.Data) {
|
setLights(message: string) {
|
||||||
this.greenLight = false;
|
let lights = {
|
||||||
this.greenBlinkLight = false;
|
greenBlinkLight: false,
|
||||||
this.redTopLight = false;
|
greenLight: false,
|
||||||
this.orangeLight = false;
|
orangeLight: false,
|
||||||
this.redBottomLight = false;
|
redBottomLight: false,
|
||||||
|
redTopLight: false
|
||||||
|
};
|
||||||
|
|
||||||
if (status == Status.Data.Initialized) {
|
switch (message) {
|
||||||
this.redTopLight = true;
|
case 'S3':
|
||||||
|
lights.greenBlinkLight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S2':
|
||||||
|
lights.greenLight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S1-offline':
|
||||||
|
lights.redTopLight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S1a-connection':
|
||||||
|
case 'S1a-sceneries':
|
||||||
|
lights.redTopLight = true;
|
||||||
|
lights.redBottomLight = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S5-dispatchers':
|
||||||
|
case 'S5-trains':
|
||||||
|
lights.orangeLight = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == Status.Data.Loaded) {
|
return lights;
|
||||||
this.greenLight = true;
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
indicator(): Indicator {
|
||||||
|
const dataStatuses = this.apiStore.dataStatuses;
|
||||||
|
const swdrStatuses = this.apiStore.activeData?.apiStatuses;
|
||||||
|
|
||||||
|
let message = 'S3';
|
||||||
|
|
||||||
|
switch (dataStatuses.connection) {
|
||||||
|
case Status.Data.Loading:
|
||||||
|
message = 'S3';
|
||||||
|
break;
|
||||||
|
case Status.Data.Loaded:
|
||||||
|
message = 'S2';
|
||||||
|
break;
|
||||||
|
case Status.Data.Offline:
|
||||||
|
message = 'S1-offline';
|
||||||
|
break;
|
||||||
|
case Status.Data.Error:
|
||||||
|
message = 'S1a-connection';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == Status.Data.Warning) {
|
if (swdrStatuses?.dispatchersAPI == APIDataStatus.WARNING) {
|
||||||
this.orangeLight = true;
|
message = 'S5-dispatchers';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == Status.Data.Error) {
|
if (swdrStatuses?.trainsAPI == APIDataStatus.WARNING) {
|
||||||
this.redTopLight = true;
|
message = 'S5-trains';
|
||||||
this.redBottomLight = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == Status.Data.Loading) {
|
if (swdrStatuses?.stationsAPI == APIDataStatus.WARNING) {
|
||||||
this.greenBlinkLight = true;
|
message = 'S1a-sceneries';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
lights: this.setLights(message),
|
||||||
|
message
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
emits: ['toggleModal'],
|
emits: ['toggleModal'],
|
||||||
@@ -23,7 +23,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore()
|
store: useMainStore()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, Ref, ref } from 'vue';
|
import { defineComponent, Ref, ref } from 'vue';
|
||||||
import { regions as regionsJSON } from '../../data/options.json';
|
import { regions as regionsJSON } from '../../data/options.json';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -41,7 +41,7 @@ interface Item {
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
selectedItemIndex: 0,
|
selectedItemIndex: 0,
|
||||||
listOpen: false
|
listOpen: false
|
||||||
};
|
};
|
||||||
@@ -59,6 +59,21 @@ export default defineComponent({
|
|||||||
'store.region.id': {
|
'store.region.id': {
|
||||||
handler(regionId) {
|
handler(regionId) {
|
||||||
this.selectedItemIndex = this.regionList.findIndex((reg) => reg.id == regionId);
|
this.selectedItemIndex = this.regionList.findIndex((reg) => reg.id == regionId);
|
||||||
|
|
||||||
|
console.log('region id', regionId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'$route.query.region': {
|
||||||
|
immediate: true,
|
||||||
|
handler(regionQuery: string) {
|
||||||
|
if (regionQuery) {
|
||||||
|
this.store.region.id =
|
||||||
|
regionsJSON.find(
|
||||||
|
(reg) =>
|
||||||
|
reg.id == regionQuery.toLocaleLowerCase() ||
|
||||||
|
reg.value.toLocaleLowerCase() == regionQuery.toLocaleLowerCase()
|
||||||
|
)?.id || 'eu';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -70,7 +85,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
regionList() {
|
regionList() {
|
||||||
return regionsJSON.map((region) => {
|
return regionsJSON.map((region) => {
|
||||||
const regionStationCount = this.store.onlineSceneryList.filter(
|
const regionStationCount = this.store.activeSceneryList.filter(
|
||||||
(scenery) => scenery.region == region.id
|
(scenery) => scenery.region == region.id
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ export default defineComponent({
|
|||||||
case Status.ActiveDispatcher.UNKNOWN:
|
case Status.ActiveDispatcher.UNKNOWN:
|
||||||
return 'unknown';
|
return 'unknown';
|
||||||
|
|
||||||
|
case Status.ActiveDispatcher.FREE:
|
||||||
|
return 'free';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (this.dispatcherTimestamp != null && this.dispatcherStatus >= Date.now() + 25500000)
|
if (this.dispatcherTimestamp != null && this.dispatcherStatus >= Date.now() + 25500000)
|
||||||
return 'no-limit';
|
return 'no-limit';
|
||||||
|
|||||||
@@ -50,8 +50,8 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
|
||||||
import { API } from '../../typings/api';
|
import { API } from '../../typings/api';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -63,15 +63,15 @@ export default defineComponent({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore()
|
apiStore: useApiStore()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onImageError(event: Event, stockName: string) {
|
onImageError(event: Event, stockName: string) {
|
||||||
const fallbackName =
|
const fallbackName =
|
||||||
Object.keys(this.store.rollingStockData!.info).find((type) => {
|
Object.keys(this.apiStore.rollingStockData!.info).find((type) => {
|
||||||
return this.store.rollingStockData!.info[type as keyof API.RollingStock.Info].find(
|
return this.apiStore.rollingStockData!.info[type as keyof API.RollingStock.Info].find(
|
||||||
(v) => v[0] === stockName.split(':')[0]
|
(v) => v[0] === stockName.split(':')[0]
|
||||||
);
|
);
|
||||||
}) || 'vehicle-unknown';
|
}) || 'vehicle-unknown';
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
<template>
|
|
||||||
<span class="stop-date">
|
|
||||||
<span
|
|
||||||
class="date arrival"
|
|
||||||
v-if="!stop.beginsHere"
|
|
||||||
:class="{
|
|
||||||
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 || stop.stopped)">
|
|
||||||
<s>{{ timestampToString(stop.arrivalTimestamp) }}</s>
|
|
||||||
{{ timestampToString(stop.arrivalRealTimestamp) }}
|
|
||||||
({{ stop.arrivalDelay > 0 ? '+' : '' }}{{ stop.arrivalDelay }})
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else>
|
|
||||||
{{ timestampToString(stop.arrivalTimestamp) }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<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 || stop.stopped)"
|
|
||||||
:class="{
|
|
||||||
delayed: stop.departureDelay > 0 && stop.confirmed,
|
|
||||||
preponed: stop.departureDelay < 0 && stop.confirmed
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="stop.departureDelay != 0 && stop.confirmed">
|
|
||||||
<s>{{ timestampToString(stop.departureTimestamp) }}</s>
|
|
||||||
{{ timestampToString(stop.departureRealTimestamp) }}
|
|
||||||
|
|
||||||
({{ stop.departureDelay > 0 ? '+' : '' }}{{ stop.departureDelay }})
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else>
|
|
||||||
{{ timestampToString(stop.departureTimestamp) }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { PropType, defineComponent } from 'vue';
|
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
|
||||||
import { TrainStop } from '../../store/typings';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
mixins: [dateMixin],
|
|
||||||
|
|
||||||
props: {
|
|
||||||
stop: {
|
|
||||||
type: Object as PropType<TrainStop>,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
$preponedClr: lime;
|
|
||||||
$delayedClr: salmon;
|
|
||||||
$dateClr: #525151;
|
|
||||||
$stopExchangeClr: #db8e29;
|
|
||||||
$stopDefaultClr: #252525;
|
|
||||||
|
|
||||||
.stop-date {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.date {
|
|
||||||
background: $dateClr;
|
|
||||||
padding: 0.3em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stop {
|
|
||||||
&.ph,
|
|
||||||
&.ph-pm,
|
|
||||||
&.pm {
|
|
||||||
background: $stopExchangeClr;
|
|
||||||
}
|
|
||||||
|
|
||||||
background: $stopDefaultClr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrival,
|
|
||||||
.departure {
|
|
||||||
&.delayed {
|
|
||||||
s {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
color: $delayedClr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.preponed {
|
|
||||||
s {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
color: $preponedClr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
|
||||||
import { API } from '../../typings/api';
|
import { API } from '../../typings/api';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -34,7 +34,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
apiStore: useApiStore(),
|
||||||
isNotFound: false,
|
isNotFound: false,
|
||||||
isLoaded: false
|
isLoaded: false
|
||||||
};
|
};
|
||||||
@@ -50,11 +50,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
stockType() {
|
stockType() {
|
||||||
if (!this.store.rollingStockData) return 'vehicle-unknown';
|
if (!this.apiStore.rollingStockData) return 'vehicle-unknown';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Object.keys(this.store.rollingStockData.info).find((type) => {
|
Object.keys(this.apiStore.rollingStockData.info).find((type) => {
|
||||||
return this.store.rollingStockData?.info[type as keyof API.RollingStock.Info].find(
|
return this.apiStore.rollingStockData?.info[type as keyof API.RollingStock.Info].find(
|
||||||
(v) => v[0] === this.name.split(':')[0]
|
(v) => v[0] === this.name.split(':')[0]
|
||||||
);
|
);
|
||||||
}) || 'vehicle-unknown'
|
}) || 'vehicle-unknown'
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section class="daily-stats">
|
|
||||||
<span :data-active="statsStatus">
|
|
||||||
<b v-if="statsStatus == Status.Data.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.maxTimetable">
|
|
||||||
•
|
|
||||||
<i18n-t keypath="journal.timetable-stats-longest">
|
|
||||||
<template #id>
|
|
||||||
<router-link :to="`/journal/timetables?timetableId=${stats.maxTimetable.id}`">
|
|
||||||
<b>{{ stats.maxTimetable.id }}</b>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
<template #author>
|
|
||||||
<router-link
|
|
||||||
:to="`/journal/dispatchers?dispatcherName=${stats.maxTimetable.authorName}`"
|
|
||||||
>
|
|
||||||
<b>{{ stats.maxTimetable.authorName }}</b>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
<template #driver>
|
|
||||||
<b class="text--primary">{{ stats.maxTimetable.driverName }}</b>
|
|
||||||
</template>
|
|
||||||
<template #distance>
|
|
||||||
<b class="text--primary">{{ stats.maxTimetable.routeDistance }} 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 { URLs } from '../../scripts/utils/apiURLs';
|
|
||||||
import { API } from '../../typings/api';
|
|
||||||
import { Status } from '../../typings/common';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
mixins: [dateMixin],
|
|
||||||
emits: ['toggleStatsOpen'],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
Status,
|
|
||||||
statsStatus: Status.Data.Loading,
|
|
||||||
intervalId: -1,
|
|
||||||
|
|
||||||
stats: {} as API.DailyStats.Response
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
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: API.DailyStats.Response = 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.stats = res;
|
|
||||||
|
|
||||||
this.statsStatus = Status.Data.Loaded;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Ups! Wystąpił błąd podczas pobierania statystyk rozkładów jazdy...');
|
|
||||||
this.statsStatus = Status.Data.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,165 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="stats_container" v-click-outside="() => (cardVisible = false)">
|
|
||||||
<button class="stats_button" @click="toggleCard">
|
|
||||||
Statystyki dyżurnego {{ store.dispatcherStatsName }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="stats_card" v-if="store.dispatcherStatsName && cardVisible">
|
|
||||||
<div>
|
|
||||||
<Loading v-if="!store.dispatcherStatsData" />
|
|
||||||
|
|
||||||
<div class="loading" v-else-if="!store.dispatcherStatsData._count._all">
|
|
||||||
Ten dyżurny nie ma jeszcze szczegółowych statystyk!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<span>{{ store.dispatcherStatsData._count._all }}</span>
|
|
||||||
</span>
|
|
||||||
<span class="stat-badge">
|
|
||||||
<span>SUMA (KM)</span>
|
|
||||||
<span>{{ store.dispatcherStatsData._sum.routeDistance.toFixed(2) }}km</span>
|
|
||||||
</span>
|
|
||||||
<span class="stat-badge">
|
|
||||||
<span>NAJDŁUŻSZY</span>
|
|
||||||
<span>{{ store.dispatcherStatsData._max.routeDistance.toFixed(2) }}km</span>
|
|
||||||
</span>
|
|
||||||
<span class="stat-badge">
|
|
||||||
<span>ŚREDNIO</span>
|
|
||||||
<span>{{ store.dispatcherStatsData._avg.routeDistance.toFixed(2) }}km</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>OSTATNIE WYSTAWIONE ROZKŁADY</h3>
|
|
||||||
<div class="last-timetables">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import axios from 'axios';
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { URLs } from '../../scripts/utils/apiURLs';
|
|
||||||
import { useStore } from '../../store/mainStore';
|
|
||||||
import Loading from '../Global/Loading.vue';
|
|
||||||
import { API } from '../../typings/api';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { Loading },
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const store = useStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
store
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
cardVisible: false,
|
|
||||||
lastDispatcherName: '',
|
|
||||||
timetables: [] as API.TimetableHistory.Response
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleCard() {
|
|
||||||
if (!this.store.dispatcherStatsName) return;
|
|
||||||
|
|
||||||
this.cardVisible = !this.cardVisible;
|
|
||||||
if (this.cardVisible) this.fetchDispatcherStats();
|
|
||||||
},
|
|
||||||
|
|
||||||
async fetchDispatcherStats() {
|
|
||||||
if (this.lastDispatcherName != this.store.dispatcherStatsName) {
|
|
||||||
this.store.dispatcherStatsData = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const statsData: API.DispatcherStats.Response = await (
|
|
||||||
await axios.get(
|
|
||||||
`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`
|
|
||||||
)
|
|
||||||
).data;
|
|
||||||
|
|
||||||
const timetables: API.TimetableHistory.Response = await (
|
|
||||||
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>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../styles/responsive.scss';
|
|
||||||
@import '../../styles/variables.scss';
|
|
||||||
|
|
||||||
.stats_container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats_card {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
|
||||||
top: 120%;
|
|
||||||
right: 0;
|
|
||||||
width: 500px;
|
|
||||||
max-width: 97vw;
|
|
||||||
min-height: 100px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
border-radius: 1em 0 1em 1em;
|
|
||||||
background-color: #222222f1;
|
|
||||||
box-shadow: 0 3px 10px 5px #131313;
|
|
||||||
padding: 1em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.last-timetables {
|
|
||||||
max-height: 400px;
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetable-row {
|
|
||||||
width: 95%;
|
|
||||||
margin: 0.5em auto;
|
|
||||||
padding: 0.5em;
|
|
||||||
|
|
||||||
background-color: #4d4d4d;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2.card-title {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.last-timetables {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
<template>
|
||||||
|
<section class="daily-stats">
|
||||||
|
<span :data-active="statsStatus">
|
||||||
|
<span class="stats-list">
|
||||||
|
<h3>
|
||||||
|
{{ $t('journal.daily-stats.title') }}
|
||||||
|
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<hr class="header-separator" />
|
||||||
|
|
||||||
|
<b v-if="statsStatus == Status.Data.Loading">
|
||||||
|
{{ $t('app.loading') }}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<b class="text--error" v-else-if="statsStatus == Status.Data.Error">
|
||||||
|
{{ $t('journal.stats-error') }}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<b v-else-if="topDispatchers.length == 0">
|
||||||
|
{{ $t('journal.daily-stats.info') }}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<div v-if="stats.totalTimetables">
|
||||||
|
•
|
||||||
|
<i18n-t keypath="journal.daily-stats.total">
|
||||||
|
<template #count>
|
||||||
|
<b class="text--primary">
|
||||||
|
{{ stats.totalTimetables }}
|
||||||
|
{{ $t('journal.daily-stats.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.maxTimetable">
|
||||||
|
•
|
||||||
|
<i18n-t keypath="journal.daily-stats.longest">
|
||||||
|
<template #id>
|
||||||
|
<router-link :to="`/journal/timetables?search-train=%23${stats.maxTimetable.id}`">
|
||||||
|
<b>{{ stats.maxTimetable.id }}</b>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #author>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/timetables?search-dispatcher=${stats.maxTimetable.authorName}`"
|
||||||
|
>
|
||||||
|
<b>{{ stats.maxTimetable.authorName }}</b>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #driver>
|
||||||
|
<b class="text--primary">{{ stats.maxTimetable.driverName }}</b>
|
||||||
|
</template>
|
||||||
|
<template #distance>
|
||||||
|
<b class="text--primary">{{ stats.maxTimetable.routeDistance }} km</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="topDispatchers.length == 1">
|
||||||
|
•
|
||||||
|
<i18n-t keypath="journal.daily-stats.most-active-dr">
|
||||||
|
<template #dispatcher>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/dispatchers?search-dispatcher=${topDispatchers[0].name}`"
|
||||||
|
>
|
||||||
|
<b>{{ topDispatchers[0].name }}</b>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #count>
|
||||||
|
<b class="text--primary">
|
||||||
|
{{ topDispatchers[0].count }}
|
||||||
|
{{ $t('journal.daily-stats.count', topDispatchers[0].count) }}
|
||||||
|
</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="topDispatchers.length > 1">
|
||||||
|
•
|
||||||
|
<i18n-t keypath="journal.daily-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?search-dispatcher=${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.daily-stats.count', topDispatchers[0].count) }}
|
||||||
|
</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="stats.longestDuties.length > 0">
|
||||||
|
•
|
||||||
|
<i18n-t keypath="journal.daily-stats.longest-duties">
|
||||||
|
<template #dispatcher>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/dispatchers?search-dispatcher=${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.daily-stats.most-active-driver">
|
||||||
|
<template #driver>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/timetables?search-driver=${stats.mostActiveDrivers[0].name}`"
|
||||||
|
>
|
||||||
|
<b>{{ stats.mostActiveDrivers[0].name }}</b>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #distance>
|
||||||
|
<b class="text--primary">{{ stats.mostActiveDrivers[0].distance.toFixed(2) }} km</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="section-separator" />
|
||||||
|
|
||||||
|
<div class="stats-badges">
|
||||||
|
<span
|
||||||
|
class="stat-badge"
|
||||||
|
v-for="key in [
|
||||||
|
'rippedSwitches',
|
||||||
|
'derailments',
|
||||||
|
'skippedStopSignals',
|
||||||
|
'radioStops',
|
||||||
|
'kills'
|
||||||
|
]"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<span>{{ $t(`journal.daily-stats.${key}`) }}</span>
|
||||||
|
<span>{{
|
||||||
|
Object.entries(stats.globalDiff).find(([k, v]) => k == key)?.[1] || '--'
|
||||||
|
}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
|
|
||||||
|
import { API } from '../../typings/api';
|
||||||
|
import { Status } from '../../typings/common';
|
||||||
|
import http from '../../http';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'journal-daily-stats',
|
||||||
|
|
||||||
|
mixins: [dateMixin],
|
||||||
|
// emits: ['toggleStatsOpen'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
Status,
|
||||||
|
statsStatus: Status.Data.Loading,
|
||||||
|
intervalId: -1,
|
||||||
|
|
||||||
|
stats: {} as API.DailyStats.Response
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
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: API.DailyStats.Response = await (await http.get('api/getDailyStats')).data;
|
||||||
|
|
||||||
|
this.stats = res;
|
||||||
|
|
||||||
|
this.statsStatus = Status.Data.Loaded;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ups! Wystąpił błąd podczas pobierania statystyk rozkładów jazdy...');
|
||||||
|
this.statsStatus = Status.Data.Error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
startFetchingDailyStats() {
|
||||||
|
this.fetchDailyTimetableStats();
|
||||||
|
|
||||||
|
if (this.intervalId != -1) return;
|
||||||
|
|
||||||
|
this.intervalId = window.setInterval(this.fetchDailyTimetableStats, 60000);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopFetchingDailyStats() {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../../styles/responsive.scss';
|
||||||
|
@import '../../styles/JournalStats.scss';
|
||||||
|
@import '../../styles/badge.scss';
|
||||||
|
|
||||||
|
.daily-stats {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.daily-stats > span[data-active='0'] {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-list a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-badges {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include smallScreen {
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<div class="journal-stats dispatcher" v-if="dispatcherName && stats">
|
||||||
|
<span class="loading" v-if="!stats.issuedTimetables && !stats.services">
|
||||||
|
{{ $t('journal.dispatcher-stats.empty') }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else>
|
||||||
|
<h3>
|
||||||
|
<i18n-t keypath="journal.dispatcher-stats.title">
|
||||||
|
<template #name>
|
||||||
|
<span class="text--primary">{{ dispatcherName.toUpperCase() }}</span>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<hr class="header-separator" />
|
||||||
|
|
||||||
|
<div class="info-stats">
|
||||||
|
<span class="stat-badge" v-if="stats.services">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.services-count') }}</span>
|
||||||
|
<span>{{ stats.services.count }}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stat-badge" v-if="stats.services">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.service-max') }}</span>
|
||||||
|
<span>{{ calculateDuration(stats.services.durationMax) }}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stat-badge" v-if="stats.services">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.service-avg') }}</span>
|
||||||
|
<span>{{ calculateDuration(stats.services.durationAvg) }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="section-separator" />
|
||||||
|
|
||||||
|
<div class="info-stats">
|
||||||
|
<span class="stat-badge" v-if="stats.issuedTimetables">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.timetables-count') }}</span>
|
||||||
|
<span>{{ stats.issuedTimetables.count }}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stat-badge" v-if="stats.issuedTimetables">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.timetables-sum') }}</span>
|
||||||
|
<span>{{ stats.issuedTimetables.distanceSum.toFixed(2) }}km</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stat-badge" v-if="stats.issuedTimetables">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.timetables-max') }}</span>
|
||||||
|
<span>{{ stats.issuedTimetables.distanceMax.toFixed(2) }}km</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stat-badge" v-if="stats.issuedTimetables">
|
||||||
|
<span>{{ $t('journal.dispatcher-stats.timetables-avg') }}</span>
|
||||||
|
<span>{{ stats.issuedTimetables.distanceAvg.toFixed(2) }}km</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import dateMixin from '../../../mixins/dateMixin';
|
||||||
|
import { useMainStore } from '../../../store/mainStore';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'journal-dispatcher-stats',
|
||||||
|
|
||||||
|
mixins: [dateMixin],
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const store = useMainStore();
|
||||||
|
|
||||||
|
return {
|
||||||
|
stats: store.dispatcherStatsData,
|
||||||
|
dispatcherName: store.dispatcherStatsName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../../../styles/JournalStats.scss';
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
<template>
|
||||||
|
<transition name="status-anim" mode="out-in">
|
||||||
|
<div :key="dataStatus">
|
||||||
|
<div class="journal_warning" v-if="store.isOffline">
|
||||||
|
{{ $t('app.offline') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Loading v-else-if="dataStatus == Status.Data.Loading" />
|
||||||
|
|
||||||
|
<div v-else-if="dataStatus == Status.Data.Error" class="journal_warning error">
|
||||||
|
{{ $t('app.error') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="journal_warning" v-else-if="dispatcherHistory.length == 0">
|
||||||
|
{{ $t('app.no-result') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<table class="dispatchers-table">
|
||||||
|
<thead>
|
||||||
|
<th>{{ $t('journal.history-name') }}</th>
|
||||||
|
<th>{{ $t('journal.history-hash') }}</th>
|
||||||
|
<th>{{ $t('journal.history-dispatcher') }}</th>
|
||||||
|
<th>{{ $t('journal.history-level') }}</th>
|
||||||
|
<th>{{ $t('journal.history-rate') }}</th>
|
||||||
|
<th>{{ $t('journal.history-region') }}</th>
|
||||||
|
<th>{{ $t('journal.history-date') }}</th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<transition-group name="list-anim">
|
||||||
|
<tr v-for="historyItem in dispatcherHistory" :key="historyItem.id">
|
||||||
|
<td>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/dispatchers?search-station=${historyItem.stationName}`"
|
||||||
|
>
|
||||||
|
<b>{{ historyItem.stationName }}</b>
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>#{{ historyItem.stationHash }}</td>
|
||||||
|
<td>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
|
||||||
|
>
|
||||||
|
<b
|
||||||
|
v-if="isDonator(historyItem.dispatcherName)"
|
||||||
|
class="text--donator"
|
||||||
|
:title="$t('donations.dispatcher-message')"
|
||||||
|
>
|
||||||
|
{{ historyItem.dispatcherName }}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<b v-else>
|
||||||
|
{{ historyItem.dispatcherName }}
|
||||||
|
</b>
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b
|
||||||
|
v-if="historyItem.dispatcherLevel !== null"
|
||||||
|
class="level-badge dispatcher"
|
||||||
|
:style="
|
||||||
|
calculateExpStyle(
|
||||||
|
historyItem.dispatcherLevel,
|
||||||
|
historyItem.dispatcherIsSupporter
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
|
||||||
|
</b>
|
||||||
|
</td>
|
||||||
|
<td class="text--primary">
|
||||||
|
<b>{{ historyItem.dispatcherRate }}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b class="region-badge" :aria-describedby="historyItem.region">{{
|
||||||
|
regions.find((r) => r.id == historyItem.region)?.value || '???'
|
||||||
|
}}</b>
|
||||||
|
</td>
|
||||||
|
<td style="min-width: 200px" class="time">
|
||||||
|
<span v-if="historyItem.timestampTo" class="text--offline">
|
||||||
|
<b>{{ $d(historyItem.timestampFrom) }}</b>
|
||||||
|
{{ timestampToString(historyItem.timestampFrom) }}
|
||||||
|
- {{ timestampToString(historyItem.timestampTo) }} ({{
|
||||||
|
calculateDuration(historyItem.currentDuration)
|
||||||
|
}})
|
||||||
|
</span>
|
||||||
|
<span class="dispatcher-online" v-else>
|
||||||
|
<b class="text--online">
|
||||||
|
<router-link :to="`/scenery?station=${historyItem.stationName}`">{{
|
||||||
|
$t('journal.online-since')
|
||||||
|
}}</router-link>
|
||||||
|
{{ timestampToString(historyItem.timestampFrom) }}
|
||||||
|
</b>
|
||||||
|
({{ calculateDuration(historyItem.currentDuration) }})
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</transition-group>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<AddDataButton
|
||||||
|
:list="dispatcherHistory"
|
||||||
|
:scrollDataLoaded="scrollDataLoaded"
|
||||||
|
:scrollNoMoreData="scrollNoMoreData"
|
||||||
|
@addHistoryData="addHistoryData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="journal_warning" v-if="scrollNoMoreData">
|
||||||
|
{{ $t('journal.no-further-data') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="journal_warning" v-else-if="!scrollDataLoaded">
|
||||||
|
{{ $t('journal.loading-further-data') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from 'vue';
|
||||||
|
import { regions } from '../../../data/options.json';
|
||||||
|
import { useMainStore } from '../../../store/mainStore';
|
||||||
|
import { API } from '../../../typings/api';
|
||||||
|
import { Status } from '../../../typings/common';
|
||||||
|
import Loading from '../../Global/Loading.vue';
|
||||||
|
import AddDataButton from '../../Global/AddDataButton.vue';
|
||||||
|
import dateMixin from '../../../mixins/dateMixin';
|
||||||
|
import donatorMixin from '../../../mixins/donatorMixin';
|
||||||
|
import styleMixin from '../../../mixins/styleMixin';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { Loading, AddDataButton },
|
||||||
|
|
||||||
|
mixins: [dateMixin, styleMixin, donatorMixin],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
dispatcherHistory: {
|
||||||
|
type: Array as PropType<API.DispatcherHistory.Response>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
scrollNoMoreData: {
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
scrollDataLoaded: {
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
addHistoryData: {
|
||||||
|
type: Function as PropType<() => void>
|
||||||
|
},
|
||||||
|
dataStatus: {
|
||||||
|
type: Number as PropType<Status.Data>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
Status,
|
||||||
|
store: useMainStore(),
|
||||||
|
regions
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
computedDispatcherHistory() {
|
||||||
|
return this.dispatcherHistory.reduce(
|
||||||
|
(acc, historyItem, i) => {
|
||||||
|
if (this.isAnotherDay(i - 1, i))
|
||||||
|
acc.push(new Date(historyItem.timestampFrom).toLocaleDateString('pl-PL'));
|
||||||
|
acc.push(historyItem);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
[] as (API.DispatcherHistory.Data | string)[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
navigateToScenery(name: string, isOnline: boolean) {
|
||||||
|
if (!isOnline) return;
|
||||||
|
|
||||||
|
this.$router.push(`/scenery?station=${name.trim().replace(/ /g, '_')}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
isAnotherDay(prevIndex: number, currIndex: number) {
|
||||||
|
if (currIndex == 0) return true;
|
||||||
|
|
||||||
|
return (
|
||||||
|
new Date(this.dispatcherHistory[prevIndex].timestampFrom).getDate() !=
|
||||||
|
new Date(this.dispatcherHistory[currIndex].timestampFrom).getDate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../../../styles/animations.scss';
|
||||||
|
@import '../../../styles/responsive.scss';
|
||||||
|
@import '../../../styles/badge.scss';
|
||||||
|
@import '../../../styles/variables.scss';
|
||||||
|
@import '../../../styles/JournalSection.scss';
|
||||||
|
|
||||||
|
table.dispatchers-table {
|
||||||
|
--_bg-table: #111;
|
||||||
|
--_bg-head: #101010;
|
||||||
|
--_bg-row: #2f2f2f;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--_bg-head);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
background-color: var(--_bg-row);
|
||||||
|
border-bottom: 2px solid black;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0.75em;
|
||||||
|
|
||||||
|
.level-badge {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
&--online {
|
||||||
|
color: springgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--offline {
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<transition name="status-anim" mode="out-in">
|
|
||||||
<div :key="dataStatus">
|
|
||||||
<div class="journal_warning" v-if="store.isOffline">
|
|
||||||
{{ $t('app.offline') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Loading v-else-if="dataStatus == Status.Data.Loading" />
|
|
||||||
|
|
||||||
<div v-else-if="dataStatus == Status.Data.Error" class="journal_warning error">
|
|
||||||
{{ $t('app.error') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="journal_warning" v-else-if="dispatcherHistory.length == 0">
|
|
||||||
{{ $t('app.no-result') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else>
|
|
||||||
<table class="scenery-history-table">
|
|
||||||
<thead>
|
|
||||||
<th>{{ $t('journal.history-name') }}</th>
|
|
||||||
<th>{{ $t('journal.history-hash') }}</th>
|
|
||||||
<th>{{ $t('journal.history-dispatcher') }}</th>
|
|
||||||
<th>{{ $t('journal.history-level') }}</th>
|
|
||||||
<th>{{ $t('journal.history-rate') }}</th>
|
|
||||||
<th>{{ $t('journal.history-region') }}</th>
|
|
||||||
<th>{{ $t('journal.history-date') }}</th>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<transition-group name="list-anim">
|
|
||||||
<tr v-for="historyItem in dispatcherHistory" :key="historyItem.id">
|
|
||||||
<td>
|
|
||||||
<router-link
|
|
||||||
:to="`/journal/dispatchers?sceneryName=${historyItem.stationName}`"
|
|
||||||
>
|
|
||||||
<b>{{ historyItem.stationName }}</b>
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>#{{ historyItem.stationHash }}</td>
|
|
||||||
<td>
|
|
||||||
<router-link
|
|
||||||
:to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`"
|
|
||||||
>
|
|
||||||
<b
|
|
||||||
v-if="isDonator(historyItem.dispatcherName)"
|
|
||||||
class="text--donator"
|
|
||||||
:title="$t('donations.dispatcher-message')"
|
|
||||||
>
|
|
||||||
{{ historyItem.dispatcherName }}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<b v-else>
|
|
||||||
{{ historyItem.dispatcherName }}
|
|
||||||
</b>
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<b
|
|
||||||
v-if="historyItem.dispatcherLevel !== null"
|
|
||||||
class="level-badge dispatcher"
|
|
||||||
:style="
|
|
||||||
calculateExpStyle(
|
|
||||||
historyItem.dispatcherLevel,
|
|
||||||
historyItem.dispatcherIsSupporter
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
|
|
||||||
</b>
|
|
||||||
</td>
|
|
||||||
<td class="text--primary">
|
|
||||||
<b>{{ historyItem.dispatcherRate }}</b>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<b class="region-badge" :aria-describedby="historyItem.region">{{
|
|
||||||
regions.find((r) => r.id == historyItem.region)?.value || '???'
|
|
||||||
}}</b>
|
|
||||||
</td>
|
|
||||||
<td style="min-width: 200px" class="time">
|
|
||||||
<span v-if="historyItem.timestampTo" class="text--offline">
|
|
||||||
<b>{{ $d(historyItem.timestampFrom) }}</b>
|
|
||||||
{{ timestampToString(historyItem.timestampFrom) }}
|
|
||||||
- {{ timestampToString(historyItem.timestampTo) }} ({{
|
|
||||||
calculateDuration(historyItem.currentDuration)
|
|
||||||
}})
|
|
||||||
</span>
|
|
||||||
<span class="dispatcher-online" v-else>
|
|
||||||
<b class="text--online">
|
|
||||||
<router-link :to="`/scenery?station=${historyItem.stationName}`">{{
|
|
||||||
$t('journal.online-since')
|
|
||||||
}}</router-link>
|
|
||||||
{{ timestampToString(historyItem.timestampFrom) }}
|
|
||||||
</b>
|
|
||||||
({{ calculateDuration(historyItem.currentDuration) }})
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</transition-group>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<AddDataButton
|
|
||||||
:list="dispatcherHistory"
|
|
||||||
:scrollDataLoaded="scrollDataLoaded"
|
|
||||||
:scrollNoMoreData="scrollNoMoreData"
|
|
||||||
@addHistoryData="addHistoryData"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<div class="journal_warning" v-if="scrollNoMoreData">
|
|
||||||
{{ $t('journal.no-further-data') }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="journal_warning" v-else-if="!scrollDataLoaded">
|
|
||||||
{{ $t('journal.loading-further-data') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, PropType } from 'vue';
|
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
|
||||||
import styleMixin from '../../mixins/styleMixin';
|
|
||||||
import { useStore } from '../../store/mainStore';
|
|
||||||
import Loading from '../Global/Loading.vue';
|
|
||||||
import { regions } from '../../data/options.json';
|
|
||||||
import AddDataButton from '../Global/AddDataButton.vue';
|
|
||||||
import { API } from '../../typings/api';
|
|
||||||
import { Status } from '../../typings/common';
|
|
||||||
import donatorMixin from '../../mixins/donatorMixin';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { Loading, AddDataButton },
|
|
||||||
|
|
||||||
mixins: [dateMixin, styleMixin, donatorMixin],
|
|
||||||
|
|
||||||
props: {
|
|
||||||
dispatcherHistory: {
|
|
||||||
type: Array as PropType<API.DispatcherHistory.Response>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
scrollNoMoreData: {
|
|
||||||
type: Boolean
|
|
||||||
},
|
|
||||||
scrollDataLoaded: {
|
|
||||||
type: Boolean
|
|
||||||
},
|
|
||||||
addHistoryData: {
|
|
||||||
type: Function as PropType<() => void>
|
|
||||||
},
|
|
||||||
dataStatus: {
|
|
||||||
type: Number as PropType<Status.Data>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
Status,
|
|
||||||
store: useStore(),
|
|
||||||
regions
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
computedDispatcherHistory() {
|
|
||||||
console.log(this.dispatcherHistory.length);
|
|
||||||
|
|
||||||
return this.dispatcherHistory.reduce(
|
|
||||||
(acc, historyItem, i) => {
|
|
||||||
if (this.isAnotherDay(i - 1, i))
|
|
||||||
acc.push(new Date(historyItem.timestampFrom).toLocaleDateString('pl-PL'));
|
|
||||||
acc.push(historyItem);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[] as (API.DispatcherHistory.Data | string)[]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
navigateToScenery(name: string, isOnline: boolean) {
|
|
||||||
if (!isOnline) return;
|
|
||||||
|
|
||||||
this.$router.push(`/scenery?station=${name.trim().replace(/ /g, '_')}`);
|
|
||||||
},
|
|
||||||
|
|
||||||
isAnotherDay(prevIndex: number, currIndex: number) {
|
|
||||||
if (currIndex == 0) return true;
|
|
||||||
|
|
||||||
return (
|
|
||||||
new Date(this.dispatcherHistory[prevIndex].timestampFrom).getDate() !=
|
|
||||||
new Date(this.dispatcherHistory[currIndex].timestampFrom).getDate()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../styles/animations.scss';
|
|
||||||
@import '../../styles/responsive.scss';
|
|
||||||
@import '../../styles/badge.scss';
|
|
||||||
@import '../../styles/variables.scss';
|
|
||||||
@import '../../styles/JournalSection.scss';
|
|
||||||
|
|
||||||
table.scenery-history-table {
|
|
||||||
--_bg-table: #111;
|
|
||||||
--_bg-head: #101010;
|
|
||||||
--_bg-row: #2f2f2f;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
thead {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
background-color: var(--_bg-head);
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr {
|
|
||||||
background-color: var(--_bg-row);
|
|
||||||
border-bottom: 2px solid black;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 0.75em;
|
|
||||||
|
|
||||||
.level-badge {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
&--online {
|
|
||||||
color: springgreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--offline {
|
|
||||||
color: #ddd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<h1 class="option-title">{{ $t('options.search-title') }}</h1>
|
<h1 class="option-title">{{ $t('options.search-title') }}</h1>
|
||||||
<div class="search_content">
|
<div class="search_content">
|
||||||
<div class="search" v-for="(_, propName) in searchersValues" :key="propName">
|
<div class="search" v-for="(_, propName) in searchersValues" :key="propName">
|
||||||
<label v-if="propName == 'search-date'" for="date">{{
|
<label v-if="propName == 'search-date'" for="search-date">{{
|
||||||
$t(`options.search-${optionsType}-date`)
|
$t(`options.search-${optionsType}-date`)
|
||||||
}}</label>
|
}}</label>
|
||||||
|
|
||||||
@@ -41,12 +41,13 @@
|
|||||||
<input
|
<input
|
||||||
class="search-input"
|
class="search-input"
|
||||||
v-model="searchersValues[propName]"
|
v-model="searchersValues[propName]"
|
||||||
@keydown.enter="onSearchConfirm"
|
@keydown.enter="searchConfirm"
|
||||||
@focus="preventKeyDown = true"
|
@focus="preventKeyDown = true"
|
||||||
@blur="preventKeyDown = false"
|
@blur="preventKeyDown = false"
|
||||||
:placeholder="$t(`options.${propName}`)"
|
:placeholder="$t(`options.${propName}`)"
|
||||||
:type="propName == 'search-date' ? 'date' : 'text'"
|
:type="propName == 'search-date' ? 'date' : 'text'"
|
||||||
:min="propName == 'search-date' ? '2022-02-01' : undefined"
|
:min="propName == 'search-date' ? '2022-02-01' : undefined"
|
||||||
|
:id="`${propName}`"
|
||||||
:list="propName.toString()"
|
:list="propName.toString()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -110,14 +111,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import axios from 'axios';
|
|
||||||
import { defineComponent, inject, PropType } from 'vue';
|
import { defineComponent, inject, PropType } from 'vue';
|
||||||
import keyMixin from '../../mixins/keyMixin';
|
import keyMixin from '../../mixins/keyMixin';
|
||||||
import { URLs } from '../../scripts/utils/apiURLs';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import { useStore } from '../../store/mainStore';
|
|
||||||
import { Journal } from './typings';
|
import { Journal } from './typings';
|
||||||
import { API } from '../../typings/api';
|
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import http from '../../http';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
|
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
|
||||||
@@ -158,7 +157,7 @@ export default defineComponent({
|
|||||||
dispatcherSuggestions: [] as string[],
|
dispatcherSuggestions: [] as string[],
|
||||||
|
|
||||||
searchTimeout: 0,
|
searchTimeout: 0,
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
|
|
||||||
JournalFilterSection: Journal.FilterSection
|
JournalFilterSection: Journal.FilterSection
|
||||||
};
|
};
|
||||||
@@ -182,12 +181,6 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
async 'store.driverStatsName'() {
|
|
||||||
await this.fetchDriverStats();
|
|
||||||
|
|
||||||
// if (value) this.store.currentStatsTab = 'driver';
|
|
||||||
},
|
|
||||||
|
|
||||||
async 'searchersValues.search-driver'(value: string | undefined) {
|
async 'searchersValues.search-driver'(value: string | undefined) {
|
||||||
clearTimeout(this.searchTimeout);
|
clearTimeout(this.searchTimeout);
|
||||||
|
|
||||||
@@ -206,29 +199,34 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async fetchDriverStats() {
|
// filters & sorters from URL params
|
||||||
this.store.driverStatsData = undefined;
|
handleRouteParams() {
|
||||||
|
this.$router.push({
|
||||||
|
query: {
|
||||||
|
...this.$route.query,
|
||||||
|
'sorter-active':
|
||||||
|
this.sorterOptionIds.indexOf(`${this.sorterActive.id}`) != 0
|
||||||
|
? this.sorterActive.id
|
||||||
|
: undefined,
|
||||||
|
...Object.keys(this.searchersValues).reduce(
|
||||||
|
(acc, k) => {
|
||||||
|
const searchVal = this.searchersValues[k as Journal.TimetableSearchKey];
|
||||||
|
|
||||||
if (!this.store.driverStatsName) {
|
acc[k] = searchVal || undefined;
|
||||||
this.store.driverStatsStatus = Status.Data.Initialized;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
return acc;
|
||||||
this.store.driverStatsStatus = Status.Data.Loading;
|
},
|
||||||
|
{} as { [k: string]: string | undefined }
|
||||||
const statsData: API.DriverStats.Response = await (
|
),
|
||||||
await axios.get(
|
...this.filterList?.reduce(
|
||||||
`${URLs.stacjownikAPI}/api/getDriverInfo?name=${this.store.driverStatsName}`
|
(acc, f) => {
|
||||||
|
if (f.isActive) acc[f.filterSection] = f.default ? undefined : f.id;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as { [k: string]: string | undefined }
|
||||||
)
|
)
|
||||||
).data;
|
}
|
||||||
|
});
|
||||||
this.store.driverStatsData = statsData;
|
|
||||||
this.store.driverStatsStatus = Status.Data.Loaded;
|
|
||||||
} catch (error) {
|
|
||||||
this.store.driverStatsStatus = Status.Data.Error;
|
|
||||||
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshData() {
|
refreshData() {
|
||||||
@@ -240,17 +238,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
window.clearTimeout(this.searchTimeout);
|
window.clearTimeout(this.searchTimeout);
|
||||||
|
|
||||||
this.searchTimeout = setTimeout(async () => {
|
this.searchTimeout = window.setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const suggestions: string[] = await (
|
const suggestions: string[] = await (
|
||||||
await axios.get(`${URLs.stacjownikAPI}/api/get${type}Suggestions?name=${value}`)
|
await http.get(`api/get${type}Suggestions?name=${value}`)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
this[`${type}Suggestions`] = suggestions;
|
this[`${type}Suggestions`] = suggestions;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this[`${type}Suggestions`] = [];
|
this[`${type}Suggestions`] = [];
|
||||||
}
|
}
|
||||||
}, 450);
|
}, 250);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override keyMixin function
|
// Override keyMixin function
|
||||||
@@ -265,7 +263,7 @@ export default defineComponent({
|
|||||||
onSorterChange(item: { id: string | number; value: string }) {
|
onSorterChange(item: { id: string | number; value: string }) {
|
||||||
this.sorterActive.id = item.id;
|
this.sorterActive.id = item.id;
|
||||||
this.sorterActive.dir = -1;
|
this.sorterActive.dir = -1;
|
||||||
this.$emit('onSearchConfirm');
|
this.searchConfirm();
|
||||||
},
|
},
|
||||||
|
|
||||||
onFilterChange(filter: Journal.TimetableFilter) {
|
onFilterChange(filter: Journal.TimetableFilter) {
|
||||||
@@ -275,25 +273,27 @@ export default defineComponent({
|
|||||||
.forEach((f) => (f.isActive = false));
|
.forEach((f) => (f.isActive = false));
|
||||||
filter.isActive = true;
|
filter.isActive = true;
|
||||||
|
|
||||||
this.$emit('onSearchConfirm');
|
this.searchConfirm();
|
||||||
},
|
},
|
||||||
|
|
||||||
onInputClear(id: any) {
|
onInputClear(id: any) {
|
||||||
this.searchersValues[id] = '';
|
this.searchersValues[id] = '';
|
||||||
this.$emit('onSearchConfirm');
|
this.searchConfirm();
|
||||||
},
|
},
|
||||||
|
|
||||||
onSearchConfirm() {
|
searchConfirm() {
|
||||||
this.$emit('onSearchConfirm');
|
this.$emit('onSearchConfirm');
|
||||||
|
this.handleRouteParams();
|
||||||
},
|
},
|
||||||
|
|
||||||
onSearchButtonConfirm() {
|
onSearchButtonConfirm() {
|
||||||
this.showOptions = false;
|
this.showOptions = false;
|
||||||
this.$emit('onSearchConfirm');
|
this.searchConfirm();
|
||||||
},
|
},
|
||||||
|
|
||||||
onResetButtonClick() {
|
onResetButtonClick() {
|
||||||
this.$emit('onOptionsReset');
|
this.$emit('onOptionsReset');
|
||||||
|
this.handleRouteParams();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,122 +1,85 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="journal-stats" v-if="!store.isOffline">
|
<div
|
||||||
<div class="tabs">
|
class="journal-stats dropdown"
|
||||||
|
v-if="!mainStore.isOffline"
|
||||||
|
@keydown.esc="currentStatsTab = null"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="dropdown_background"
|
||||||
|
v-if="currentStatsTab !== null"
|
||||||
|
@click="currentStatsTab = null"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div class="actions-bar">
|
||||||
<button
|
<button
|
||||||
v-for="tab in data.tabs"
|
v-for="button in statsButtons"
|
||||||
:key="tab.name"
|
:key="button.tab"
|
||||||
class="btn--filled"
|
class="btn--filled btn--image"
|
||||||
:data-selected="tab.name == store.currentStatsTab && areStatsOpen"
|
:data-selected="button.tab == currentStatsTab"
|
||||||
:data-inactive="tab.inactive"
|
:data-disabled="button.disabled"
|
||||||
:data-disabled="tab.inactive"
|
:disabled="button.disabled"
|
||||||
:disabled="tab.inactive"
|
@click="onTabButtonClick(button.tab)"
|
||||||
@click="onTabButtonClick(tab.name)"
|
|
||||||
>
|
>
|
||||||
{{ $t(tab.titlePath) }}
|
<img
|
||||||
|
v-if="button.iconName"
|
||||||
|
:src="`/images/icon-${button.iconName}.svg`"
|
||||||
|
:alt="button.iconName"
|
||||||
|
/>
|
||||||
|
{{ $t(button.localeKey) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stats-tab" v-show="areStatsOpen">
|
<transition name="dropdown-anim">
|
||||||
<keep-alive>
|
<div class="dropdown_wrapper" v-if="currentStatsTab !== null">
|
||||||
<JournalDailyStats
|
<keep-alive>
|
||||||
v-if="store.currentStatsTab == 'daily'"
|
<component :is="currentStatsTab" :key="currentStatsTab"></component>
|
||||||
@toggleStatsOpen="toggleStatsOpen"
|
</keep-alive>
|
||||||
/>
|
</div>
|
||||||
<JournalDriverStats v-else-if="store.currentStatsTab == 'driver'" />
|
</transition>
|
||||||
</keep-alive>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { computed, onMounted, reactive, Ref, ref, watch } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import JournalDailyStats from './DailyStats.vue';
|
|
||||||
import JournalDriverStats from './JournalDriverStats.vue';
|
|
||||||
import StorageManager from '../../managers/storageManager';
|
import StorageManager from '../../managers/storageManager';
|
||||||
|
import { Journal } from './typings';
|
||||||
|
import JournalDailyStats from './JournalDailyStats.vue';
|
||||||
|
import JournalDispatcherStats from '../JournalView/JournalDispatchers/JournalDispatcherStats.vue';
|
||||||
|
import JournalDriverStats from '../JournalView/JournalTimetables/JournalDriverStats.vue';
|
||||||
|
|
||||||
// Types
|
export default defineComponent({
|
||||||
type TStatTab = 'daily' | 'driver';
|
components: { JournalDailyStats, JournalDriverStats, JournalDispatcherStats },
|
||||||
|
props: {
|
||||||
// Variables
|
statsButtons: {
|
||||||
const store = useStore();
|
type: Array as PropType<Journal.StatsButton[]>,
|
||||||
|
required: true
|
||||||
const lastDailyStatsOpen = ref(false);
|
|
||||||
const areStatsOpen = ref(false);
|
|
||||||
const lastClickedTab: Ref<'daily' | 'driver' | null> = ref(null);
|
|
||||||
|
|
||||||
let data = reactive({
|
|
||||||
tabs: [
|
|
||||||
{
|
|
||||||
name: 'daily',
|
|
||||||
titlePath: 'journal.daily-stats-title'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'driver',
|
|
||||||
titlePath: 'journal.driver-stats-title'
|
|
||||||
// inactive: true,
|
|
||||||
}
|
}
|
||||||
] as { name: TStatTab; titlePath: string; inactive?: boolean }[]
|
},
|
||||||
});
|
data() {
|
||||||
|
return {
|
||||||
|
Journal,
|
||||||
|
mainStore: useMainStore(),
|
||||||
|
currentStatsTab: null as Journal.StatsTab | null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
// Methods
|
methods: {
|
||||||
function onTabButtonClick(tab: TStatTab) {
|
onTabButtonClick(tab: Journal.StatsTab) {
|
||||||
if (lastClickedTab.value == tab || !lastClickedTab.value || !areStatsOpen.value)
|
this.currentStatsTab = tab == this.currentStatsTab ? null : tab;
|
||||||
areStatsOpen.value = !areStatsOpen.value;
|
|
||||||
|
|
||||||
if (tab == 'daily') {
|
StorageManager.setStringValue('journalStatsTab', this.currentStatsTab ?? '');
|
||||||
StorageManager.setBooleanValue('dailyStatsOpen', areStatsOpen.value);
|
}
|
||||||
lastDailyStatsOpen.value = areStatsOpen.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
store.currentStatsTab = tab;
|
|
||||||
lastClickedTab.value = tab;
|
|
||||||
|
|
||||||
if (areStatsOpen.value == false) store.currentStatsTab = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleStatsOpen(open: boolean) {
|
|
||||||
areStatsOpen.value = open;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
computed(() => store.driverStatsData),
|
|
||||||
(statsData) => {
|
|
||||||
store.currentStatsTab = statsData ? 'driver' : lastClickedTab.value;
|
|
||||||
areStatsOpen.value = statsData ? true : lastClickedTab.value !== null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (StorageManager.getBooleanValue('dailyStatsOpen')) {
|
|
||||||
areStatsOpen.value = true;
|
|
||||||
store.currentStatsTab = 'daily';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/JournalStats.scss';
|
@import '../../styles/dropdown.scss';
|
||||||
|
@import '../../styles/dropdown_filters.scss';
|
||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
.tabs {
|
.dropdown_wrapper {
|
||||||
position: relative;
|
max-width: 100%;
|
||||||
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5em;
|
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.5em 0.75em;
|
|
||||||
|
|
||||||
&[data-inactive='true'] {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-selected='true'] {
|
|
||||||
color: $accentCol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+20
-21
@@ -1,14 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="journal-stats">
|
<div class="journal-stats driver" v-if="store.driverStatsData">
|
||||||
<span v-if="store.driverStatsData">
|
<span>
|
||||||
<h3>
|
<h3>
|
||||||
{{ $t('journal.stats-title') }}
|
<i18n-t keypath="journal.driver-stats.title">
|
||||||
<span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
|
<template #name>
|
||||||
|
<span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<hr class="header-separator" />
|
||||||
|
|
||||||
<div class="info-stats">
|
<div class="info-stats">
|
||||||
<span class="stat-badge">
|
<span class="stat-badge">
|
||||||
<span>{{ $t('journal.stats-timetables') }}</span>
|
<span>{{ $t('journal.driver-stats.timetables') }}</span>
|
||||||
<span
|
<span
|
||||||
>{{ store.driverStatsData._count.fulfilled }} /
|
>{{ store.driverStatsData._count.fulfilled }} /
|
||||||
{{ store.driverStatsData._count._all }}</span
|
{{ store.driverStatsData._count._all }}</span
|
||||||
@@ -16,17 +21,17 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="stat-badge">
|
||||||
<span>{{ $t('journal.stats-longest-timetable') }}</span>
|
<span>{{ $t('journal.driver-stats.longest-timetable') }}</span>
|
||||||
<span> {{ store.driverStatsData._max.routeDistance.toFixed(2) }}km </span>
|
<span> {{ store.driverStatsData._max.routeDistance.toFixed(2) }}km </span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="stat-badge">
|
||||||
<span>{{ $t('journal.stats-avg-timetable') }}</span>
|
<span>{{ $t('journal.driver-stats.avg-timetable') }}</span>
|
||||||
<span> {{ store.driverStatsData._avg.routeDistance.toFixed(2) }}km </span>
|
<span> {{ store.driverStatsData._avg.routeDistance.toFixed(2) }}km </span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="stat-badge">
|
||||||
<span>{{ $t('journal.stats-distance') }}</span>
|
<span>{{ $t('journal.driver-stats.distance') }}</span>
|
||||||
<span>
|
<span>
|
||||||
{{ store.driverStatsData._sum.currentDistance.toFixed(2) }} /
|
{{ store.driverStatsData._sum.currentDistance.toFixed(2) }} /
|
||||||
{{ store.driverStatsData._sum.routeDistance.toFixed(2) }}km
|
{{ store.driverStatsData._sum.routeDistance.toFixed(2) }}km
|
||||||
@@ -34,7 +39,7 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="stat-badge">
|
||||||
<span>{{ $t('journal.stats-stations') }}</span>
|
<span>{{ $t('journal.driver-stats.stations') }}</span>
|
||||||
<span>
|
<span>
|
||||||
{{ store.driverStatsData._sum.confirmedStopsCount }} /
|
{{ store.driverStatsData._sum.confirmedStopsCount }} /
|
||||||
{{ store.driverStatsData._sum.allStopsCount }}
|
{{ store.driverStatsData._sum.allStopsCount }}
|
||||||
@@ -42,26 +47,20 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<b v-else-if="store.driverStatsStatus == Status.Data.Loading">{{
|
|
||||||
$t('journal.stats-loading')
|
|
||||||
}}</b>
|
|
||||||
<b v-else-if="store.driverStatsStatus == Status.Data.Error">
|
|
||||||
{{ $t('journal.stats-error ') }}
|
|
||||||
</b>
|
|
||||||
<b v-else>{{ $t('journal.driver-stats-info') }}</b>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../../store/mainStore';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../../typings/common';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
name: 'journal-driver-stats',
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
Status: Status
|
Status: Status
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -69,5 +68,5 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/JournalStats.scss';
|
@import '../../../styles/JournalStats.scss';
|
||||||
</style>
|
</style>
|
||||||
@@ -42,7 +42,7 @@ import { defineComponent, PropType } from 'vue';
|
|||||||
import Loading from '../../Global/Loading.vue';
|
import Loading from '../../Global/Loading.vue';
|
||||||
import AddDataButton from '../../Global/AddDataButton.vue';
|
import AddDataButton from '../../Global/AddDataButton.vue';
|
||||||
import TimetableHistoryList from './TimetableHistoryList.vue';
|
import TimetableHistoryList from './TimetableHistoryList.vue';
|
||||||
import { useStore } from '../../../store/mainStore';
|
import { useMainStore } from '../../../store/mainStore';
|
||||||
import { Status } from '../../../typings/common';
|
import { Status } from '../../../typings/common';
|
||||||
import { API } from '../../../typings/api';
|
import { API } from '../../../typings/api';
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
Status,
|
Status,
|
||||||
store: useStore()
|
store: useMainStore()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -111,16 +111,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
@include smallScreen() {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-date {
|
.info-date {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badges {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
.info-badge {
|
.info-badge {
|
||||||
padding: 0.05em 0.35em;
|
padding: 0.05em 0.35em;
|
||||||
color: black;
|
color: black;
|
||||||
@@ -142,7 +143,14 @@ export default defineComponent({
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include smallScreen {
|
||||||
|
.item-general {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
export namespace Journal {
|
export namespace Journal {
|
||||||
export type DispatcherSearcher = {
|
export type DispatcherSearchKey = 'search-dispatcher' | 'search-station' | 'search-date';
|
||||||
[key in 'search-dispatcher' | 'search-station' | 'search-date']: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface DispatcherSorter {
|
|
||||||
id: 'timestampFrom' | 'duration';
|
|
||||||
dir: -1 | 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TimetableSearchKey =
|
export type TimetableSearchKey =
|
||||||
| 'search-driver'
|
| 'search-driver'
|
||||||
@@ -19,11 +12,29 @@ export namespace Journal {
|
|||||||
[key in TimetableSearchKey]: string;
|
[key in TimetableSearchKey]: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DispatcherSearchType = {
|
||||||
|
[key in DispatcherSearchKey]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TimetableSorterKey = 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
|
||||||
|
export type DispatcherSorterKey = 'timestampFrom' | 'duration';
|
||||||
|
|
||||||
|
export interface DispatcherSorter {
|
||||||
|
id: DispatcherSorterKey;
|
||||||
|
dir: -1 | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimetableSorter {
|
||||||
|
id: TimetableSorterKey;
|
||||||
|
dir: 'asc' | 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
export const enum TimetableFilterId {
|
export const enum TimetableFilterId {
|
||||||
|
ALL_STATUSES = 'all-statuses',
|
||||||
ACTIVE = 'active',
|
ACTIVE = 'active',
|
||||||
FULFILLED = 'fulfilled',
|
FULFILLED = 'fulfilled',
|
||||||
ABANDONED = 'abandoned',
|
ABANDONED = 'abandoned',
|
||||||
ALL = 'all',
|
ALL_SPECIALS = 'all-specials',
|
||||||
TWR = 'twr',
|
TWR = 'twr',
|
||||||
SKR = 'skr',
|
SKR = 'skr',
|
||||||
TWR_SKR = 'twr-skr'
|
TWR_SKR = 'twr-skr'
|
||||||
@@ -31,19 +42,26 @@ export namespace Journal {
|
|||||||
|
|
||||||
export enum FilterSection {
|
export enum FilterSection {
|
||||||
TIMETABLE_STATUS = 'timetable-status',
|
TIMETABLE_STATUS = 'timetable-status',
|
||||||
TWRSKR = 'twrskr'
|
SPECIAL = 'special'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TimetableFilter {
|
export interface TimetableFilter {
|
||||||
id: TimetableFilterId;
|
id: TimetableFilterId;
|
||||||
filterSection: string;
|
filterSection: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
default: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TimetableSorterKey = 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
|
export enum StatsTab {
|
||||||
|
DRIVER_STATS = 'journal-driver-stats',
|
||||||
|
DISPATCHER_STATS = 'journal-dispatcher-stats',
|
||||||
|
DAILY_STATS = 'journal-daily-stats'
|
||||||
|
}
|
||||||
|
|
||||||
export interface TimetableSorter {
|
export interface StatsButton {
|
||||||
id: TimetableSorterKey;
|
tab: StatsTab;
|
||||||
dir: 'asc' | 'desc';
|
localeKey: string;
|
||||||
|
iconName: string;
|
||||||
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
<tr v-for="historyItem in historyList" :key="historyItem.id">
|
<tr v-for="historyItem in historyList" :key="historyItem.id">
|
||||||
<td>#{{ historyItem.stationHash }}</td>
|
<td>#{{ historyItem.stationHash }}</td>
|
||||||
<td>
|
<td>
|
||||||
<router-link :to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`">
|
<router-link
|
||||||
|
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
|
||||||
|
>
|
||||||
<b>{{ historyItem.dispatcherName }}</b>
|
<b>{{ historyItem.dispatcherName }}</b>
|
||||||
</router-link>
|
</router-link>
|
||||||
</td>
|
</td>
|
||||||
@@ -33,6 +35,8 @@
|
|||||||
>
|
>
|
||||||
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
|
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
|
<b v-else>?</b>
|
||||||
</td>
|
</td>
|
||||||
<td class="text--primary">
|
<td class="text--primary">
|
||||||
<b>{{ historyItem.dispatcherRate }}</b>
|
<b>{{ historyItem.dispatcherRate }}</b>
|
||||||
@@ -66,17 +70,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import axios from 'axios';
|
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { URLs } from '../../scripts/utils/apiURLs';
|
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import styleMixin from '../../mixins/styleMixin';
|
import styleMixin from '../../mixins/styleMixin';
|
||||||
import listObserverMixin from '../../mixins/listObserverMixin';
|
import listObserverMixin from '../../mixins/listObserverMixin';
|
||||||
import { OnlineScenery } from '../../store/typings';
|
import { ActiveScenery } from '../../store/typings';
|
||||||
import { API } from '../../typings/api';
|
import { API } from '../../typings/api';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import http from '../../http';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SceneryDispatchersHistory',
|
name: 'SceneryDispatchersHistory',
|
||||||
@@ -84,12 +87,10 @@ export default defineComponent({
|
|||||||
components: { Loading },
|
components: { Loading },
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>
|
||||||
required: true
|
|
||||||
},
|
},
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -113,12 +114,20 @@ export default defineComponent({
|
|||||||
countFrom = 0,
|
countFrom = 0,
|
||||||
countLimit = 30
|
countLimit = 30
|
||||||
): Promise<API.DispatcherHistory.Response | null> {
|
): Promise<API.DispatcherHistory.Response | null> {
|
||||||
|
if (!this.station && !this.onlineScenery) {
|
||||||
|
this.dataStatus = Status.Data.Loaded;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.dataStatus = Status.Data.Loading;
|
this.dataStatus = Status.Data.Loading;
|
||||||
|
|
||||||
const requestString = `${URLs.stacjownikAPI}/api/getDispatchers?stationName=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
|
const requestString = `api/getDispatchers?stationName=${
|
||||||
|
this.station?.name || this.onlineScenery?.name
|
||||||
|
}&countFrom=${countFrom}&countLimit=${countLimit}`;
|
||||||
|
|
||||||
const historyAPIData: API.DispatcherHistory.Response = await (
|
const historyAPIData: API.DispatcherHistory.Response = await (
|
||||||
await axios.get(requestString)
|
await http.get(requestString)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
this.dataStatus = Status.Data.Loaded;
|
this.dataStatus = Status.Data.Loaded;
|
||||||
@@ -130,7 +139,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
navigateToHistory() {
|
navigateToHistory() {
|
||||||
this.$router.push(`/journal/dispatchers?sceneryName=${this.station.name}`);
|
this.$router.push(
|
||||||
|
`/journal/dispatchers?search-station=${this.station?.name || this.onlineScenery?.name}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="info-header">
|
<section class="info-header">
|
||||||
<a class="scenery-name" :href="station.generalInfo?.url" target="_blank">
|
<a class="scenery-name" :href="station?.generalInfo?.url" target="_blank">
|
||||||
{{ station.name }}
|
{{ stationName.replace(/_/g, ' ') }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="scenery-abbrev">
|
<div class="scenery-abbrev" v-if="station?.generalInfo?.abbr">
|
||||||
{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b>
|
{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo.abbr }}</b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="scenery-hash" v-if="onlineScenery?.hash">#{{ onlineScenery.hash }}</div>
|
<div class="scenery-hash" v-if="onlineScenery?.hash">#{{ onlineScenery.hash }}</div>
|
||||||
@@ -15,18 +15,21 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { OnlineScenery } from '../../store/typings';
|
import { ActiveScenery } from '../../store/typings';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>
|
||||||
|
},
|
||||||
|
|
||||||
|
stationName: {
|
||||||
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -58,4 +61,3 @@ export default defineComponent({
|
|||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
../../store/storeTypes
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="scenery-info">
|
<div class="scenery-info">
|
||||||
<section>
|
<section>
|
||||||
<div class="scenery-info-general" v-if="station.generalInfo">
|
<div class="scenery-info-general">
|
||||||
<SceneryInfoIcons :station="station" />
|
<SceneryInfoIcons :station="station" />
|
||||||
|
|
||||||
<div class="scenery-general-list">
|
<div class="scenery-general-list" v-if="station?.generalInfo">
|
||||||
<span>
|
<span>
|
||||||
<b>{{ $t('availability.title') }}:</b>
|
<b>{{ $t('availability.title') }}:</b>
|
||||||
{{ $t(`availability.${station.generalInfo.availability}`) }}
|
{{ $t(`availability.${station.generalInfo.availability}`) }}
|
||||||
@@ -46,11 +46,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SceneryInfoRoutes :station="station" />
|
<SceneryInfoRoutes v-if="station" :station="station" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="scenery-authors"
|
class="scenery-authors"
|
||||||
v-if="station.generalInfo.authors && station.generalInfo.authors.length > 0"
|
v-if="station?.generalInfo?.authors && station.generalInfo.authors.length > 0"
|
||||||
>
|
>
|
||||||
<b>
|
<b>
|
||||||
{{
|
{{
|
||||||
@@ -90,7 +90,7 @@ import SceneryInfoUserList from './SceneryInfo/SceneryInfoUserList.vue';
|
|||||||
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
|
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
|
||||||
import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue';
|
import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue';
|
||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { OnlineScenery } from '../../store/typings';
|
import { ActiveScenery } from '../../store/typings';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -102,13 +102,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>
|
||||||
required: true
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="info-dispatcher">
|
<section class="info-dispatcher">
|
||||||
<div class="dispatcher" v-if="onlineScenery">
|
<div class="dispatcher" v-if="onlineScenery && onlineScenery.dispatcherExp != -1">
|
||||||
<span
|
<span
|
||||||
class="dispatcher_level"
|
class="dispatcher_level"
|
||||||
:style="calculateExpStyle(onlineScenery.dispatcherExp, onlineScenery.dispatcherIsSupporter)"
|
:style="calculateExpStyle(onlineScenery.dispatcherExp, onlineScenery.dispatcherIsSupporter)"
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
class="dispatcher_name"
|
class="dispatcher_name"
|
||||||
:to="`/journal/dispatchers?dispatcherName=${onlineScenery.dispatcherName}`"
|
:to="`/journal/dispatchers?search-dispatcher=${onlineScenery.dispatcherName}`"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="text--donator"
|
class="text--donator"
|
||||||
@@ -42,14 +42,14 @@ import dateMixin from '../../../mixins/dateMixin';
|
|||||||
import routerMixin from '../../../mixins/routerMixin';
|
import routerMixin from '../../../mixins/routerMixin';
|
||||||
import styleMixin from '../../../mixins/styleMixin';
|
import styleMixin from '../../../mixins/styleMixin';
|
||||||
import StationStatusBadge from '../../Global/StationStatusBadge.vue';
|
import StationStatusBadge from '../../Global/StationStatusBadge.vue';
|
||||||
import { OnlineScenery } from '../../../store/typings';
|
import { ActiveScenery } from '../../../store/typings';
|
||||||
import donatorMixin from '../../../mixins/donatorMixin';
|
import donatorMixin from '../../../mixins/donatorMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [styleMixin, dateMixin, routerMixin, donatorMixin],
|
mixins: [styleMixin, dateMixin, routerMixin, donatorMixin],
|
||||||
props: {
|
props: {
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>,
|
||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,24 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="info-icons">
|
<section class="info-icons">
|
||||||
<span
|
<span v-if="!station || !station.generalInfo">
|
||||||
v-if="station.generalInfo && station.generalInfo.reqLevel >= 0"
|
<img
|
||||||
class="scenery-icon icon-info level"
|
class="icon-info"
|
||||||
:style="calculateExpStyle(station.generalInfo.reqLevel)"
|
src="/images/icon-unknown.svg"
|
||||||
>
|
alt="icon-unknown"
|
||||||
{{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }}
|
:title="$t('desc.unknown')"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="station.generalInfo"
|
v-if="station?.generalInfo && station?.generalInfo.reqLevel >= 0"
|
||||||
|
class="scenery-icon icon-info level"
|
||||||
|
:style="calculateExpStyle(station?.generalInfo.reqLevel)"
|
||||||
|
>
|
||||||
|
{{ station?.generalInfo.reqLevel >= 2 ? station?.generalInfo.reqLevel : 'L' }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="station?.generalInfo"
|
||||||
class="scenery-icon icon-info"
|
class="scenery-icon icon-info"
|
||||||
:class="station.generalInfo.controlType.replace('+', '-')"
|
:class="station?.generalInfo.controlType.replace('+', '-')"
|
||||||
:title="$t('desc.control-type') + $t(`controls.${station.generalInfo.controlType}`)"
|
:title="$t('desc.control-type') + $t(`controls.${station?.generalInfo.controlType}`)"
|
||||||
v-html="getControlTypeAbbrev(station.generalInfo.controlType)"
|
v-html="getControlTypeAbbrev(station?.generalInfo.controlType)"
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="station.generalInfo?.SUP"
|
v-if="station?.generalInfo?.SUP"
|
||||||
class="icon-info"
|
class="icon-info"
|
||||||
src="/images/icon-SUP.svg"
|
src="/images/icon-SUP.svg"
|
||||||
alt="SUP (RASP-UZK)"
|
alt="SUP (RASP-UZK)"
|
||||||
@@ -26,7 +35,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="station.generalInfo?.signalType"
|
v-if="station?.generalInfo?.signalType"
|
||||||
class="icon-info"
|
class="icon-info"
|
||||||
:src="`/images/icon-${station.generalInfo.signalType}.svg`"
|
:src="`/images/icon-${station.generalInfo.signalType}.svg`"
|
||||||
:alt="station.generalInfo.signalType"
|
:alt="station.generalInfo.signalType"
|
||||||
@@ -34,7 +43,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="station.generalInfo?.availability == 'nonPublic'"
|
v-if="station?.generalInfo?.availability == 'nonPublic'"
|
||||||
class="icon-info"
|
class="icon-info"
|
||||||
src="/images/icon-lock.svg"
|
src="/images/icon-lock.svg"
|
||||||
alt="Non-public scenery"
|
alt="Non-public scenery"
|
||||||
@@ -42,7 +51,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="station.generalInfo?.availability == 'unavailable'"
|
v-if="station?.generalInfo?.availability == 'unavailable'"
|
||||||
class="icon-info"
|
class="icon-info"
|
||||||
src="/images/icon-unavailable.svg"
|
src="/images/icon-unavailable.svg"
|
||||||
alt="Unavailable scenery"
|
alt="Unavailable scenery"
|
||||||
@@ -50,7 +59,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="station.generalInfo?.availability == 'abandoned'"
|
v-if="station?.generalInfo?.availability == 'abandoned'"
|
||||||
class="icon-info"
|
class="icon-info"
|
||||||
src="/images/icon-abandoned.svg"
|
src="/images/icon-abandoned.svg"
|
||||||
alt="Abandoned scenery"
|
alt="Abandoned scenery"
|
||||||
@@ -58,20 +67,12 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="station.generalInfo?.lines"
|
v-if="station?.generalInfo?.lines"
|
||||||
class="icon-info"
|
class="icon-info"
|
||||||
src="/images/icon-real.svg"
|
src="/images/icon-real.svg"
|
||||||
alt="real scenery"
|
alt="real scenery"
|
||||||
:title="`${$t('desc.real')} ${station.generalInfo.lines}`"
|
:title="`${$t('desc.real')} ${station.generalInfo.lines}`"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
|
||||||
v-if="!station.generalInfo"
|
|
||||||
class="icon-info"
|
|
||||||
src="/images/icon-unknown.svg"
|
|
||||||
alt="icon-unknown"
|
|
||||||
:title="$t('desc.unknown')"
|
|
||||||
/>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -85,8 +86,7 @@ export default defineComponent({
|
|||||||
mixins: [stationInfoMixin, styleMixin],
|
mixins: [stationInfoMixin, styleMixin],
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>
|
||||||
required: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,41 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="info-routes" v-if="station.generalInfo">
|
<section class="info-routes" v-if="station.generalInfo">
|
||||||
<div class="routes one-way" v-if="station.generalInfo.routes.oneWay.length > 0">
|
<div class="routes one-way" v-if="filteredOneWayRoutes.length > 0">
|
||||||
<b>{{ $t('scenery.one-way-routes') }}</b>
|
<b>{{ $t('scenery.one-way-routes') }}</b>
|
||||||
|
|
||||||
<ul class="routes-list">
|
<ul class="routes-list">
|
||||||
<li
|
<li
|
||||||
v-for="route in station.generalInfo.routes.oneWay"
|
v-for="route in filteredOneWayRoutes"
|
||||||
:key="route.name"
|
:key="route.routeName"
|
||||||
@click="setActiveShowLength(route.name)"
|
@click="setActiveShowLength(route.routeName)"
|
||||||
>
|
>
|
||||||
<span :class="{ 'no-catenary': !route.catenary, internal: route.isInternal }">
|
<span :class="{ 'no-catenary': !route.isElectric, internal: route.isInternal }">
|
||||||
{{ route.name }}</span
|
{{ route.routeName }}</span
|
||||||
>
|
>
|
||||||
<span v-if="route.speed" class="speed">
|
<span v-if="route.routeSpeed" class="speed">
|
||||||
{{ activeShowLength.includes(route.name) ? route.length + 'm' : route.speed }}
|
{{
|
||||||
|
activeShowLength.includes(route.routeName)
|
||||||
|
? route.routeLength + 'm'
|
||||||
|
: route.routeSpeed
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="route.SBL" class="sbl">SBL</span>
|
<span v-if="route.isRouteSBL" class="sbl">SBL</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="routes two-way" v-if="station.generalInfo.routes.twoWay.length > 0">
|
<div class="routes two-way" v-if="filteredTwoWayRoutes.length > 0">
|
||||||
<b>{{ $t('scenery.two-way-routes') }}</b>
|
<b>{{ $t('scenery.two-way-routes') }}</b>
|
||||||
|
|
||||||
<ul class="routes-list">
|
<ul class="routes-list">
|
||||||
<li
|
<li
|
||||||
v-for="route in station.generalInfo.routes.twoWay"
|
v-for="route in filteredTwoWayRoutes"
|
||||||
:key="route.name"
|
:key="route.routeName"
|
||||||
@click="setActiveShowLength(route.name)"
|
@click="setActiveShowLength(route.routeName)"
|
||||||
>
|
>
|
||||||
<span :class="{ 'no-catenary': !route.catenary, internal: route.isInternal }">{{
|
<span :class="{ 'no-catenary': !route.isElectric, internal: route.isInternal }">{{
|
||||||
route.name
|
route.routeName
|
||||||
}}</span>
|
}}</span>
|
||||||
<span v-if="route.speed" class="speed">
|
<span v-if="route.routeSpeed" class="speed">
|
||||||
{{ activeShowLength.includes(route.name) ? route.length + 'm' : route.speed }}
|
{{
|
||||||
|
activeShowLength.includes(route.routeName)
|
||||||
|
? route.routeLength + 'm'
|
||||||
|
: route.routeSpeed
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="route.SBL" class="sbl">SBL</span>
|
<span v-if="route.isRouteSBL" class="sbl">SBL</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,6 +53,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import Station from '../../../scripts/interfaces/Station';
|
import Station from '../../../scripts/interfaces/Station';
|
||||||
|
import { StationRoutesInfo } from '../../../store/typings';
|
||||||
|
|
||||||
|
const routeFilter = (route: StationRoutesInfo) => !route.hidden;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -66,6 +77,16 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
activeShowLength: [] as string[]
|
activeShowLength: [] as string[]
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
filteredOneWayRoutes() {
|
||||||
|
return this.station.generalInfo?.routes.oneWay.filter(routeFilter) || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredTwoWayRoutes() {
|
||||||
|
return this.station.generalInfo?.routes.twoWay.filter(routeFilter) || [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -30,12 +30,12 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import { OnlineScenery } from '../../../store/typings';
|
import { ActiveScenery } from '../../../store/typings';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>,
|
||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -32,14 +32,14 @@
|
|||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import modalTrainMixin from '../../../mixins/modalTrainMixin';
|
import modalTrainMixin from '../../../mixins/modalTrainMixin';
|
||||||
import routerMixin from '../../../mixins/routerMixin';
|
import routerMixin from '../../../mixins/routerMixin';
|
||||||
import { OnlineScenery } from '../../../store/typings';
|
import { ActiveScenery } from '../../../store/typings';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [routerMixin, modalTrainMixin],
|
mixins: [routerMixin, modalTrainMixin],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>,
|
||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,21 @@
|
|||||||
<span>{{ $t('scenery.timetables') }}</span>
|
<span>{{ $t('scenery.timetables') }}</span>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<span class="text--primary">{{ onlineScenery?.scheduledTrainCount.all || 0 }}</span>
|
<span class="text--primary">{{ onlineScenery?.scheduledTrainCount.all ?? 0 }}</span>
|
||||||
<span> / </span>
|
<span> / </span>
|
||||||
<span class="text--grayed">
|
<span class="text--grayed">
|
||||||
{{ onlineScenery?.scheduledTrainCount.confirmed || '0' }}
|
{{ onlineScenery?.scheduledTrainCount.confirmed ?? 0 }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="header_links">
|
<span class="header_links" v-if="station">
|
||||||
<a
|
<!-- <a
|
||||||
:href="`https://pragotron-td2.web.app/board?name=${station.name}`"
|
:href="`https://pragotron-td2.web.app/board?name=${station.name}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:title="$t('scenery.pragotron-link')"
|
:title="$t('scenery.pragotron-link')"
|
||||||
>
|
>
|
||||||
<img src="/images/icon-pragotron.svg" alt="icon-pragotron" />
|
<img src="/images/icon-pragotron.svg" alt="icon-pragotron" />
|
||||||
</a>
|
</a> -->
|
||||||
|
|
||||||
<a :href="tabliceZbiorczeHref" target="_blank" :title="$t('scenery.tablice-link')">
|
<a :href="tabliceZbiorczeHref" target="_blank" :title="$t('scenery.tablice-link')">
|
||||||
<img src="/images/icon-tablice.ico" alt="icon-tablice" />
|
<img src="/images/icon-tablice.ico" alt="icon-tablice" />
|
||||||
@@ -33,12 +33,12 @@
|
|||||||
{{ (i > 0 && '•') || '' }}
|
{{ (i > 0 && '•') || '' }}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
:key="cp.checkpointName"
|
:key="cp"
|
||||||
class="checkpoint_item"
|
class="checkpoint_item"
|
||||||
:class="{ current: chosenCheckpoint === cp.checkpointName }"
|
:class="{ current: chosenCheckpoint === cp }"
|
||||||
@click="setCheckpoint(cp)"
|
@click="setCheckpoint(cp)"
|
||||||
>
|
>
|
||||||
{{ cp.checkpointName }}
|
{{ cp }}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<transition-group name="list-anim">
|
<transition-group name="list-anim">
|
||||||
<div
|
<div
|
||||||
style="padding-bottom: 5em"
|
style="padding-bottom: 5em"
|
||||||
v-if="store.dataStatuses.trains == 0 && computedScheduledTrains.length == 0"
|
v-if="apiStore.dataStatuses.connection == 0 && computedScheduledTrains.length == 0"
|
||||||
key="list-loading"
|
key="list-loading"
|
||||||
>
|
>
|
||||||
<Loading />
|
<Loading />
|
||||||
@@ -187,10 +187,11 @@ import Loading from '../Global/Loading.vue';
|
|||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import routerMixin from '../../mixins/routerMixin';
|
import routerMixin from '../../mixins/routerMixin';
|
||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||||
import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
|
import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
|
||||||
import { OnlineScenery } from '../../store/typings';
|
import { ActiveScenery } from '../../store/typings';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SceneryTimetable',
|
name: 'SceneryTimetable',
|
||||||
@@ -201,12 +202,10 @@ export default defineComponent({
|
|||||||
|
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>
|
||||||
required: true
|
|
||||||
},
|
},
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -226,36 +225,41 @@ export default defineComponent({
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const currentURL = computed(() => `${location.origin}${route.fullPath}`);
|
const currentURL = computed(() => `${location.origin}${route.fullPath}`);
|
||||||
|
|
||||||
const store = useStore();
|
const apiStore = useApiStore();
|
||||||
|
const mainStore = useMainStore();
|
||||||
|
|
||||||
const chosenCheckpoint = ref(
|
const chosenCheckpoint = ref(
|
||||||
props.station?.generalInfo?.checkpoints?.length == 0
|
props.station?.generalInfo?.checkpoints?.length == 0
|
||||||
? ''
|
? ''
|
||||||
: props.station?.generalInfo?.checkpoints[0].checkpointName || null
|
: props.station?.generalInfo?.checkpoints[0] ?? null
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentURL,
|
currentURL,
|
||||||
chosenCheckpoint,
|
chosenCheckpoint,
|
||||||
store
|
apiStore,
|
||||||
|
mainStore
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
tabliceZbiorczeHref() {
|
tabliceZbiorczeHref() {
|
||||||
let url = `https://tablice-td2.web.app/?station=${this.station.name}`;
|
let url = `https://tablice-td2.web.app/?station=${this.station!.name}`;
|
||||||
if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`;
|
if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`;
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|
||||||
computedScheduledTrains() {
|
computedScheduledTrains() {
|
||||||
|
if (!this.station) return [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.onlineScenery?.scheduledTrains
|
this.onlineScenery?.scheduledTrains
|
||||||
?.filter(
|
?.filter(
|
||||||
(train) =>
|
(train) =>
|
||||||
train.checkpointName.toLocaleLowerCase() ==
|
train.checkpointName.toLocaleLowerCase() ==
|
||||||
(this.chosenCheckpoint || this.station.name).toLocaleLowerCase()
|
(this.chosenCheckpoint || this.station!.name).toLocaleLowerCase() &&
|
||||||
|
train.region == this.mainStore.region.id
|
||||||
)
|
)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a.stopStatusID > b.stopStatusID) return 1;
|
if (a.stopStatusID > b.stopStatusID) return 1;
|
||||||
@@ -272,12 +276,13 @@ export default defineComponent({
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
loadSelectedOption() {
|
loadSelectedOption() {
|
||||||
this.chosenCheckpoint =
|
if (!this.station) return;
|
||||||
this.station.generalInfo?.checkpoints[0]?.checkpointName || this.station.name;
|
|
||||||
|
this.chosenCheckpoint = this.station.generalInfo?.checkpoints[0] ?? this.station.name;
|
||||||
},
|
},
|
||||||
|
|
||||||
setCheckpoint(cp: { checkpointName: string }) {
|
setCheckpoint(cp: string) {
|
||||||
this.chosenCheckpoint = cp.checkpointName;
|
this.chosenCheckpoint = cp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="historyItem in historyList" :key="historyItem.id">
|
<tr v-for="historyItem in historyList" :key="historyItem.id">
|
||||||
<td>
|
<td>
|
||||||
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">
|
<router-link :to="`/journal/timetables?search-train=%23${historyItem.id}`">
|
||||||
#{{ historyItem.id }}
|
#{{ historyItem.id }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</td>
|
</td>
|
||||||
@@ -37,11 +37,16 @@
|
|||||||
{{ historyItem.trainNo }}
|
{{ historyItem.trainNo }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ historyItem.route.replace('|', ' -> ') }}</td>
|
<td>{{ historyItem.route.replace('|', ' -> ') }}</td>
|
||||||
<td>{{ historyItem.driverName }}</td>
|
<td>
|
||||||
|
<router-link :to="`/journal/timetables?search-driver=${historyItem.driverName}`">
|
||||||
|
{{ historyItem.driverName }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="historyItem.authorName"
|
v-if="historyItem.authorName"
|
||||||
:to="`/journal/timetables?authorName=${historyItem.authorName}`"
|
:to="`/journal/timetables?search-dispatcher=${historyItem.authorName}`"
|
||||||
>{{ historyItem.authorName }}
|
>{{ historyItem.authorName }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
|
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
|
||||||
@@ -63,29 +68,26 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import axios from 'axios';
|
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
|
|
||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { URLs } from '../../scripts/utils/apiURLs';
|
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import listObserverMixin from '../../mixins/listObserverMixin';
|
import listObserverMixin from '../../mixins/listObserverMixin';
|
||||||
import { OnlineScenery } from '../../store/typings';
|
import { ActiveScenery } from '../../store/typings';
|
||||||
import { API } from '../../typings/api';
|
import { API } from '../../typings/api';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import http from '../../http';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SceneryTimetablesHistory',
|
name: 'SceneryTimetablesHistory',
|
||||||
mixins: [dateMixin, listObserverMixin],
|
mixins: [dateMixin, listObserverMixin],
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>
|
||||||
required: true
|
|
||||||
},
|
},
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
type: Object as PropType<OnlineScenery>,
|
type: Object as PropType<ActiveScenery>
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -102,11 +104,20 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async fetchAPIData(countFrom = 0, countLimit = 15) {
|
async fetchAPIData() {
|
||||||
try {
|
if (!this.station && !this.onlineScenery) {
|
||||||
const requestString = `${URLs.stacjownikAPI}/api/getTimetables?issuedFrom=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
|
this.dataStatus = Status.Data.Loaded;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const response: API.TimetableHistory.Response = await (await axios.get(requestString)).data;
|
try {
|
||||||
|
const response: API.TimetableHistory.Response = await (
|
||||||
|
await http.get('api/getTimetables', {
|
||||||
|
params: {
|
||||||
|
issuedFrom: this.station?.name || this.onlineScenery?.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
this.historyList = response;
|
this.historyList = response;
|
||||||
|
|
||||||
@@ -117,7 +128,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
navigateToHistory() {
|
navigateToHistory() {
|
||||||
this.$router.push(`/journal/timetables?issuedFrom=${this.station.name}`);
|
this.$router.push({
|
||||||
|
path: '/journal/timetables',
|
||||||
|
query: {
|
||||||
|
'search-issuedFrom': this.station?.name || this.onlineScenery?.name
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: { Loading }
|
components: { Loading }
|
||||||
|
|||||||
@@ -60,8 +60,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card_timestamp" style="text-align: center">
|
<section class="card_timestamp">
|
||||||
<div>{{ $t('filters.minimum-hours-title') }}</div>
|
<h3 class="section-header">{{ $t('filters.minimum-hours-title') }}</h3>
|
||||||
|
|
||||||
<span class="clock">
|
<span class="clock">
|
||||||
<button class="btn--action" @click="subHour">-</button>
|
<button class="btn--action" @click="subHour">-</button>
|
||||||
<span>{{
|
<span>{{
|
||||||
@@ -75,16 +76,27 @@
|
|||||||
</span>
|
</span>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<datalist id="authors">
|
||||||
|
<option v-for="(author, i) in authors" :key="i" :value="author"></option>
|
||||||
|
</datalist>
|
||||||
|
|
||||||
<section class="card_authors-search">
|
<section class="card_authors-search">
|
||||||
<input
|
<h3 class="section-header">{{ $t('filters.authors-search') }}</h3>
|
||||||
type="text"
|
|
||||||
:placeholder="$t('filters.authors-search')"
|
<form action="javascript:void(0);" @submit="handleAuthorsInput">
|
||||||
name="authors"
|
<input
|
||||||
v-model="authorsInputValue"
|
type="text"
|
||||||
@input="handleAuthorsInput"
|
id="author"
|
||||||
@focus="preventKeyDown = true"
|
list="authors"
|
||||||
@blur="preventKeyDown = false"
|
name="authors"
|
||||||
/>
|
:placeholder="$t('filters.authors-placeholder')"
|
||||||
|
v-model="authorsInputValue"
|
||||||
|
@focus="preventKeyDown = true"
|
||||||
|
@blur="preventKeyDown = false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button class="btn--action">{{ $t('filters.authors-button-title') }}</button>
|
||||||
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card_sliders">
|
<section class="card_sliders">
|
||||||
@@ -139,7 +151,7 @@ import { defineComponent, inject } from 'vue';
|
|||||||
import keyMixin from '../../mixins/keyMixin';
|
import keyMixin from '../../mixins/keyMixin';
|
||||||
import routerMixin from '../../mixins/routerMixin';
|
import routerMixin from '../../mixins/routerMixin';
|
||||||
import { useStationFiltersStore } from '../../store/stationFiltersStore';
|
import { useStationFiltersStore } from '../../store/stationFiltersStore';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
|
|
||||||
import FilterOption from './FilterOption.vue';
|
import FilterOption from './FilterOption.vue';
|
||||||
import StorageManager from '../../managers/storageManager';
|
import StorageManager from '../../managers/storageManager';
|
||||||
@@ -163,7 +175,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const isVisible = inject('isFilterCardVisible');
|
const isVisible = inject('isFilterCardVisible');
|
||||||
const store = useStore();
|
const store = useMainStore();
|
||||||
const filterStore = useStationFiltersStore();
|
const filterStore = useStationFiltersStore();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -196,6 +208,19 @@ export default defineComponent({
|
|||||||
|
|
||||||
currentOptionsActive() {
|
currentOptionsActive() {
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
authors() {
|
||||||
|
return this.store.stationList
|
||||||
|
.reduce((acc, station) => {
|
||||||
|
station.generalInfo?.authors?.forEach((author) => {
|
||||||
|
if (author.trim() != '' && !acc.includes(author.toLocaleLowerCase()))
|
||||||
|
acc.push(author.toLocaleLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] as string[])
|
||||||
|
.sort((a, b) => a.localeCompare(b));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -230,12 +255,12 @@ export default defineComponent({
|
|||||||
if (this.saveOptions) StorageManager.setStringValue(target.name, target.value);
|
if (this.saveOptions) StorageManager.setStringValue(target.name, target.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleAuthorsInput(e: Event) {
|
handleAuthorsInput() {
|
||||||
clearTimeout(this.delayInputTimer);
|
console.log(this.authorsInputValue);
|
||||||
|
|
||||||
this.delayInputTimer = window.setTimeout(() => {
|
this.filterStore.changeFilterValue('authors', this.authorsInputValue);
|
||||||
this.handleInput(e);
|
|
||||||
}, 400);
|
if (this.saveOptions) StorageManager.setStringValue('authors', this.authorsInputValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
changeNumericFilterValue(name: string, value: number, saveToStorage = false) {
|
changeNumericFilterValue(name: string, value: number, saveToStorage = false) {
|
||||||
@@ -297,136 +322,139 @@ export default defineComponent({
|
|||||||
@import '../../styles/card.scss';
|
@import '../../styles/card.scss';
|
||||||
@import '../../styles/animations.scss';
|
@import '../../styles/animations.scss';
|
||||||
|
|
||||||
|
h3.section-header {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr auto;
|
grid-template-rows: 1fr auto;
|
||||||
|
}
|
||||||
|
|
||||||
&_info {
|
.card_info {
|
||||||
background-color: #111;
|
background-color: #111;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 0.5em 0.5em 0 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_content {
|
||||||
|
padding: 1em 0.5em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
gap: 1em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_title {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $accentCol;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_regions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
label > input {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&_controls {
|
label > span {
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
margin: 0 0.25em;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
background-color: gray;
|
||||||
|
|
||||||
|
&.checked {
|
||||||
|
background-color: seagreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_timestamp {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.clock {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
font-size: 1.2em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
min-width: 120px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: $accentCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.2em 0.6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_authors-search {
|
||||||
|
margin: 1em 0;
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 70%;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 0.5em;
|
||||||
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card_actions {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
.filter-option {
|
||||||
|
max-width: 50%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
input {
|
|
||||||
border-radius: 0.5em 0.5em 0 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_content {
|
|
||||||
padding: 1em 0.5em;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
gap: 1em;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
font-size: 2em;
|
|
||||||
font-weight: 700;
|
|
||||||
color: $accentCol;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_regions {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
label > input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
label > span {
|
|
||||||
padding: 0.25em 0.5em;
|
|
||||||
margin: 0 0.25em;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
background-color: gray;
|
|
||||||
|
|
||||||
&.checked {
|
|
||||||
background-color: seagreen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_timestamp {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.clock {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
font-size: 1.2em;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
|
|
||||||
span {
|
|
||||||
min-width: 120px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: $accentCol;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 0.2em 0.6em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_modes {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.option {
|
|
||||||
margin: 0 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_authors-search {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 60%;
|
|
||||||
min-width: 240px;
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.5em;
|
|
||||||
border: 1px solid white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_actions {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5em;
|
|
||||||
|
|
||||||
.filter-option {
|
margin-top: 0.5em;
|
||||||
max-width: 50%;
|
|
||||||
|
button {
|
||||||
|
width: 50%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
padding: 0.5em;
|
||||||
|
|
||||||
.action-buttons {
|
&[data-selected='true'] {
|
||||||
display: flex;
|
background-color: forestgreen;
|
||||||
gap: 0.5em;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
margin-top: 0.5em;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 50%;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0.5em;
|
|
||||||
|
|
||||||
&[data-selected='true'] {
|
|
||||||
background-color: forestgreen;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,7 +475,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
.section-inputs {
|
.section-inputs {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
<td class="station_dispatcher-name">
|
<td class="station_dispatcher-name">
|
||||||
<span v-if="station.onlineInfo?.dispatcherName">
|
<span v-if="station.onlineInfo?.dispatcherName">
|
||||||
<b
|
<b
|
||||||
v-if="store.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
||||||
:title="$t('donations.dispatcher-message')"
|
:title="$t('donations.dispatcher-message')"
|
||||||
@click.stop="openDonationModal"
|
@click.stop="openDonationModal"
|
||||||
>
|
>
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
|
|
||||||
<td class="station_dispatcher-exp">
|
<td class="station_dispatcher-exp">
|
||||||
<span
|
<span
|
||||||
v-if="station.onlineInfo"
|
v-if="station.onlineInfo && station.onlineInfo?.dispatcherExp != -1"
|
||||||
:style="
|
:style="
|
||||||
calculateExpStyle(
|
calculateExpStyle(
|
||||||
station.onlineInfo.dispatcherExp,
|
station.onlineInfo.dispatcherExp,
|
||||||
@@ -279,7 +279,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Loading v-if="!isDataLoaded && stations.length == 0" />
|
<Loading v-if="apiStore.dataStatuses.connection == Status.Loading" />
|
||||||
|
|
||||||
<div class="no-stations" v-else-if="stations.length == 0">
|
<div class="no-stations" v-else-if="stations.length == 0">
|
||||||
{{ $t('sceneries.no-stations') }}
|
{{ $t('sceneries.no-stations') }}
|
||||||
@@ -288,17 +288,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import stationInfoMixin from '../../mixins/stationInfoMixin';
|
import stationInfoMixin from '../../mixins/stationInfoMixin';
|
||||||
import styleMixin from '../../mixins/styleMixin';
|
import styleMixin from '../../mixins/styleMixin';
|
||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { useStationFiltersStore } from '../../store/stationFiltersStore';
|
import { useStationFiltersStore } from '../../store/stationFiltersStore';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames';
|
import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames';
|
||||||
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -325,17 +326,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const mainStore = useMainStore();
|
||||||
|
const apiStore = useApiStore();
|
||||||
const stationFiltersStore = useStationFiltersStore();
|
const stationFiltersStore = useStationFiltersStore();
|
||||||
|
|
||||||
const isDataLoaded = computed(() => {
|
|
||||||
return store.dataStatuses.sceneries != Status.Data.Loading;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isDataLoaded,
|
Status: Status.Data,
|
||||||
stationFiltersStore,
|
stationFiltersStore,
|
||||||
store
|
mainStore,
|
||||||
|
apiStore
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -357,7 +356,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
openDonationModal(e: Event) {
|
openDonationModal(e: Event) {
|
||||||
this.$emit('toggleDonationModal', true);
|
this.$emit('toggleDonationModal', true);
|
||||||
this.store.modalLastClickedTarget = e.target;
|
this.mainStore.modalLastClickedTarget = e.target;
|
||||||
},
|
},
|
||||||
|
|
||||||
openForumSite(e: Event, url: string | undefined) {
|
openForumSite(e: Event, url: string | undefined) {
|
||||||
|
|||||||
@@ -42,14 +42,13 @@ export interface Filter {
|
|||||||
nonPublic: boolean;
|
nonPublic: boolean;
|
||||||
unavailable: boolean;
|
unavailable: boolean;
|
||||||
abandoned: boolean;
|
abandoned: boolean;
|
||||||
|
|
||||||
endingStatus: boolean;
|
endingStatus: boolean;
|
||||||
afkStatus: boolean;
|
afkStatus: boolean;
|
||||||
noSpaceStatus: boolean;
|
noSpaceStatus: boolean;
|
||||||
unavailableStatus: boolean;
|
unavailableStatus: boolean;
|
||||||
unsignedStatus: boolean;
|
unsignedStatus: boolean;
|
||||||
|
|
||||||
authors: string;
|
authors: string;
|
||||||
|
|
||||||
onlineFromHours: number;
|
onlineFromHours: number;
|
||||||
|
withActiveTimetables: boolean;
|
||||||
|
withoutActiveTimetables: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
<template>
|
||||||
|
<span class="stop-label" :data-sbl="stop.isSBL">
|
||||||
|
<span class="name" v-html="stop.nameHtml"></span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="stop.position != 'begin'"
|
||||||
|
class="date arrival"
|
||||||
|
:data-status="
|
||||||
|
stop.arrivalDelay > 0 && stop.status != 'unconfirmed'
|
||||||
|
? 'delayed'
|
||||||
|
: stop.arrivalDelay < 0 && stop.status != 'unconfirmed'
|
||||||
|
? 'preponed'
|
||||||
|
: stop.arrivalDelay == 0 && stop.status == 'confirmed'
|
||||||
|
? 'on-time'
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span v-if="stop.arrivalDelay != 0 && stop.status != 'unconfirmed'">
|
||||||
|
<s>{{ timestampToString(stop.arrivalScheduled) }}</s>
|
||||||
|
{{ timestampToString(stop.arrivalReal) }}
|
||||||
|
({{ stop.arrivalDelay > 0 ? '+' : '' }}{{ stop.arrivalDelay }})
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else>
|
||||||
|
{{ timestampToString(stop.arrivalScheduled) }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
stop.duration ||
|
||||||
|
(stop.status == 'stopped' &&
|
||||||
|
stop.position != 'begin' &&
|
||||||
|
stop.departureDelay != stop.arrivalDelay)
|
||||||
|
"
|
||||||
|
class="date stop"
|
||||||
|
:data-stop-types="stop.type.replace(', ', '-')"
|
||||||
|
:data-stop-status="
|
||||||
|
stop.departureDelay - stop.arrivalDelay > 0 && !stop.duration ? 'delayed' : ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ stop.duration || stop.departureDelay - stop.arrivalDelay }}
|
||||||
|
{{ stop.type == '' ? 'pt' : stop.type }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
stop.position != 'end' &&
|
||||||
|
(stop.duration != 0 || stop.status == 'stopped' || stop.departureDelay != stop.arrivalDelay)
|
||||||
|
"
|
||||||
|
class="date departure"
|
||||||
|
:data-status="
|
||||||
|
stop.departureDelay > 0 && stop.status == 'confirmed'
|
||||||
|
? 'delayed'
|
||||||
|
: stop.departureDelay < 0 && stop.status == 'confirmed'
|
||||||
|
? 'preponed'
|
||||||
|
: stop.departureDelay == 0 && stop.status == 'confirmed'
|
||||||
|
? 'on-time'
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span v-if="stop.departureDelay != 0 && stop.status == 'confirmed'">
|
||||||
|
<s>{{ timestampToString(stop.departureScheduled) }}</s>
|
||||||
|
{{ timestampToString(stop.departureReal) }}
|
||||||
|
|
||||||
|
({{ stop.departureDelay > 0 ? '+' : '' }}{{ stop.departureDelay }})
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else>
|
||||||
|
{{ timestampToString(stop.departureScheduled) }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { PropType, defineComponent } from 'vue';
|
||||||
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
|
import { TrainScheduleStop } from './TrainSchedule.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
mixins: [dateMixin],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
stop: {
|
||||||
|
type: Object as PropType<TrainScheduleStop>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$preponedClr: lime;
|
||||||
|
$delayedClr: salmon;
|
||||||
|
$dateClr: #525151;
|
||||||
|
$stopExchangeClr: #db8e29;
|
||||||
|
$stopDefaultClr: #252525;
|
||||||
|
$stopNameClr: #22a8d1;
|
||||||
|
|
||||||
|
.stop-label {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&[data-sbl='true'] {
|
||||||
|
.date {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
background: none;
|
||||||
|
color: #aaa;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
background: $stopNameClr;
|
||||||
|
padding: 0.3em 0.5em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.misc {
|
||||||
|
background: gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
background: $dateClr;
|
||||||
|
padding: 0.3em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop {
|
||||||
|
&[data-stop-types='ph'],
|
||||||
|
&[data-stop-types='ph-pm'],
|
||||||
|
&[data-stop-types='pm'] {
|
||||||
|
background: $stopExchangeClr;
|
||||||
|
}
|
||||||
|
|
||||||
|
background: $stopDefaultClr;
|
||||||
|
|
||||||
|
&[data-stop-status='delayed'] {
|
||||||
|
color: $delayedClr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrival,
|
||||||
|
.departure {
|
||||||
|
&[data-status='delayed'] {
|
||||||
|
s {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $delayedClr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-status='preponed'] {
|
||||||
|
s {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $preponedClr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
<div class="train-driver">
|
<div class="train-driver">
|
||||||
<b
|
<b
|
||||||
v-if="store.donatorsData.includes(train.driverName)"
|
v-if="apiStore.donatorsData.includes(train.driverName)"
|
||||||
:title="$t('donations.driver-message')"
|
:title="$t('donations.driver-message')"
|
||||||
>
|
>
|
||||||
{{ train.driverName }}
|
{{ train.driverName }}
|
||||||
@@ -126,7 +126,8 @@ import trainInfoMixin from '../../mixins/trainInfoMixin';
|
|||||||
import Train from '../../scripts/interfaces/Train';
|
import Train from '../../scripts/interfaces/Train';
|
||||||
import ProgressBar from '../Global/ProgressBar.vue';
|
import ProgressBar from '../Global/ProgressBar.vue';
|
||||||
import TrainThumbnail from '../Global/TrainThumbnail.vue';
|
import TrainThumbnail from '../Global/TrainThumbnail.vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [trainInfoMixin, styleMixin],
|
mixins: [trainInfoMixin, styleMixin],
|
||||||
@@ -145,7 +146,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore()
|
store: useMainStore(),
|
||||||
|
apiStore: useApiStore()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,19 +15,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||||
import trainInfoMixin from '../../mixins/trainInfoMixin';
|
import TrainInfo from './TrainInfo.vue';
|
||||||
import TrainInfo from '../TrainsView/TrainInfo.vue';
|
import TrainSchedule from './TrainSchedule.vue';
|
||||||
import TrainSchedule from '../TrainsView/TrainSchedule.vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { TrainInfo, TrainSchedule },
|
components: { TrainInfo, TrainSchedule },
|
||||||
mixins: [trainInfoMixin, modalTrainMixin],
|
mixins: [modalTrainMixin],
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isTopBarVisible: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
const contentEl = this.$refs['content'] as HTMLElement;
|
const contentEl = this.$refs['content'] as HTMLElement;
|
||||||
@@ -2,83 +2,126 @@
|
|||||||
<div class="train-schedule" @click="toggleShowState">
|
<div class="train-schedule" @click="toggleShowState">
|
||||||
<StockList :trainStockList="train.stockList" />
|
<StockList :trainStockList="train.stockList" />
|
||||||
|
|
||||||
<!-- <div class="train-stock"> -->
|
|
||||||
<!-- <ul>
|
|
||||||
<li v-for="(stockName, i) in train.stockList" :key="i">
|
|
||||||
<p>{{ stockName.split(':')[0].split('_').splice(0, 2).join(' ') }} {{ stockName.split(':')[1] }}</p>
|
|
||||||
<TrainThumbnail :name="stockName" />
|
|
||||||
</li>
|
|
||||||
</ul> -->
|
|
||||||
<!-- </div> -->
|
|
||||||
|
|
||||||
<div class="schedule-wrapper" v-if="train.timetableData">
|
<div class="schedule-wrapper" v-if="train.timetableData">
|
||||||
<ul class="stop_list">
|
<div class="stops">
|
||||||
<li
|
<div
|
||||||
v-for="(stop, i) in train.timetableData.followingStops"
|
v-for="(stop, i) in scheduleStops"
|
||||||
:key="i"
|
:key="i"
|
||||||
class="stop"
|
class="stop"
|
||||||
:class="addClasses(stop, i)"
|
:data-status="stop.status"
|
||||||
|
:data-position="stop.position"
|
||||||
|
:data-delayed="stop.departureDelay > 0"
|
||||||
|
:data-stop-type="stop.type"
|
||||||
|
:data-minor-stop-active="stop.isActive"
|
||||||
|
:data-last-confirmed="stop.isLastConfirmed"
|
||||||
|
x
|
||||||
>
|
>
|
||||||
<span class="stop_info">
|
<span class="stop_info">
|
||||||
<div class="indicator"></div>
|
<span class="distance">
|
||||||
|
{{ stop.distance ? stop.distance.toFixed(1) : '' }}
|
||||||
<div class="progress-bar"></div>
|
|
||||||
|
|
||||||
<div class="stop-bar"></div>
|
|
||||||
|
|
||||||
<span class="distance" v-if="stop.stopDistance">
|
|
||||||
{{ Math.floor(stop.stopDistance) }}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stop-name" v-html="stop.stopName"> </span>
|
<div class="progress">
|
||||||
|
<div class="line line_node line_node-top"></div>
|
||||||
|
<div class="node"></div>
|
||||||
|
<div class="line line_node line_node-bottom"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<StopDate :stop="stop" />
|
<StopLabel :stop="stop" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="stop_line" v-if="i < train.timetableData!.followingStops.length - 1">
|
<div class="stop_line">
|
||||||
<div class="progress-bar"></div>
|
<!-- Grid placeholder -->
|
||||||
|
<div></div>
|
||||||
|
|
||||||
<div v-if="stop.comments" style="color: salmon">
|
<div class="progress">
|
||||||
<b>{{ stop.stopNameRAW }} </b>: <span v-html="stop.comments"></span>
|
<div class="line line_connection" v-if="i < scheduleStops.length - 1"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span
|
<div class="bottom-line-info">
|
||||||
v-if="
|
<div class="info-comments" v-if="stop.comments" style="color: salmon">
|
||||||
stop.departureLine == train.timetableData!.followingStops[i + 1].arrivalLine &&
|
<img src="/images/icon-warning.svg" alt="icon-warning" width="20" />
|
||||||
!/sbl/gi.test(stop.departureLine!)
|
<b v-html="stop.comments"></b>
|
||||||
"
|
</div>
|
||||||
>
|
|
||||||
{{ stop.departureLine }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else-if="!/sbl/gi.test(stop.departureLine!)">
|
<!-- Routes -->
|
||||||
{{ stop.departureLine }} /
|
<span
|
||||||
{{ train.timetableData!.followingStops[i + 1].arrivalLine }}
|
v-if="
|
||||||
</span>
|
stop.departureLine &&
|
||||||
</div>
|
stop.departureLine == scheduleStops[i + 1]?.arrivalLine &&
|
||||||
|
!/sbl/gi.test(stop.departureLine)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ stop.departureLine }}
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="stop_line" v-else>
|
<span v-else-if="stop.departureLine && !/sbl/gi.test(stop.departureLine)">
|
||||||
<div v-if="stop.comments" style="color: salmon">
|
<div>{{ stop.departureLine }}</div>
|
||||||
<b>{{ stop.stopNameRAW }} </b>: <span v-html="stop.comments"></span>
|
<div
|
||||||
|
class="scenery-change-name"
|
||||||
|
v-if="
|
||||||
|
i < scheduleStops.length - 1 &&
|
||||||
|
stop.sceneryName != scheduleStops[i + 1].sceneryName
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ scheduleStops[i + 1].sceneryName }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ scheduleStops[i + 1].arrivalLine }}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import Train from '../../scripts/interfaces/Train';
|
import Train from '../../scripts/interfaces/Train';
|
||||||
import { useStore } from '../../store/mainStore';
|
import StopLabel from './StopLabel.vue';
|
||||||
import StopDate from '../Global/StopDate.vue';
|
|
||||||
import StockList from '../Global/StockList.vue';
|
import StockList from '../Global/StockList.vue';
|
||||||
import { TrainStop } from '../../store/typings';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
|
export interface TrainScheduleStop {
|
||||||
|
nameHtml: string;
|
||||||
|
nameRaw: string;
|
||||||
|
|
||||||
|
status: 'confirmed' | 'unconfirmed' | 'stopped';
|
||||||
|
type: string;
|
||||||
|
position: 'begin' | 'end' | 'en-route';
|
||||||
|
|
||||||
|
arrivalScheduled: number;
|
||||||
|
arrivalReal: number;
|
||||||
|
|
||||||
|
departureScheduled: number;
|
||||||
|
departureReal: number;
|
||||||
|
|
||||||
|
departureDelay: number;
|
||||||
|
arrivalDelay: number;
|
||||||
|
|
||||||
|
duration: number | null;
|
||||||
|
|
||||||
|
isActive: boolean;
|
||||||
|
isLastConfirmed: boolean;
|
||||||
|
isSBL: boolean;
|
||||||
|
|
||||||
|
sceneryName: string | null;
|
||||||
|
sceneryHash: string;
|
||||||
|
distance: number;
|
||||||
|
|
||||||
|
arrivalLine: string | null;
|
||||||
|
departureLine: string | null;
|
||||||
|
|
||||||
|
comments: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { StopDate, StockList },
|
components: { StopLabel, StockList },
|
||||||
props: {
|
props: {
|
||||||
train: {
|
train: {
|
||||||
type: Object as PropType<Train>,
|
type: Object as PropType<Train>,
|
||||||
@@ -90,62 +133,95 @@ export default defineComponent({
|
|||||||
|
|
||||||
emits: ['click'],
|
emits: ['click'],
|
||||||
|
|
||||||
setup(props) {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
|
apiStore: useApiStore()
|
||||||
lastConfirmed: computed(() => {
|
|
||||||
return props.train.timetableData!.followingStops.findIndex(
|
|
||||||
(stop, i, stops) => stop.confirmed && !stops[i + 1]?.confirmed && !stops[i + 1]?.stopped
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
activeMinorStops: computed(() => {
|
|
||||||
const lastMajorConfirmed = props.train.timetableData!.followingStops.findIndex(
|
|
||||||
(stop, i, stops) => stop.confirmed && !stops[i + 1]?.confirmed
|
|
||||||
);
|
|
||||||
|
|
||||||
const activeMinorStopList: number[] = [];
|
|
||||||
if (lastMajorConfirmed + 1 >= props.train.timetableData!.followingStops.length)
|
|
||||||
return activeMinorStopList;
|
|
||||||
|
|
||||||
for (
|
|
||||||
let i = lastMajorConfirmed + 1;
|
|
||||||
i < props.train.timetableData!.followingStops.length;
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
if (/po\.|sbl/gi.test(props.train.timetableData!.followingStops[i].stopNameRAW))
|
|
||||||
activeMinorStopList.push(i);
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeMinorStopList;
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
scheduleStops(): TrainScheduleStop[] {
|
||||||
|
let currentSceneryIndex = 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.train.timetableData?.followingStops.map((stop, i, arr) => {
|
||||||
|
if (
|
||||||
|
i > 0 &&
|
||||||
|
stop.arrivalLine &&
|
||||||
|
stop.arrivalLine != arr[i - 1].departureLine &&
|
||||||
|
!/sbl/gi.test(stop.arrivalLine)
|
||||||
|
)
|
||||||
|
currentSceneryIndex++;
|
||||||
|
|
||||||
|
return {
|
||||||
|
nameHtml: stop.stopName,
|
||||||
|
nameRaw: stop.stopNameRAW,
|
||||||
|
|
||||||
|
arrivalScheduled: stop.arrivalTimestamp,
|
||||||
|
arrivalReal: stop.arrivalRealTimestamp,
|
||||||
|
|
||||||
|
departureScheduled: stop.departureTimestamp,
|
||||||
|
departureReal: stop.departureRealTimestamp,
|
||||||
|
|
||||||
|
departureDelay: stop.departureDelay,
|
||||||
|
arrivalDelay: stop.arrivalDelay,
|
||||||
|
|
||||||
|
duration: stop.stopTime,
|
||||||
|
|
||||||
|
comments: stop.comments ?? null,
|
||||||
|
|
||||||
|
arrivalLine: stop.arrivalLine,
|
||||||
|
departureLine: stop.departureLine,
|
||||||
|
|
||||||
|
type: stop.stopType,
|
||||||
|
distance: stop.stopDistance,
|
||||||
|
isActive: this.activeMinorStops.includes(i),
|
||||||
|
isLastConfirmed: this.lastConfirmed === i && !stop.terminatesHere,
|
||||||
|
isSBL: /sbl/gi.test(stop.stopName),
|
||||||
|
position: stop.beginsHere ? 'begin' : stop.terminatesHere ? 'end' : 'en-route',
|
||||||
|
sceneryHash: '',
|
||||||
|
sceneryName: this.train.timetableData!.sceneryNames[currentSceneryIndex],
|
||||||
|
status: stop.confirmed ? 'confirmed' : stop.stopped ? 'stopped' : 'unconfirmed'
|
||||||
|
};
|
||||||
|
}) ?? []
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
lastConfirmed() {
|
||||||
|
return this.train.timetableData?.followingStops.findIndex(
|
||||||
|
(stop, i, stops) => stop.confirmed && !stops[i + 1]?.confirmed && !stops[i + 1]?.stopped
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
activeMinorStops() {
|
||||||
|
if (!this.train.timetableData) return [];
|
||||||
|
|
||||||
|
const lastMajorConfirmed = this.train.timetableData.followingStops.findIndex(
|
||||||
|
(stop, i, stops) => stop.confirmed && !stops[i + 1]?.confirmed
|
||||||
|
);
|
||||||
|
|
||||||
|
const activeMinorStopList: number[] = [];
|
||||||
|
if (lastMajorConfirmed + 1 >= this.train.timetableData.followingStops.length)
|
||||||
|
return activeMinorStopList;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let i = lastMajorConfirmed + 1;
|
||||||
|
i < this.train.timetableData!.followingStops.length;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
if (/po\.|sbl/gi.test(this.train.timetableData!.followingStops[i].stopNameRAW))
|
||||||
|
activeMinorStopList.push(i);
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeMinorStopList;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
toggleShowState() {
|
toggleShowState() {
|
||||||
this.$emit('click');
|
this.$emit('click');
|
||||||
},
|
|
||||||
|
|
||||||
addClasses(stop: TrainStop, index: number) {
|
|
||||||
return {
|
|
||||||
confirmed: stop.confirmed,
|
|
||||||
stopped: stop.stopped,
|
|
||||||
begin: stop.beginsHere,
|
|
||||||
end: stop.terminatesHere,
|
|
||||||
delayed: stop.departureDelay > 0,
|
|
||||||
sbl: /sbl/gi.test(stop.stopName),
|
|
||||||
[stop.stopType.replaceAll(', ', '-')]:
|
|
||||||
stop.stopType.match(new RegExp('ph|pm|pt')) && !stop.confirmed && !stop.beginsHere,
|
|
||||||
'minor-stop-active': this.activeMinorStops.includes(index),
|
|
||||||
'last-confirmed': index == this.lastConfirmed && !stop.terminatesHere
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onImageError(e: Event) {
|
|
||||||
const imageEl = e.target as HTMLImageElement;
|
|
||||||
imageEl.src = '/images/icon-unknown.png';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -155,17 +231,18 @@ export default defineComponent({
|
|||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
|
|
||||||
$barClr: #b1b1b1;
|
$barClr: #b1b1b1;
|
||||||
$confirmedClr: #18d818;
|
$confirmedClr: #4ae24a;
|
||||||
$stoppedClr: #f55f31;
|
$stoppedClr: #f55f31;
|
||||||
$haltClr: #f8bb36;
|
$haltClr: #f8bb36;
|
||||||
$stopNameClr: #22a8d1;
|
|
||||||
|
$blinkAnim: 0.5s ease-in-out alternate infinite blink;
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
from {
|
from {
|
||||||
background-color: $barClr;
|
border-color: $barClr;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
background-color: $confirmedClr;
|
border-color: $confirmedClr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,216 +258,247 @@ $stopNameClr: #22a8d1;
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar {
|
.stops {
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
top: -1px;
|
|
||||||
left: -17px;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
width: 3px;
|
|
||||||
|
|
||||||
background-color: $barClr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stop-name {
|
|
||||||
background: $stopNameClr;
|
|
||||||
padding: 0.3em 0.5em;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.misc {
|
|
||||||
background: gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stop-comment {
|
|
||||||
background: forestgreen;
|
|
||||||
padding: 0.3em 0.5em;
|
|
||||||
|
|
||||||
max-width: 250px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
width: 2em;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.stop_list {
|
|
||||||
margin-left: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.stop_list > li.stop {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-y: hidden;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
padding: 0 0.5em;
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.sbl {
|
.stop {
|
||||||
.stop-date {
|
// Begin stop
|
||||||
display: none;
|
&[data-position='begin'] {
|
||||||
}
|
.node {
|
||||||
|
|
||||||
.stop-name {
|
|
||||||
background: none;
|
|
||||||
color: #aaa;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[class*='ph'] > .stop_info > .indicator {
|
|
||||||
border-color: $stopNameClr;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[class*='pt'] > .stop_info > .indicator {
|
|
||||||
border-color: #818181;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.begin {
|
|
||||||
.stop_info > .indicator {
|
|
||||||
border-color: lightgreen;
|
border-color: lightgreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop_info > .progress-bar {
|
.line_node-top {
|
||||||
background: lightgreen;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.end {
|
// End stop
|
||||||
.stop_info > .indicator {
|
&[data-position='end'] {
|
||||||
|
.node {
|
||||||
border-color: salmon;
|
border-color: salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop_info > .progress-bar {
|
.line_node-bottom {
|
||||||
background: salmon;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.minor-stop-active {
|
// Stop types
|
||||||
.stop_info > .progress-bar {
|
&[data-stop-type*='pt'] .node {
|
||||||
animation: 0.5s ease-in-out alternate infinite blink;
|
border-color: #818181;
|
||||||
}
|
|
||||||
|
|
||||||
.stop_line > .progress-bar {
|
|
||||||
animation: 0.5s ease-in-out alternate infinite blink;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.last-confirmed {
|
&[data-stop-type*='ph'] .node {
|
||||||
.stop_line > .progress-bar {
|
border-color: $haltClr;
|
||||||
animation: 0.5s ease-in-out alternate infinite blink;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.confirmed {
|
&[data-minor-stop-active='true'] {
|
||||||
.stop_info {
|
.progress > .line {
|
||||||
> .progress-bar {
|
animation: $blinkAnim;
|
||||||
background-color: $confirmedClr;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .indicator {
|
|
||||||
border-color: $confirmedClr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop_line > .progress-bar {
|
& + div {
|
||||||
background-color: $confirmedClr;
|
.progress > .line_node-top {
|
||||||
}
|
animation: $blinkAnim;
|
||||||
}
|
|
||||||
|
|
||||||
&.stopped {
|
|
||||||
.stop_info {
|
|
||||||
> .indicator {
|
|
||||||
border-color: $stoppedClr;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .stop-bar {
|
|
||||||
background: $stoppedClr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop_line {
|
// Last confirmed outpost / checkpoint
|
||||||
font-size: 0.8em;
|
&[data-last-confirmed='true'] {
|
||||||
color: #ccc;
|
.progress > .line_connection {
|
||||||
|
animation: $blinkAnim;
|
||||||
|
}
|
||||||
|
|
||||||
padding: 0.35em 0;
|
.progress > .line_node-bottom {
|
||||||
|
animation: $blinkAnim;
|
||||||
|
}
|
||||||
|
|
||||||
position: relative;
|
& + div {
|
||||||
|
.progress > .line_node-top {
|
||||||
.line-segment {
|
animation: $blinkAnim;
|
||||||
color: $barClr;
|
}
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop_info {
|
// Confirmed status
|
||||||
display: flex;
|
&[data-status='confirmed'] {
|
||||||
|
.progress > .node {
|
||||||
position: relative;
|
border-color: $confirmedClr;
|
||||||
text-align: center;
|
}
|
||||||
|
.progress > .line {
|
||||||
flex-wrap: wrap;
|
border-left: 2px solid $confirmedClr;
|
||||||
|
border-right: 2px solid $confirmedClr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stop-bar {
|
// Stopped status
|
||||||
|
&[data-status='stopped'] {
|
||||||
|
.progress > .node {
|
||||||
|
border-color: $stoppedClr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress > .line_node {
|
||||||
|
border-color: $stoppedClr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unused so far
|
||||||
|
&[data-track-count-departure='2'] {
|
||||||
|
.progress > .line {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-track-count-arrival='2'] {
|
||||||
|
.progress > .line_node-top {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-track-count-arrival='1'] {
|
||||||
|
.progress > .line_node-top {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-electrified-departure] {
|
||||||
|
.stop_line > .line-speed > .speed-departure {
|
||||||
|
color: #00c1c7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-electrified-arrival] {
|
||||||
|
.stop_line > .line-speed > .speed-next-arrival {
|
||||||
|
color: #00c1c7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop_info,
|
||||||
|
.stop_line {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 30px 40px auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-speed {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #9b9b9b;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop_info {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop_line {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #ccc;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.distance {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > .node {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
|
||||||
left: -17px;
|
|
||||||
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
width: 3px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.distance {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-100%, -50%);
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
margin-left: -1.75rem;
|
z-index: 15;
|
||||||
|
|
||||||
font-size: 0.75em;
|
|
||||||
color: #d6d6d6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indicator {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 11;
|
|
||||||
|
|
||||||
top: 50%;
|
|
||||||
left: -1rem;
|
|
||||||
|
|
||||||
transform: translate(-47%, -50%);
|
|
||||||
|
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
width: 15px;
|
width: 15px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
|
|
||||||
background: var(--clr-secondary);
|
background-color: var(--clr-secondary);
|
||||||
border: 3px solid $barClr;
|
border: 4px solid $barClr;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .line {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
// background-color: $barClr;
|
||||||
|
border-left: 2px solid $barClr;
|
||||||
|
border-right: 2px solid $barClr;
|
||||||
|
|
||||||
|
&.line_connection {
|
||||||
|
transform: translate(-50%, -6px);
|
||||||
|
height: calc(100% + 12px);
|
||||||
|
// height: calc(100% + 0.25em);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line_node-top {
|
||||||
|
top: 0;
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line_node-bottom {
|
||||||
|
top: 50%;
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line_stop {
|
||||||
|
border-color: $stoppedClr;
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-comments {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
|
||||||
|
margin: 0.25em 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-line-info {
|
||||||
|
.scenery-change-name {
|
||||||
|
position: relative;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
height: 2px;
|
||||||
|
width: 30px;
|
||||||
|
background-color: #aaa;
|
||||||
|
|
||||||
|
top: 50%;
|
||||||
|
right: calc(100% + 5px);
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<hr style="margin: 0.5em 0" />
|
<hr style="margin: 0.5em 0" />
|
||||||
|
|
||||||
<div v-if="store.dataStatuses.trains == Status.Loaded && regionTrains.length > 0">
|
<div v-if="apiStore.dataStatuses.connection == Status.Loaded && regionTrains.length > 0">
|
||||||
<div class="top-list general">
|
<div class="top-list general">
|
||||||
<transition-group tag="ul" name="stats-anim">
|
<transition-group tag="ul" name="stats-anim">
|
||||||
<li class="badge" key="timetable-count">
|
<li class="badge" key="timetable-count">
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="store.dataStatuses.trains != Status.Loaded">
|
<div v-else-if="apiStore.dataStatuses.connection != Status.Loaded">
|
||||||
{{ $t('train-stats.stats-loading') }}
|
{{ $t('train-stats.stats-loading') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -102,8 +102,9 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
interface ITop {
|
interface ITop {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -127,7 +128,8 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showOptions: false,
|
showOptions: false,
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
|
apiStore: useApiStore(),
|
||||||
Status: Status.Data
|
Status: Status.Data
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="status-anim" mode="out-in" tag="div" class="train-table">
|
<transition name="status-anim" mode="out-in" tag="div" class="train-table">
|
||||||
<div :key="store.dataStatuses.trains">
|
<div :key="apiStore.dataStatuses.connection">
|
||||||
<div class="table-info" key="offline" v-if="store.isOffline">
|
<div class="table-info" key="offline" v-if="store.isOffline">
|
||||||
{{ $t('app.offline') }}
|
{{ $t('app.offline') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Loading v-else-if="trains.length == 0 && store.dataStatuses.trains == 0" key="loading" />
|
<Loading v-else-if="apiStore.dataStatuses.connection == Status.Loading" key="loading" />
|
||||||
|
|
||||||
<div
|
<div class="table-info" key="no-trains" v-else-if="trains.length == 0">
|
||||||
class="table-info"
|
|
||||||
key="no-trains"
|
|
||||||
v-else-if="trains.length == 0 && store.dataStatuses.trains != 0"
|
|
||||||
>
|
|
||||||
{{ $t('trains.no-trains') }}
|
{{ $t('trains.no-trains') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -35,10 +31,11 @@
|
|||||||
import { defineComponent, inject, PropType, Ref } from 'vue';
|
import { defineComponent, inject, PropType, Ref } from 'vue';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||||
import Train from '../../scripts/interfaces/Train';
|
import Train from '../../scripts/interfaces/Train';
|
||||||
import { useStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import TrainInfo from './TrainInfo.vue';
|
import TrainInfo from './TrainInfo.vue';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { Loading, TrainInfo },
|
components: { Loading, TrainInfo },
|
||||||
@@ -53,7 +50,8 @@ export default defineComponent({
|
|||||||
mixins: [modalTrainMixin],
|
mixins: [modalTrainMixin],
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useMainStore();
|
||||||
|
const apiStore = useApiStore();
|
||||||
const searchedTrain = inject('searchedTrain') as Ref<string>;
|
const searchedTrain = inject('searchedTrain') as Ref<string>;
|
||||||
const searchedDriver = inject('searchedDriver') as Ref<string>;
|
const searchedDriver = inject('searchedDriver') as Ref<string>;
|
||||||
|
|
||||||
@@ -61,6 +59,8 @@ export default defineComponent({
|
|||||||
searchedTrain,
|
searchedTrain,
|
||||||
searchedDriver,
|
searchedDriver,
|
||||||
store,
|
store,
|
||||||
|
apiStore,
|
||||||
|
Status: Status.Data,
|
||||||
sorterActive: inject('sorterActive') as {
|
sorterActive: inject('sorterActive') as {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
dir: number;
|
dir: number;
|
||||||
@@ -72,7 +72,7 @@ export default defineComponent({
|
|||||||
dataStatus() {
|
dataStatus() {
|
||||||
if (this.store.isOffline) return Status.Data.Offline;
|
if (this.store.isOffline) return Status.Data.Offline;
|
||||||
|
|
||||||
if (this.trains.length == 0 && this.store.dataStatuses.trains == Status.Data.Loading)
|
if (this.trains.length == 0 && this.apiStore.dataStatuses.connection == Status.Data.Loading)
|
||||||
return Status.Data.Loading;
|
return Status.Data.Loading;
|
||||||
|
|
||||||
return Status.Data.Loaded;
|
return Status.Data.Loaded;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { PropType, defineComponent } from 'vue';
|
||||||
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
|
import { TrainScheduleStop } from './TrainSchedule.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
mixins: [dateMixin],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
stop: {
|
||||||
|
type: Object as PropType<TrainScheduleStop>,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+16
-1
@@ -7,7 +7,8 @@
|
|||||||
"addons",
|
"addons",
|
||||||
"blockades",
|
"blockades",
|
||||||
"signals",
|
"signals",
|
||||||
"status"
|
"status",
|
||||||
|
"timetables"
|
||||||
],
|
],
|
||||||
|
|
||||||
"options": [
|
"options": [
|
||||||
@@ -228,6 +229,20 @@
|
|||||||
"section": "status",
|
"section": "status",
|
||||||
"value": true,
|
"value": true,
|
||||||
"defaultValue": true
|
"defaultValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "withActiveTimetables",
|
||||||
|
"name": "withActiveTimetables",
|
||||||
|
"section": "timetables",
|
||||||
|
"value": true,
|
||||||
|
"defaultValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "withoutActiveTimetables",
|
||||||
|
"name": "withoutActiveTimetables",
|
||||||
|
"section": "timetables",
|
||||||
|
"value": true,
|
||||||
|
"defaultValue": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sliders": [
|
"sliders": [
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const http = axios.create({
|
||||||
|
baseURL:
|
||||||
|
import.meta.env.VITE_API_MODE === 'development'
|
||||||
|
? 'http://localhost:3001'
|
||||||
|
: 'https://stacjownik.spythere.eu'
|
||||||
|
});
|
||||||
|
|
||||||
|
export default http;
|
||||||
+57
-28
@@ -12,7 +12,7 @@
|
|||||||
"p4": "Every person who decides to contribute at least {b1} (in case of PayPal it must be a payment including additional transaction fees) for the development of Stacjownik, will receive (upon a personal request) {img}{b2} of username in the app and on my Discord server (after verifying the payment author, preferably by providing the username directly with the payment).",
|
"p4": "Every person who decides to contribute at least {b1} (in case of PayPal it must be a payment including additional transaction fees) for the development of Stacjownik, will receive (upon a personal request) {img}{b2} of username in the app and on my Discord server (after verifying the payment author, preferably by providing the username directly with the payment).",
|
||||||
"p4-b1": "5 PLN",
|
"p4-b1": "5 PLN",
|
||||||
"p4-b2": "a symbolic highlight",
|
"p4-b2": "a symbolic highlight",
|
||||||
"p5": "Thank you and enjoy the app!<br />~ Spythegre",
|
"p5": "Thank you and enjoy the app!<br />~ Spythere",
|
||||||
"action-exit": "Maybe next time...",
|
"action-exit": "Maybe next time...",
|
||||||
"action-paypal": "DONATE WITH PAYPAL",
|
"action-paypal": "DONATE WITH PAYPAL",
|
||||||
"action-buycoffee": "BUY ME A COFFEE!",
|
"action-buycoffee": "BUY ME A COFFEE!",
|
||||||
@@ -144,7 +144,8 @@
|
|||||||
"filter-withComments": "COMMENTS",
|
"filter-withComments": "COMMENTS",
|
||||||
"filter-twr": "HIGH RISK CARGO",
|
"filter-twr": "HIGH RISK CARGO",
|
||||||
"filter-skr": "EXCEEDED GAUGE",
|
"filter-skr": "EXCEEDED GAUGE",
|
||||||
"filter-twr-skr": "ALL TYPES",
|
"filter-twr-skr": "BOTH TYPES",
|
||||||
|
"filter-all-specials": "ALL",
|
||||||
"filter-common": "NO WARNINGS",
|
"filter-common": "NO WARNINGS",
|
||||||
"filter-passenger": "PASSENGER",
|
"filter-passenger": "PASSENGER",
|
||||||
"filter-freight": "FREIGHT",
|
"filter-freight": "FREIGHT",
|
||||||
@@ -156,9 +157,9 @@
|
|||||||
"filter-clear": "CLEAR FILTERS",
|
"filter-clear": "CLEAR FILTERS",
|
||||||
|
|
||||||
"filter-section-timetable-status": "TIMETABLE STATUS",
|
"filter-section-timetable-status": "TIMETABLE STATUS",
|
||||||
"filter-section-twrskr": "WARNINGS",
|
"filter-section-special": "SPECIAL TYPE",
|
||||||
|
|
||||||
"filter-all": "ALL ENTRIES",
|
"filter-all-statuses": "ALL",
|
||||||
"filter-abandoned": "ABANDONED",
|
"filter-abandoned": "ABANDONED",
|
||||||
"filter-fulfilled": "FULFILLED",
|
"filter-fulfilled": "FULFILLED",
|
||||||
"filter-active": "ACTIVE"
|
"filter-active": "ACTIVE"
|
||||||
@@ -175,7 +176,8 @@
|
|||||||
"signals": "SIGNALLING",
|
"signals": "SIGNALLING",
|
||||||
"addons": "ADDITIONAL PROGRAMS",
|
"addons": "ADDITIONAL PROGRAMS",
|
||||||
"blockades": "BLOCK SIGNALLING",
|
"blockades": "BLOCK SIGNALLING",
|
||||||
"status": "ONLINE STATUS"
|
"status": "ONLINE STATUS",
|
||||||
|
"timetables": "ACTIVE TIMETABLES"
|
||||||
},
|
},
|
||||||
|
|
||||||
"all-available": "ALL AVAILABLE",
|
"all-available": "ALL AVAILABLE",
|
||||||
@@ -218,6 +220,10 @@
|
|||||||
"historical": "HISTORICAL",
|
"historical": "HISTORICAL",
|
||||||
"free": "FREE",
|
"free": "FREE",
|
||||||
"occupied": "OCCUPIED",
|
"occupied": "OCCUPIED",
|
||||||
|
|
||||||
|
"withActiveTimetables": "ACTIVE",
|
||||||
|
"withoutActiveTimetables": "NO ACTIVE",
|
||||||
|
|
||||||
"sliders": {
|
"sliders": {
|
||||||
"min-lvl": "MIN. REQUIRED DISPATCHER LEVEL",
|
"min-lvl": "MIN. REQUIRED DISPATCHER LEVEL",
|
||||||
"max-lvl": "MAX. REQUIRED DISPATCHER LEVEL",
|
"max-lvl": "MAX. REQUIRED DISPATCHER LEVEL",
|
||||||
@@ -226,7 +232,10 @@
|
|||||||
"routes-2t-cat": "MIN. CATENARY DOUBLE TRACK ROUTES",
|
"routes-2t-cat": "MIN. CATENARY DOUBLE TRACK ROUTES",
|
||||||
"routes-2t-other": "MIN. OTHER DOUBLE TRACK ROUTES"
|
"routes-2t-other": "MIN. OTHER DOUBLE TRACK ROUTES"
|
||||||
},
|
},
|
||||||
"authors-search": "Search by author (other filters apply)",
|
"authors-search": "SEARCH BY AUTHOR NAME (other filters apply):",
|
||||||
|
"authors-placeholder": "Enter the author nickname...",
|
||||||
|
"authors-button-title": "Search",
|
||||||
|
|
||||||
"minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:",
|
"minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:",
|
||||||
"now": "NOW",
|
"now": "NOW",
|
||||||
"hour": "h",
|
"hour": "h",
|
||||||
@@ -347,29 +356,49 @@
|
|||||||
"last-seen-at": "Last seen at",
|
"last-seen-at": "Last seen at",
|
||||||
"currently-at": "Currently at",
|
"currently-at": "Currently at",
|
||||||
|
|
||||||
"stats-title": "DRIVING STATISTICS OF",
|
"driver-stats": {
|
||||||
|
"button": "DRIVER STATS",
|
||||||
|
"title": "{name}'s DRIVER STATS",
|
||||||
|
"info": "Enter a proper nickname into filters [F] to see user's driving statistics!",
|
||||||
|
"timetables": "TIMETABLES",
|
||||||
|
"longest-timetable": "LONGEST TIMETABLE",
|
||||||
|
"avg-timetable": "AVERAGE TIMETABLE LENGTH",
|
||||||
|
"distance": "DISTANCE",
|
||||||
|
"stations": "STATIONS"
|
||||||
|
},
|
||||||
|
|
||||||
"stats-timetables": "TIMETABLES",
|
"daily-stats": {
|
||||||
"stats-longest-timetable": "LONGEST TIMETABLE",
|
"button": "DAILY STATS",
|
||||||
"stats-avg-timetable": "AVERAGE TIMETABLE LENGTH",
|
"title": "STATS OF THE DAY",
|
||||||
"stats-distance": "DISTANCE",
|
"info": "Today's statistics are unavailable yet!",
|
||||||
"stats-stations": "STATIONS",
|
"total": "Issued timetables: {count} (total distance: {distance})",
|
||||||
|
"longest": "The longest timetable: #{id} (made by {author} for {driver}, distance: {distance})",
|
||||||
|
"most-active-dr": "The most active dispatcher: {dispatcher} (created {count})",
|
||||||
|
"most-active-dr-many": "The most active dispatchers: {dispatchers} (created {count} each)",
|
||||||
|
"most-active-driver": "The most active driver: {driver} (total driven distance: {distance})",
|
||||||
|
"longest-duties": "The longest service: {dispatcher} at {station} (duration: {duration})",
|
||||||
|
"count": "timetable | timetables",
|
||||||
|
|
||||||
"timetable-stats-title": "Daily stats on {date}",
|
"rippedSwitches": "RIPPED SWITCHES",
|
||||||
"timetable-stats-total": "Issued timetables: {count} (total distance: {distance})",
|
"derailments": "DERAILMENTS",
|
||||||
"timetable-stats-longest": "The longest timetable: #{id} (made by {author} for {driver}, distance: {distance})",
|
"skippedStopSignals": "SKIPPED STOP SIGNALS",
|
||||||
"timetable-stats-most-active-dr": "The most active dispatcher: {dispatcher} (created {count})",
|
"radioStops": "RADIOSTOPS",
|
||||||
"timetable-stats-most-active-dr-many": "The most active dispatchers: {dispatchers} (created {count} each)",
|
"kills": "KILLS"
|
||||||
"timetable-stats-most-active-driver": "The most active driver: {driver} (total driven distance: {distance})",
|
},
|
||||||
"timetable-stats-longest-duties": "The longest service: {dispatcher} at {station} (duration: {duration})",
|
|
||||||
|
|
||||||
"timetable-count": "timetable | timetables",
|
"dispatcher-stats": {
|
||||||
|
"button": "DISPATCHER STATS",
|
||||||
"daily-stats-title": "DAILY STATS",
|
"title": "{name}'s DISPATCHER STATS",
|
||||||
"daily-stats-info": "Today's statistics are unavailable yet!",
|
"empty": "This user has no statistics saved yet!",
|
||||||
|
"info": "Enter a proper nickname into filters [F] to see user's dispatcher statistics!",
|
||||||
"driver-stats-title": "DRIVER STATS",
|
"services-count": "SERVICES",
|
||||||
"driver-stats-info": "Enter a proper nickname into filters [F] to see user's driving statistics!",
|
"service-max": "MAX SERVICE DURATION",
|
||||||
|
"service-avg": "AVG SERVICE DURATION",
|
||||||
|
"timetables-count": "ISSUED TIMETABLES",
|
||||||
|
"timetables-sum": "TIMETABLES DISTANCE SUM",
|
||||||
|
"timetables-max": "LONGEST TIMETABLE",
|
||||||
|
"timetables-avg": "AVG TIMETABLE DISTANCE"
|
||||||
|
},
|
||||||
|
|
||||||
"stats-loading": "Fetching statistics...",
|
"stats-loading": "Fetching statistics...",
|
||||||
"stats-error": "Oops! An unexpected error occurred while trying to fetch statistics! :/",
|
"stats-error": "Oops! An unexpected error occurred while trying to fetch statistics! :/",
|
||||||
@@ -405,8 +434,8 @@
|
|||||||
"two-way-routes": "Two way routes",
|
"two-way-routes": "Two way routes",
|
||||||
|
|
||||||
"option-active-timetables": "Active timetables",
|
"option-active-timetables": "Active timetables",
|
||||||
"option-timetables-history": "Timetables history",
|
"option-timetables-history": "Timetables history PL1",
|
||||||
"option-dispatchers-history": "Dispatchers history",
|
"option-dispatchers-history": "Dispatchers history PL1",
|
||||||
|
|
||||||
"timetable-author-title": "Issued by",
|
"timetable-author-title": "Issued by",
|
||||||
"timetable-author-unknown": "Author unknown",
|
"timetable-author-unknown": "Author unknown",
|
||||||
|
|||||||
+53
-26
@@ -133,7 +133,8 @@
|
|||||||
"filter-noComments": "BEZ UWAG",
|
"filter-noComments": "BEZ UWAG",
|
||||||
"filter-twr": "WYS. RYZYKA",
|
"filter-twr": "WYS. RYZYKA",
|
||||||
"filter-skr": "SKRAJNIA",
|
"filter-skr": "SKRAJNIA",
|
||||||
"filter-twr-skr": "WSZYSTKIE",
|
"filter-twr-skr": "TWR/SKR",
|
||||||
|
"filter-all-statuses": "WSZYSTKIE",
|
||||||
"filter-common": "ZWYKŁE",
|
"filter-common": "ZWYKŁE",
|
||||||
"filter-passenger": "PASAŻERSKIE",
|
"filter-passenger": "PASAŻERSKIE",
|
||||||
"filter-freight": "TOWAROWE",
|
"filter-freight": "TOWAROWE",
|
||||||
@@ -145,9 +146,9 @@
|
|||||||
"filter-clear": "WYŁĄCZ FILTRY",
|
"filter-clear": "WYŁĄCZ FILTRY",
|
||||||
|
|
||||||
"filter-section-timetable-status": "STATUS ROZKŁADU JAZDY",
|
"filter-section-timetable-status": "STATUS ROZKŁADU JAZDY",
|
||||||
"filter-section-twrskr": "UWAGI",
|
"filter-section-special": "TYPY SPECJALNE",
|
||||||
|
|
||||||
"filter-all": "WSZYSTKIE",
|
"filter-all-specials": "WSZYSTKIE",
|
||||||
"filter-abandoned": "PORZUCONE",
|
"filter-abandoned": "PORZUCONE",
|
||||||
"filter-fulfilled": "WYPEŁNIONE",
|
"filter-fulfilled": "WYPEŁNIONE",
|
||||||
"filter-active": "AKTYWNE"
|
"filter-active": "AKTYWNE"
|
||||||
@@ -164,7 +165,8 @@
|
|||||||
"signals": "TYP SYGNALIZACJI",
|
"signals": "TYP SYGNALIZACJI",
|
||||||
"addons": "DODATKOWE PROGRAMY",
|
"addons": "DODATKOWE PROGRAMY",
|
||||||
"blockades": "BLOKADY LINIOWE",
|
"blockades": "BLOKADY LINIOWE",
|
||||||
"status": "STATUS ONLINE"
|
"status": "STATUS ONLINE",
|
||||||
|
"timetables": "AKTYWNE ROZKŁADY JAZDY"
|
||||||
},
|
},
|
||||||
|
|
||||||
"all-available": "WSZYSTKIE DOSTĘPNE",
|
"all-available": "WSZYSTKIE DOSTĘPNE",
|
||||||
@@ -208,6 +210,9 @@
|
|||||||
"free": "WOLNA",
|
"free": "WOLNA",
|
||||||
"occupied": "ZAJĘTA",
|
"occupied": "ZAJĘTA",
|
||||||
|
|
||||||
|
"withActiveTimetables": "AKTYWNE RJ",
|
||||||
|
"withoutActiveTimetables": "BEZ AKTYWNYCH RJ",
|
||||||
|
|
||||||
"sliders": {
|
"sliders": {
|
||||||
"min-lvl": "MIN. WYMAGANY POZIOM DYŻURNEGO",
|
"min-lvl": "MIN. WYMAGANY POZIOM DYŻURNEGO",
|
||||||
"max-lvl": "MAKS. WYMAGANY POZIOM DYŻURNEGO",
|
"max-lvl": "MAKS. WYMAGANY POZIOM DYŻURNEGO",
|
||||||
@@ -217,7 +222,9 @@
|
|||||||
"routes-2t-other": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)"
|
"routes-2t-other": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)"
|
||||||
},
|
},
|
||||||
|
|
||||||
"authors-search": "Szukaj autora (uwzględnia inne filtry)",
|
"authors-search": "SZUKAJ AUTORA (uwzględnia inne filtry):",
|
||||||
|
"authors-placeholder": "Wpisz nick autora...",
|
||||||
|
"authors-button-title": "Szukaj",
|
||||||
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
|
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
|
||||||
"now": "TERAZ",
|
"now": "TERAZ",
|
||||||
"hour": " godz.",
|
"hour": " godz.",
|
||||||
@@ -326,31 +333,51 @@
|
|||||||
|
|
||||||
"load-data": "Pobierz dalszą historię...",
|
"load-data": "Pobierz dalszą historię...",
|
||||||
|
|
||||||
"stats-title": "STATYSTYKI MASZYNISTY",
|
|
||||||
|
|
||||||
"last-seen-at": "Ostatnio widziany na: ",
|
"last-seen-at": "Ostatnio widziany na: ",
|
||||||
"currently-at": "Obecnie na scenerii: ",
|
"currently-at": "Obecnie na scenerii: ",
|
||||||
|
|
||||||
"stats-timetables": "ROZKŁADY JAZDY",
|
"driver-stats": {
|
||||||
"stats-longest-timetable": "NAJDŁUŻSZY RJ",
|
"button": "STAT. MASZYNISTY",
|
||||||
"stats-avg-timetable": "ŚREDNIA DŁUGOŚĆ RJ",
|
"title": "STATYSTYKI MASZYNISTY {name}",
|
||||||
"stats-distance": "DYSTANS",
|
"info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki maszynisty!",
|
||||||
"stats-stations": "STACJE",
|
"timetables": "ROZKŁADY JAZDY",
|
||||||
|
"longest-timetable": "NAJDŁUŻSZY RJ",
|
||||||
|
"avg-timetable": "ŚREDNIA DŁUGOŚĆ RJ",
|
||||||
|
"distance": "DYSTANS",
|
||||||
|
"stations": "STACJE"
|
||||||
|
},
|
||||||
|
|
||||||
"timetable-stats-total": "Stworzone rozkłady jazdy: {count} (łączny dystans: {distance})",
|
"daily-stats": {
|
||||||
"timetable-stats-longest": "Najdłuższy rozkład jazdy: #{id} (stworzony przez dyżurnego {author} dla maszynisty {driver} o dystansie {distance})",
|
"button": "STATYSTYKI DNIA",
|
||||||
"timetable-stats-most-active-dr": "Najaktywniejszy dyżurny: {dispatcher} (stworzył {count})",
|
"title": "STATYSTYKI DNIA",
|
||||||
"timetable-stats-most-active-dr-many": "Najaktywniejsi dyżurni: {dispatchers} (stworzyli po {count})",
|
"info": "Dzisiejsze statystyki nie są jeszcze dostępne!",
|
||||||
"timetable-stats-most-active-driver": "Najaktywniejszy maszynista: {driver} (łączny przejechany dystans: {distance})",
|
"total": "Stworzone rozkłady jazdy: {count} (łączny dystans: {distance})",
|
||||||
"timetable-stats-longest-duties": "Najdłuższa służba: {dispatcher} na scenerii {station} (czas trwania: {duration})",
|
"longest": "Najdłuższy rozkład jazdy: #{id} (stworzony przez dyżurnego {author} dla maszynisty {driver} o dystansie {distance})",
|
||||||
|
"most-active-dr": "Najaktywniejszy dyżurny: {dispatcher} (stworzył {count})",
|
||||||
|
"most-active-dr-many": "Najaktywniejsi dyżurni: {dispatchers} (stworzyli po {count})",
|
||||||
|
"most-active-driver": "Najaktywniejszy maszynista: {driver} (łączny przejechany dystans: {distance})",
|
||||||
|
"longest-duties": "Najdłuższa służba: {dispatcher} na scenerii {station} (czas trwania: {duration})",
|
||||||
|
"count": "rozkład jazdy | rozkładów jazdy",
|
||||||
|
|
||||||
"timetable-count": "rozkład jazdy | rozkładów jazdy",
|
"rippedSwitches": "ROZPRUTE ZWROTNICE",
|
||||||
|
"derailments": "WYKOLEJENIA",
|
||||||
|
"skippedStopSignals": "POMINIĘTE S1",
|
||||||
|
"radioStops": "RADIOSTOPY",
|
||||||
|
"kills": "POTRĄCENIA"
|
||||||
|
},
|
||||||
|
|
||||||
"daily-stats-title": "STATYSTYKI DNIA",
|
"dispatcher-stats": {
|
||||||
"daily-stats-info": "Dzisiejsze statystyki nie są jeszcze dostępne!",
|
"button": "STATYSTYKI DYŻURNEGO",
|
||||||
|
"title": "STATYSTYKI DYŻURNEGO {name}",
|
||||||
"driver-stats-title": "STATYSTYKI GRACZA",
|
"info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki dyżurnego!",
|
||||||
"driver-stats-info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki maszynisty!",
|
"services-count": "DYŻURY",
|
||||||
|
"service-max": "MAKS. CZAS DYŻURU",
|
||||||
|
"service-avg": "ŚREDNI CZAS DYŻURU",
|
||||||
|
"timetables-count": "WYSTAWIONE RJ",
|
||||||
|
"timetables-sum": "SUMA WYSTAWIONYCH RJ",
|
||||||
|
"timetables-max": "NAJDŁUŻSZY WYSTAWIONY RJ",
|
||||||
|
"timetables-avg": "ŚREDNIA WYSTAWIONYCH RJ"
|
||||||
|
},
|
||||||
|
|
||||||
"stats-loading": "Pobieranie statystyk...",
|
"stats-loading": "Pobieranie statystyk...",
|
||||||
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk!",
|
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk!",
|
||||||
@@ -386,8 +413,8 @@
|
|||||||
"two-way-routes": "Szlaki dwutorowe",
|
"two-way-routes": "Szlaki dwutorowe",
|
||||||
|
|
||||||
"option-active-timetables": "Aktywne rozkłady jazdy",
|
"option-active-timetables": "Aktywne rozkłady jazdy",
|
||||||
"option-timetables-history": "Historia rozkładów",
|
"option-timetables-history": "Historia rozkładów PL1",
|
||||||
"option-dispatchers-history": "Historia dyżurów",
|
"option-dispatchers-history": "Historia dyżurów PL1",
|
||||||
|
|
||||||
"timetable-author-title": "Wydany przez",
|
"timetable-author-title": "Wydany przez",
|
||||||
"timetable-author-unknown": "Autor nieznany",
|
"timetable-author-unknown": "Autor nieznany",
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../store/mainStore';
|
import { useApiStore } from '../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore()
|
apiStore: useApiStore()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
isDonator(name: string) {
|
isDonator(name: string) {
|
||||||
return this.store.donatorsData.includes(name);
|
return this.apiStore.donatorsData.includes(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ export default defineComponent({
|
|||||||
mountObserver(actionFunction: () => void, target: Element) {
|
mountObserver(actionFunction: () => void, target: Element) {
|
||||||
this.observer = new IntersectionObserver(
|
this.observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
console.log(entries);
|
|
||||||
|
|
||||||
if (entries[0].intersectionRatio > 0.5) actionFunction();
|
if (entries[0].intersectionRatio > 0.5) actionFunction();
|
||||||
},
|
},
|
||||||
{ threshold: 0.2 }
|
{ threshold: 0.2 }
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useStore()
|
store: useMainStore()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+5
-9
@@ -18,7 +18,8 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
props: (route) => ({
|
props: (route) => ({
|
||||||
train: route.query.train,
|
train: route.query.train,
|
||||||
driver: route.query.driver,
|
driver: route.query.driver,
|
||||||
trainId: route.query.trainId
|
trainId: route.query.trainId,
|
||||||
|
region: route.query.region
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -39,9 +40,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
name: 'JournalTimetables',
|
name: 'JournalTimetables',
|
||||||
component: JournalTimetablesVue,
|
component: JournalTimetablesVue,
|
||||||
props: (route) => ({
|
props: (route) => ({
|
||||||
trainNo: route.query.trainNo,
|
region: route.query.region
|
||||||
driverName: route.query.driverName,
|
|
||||||
timetableId: route.query.timetableId
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -49,8 +48,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
name: 'JournalDispatchers',
|
name: 'JournalDispatchers',
|
||||||
component: JournalDispatchersVue,
|
component: JournalDispatchersVue,
|
||||||
props: (route) => ({
|
props: (route) => ({
|
||||||
sceneryName: route.query.sceneryName,
|
region: route.query.region
|
||||||
dispatcherName: route.query.dispatcherName
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,12 +59,10 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
scrollBehavior(to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
if (to.name == 'SceneryView' && from.name && from.query['view'] === undefined)
|
if (to.name == 'SceneryView' && from.name !== to.name && from.query['view'] === undefined)
|
||||||
return { el: `.app_main` };
|
return { el: `.app_main` };
|
||||||
|
|
||||||
if (savedPosition) return savedPosition;
|
if (savedPosition) return savedPosition;
|
||||||
|
|
||||||
// if (from.name == 'SceneryView' && to.name == 'StationsView') return { el: `.last-selected`, top: 20 };
|
|
||||||
},
|
},
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
routes
|
routes
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Availability, OnlineScenery, ScheduledTrain } from '../../store/typings';
|
import { Availability, ActiveScenery, ScheduledTrain } from '../../store/typings';
|
||||||
import StationRoutes from './StationRoutes';
|
import { StationRoutes } from './StationRoutes';
|
||||||
|
|
||||||
export default interface Station {
|
export default interface Station {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -8,6 +8,7 @@ export default interface Station {
|
|||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
abbr: string;
|
abbr: string;
|
||||||
|
hash?: string;
|
||||||
|
|
||||||
reqLevel: number;
|
reqLevel: number;
|
||||||
// supportersOnly: boolean;
|
// supportersOnly: boolean;
|
||||||
@@ -25,11 +26,8 @@ export default interface Station {
|
|||||||
availability: Availability;
|
availability: Availability;
|
||||||
routes: StationRoutes;
|
routes: StationRoutes;
|
||||||
|
|
||||||
checkpoints: {
|
checkpoints: string[];
|
||||||
checkpointName: string;
|
|
||||||
scheduledTrains: ScheduledTrain[];
|
|
||||||
}[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onlineInfo?: OnlineScenery;
|
onlineInfo?: ActiveScenery;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,8 @@
|
|||||||
export default interface StationRoutes {
|
import { StationRoutesInfo } from '../../store/typings';
|
||||||
oneWay: {
|
|
||||||
name: string;
|
|
||||||
catenary: boolean;
|
|
||||||
SBL: boolean;
|
|
||||||
TWB: boolean;
|
|
||||||
isInternal: boolean;
|
|
||||||
tracks: number;
|
|
||||||
speed: number;
|
|
||||||
length: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
twoWay: {
|
export interface StationRoutes {
|
||||||
name: string;
|
oneWay: StationRoutesInfo[];
|
||||||
catenary: boolean;
|
twoWay: StationRoutesInfo[];
|
||||||
SBL: boolean;
|
|
||||||
TWB: boolean;
|
|
||||||
isInternal: boolean;
|
|
||||||
tracks: number;
|
|
||||||
speed: number;
|
|
||||||
length: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
/* [catenary, noCatenary] */
|
/* [catenary, noCatenary] */
|
||||||
oneWayCatenaryRouteNames: string[];
|
oneWayCatenaryRouteNames: string[];
|
||||||
|
|||||||
@@ -33,5 +33,6 @@ export default interface Train {
|
|||||||
SKR: boolean;
|
SKR: boolean;
|
||||||
routeDistance: number;
|
routeDistance: number;
|
||||||
sceneries: string[];
|
sceneries: string[];
|
||||||
|
sceneryNames: string[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
export const URLs = {
|
|
||||||
stacjownikAPI:
|
|
||||||
import.meta.env.VITE_APP_API_DEV === '1' && !import.meta.env.PROD
|
|
||||||
? 'http://localhost:3001'
|
|
||||||
: 'https://stacjownik.spythere.eu',
|
|
||||||
stacjownikAPIDev: 'localhost:3000'
|
|
||||||
};
|
|
||||||
@@ -93,26 +93,41 @@ export const sortStations = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const filterStations = (station: Station, filters: Filter) => {
|
export const filterStations = (station: Station, filters: Filter) => {
|
||||||
if (!station.onlineInfo && filters['free']) return false;
|
if (filters['free'] && (!station.onlineInfo || station.onlineInfo.dispatcherId == -1))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (station.onlineInfo) {
|
if (station.onlineInfo) {
|
||||||
const { dispatcherStatus } = station.onlineInfo;
|
const { dispatcherStatus } = station.onlineInfo;
|
||||||
|
|
||||||
const isEnding = dispatcherStatus == Status.ActiveDispatcher.ENDING && filters['endingStatus'];
|
const excludeEnding =
|
||||||
|
dispatcherStatus == Status.ActiveDispatcher.ENDING && filters['endingStatus'];
|
||||||
|
|
||||||
const isNotSigned =
|
const excludeNotSigned =
|
||||||
(dispatcherStatus == Status.ActiveDispatcher.NOT_LOGGED_IN ||
|
(dispatcherStatus == Status.ActiveDispatcher.NOT_LOGGED_IN ||
|
||||||
dispatcherStatus == Status.ActiveDispatcher.UNAVAILABLE) &&
|
dispatcherStatus == Status.ActiveDispatcher.UNAVAILABLE) &&
|
||||||
filters['unavailableStatus'];
|
filters['unavailableStatus'];
|
||||||
|
|
||||||
const isAFK = dispatcherStatus == Status.ActiveDispatcher.AFK && filters['afkStatus'];
|
const excludeAFK = dispatcherStatus == Status.ActiveDispatcher.AFK && filters['afkStatus'];
|
||||||
|
|
||||||
const isNoSpace =
|
const excludeNoSpace =
|
||||||
dispatcherStatus == Status.ActiveDispatcher.NO_SPACE && filters['noSpaceStatus'];
|
dispatcherStatus == Status.ActiveDispatcher.NO_SPACE && filters['noSpaceStatus'];
|
||||||
|
|
||||||
const isOccupied = station.onlineInfo && filters['occupied'];
|
const excludeOccupied = filters['occupied'] && dispatcherStatus != Status.ActiveDispatcher.FREE;
|
||||||
|
|
||||||
if (isEnding || isNotSigned || isAFK || isNoSpace || isOccupied) return false;
|
const excludeActiveTTs =
|
||||||
|
(dispatcherStatus == Status.ActiveDispatcher.FREE ||
|
||||||
|
station.onlineInfo.scheduledTrainCount.all != 0) &&
|
||||||
|
filters['withActiveTimetables'];
|
||||||
|
|
||||||
|
if (
|
||||||
|
excludeEnding ||
|
||||||
|
excludeAFK ||
|
||||||
|
excludeNoSpace ||
|
||||||
|
excludeNotSigned ||
|
||||||
|
excludeOccupied ||
|
||||||
|
excludeActiveTTs
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
filters['onlineFromHours'] > 0 &&
|
filters['onlineFromHours'] > 0 &&
|
||||||
@@ -121,6 +136,12 @@ export const filterStations = (station: Station, filters: Filter) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const excludeNoActiveTTs =
|
||||||
|
filters['withoutActiveTimetables'] &&
|
||||||
|
(!station.onlineInfo || station.onlineInfo.scheduledTrainCount.all == 0);
|
||||||
|
|
||||||
|
if (excludeNoActiveTTs) return false;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(station.generalInfo?.availability == 'nonPublic' || !station.generalInfo) &&
|
(station.generalInfo?.availability == 'nonPublic' || !station.generalInfo) &&
|
||||||
filters['nonPublic']
|
filters['nonPublic']
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import http from '../http';
|
||||||
|
import { API } from '../typings/api';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { Status } from '../typings/common';
|
||||||
|
import { StationJSONData } from './typings';
|
||||||
|
|
||||||
|
// Update seconds cron for active data scheduler
|
||||||
|
const UPDATE_SECONDS = [3, 23, 43];
|
||||||
|
|
||||||
|
export const useApiStore = defineStore('apiStore', {
|
||||||
|
state: () => ({
|
||||||
|
dataStatuses: {
|
||||||
|
connection: Status.Data.Loading,
|
||||||
|
sceneries: Status.Data.Loading
|
||||||
|
},
|
||||||
|
|
||||||
|
activeData: undefined as API.ActiveData.Response | undefined,
|
||||||
|
rollingStockData: undefined as API.RollingStock.Response | undefined,
|
||||||
|
donatorsData: [] as API.Donators.Response,
|
||||||
|
sceneryData: [] as StationJSONData[],
|
||||||
|
|
||||||
|
activeDataScheduler: undefined as number | undefined
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async setupAPIData() {
|
||||||
|
// Static data
|
||||||
|
this.fetchStockInfoData();
|
||||||
|
this.fetchDonatorsData();
|
||||||
|
this.fetchStationsGeneralInfo();
|
||||||
|
|
||||||
|
// Active data schedueler
|
||||||
|
this.fetchActiveData();
|
||||||
|
this.setupActiveDataFetcher();
|
||||||
|
},
|
||||||
|
|
||||||
|
async setupActiveDataFetcher() {
|
||||||
|
if (this.activeDataScheduler) return;
|
||||||
|
|
||||||
|
this.dataStatuses.connection = Status.Data.Loading;
|
||||||
|
|
||||||
|
this.activeDataScheduler = window.setInterval(() => {
|
||||||
|
if (UPDATE_SECONDS.includes(new Date().getSeconds())) {
|
||||||
|
this.fetchActiveData();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchActiveData() {
|
||||||
|
try {
|
||||||
|
const response = await http.get<API.ActiveData.Response>('api/getActiveData');
|
||||||
|
|
||||||
|
this.activeData = response.data;
|
||||||
|
this.dataStatuses.connection = Status.Data.Loaded;
|
||||||
|
|
||||||
|
console.log('Fetching active data at ' + new Date().toLocaleTimeString('pl-PL'));
|
||||||
|
} catch (error) {
|
||||||
|
this.dataStatuses.connection = Status.Data.Error;
|
||||||
|
console.error('Ups! Wystąpił błąd podczas pobierania danych online:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchDonatorsData() {
|
||||||
|
try {
|
||||||
|
const response = await http.get<API.Donators.Response>('api/getDonators');
|
||||||
|
|
||||||
|
this.donatorsData = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ups! Wystąpił błąd podczas pobierania informacji o donatorach:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchStockInfoData() {
|
||||||
|
try {
|
||||||
|
this.rollingStockData = (
|
||||||
|
await axios.get<API.RollingStock.Response>(
|
||||||
|
'https://raw.githubusercontent.com/Spythere/api/main/td2/data/stockInfo.json'
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ups! Wystąpił błąd podczas pobierania informacji o taborze z API:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchStationsGeneralInfo() {
|
||||||
|
const sceneryData: StationJSONData[] = (await http.get<StationJSONData[]>('api/getSceneries'))
|
||||||
|
.data;
|
||||||
|
|
||||||
|
if (!sceneryData) {
|
||||||
|
this.dataStatuses.sceneries = Status.Data.Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataStatuses.sceneries = Status.Data.Loaded;
|
||||||
|
this.sceneryData = sceneryData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
+149
-216
@@ -1,42 +1,23 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { io } from 'socket.io-client';
|
|
||||||
import StationRoutes from '../scripts/interfaces/StationRoutes';
|
|
||||||
import Train from '../scripts/interfaces/Train';
|
import Train from '../scripts/interfaces/Train';
|
||||||
import { URLs } from '../scripts/utils/apiURLs';
|
|
||||||
import { parseSpawns, getScheduledTrains, getStationTrains } from './utils';
|
import { parseSpawns, getScheduledTrains, getStationTrains } from './utils';
|
||||||
|
|
||||||
import { OnlineScenery, ScheduledTrain, StationJSONData, StoreState } from './typings';
|
import { ActiveScenery, ScheduledTrain, StoreState } from './typings';
|
||||||
|
|
||||||
import packageInfo from '../../package.json';
|
|
||||||
import { Websocket, API } from '../typings/api';
|
|
||||||
import { Status } from '../typings/common';
|
import { Status } from '../typings/common';
|
||||||
|
import Station from '../scripts/interfaces/Station';
|
||||||
|
import { useApiStore } from './apiStore';
|
||||||
|
import { StationRoutes } from '../scripts/interfaces/StationRoutes';
|
||||||
|
|
||||||
export const useStore = defineStore('store', {
|
export const useMainStore = defineStore('store', {
|
||||||
state: () =>
|
state: () =>
|
||||||
({
|
({
|
||||||
activeData: {} as unknown,
|
|
||||||
rollingStockData: undefined,
|
|
||||||
donatorsData: [],
|
|
||||||
|
|
||||||
stationList: [],
|
|
||||||
regionOnlineCounters: [],
|
|
||||||
|
|
||||||
routesList: [],
|
|
||||||
|
|
||||||
sceneryData: [],
|
|
||||||
lastDispatcherStatuses: [],
|
|
||||||
|
|
||||||
region: { id: 'eu', value: 'PL1' },
|
region: { id: 'eu', value: 'PL1' },
|
||||||
|
|
||||||
trainCount: 0,
|
|
||||||
stationCount: 0,
|
|
||||||
|
|
||||||
webSocket: undefined,
|
|
||||||
isOffline: false,
|
isOffline: false,
|
||||||
|
|
||||||
dispatcherStatsName: '',
|
dispatcherStatsName: '',
|
||||||
dispatcherStatsData: undefined,
|
dispatcherStatsStatus: Status.Data.Initialized,
|
||||||
|
|
||||||
driverStatsName: '',
|
driverStatsName: '',
|
||||||
driverStatsData: undefined,
|
driverStatsData: undefined,
|
||||||
@@ -44,31 +25,15 @@ export const useStore = defineStore('store', {
|
|||||||
|
|
||||||
chosenModalTrainId: undefined,
|
chosenModalTrainId: undefined,
|
||||||
|
|
||||||
dataStatuses: {
|
|
||||||
connection: Status.Data.Loading,
|
|
||||||
sceneries: Status.Data.Loading,
|
|
||||||
timetables: Status.Data.Loading,
|
|
||||||
dispatchers: Status.Data.Loading,
|
|
||||||
trains: Status.Data.Loading
|
|
||||||
},
|
|
||||||
|
|
||||||
currentStatsTab: null,
|
|
||||||
|
|
||||||
blockScroll: false,
|
blockScroll: false,
|
||||||
listenerLaunched: false,
|
modalLastClickedTarget: null
|
||||||
modalLastClickedTarget: null,
|
|
||||||
|
|
||||||
tooltip: {
|
|
||||||
content: '',
|
|
||||||
visible: false,
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
}
|
|
||||||
}) as StoreState,
|
}) as StoreState,
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
trainList(): Train[] {
|
trainList(): Train[] {
|
||||||
return (this.activeData?.trains ?? [])
|
const apiStore = useApiStore();
|
||||||
|
|
||||||
|
return (apiStore.activeData?.trains ?? [])
|
||||||
.filter((train) => train.timetable || train.online)
|
.filter((train) => train.timetable || train.online)
|
||||||
.map((train) => {
|
.map((train) => {
|
||||||
const stock = train.stockString.split(';');
|
const stock = train.stockString.split(';');
|
||||||
@@ -76,6 +41,14 @@ export const useStore = defineStore('store', {
|
|||||||
|
|
||||||
const timetable = train.timetable;
|
const timetable = train.timetable;
|
||||||
|
|
||||||
|
const sceneryNames =
|
||||||
|
train.timetable?.sceneries?.map(
|
||||||
|
(sceneryHash) =>
|
||||||
|
this.activeSceneryList.find((st) => st.hash === sceneryHash)?.name ??
|
||||||
|
apiStore.sceneryData.find((sd) => sd.hash === sceneryHash)?.name ??
|
||||||
|
sceneryHash
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
trainId: train.driverName + train.trainNo.toString(),
|
trainId: train.driverName + train.trainNo.toString(),
|
||||||
|
|
||||||
@@ -87,7 +60,7 @@ export const useStore = defineStore('store', {
|
|||||||
|
|
||||||
distance: train.distance,
|
distance: train.distance,
|
||||||
signal: train.signal,
|
signal: train.signal,
|
||||||
online: train.online,
|
online: Boolean(train.online),
|
||||||
driverId: train.driverId,
|
driverId: train.driverId,
|
||||||
driverName: train.driverName,
|
driverName: train.driverName,
|
||||||
currentStationName: train.currentStationName,
|
currentStationName: train.currentStationName,
|
||||||
@@ -111,41 +84,67 @@ export const useStore = defineStore('store', {
|
|||||||
category: timetable.category,
|
category: timetable.category,
|
||||||
followingStops: timetable.stopList,
|
followingStops: timetable.stopList,
|
||||||
routeDistance: timetable.stopList[timetable.stopList.length - 1].stopDistance,
|
routeDistance: timetable.stopList[timetable.stopList.length - 1].stopDistance,
|
||||||
sceneries: timetable.sceneries
|
sceneries: timetable.sceneries,
|
||||||
|
sceneryNames: sceneryNames.reverse()
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
} as Train;
|
} as Train;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onlineSceneryList(state): OnlineScenery[] {
|
activeSceneryList(state): ActiveScenery[] {
|
||||||
if (state.isOffline) return [];
|
const apiStore = useApiStore();
|
||||||
if (!state.activeData?.activeSceneries) return [];
|
|
||||||
|
|
||||||
return state.activeData?.activeSceneries.reduce((list, scenery) => {
|
if (state.isOffline) return [];
|
||||||
|
|
||||||
|
if (!apiStore.activeData?.activeSceneries) return [];
|
||||||
|
|
||||||
|
const offlineActiveSceneries = this.trainList.reduce((acc, train) => {
|
||||||
|
if (!train.timetableData) return acc;
|
||||||
|
|
||||||
|
train.timetableData.sceneryNames.forEach((name) => {
|
||||||
|
if (
|
||||||
|
acc.findIndex((v) => v.name == name && v.region == train.region) != -1 ||
|
||||||
|
apiStore.activeData?.activeSceneries?.findIndex(
|
||||||
|
(sc) => sc.stationName === name && sc.region == train.region
|
||||||
|
) != -1
|
||||||
|
)
|
||||||
|
return acc;
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
name: name,
|
||||||
|
hash: '',
|
||||||
|
region: train.region,
|
||||||
|
maxUsers: 0,
|
||||||
|
currentUsers: 0,
|
||||||
|
spawns: [],
|
||||||
|
dispatcherName: '',
|
||||||
|
dispatcherRate: 0,
|
||||||
|
dispatcherId: -1,
|
||||||
|
dispatcherExp: -1,
|
||||||
|
dispatcherIsSupporter: false,
|
||||||
|
scheduledTrains: [],
|
||||||
|
stationTrains: [],
|
||||||
|
dispatcherStatus: Status.ActiveDispatcher.FREE,
|
||||||
|
dispatcherTimestamp: -1,
|
||||||
|
|
||||||
|
isOnline: false,
|
||||||
|
|
||||||
|
scheduledTrainCount: {
|
||||||
|
all: 0,
|
||||||
|
confirmed: 0,
|
||||||
|
unconfirmed: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] as ActiveScenery[]);
|
||||||
|
|
||||||
|
const onlineActiveSceneries = apiStore.activeData?.activeSceneries.reduce((list, scenery) => {
|
||||||
if (scenery.isOnline !== 1 && Date.now() - scenery.lastSeen > 1000 * 60 * 2) return list;
|
if (scenery.isOnline !== 1 && Date.now() - scenery.lastSeen > 1000 * 60 * 2) return list;
|
||||||
if (scenery.dispatcherStatus == Status.ActiveDispatcher.UNKNOWN) return list;
|
if (scenery.dispatcherStatus == Status.ActiveDispatcher.UNKNOWN) return list;
|
||||||
|
|
||||||
const station = this.stationList.find((s) => s.name === scenery.stationName);
|
|
||||||
|
|
||||||
const scheduledTrains = getScheduledTrains(this.trainList, scenery, station?.generalInfo);
|
|
||||||
|
|
||||||
const stationTrains = getStationTrains(
|
|
||||||
this.trainList,
|
|
||||||
scheduledTrains,
|
|
||||||
this.region.id,
|
|
||||||
scenery
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove checkpoint duplicates
|
|
||||||
const uniqueScheduledTrains = scheduledTrains.reduce(
|
|
||||||
(uniqueList, sTrain) =>
|
|
||||||
uniqueList.find((v) => v.trainId === sTrain.trainId)
|
|
||||||
? uniqueList
|
|
||||||
: [...uniqueList, sTrain],
|
|
||||||
[] as ScheduledTrain[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const dispatcherTimestamp =
|
const dispatcherTimestamp =
|
||||||
scenery.dispatcherStatus == Status.ActiveDispatcher.NO_LIMIT
|
scenery.dispatcherStatus == Status.ActiveDispatcher.NO_LIMIT
|
||||||
? Date.now() + 25500000
|
? Date.now() + 25500000
|
||||||
@@ -165,173 +164,107 @@ export const useStore = defineStore('store', {
|
|||||||
dispatcherId: scenery.dispatcherId,
|
dispatcherId: scenery.dispatcherId,
|
||||||
dispatcherExp: scenery.dispatcherExp,
|
dispatcherExp: scenery.dispatcherExp,
|
||||||
dispatcherIsSupporter: scenery.dispatcherIsSupporter,
|
dispatcherIsSupporter: scenery.dispatcherIsSupporter,
|
||||||
scheduledTrains: scheduledTrains,
|
|
||||||
stationTrains: stationTrains,
|
|
||||||
dispatcherStatus: scenery.dispatcherStatus,
|
dispatcherStatus: scenery.dispatcherStatus,
|
||||||
dispatcherTimestamp: dispatcherTimestamp,
|
dispatcherTimestamp: dispatcherTimestamp,
|
||||||
|
|
||||||
isOnline: scenery.isOnline == 1,
|
isOnline: scenery.isOnline == 1,
|
||||||
|
|
||||||
|
scheduledTrains: [],
|
||||||
|
stationTrains: [],
|
||||||
scheduledTrainCount: {
|
scheduledTrainCount: {
|
||||||
all: uniqueScheduledTrains.length,
|
all: 0,
|
||||||
confirmed: uniqueScheduledTrains.filter((train) => train.stopInfo.confirmed).length,
|
confirmed: 0,
|
||||||
unconfirmed: uniqueScheduledTrains.filter((train) => !train.stopInfo.confirmed).length
|
unconfirmed: 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}, [] as OnlineScenery[]);
|
}, [] as ActiveScenery[]);
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
async fetchStationsGeneralInfo() {
|
|
||||||
const sceneryData: StationJSONData[] = await (
|
|
||||||
await axios.get(`${URLs.stacjownikAPI}/api/getSceneries`)
|
|
||||||
).data;
|
|
||||||
|
|
||||||
if (!sceneryData) {
|
const allActiveSceneries = [...onlineActiveSceneries, ...offlineActiveSceneries];
|
||||||
this.dataStatuses.sceneries = Status.Data.Error;
|
|
||||||
return;
|
for (let i = 0, n = allActiveSceneries.length; i < n; i++) {
|
||||||
|
const scenery = allActiveSceneries[i];
|
||||||
|
|
||||||
|
const station = this.stationList.find((s) => s.name === scenery.name);
|
||||||
|
|
||||||
|
const scheduledTrains = getScheduledTrains(
|
||||||
|
this.trainList,
|
||||||
|
station?.generalInfo,
|
||||||
|
scenery.name,
|
||||||
|
scenery.region
|
||||||
|
);
|
||||||
|
|
||||||
|
const stationTrains = getStationTrains(
|
||||||
|
this.trainList,
|
||||||
|
scheduledTrains,
|
||||||
|
this.region.id,
|
||||||
|
scenery.name
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove checkpoint duplicates
|
||||||
|
const uniqueScheduledTrains = scheduledTrains.reduce(
|
||||||
|
(uniqueList, sTrain) =>
|
||||||
|
uniqueList.find((v) => v.trainId === sTrain.trainId)
|
||||||
|
? uniqueList
|
||||||
|
: [...uniqueList, sTrain],
|
||||||
|
[] as ScheduledTrain[]
|
||||||
|
);
|
||||||
|
|
||||||
|
scenery.scheduledTrains = scheduledTrains;
|
||||||
|
scenery.stationTrains = stationTrains;
|
||||||
|
|
||||||
|
scenery.scheduledTrainCount = {
|
||||||
|
all: uniqueScheduledTrains.length,
|
||||||
|
confirmed: uniqueScheduledTrains.filter((train) => train.stopInfo.confirmed).length,
|
||||||
|
unconfirmed: uniqueScheduledTrains.filter((train) => !train.stopInfo.confirmed).length
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stationList = sceneryData.map((scenery) => {
|
return allActiveSceneries;
|
||||||
|
},
|
||||||
|
|
||||||
|
stationList(): Station[] {
|
||||||
|
const apiStore = useApiStore();
|
||||||
|
|
||||||
|
return apiStore.sceneryData.map((scenery) => {
|
||||||
|
const routes = scenery.routesInfo.reduce(
|
||||||
|
(acc, route) => {
|
||||||
|
const tracksKey = route.routeTracks == 2 ? 'twoWay' : 'oneWay';
|
||||||
|
const isElectric = route.isElectric;
|
||||||
|
const routesKey: keyof StationRoutes = `${tracksKey}${
|
||||||
|
!isElectric ? 'No' : ''
|
||||||
|
}CatenaryRouteNames`;
|
||||||
|
|
||||||
|
if (!route.isInternal) acc[routesKey].push(route.routeName);
|
||||||
|
if (route.isRouteSBL) acc['sblRouteNames'].push(route.routeName);
|
||||||
|
|
||||||
|
acc[tracksKey].push(route);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oneWay: [],
|
||||||
|
oneWayCatenaryRouteNames: [],
|
||||||
|
oneWayNoCatenaryRouteNames: [],
|
||||||
|
twoWay: [],
|
||||||
|
twoWayCatenaryRouteNames: [],
|
||||||
|
twoWayNoCatenaryRouteNames: [],
|
||||||
|
sblRouteNames: []
|
||||||
|
} as StationRoutes
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: scenery.name,
|
name: scenery.name,
|
||||||
|
|
||||||
generalInfo: {
|
generalInfo: {
|
||||||
...scenery,
|
...scenery,
|
||||||
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
||||||
routes:
|
routes: routes,
|
||||||
scenery.routesInfo.reduce(
|
checkpoints: scenery.checkpoints?.split(';') ?? []
|
||||||
(acc, route) => {
|
|
||||||
const propName: keyof StationRoutes = `${
|
|
||||||
route.routeTracks == 2 ? 'twoWay' : 'oneWay'
|
|
||||||
}${route.isElectric ? '' : 'No'}CatenaryRouteNames`;
|
|
||||||
|
|
||||||
acc[route.routeTracks == 2 ? 'twoWay' : 'oneWay'].push({
|
|
||||||
name: route.routeName,
|
|
||||||
SBL: route.isRouteSBL,
|
|
||||||
TWB: false,
|
|
||||||
catenary: route.isElectric,
|
|
||||||
isInternal: route.isInternal,
|
|
||||||
tracks: route.routeTracks,
|
|
||||||
length: route.routeLength,
|
|
||||||
speed: route.routeSpeed
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!route.isInternal) acc[propName].push(route.routeName);
|
|
||||||
|
|
||||||
if (route.isRouteSBL) acc['sblRouteNames'].push(route.routeName);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
oneWay: [],
|
|
||||||
twoWay: [],
|
|
||||||
sblRouteNames: [],
|
|
||||||
oneWayCatenaryRouteNames: [],
|
|
||||||
oneWayNoCatenaryRouteNames: [],
|
|
||||||
twoWayCatenaryRouteNames: [],
|
|
||||||
twoWayNoCatenaryRouteNames: []
|
|
||||||
} as StationRoutes
|
|
||||||
) || {},
|
|
||||||
checkpoints: scenery.checkpoints
|
|
||||||
? scenery.checkpoints
|
|
||||||
.split(';')
|
|
||||||
.map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
|
|
||||||
: []
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
async connectToWebsocket() {
|
|
||||||
if (import.meta.env.VITE_APP_WS_DEV === '1') {
|
|
||||||
const mockWebsocketData = await import('../data/mockWebsocketData.json');
|
|
||||||
this.dataStatuses.connection = Status.Data.Loaded;
|
|
||||||
this.activeData = mockWebsocketData as any;
|
|
||||||
this.setStatuses();
|
|
||||||
|
|
||||||
console.warn('Stacjownik działa w trybie mockowania danych z WS');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const socket = io(URLs.stacjownikAPI, {
|
|
||||||
transports: ['websocket', 'polling'],
|
|
||||||
rememberUpgrade: true,
|
|
||||||
reconnection: true
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.emit('CONNECTION', { version: packageInfo.version });
|
|
||||||
|
|
||||||
socket.on('connect_error', () => {
|
|
||||||
this.dataStatuses.connection = Status.Data.Error;
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('UPDATE', (data: Websocket.ActiveData) => {
|
|
||||||
this.activeData = data;
|
|
||||||
this.dataStatuses.connection = Status.Data.Loaded;
|
|
||||||
|
|
||||||
this.setStatuses();
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.emit('FETCH_DATA', { version: packageInfo.version }, (data: Websocket.ActiveData) => {
|
|
||||||
this.dataStatuses.connection = Status.Data.Loaded;
|
|
||||||
this.activeData = data;
|
|
||||||
this.setStatuses();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.webSocket = socket;
|
|
||||||
},
|
|
||||||
|
|
||||||
async connectToAPI() {
|
|
||||||
this.connectToWebsocket();
|
|
||||||
this.fetchStockInfoData();
|
|
||||||
this.fetchDonatorsData();
|
|
||||||
this.fetchStationsGeneralInfo();
|
|
||||||
},
|
|
||||||
|
|
||||||
async changeRegion(region: StoreState['region']) {
|
|
||||||
this.region = region;
|
|
||||||
},
|
|
||||||
|
|
||||||
async fetchStockInfoData() {
|
|
||||||
try {
|
|
||||||
this.rollingStockData = (
|
|
||||||
await axios.get<API.RollingStock.Response>(
|
|
||||||
'https://raw.githubusercontent.com/Spythere/api/main/td2/data/stockInfo.json'
|
|
||||||
)
|
|
||||||
).data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Ups! Wystąpił błąd podczas pobierania informacji o taborze z API:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async fetchDonatorsData() {
|
|
||||||
try {
|
|
||||||
const response = await axios.get<API.Donators.Response>(
|
|
||||||
`${URLs.stacjownikAPI}/api/getDonators`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data) this.donatorsData = response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Ups! Wystąpił błąd podczas pobierania informacji o donatorach:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async setStatuses() {
|
|
||||||
if (!this.activeData.activeSceneries) {
|
|
||||||
this.dataStatuses.sceneries = Status.Data.Error;
|
|
||||||
this.dataStatuses.trains = Status.Data.Error;
|
|
||||||
this.dataStatuses.dispatchers = Status.Data.Error;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dataStatuses.sceneries = Status.Data.Loaded;
|
|
||||||
this.dataStatuses.trains = !this.activeData.trains ? Status.Data.Warning : Status.Data.Loaded;
|
|
||||||
this.dataStatuses.dispatchers = Status.Data.Loaded;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import inputData from '../data/options.json';
|
import inputData from '../data/options.json';
|
||||||
import { useStore } from './mainStore';
|
import { useMainStore } from './mainStore';
|
||||||
import { filterStations, sortStations } from '../scripts/utils/filterUtils';
|
import { filterStations, sortStations } from '../scripts/utils/filterUtils';
|
||||||
import { HeadIdsTypes } from '../scripts/data/stationHeaderNames';
|
import { HeadIdsTypes } from '../scripts/data/stationHeaderNames';
|
||||||
import StorageManager from '../managers/storageManager';
|
import StorageManager from '../managers/storageManager';
|
||||||
@@ -48,6 +48,8 @@ const filterInitStates: Filter = {
|
|||||||
noSpaceStatus: false,
|
noSpaceStatus: false,
|
||||||
unavailableStatus: false,
|
unavailableStatus: false,
|
||||||
unsignedStatus: false,
|
unsignedStatus: false,
|
||||||
|
withActiveTimetables: false,
|
||||||
|
withoutActiveTimetables: false,
|
||||||
|
|
||||||
authors: '',
|
authors: '',
|
||||||
|
|
||||||
@@ -70,14 +72,27 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
filteredStationList: (state) => {
|
filteredStationList: (state) => {
|
||||||
const store = useStore();
|
const store = useMainStore();
|
||||||
return store.stationList
|
const savedStationNames = store.stationList.map((s) => s.name);
|
||||||
.map((station) => ({
|
|
||||||
|
const onlineUnsavedStations = store.activeSceneryList
|
||||||
|
.filter((os) => !savedStationNames.includes(os.name) && os.region == store.region.id)
|
||||||
|
.map((os) => ({
|
||||||
|
name: os.name,
|
||||||
|
generalInfo: undefined,
|
||||||
|
onlineInfo: os
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [
|
||||||
|
...onlineUnsavedStations,
|
||||||
|
...store.stationList.map((station) => ({
|
||||||
...station,
|
...station,
|
||||||
onlineInfo: store.onlineSceneryList.find(
|
// append to 'onlineInfo' object for filtering legacy reasons - to optimize later (hopefully)
|
||||||
|
onlineInfo: store.activeSceneryList.find(
|
||||||
(os) => os.name == station.name && os.region == store.region.id
|
(os) => os.name == station.name && os.region == store.region.id
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
]
|
||||||
.filter((station) => filterStations(station, state.filters))
|
.filter((station) => filterStations(station, state.filters))
|
||||||
.sort((a, b) => sortStations(a, b, state.sorterActive));
|
.sort((a, b) => sortStations(a, b, state.sorterActive));
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-40
@@ -1,6 +1,4 @@
|
|||||||
import { Socket } from 'socket.io-client';
|
import { API } from '../typings/api';
|
||||||
import Station from '../scripts/interfaces/Station';
|
|
||||||
import { API, Websocket } from '../typings/api';
|
|
||||||
import { Status } from '../typings/common';
|
import { Status } from '../typings/common';
|
||||||
|
|
||||||
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
|
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
|
||||||
@@ -12,26 +10,8 @@ export interface RegionCounters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
stationList: Station[];
|
|
||||||
activeData: Websocket.ActiveData;
|
|
||||||
rollingStockData?: API.RollingStock.Response;
|
|
||||||
donatorsData: API.Donators.Response;
|
|
||||||
|
|
||||||
regionOnlineCounters: RegionCounters[];
|
|
||||||
|
|
||||||
lastDispatcherStatuses: {
|
|
||||||
hash: string;
|
|
||||||
statusTimestamp: number;
|
|
||||||
statusID: Status.ActiveDispatcher;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
sceneryData: any[][];
|
|
||||||
|
|
||||||
region: { id: string; value: string };
|
region: { id: string; value: string };
|
||||||
trainCount: number;
|
|
||||||
stationCount: number;
|
|
||||||
|
|
||||||
webSocket?: Socket;
|
|
||||||
isOffline: boolean;
|
isOffline: boolean;
|
||||||
|
|
||||||
dispatcherStatsName: string;
|
dispatcherStatsName: string;
|
||||||
@@ -43,26 +23,8 @@ export interface StoreState {
|
|||||||
|
|
||||||
chosenModalTrainId?: string;
|
chosenModalTrainId?: string;
|
||||||
|
|
||||||
currentStatsTab: 'daily' | 'driver' | null;
|
|
||||||
|
|
||||||
dataStatuses: {
|
|
||||||
connection: Status.Data;
|
|
||||||
sceneries: Status.Data;
|
|
||||||
timetables: Status.Data;
|
|
||||||
dispatchers: Status.Data;
|
|
||||||
trains: Status.Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
listenerLaunched: boolean;
|
|
||||||
blockScroll: boolean;
|
blockScroll: boolean;
|
||||||
modalLastClickedTarget: EventTarget | null;
|
modalLastClickedTarget: EventTarget | null;
|
||||||
|
|
||||||
tooltip: {
|
|
||||||
visible: boolean;
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
content: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StationRoutesInfo {
|
export interface StationRoutesInfo {
|
||||||
@@ -73,6 +35,7 @@ export interface StationRoutesInfo {
|
|||||||
routeLength: number;
|
routeLength: number;
|
||||||
routeSpeed: number;
|
routeSpeed: number;
|
||||||
routeTracks: number;
|
routeTracks: number;
|
||||||
|
hidden?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StationJSONData {
|
export interface StationJSONData {
|
||||||
@@ -82,6 +45,7 @@ export interface StationJSONData {
|
|||||||
lines: string;
|
lines: string;
|
||||||
project: string;
|
project: string;
|
||||||
projectUrl: string;
|
projectUrl: string;
|
||||||
|
hash: string;
|
||||||
|
|
||||||
reqLevel: number;
|
reqLevel: number;
|
||||||
|
|
||||||
@@ -99,7 +63,7 @@ export interface StationJSONData {
|
|||||||
availability: Availability;
|
availability: Availability;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OnlineScenery {
|
export interface ActiveScenery {
|
||||||
name: string;
|
name: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
region: string;
|
region: string;
|
||||||
@@ -166,6 +130,8 @@ export interface ScheduledTrain {
|
|||||||
stopLabel: string;
|
stopLabel: string;
|
||||||
stopStatus: StopStatus;
|
stopStatus: StopStatus;
|
||||||
stopStatusID: number;
|
stopStatusID: number;
|
||||||
|
|
||||||
|
region: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum StopStatus {
|
export enum StopStatus {
|
||||||
|
|||||||
+39
-83
@@ -1,6 +1,5 @@
|
|||||||
import Station from '../scripts/interfaces/Station';
|
import Station from '../scripts/interfaces/Station';
|
||||||
import Train from '../scripts/interfaces/Train';
|
import Train from '../scripts/interfaces/Train';
|
||||||
import { API } from '../typings/api';
|
|
||||||
import { ScheduledTrain, StationTrain, StopStatus, TrainStop } from './typings';
|
import { ScheduledTrain, StationTrain, StopStatus, TrainStop } from './typings';
|
||||||
|
|
||||||
export function getLocoURL(locoType: string): string {
|
export function getLocoURL(locoType: string): string {
|
||||||
@@ -102,51 +101,33 @@ export function getCheckpointTrain(
|
|||||||
let prevStationName = '',
|
let prevStationName = '',
|
||||||
nextStationName = '';
|
nextStationName = '';
|
||||||
|
|
||||||
let prevDepartureLine: string | null = null,
|
|
||||||
nextArrivalLine: string | null = null;
|
|
||||||
|
|
||||||
for (let i = trainStopIndex - 1; i >= 0; i--) {
|
|
||||||
if (/strong|podg/g.test(followingStops[i].stopName)) {
|
|
||||||
prevStationName = followingStops[i].stopNameRAW.replace(/,.*/g, '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = trainStopIndex + 1; i < followingStops.length; i++) {
|
|
||||||
if (/strong|podg/g.test(followingStops[i].stopName)) {
|
|
||||||
nextStationName = followingStops[i].stopNameRAW.replace(/,.*/g, '');
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let departureLine: string | null = null;
|
let departureLine: string | null = null;
|
||||||
let arrivingLine: string | null = null;
|
let arrivingLine: string | null = null;
|
||||||
|
|
||||||
for (let i = trainStopIndex; i < followingStops.length; i++) {
|
let prevDepartureLine: string | null = null,
|
||||||
const currentStop = followingStops[i];
|
nextArrivalLine: string | null = null;
|
||||||
|
|
||||||
if (currentStop.departureLine == null) continue;
|
for (let i = trainStopIndex; i >= 0; i--) {
|
||||||
|
const stop = followingStops[i];
|
||||||
|
|
||||||
if (!/-|_|it|sbl/gi.test(currentStop.departureLine)) {
|
if (/strong|podg\.|pe\./g.test(stop.stopName) && !prevStationName && i <= trainStopIndex - 1)
|
||||||
departureLine = currentStop.departureLine;
|
prevStationName = stop.stopNameRAW.replace(/,.*/g, '');
|
||||||
nextArrivalLine = followingStops[i + 1]?.arrivalLine || null;
|
|
||||||
|
|
||||||
break;
|
if (stop.arrivalLine != null && !arrivingLine && !/-|_|it|sbl/gi.test(stop.arrivalLine)) {
|
||||||
|
arrivingLine = stop.arrivalLine;
|
||||||
|
prevDepartureLine = followingStops[i - 1]?.departureLine || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = trainStopIndex; i >= 0; i--) {
|
for (let i = trainStopIndex; i < followingStops.length; i++) {
|
||||||
const currentStop = followingStops[i];
|
const stop = followingStops[i];
|
||||||
|
|
||||||
if (currentStop.arrivalLine == null) continue;
|
if (/strong|podg\.|pe\./g.test(stop.stopName) && !nextStationName && i > trainStopIndex)
|
||||||
|
nextStationName = stop.stopNameRAW.replace(/,.*/g, '');
|
||||||
|
|
||||||
if (!/-|_|it|sbl/gi.test(currentStop.arrivalLine)) {
|
if (stop.departureLine && !departureLine && !/-|_|it|sbl/gi.test(stop.departureLine)) {
|
||||||
arrivingLine = currentStop.arrivalLine;
|
departureLine = stop.departureLine;
|
||||||
prevDepartureLine = followingStops[i - 1]?.departureLine || null;
|
nextArrivalLine = followingStops[i + 1]?.arrivalLine || null;
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +156,10 @@ export function getCheckpointTrain(
|
|||||||
stopStatus: trainStopStatus.stopStatus,
|
stopStatus: trainStopStatus.stopStatus,
|
||||||
stopStatusID: trainStopStatus.stopStatusID,
|
stopStatusID: trainStopStatus.stopStatusID,
|
||||||
|
|
||||||
arrivingLine,
|
region: train.region,
|
||||||
departureLine,
|
|
||||||
|
arrivingLine: arrivingLine,
|
||||||
|
departureLine: departureLine,
|
||||||
|
|
||||||
nextArrivalLine,
|
nextArrivalLine,
|
||||||
prevDepartureLine
|
prevDepartureLine
|
||||||
@@ -185,58 +168,33 @@ export function getCheckpointTrain(
|
|||||||
|
|
||||||
export function getScheduledTrains(
|
export function getScheduledTrains(
|
||||||
trainList: Train[],
|
trainList: Train[],
|
||||||
sceneryData: API.ActiveSceneries.Data,
|
stationGeneralInfo: Station['generalInfo'],
|
||||||
stationGeneralInfo: Station['generalInfo']
|
stationName: string,
|
||||||
|
region: string
|
||||||
|
// sceneryData: API.ActiveSceneries.Data,
|
||||||
): ScheduledTrain[] {
|
): ScheduledTrain[] {
|
||||||
const stationNameLower = sceneryData.stationName.toLocaleLowerCase();
|
// stationGeneralInfo?.checkpoints.forEach((cp) => (cp.scheduledTrains.length = 0));
|
||||||
|
|
||||||
stationGeneralInfo?.checkpoints.forEach((cp) => (cp.scheduledTrains.length = 0));
|
|
||||||
|
|
||||||
return trainList.reduce((acc: ScheduledTrain[], train) => {
|
return trainList.reduce((acc: ScheduledTrain[], train) => {
|
||||||
if (!train.timetableData) return acc;
|
if (!train.timetableData) return acc;
|
||||||
|
if (train.region != region) return acc;
|
||||||
|
|
||||||
const timetable = train.timetableData;
|
const timetable = train.timetableData;
|
||||||
if (!timetable.sceneries.includes(sceneryData.stationHash)) return acc;
|
if (!timetable.sceneryNames.includes(stationName)) return acc;
|
||||||
|
|
||||||
const stopInfoIndex = timetable.followingStops.findIndex((stop) => {
|
const checkpoints = [stationName];
|
||||||
const stopNameLower = stop.stopNameRAW.toLocaleLowerCase();
|
if (stationGeneralInfo?.checkpoints) checkpoints.push(...stationGeneralInfo.checkpoints);
|
||||||
|
|
||||||
return (
|
|
||||||
stationNameLower == stopNameLower ||
|
|
||||||
(!/(po\.|podg\.)/.test(stopNameLower) && stopNameLower.includes(stationNameLower)) ||
|
|
||||||
(!/(po\.|podg\.)/.test(stationNameLower) && stationNameLower.includes(stopNameLower)) ||
|
|
||||||
(stopNameLower.split(', podg.')[0] !== undefined &&
|
|
||||||
stationNameLower.startsWith(stopNameLower.split(', podg.')[0]))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const checkpointScheduledTrains: ScheduledTrain[] = [];
|
const checkpointScheduledTrains: ScheduledTrain[] = [];
|
||||||
|
for (let i = 0; i < timetable.followingStops.length; i++) {
|
||||||
if (stopInfoIndex != -1) {
|
|
||||||
checkpointScheduledTrains.push(
|
|
||||||
getCheckpointTrain(train, stopInfoIndex, sceneryData.stationName)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
stationGeneralInfo?.checkpoints?.forEach((checkpoint) => {
|
|
||||||
// if (checkpoint.checkpointName.toLocaleLowerCase() == stationNameLower) return;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
checkpointScheduledTrains.findIndex(
|
new RegExp(`^(${checkpoints.join('|')})$`, 'i').test(
|
||||||
(cpTrain) =>
|
timetable.followingStops[i].stopNameRAW
|
||||||
cpTrain.checkpointName.toLocaleLowerCase() ==
|
)
|
||||||
checkpoint.checkpointName.toLocaleLowerCase()
|
) {
|
||||||
) != -1
|
checkpointScheduledTrains.push(getCheckpointTrain(train, i, stationName));
|
||||||
)
|
}
|
||||||
return;
|
}
|
||||||
|
|
||||||
const index = timetable.followingStops.findIndex(
|
|
||||||
(stop) => stop.stopNameRAW.toLowerCase() == checkpoint.checkpointName.toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index > -1)
|
|
||||||
checkpointScheduledTrains.push(getCheckpointTrain(train, index, sceneryData.stationName));
|
|
||||||
});
|
|
||||||
|
|
||||||
acc.push(...checkpointScheduledTrains);
|
acc.push(...checkpointScheduledTrains);
|
||||||
return acc;
|
return acc;
|
||||||
@@ -247,14 +205,12 @@ export function getStationTrains(
|
|||||||
trainList: Train[],
|
trainList: Train[],
|
||||||
scheduledTrainList: ScheduledTrain[],
|
scheduledTrainList: ScheduledTrain[],
|
||||||
region: string,
|
region: string,
|
||||||
sceneryData: API.ActiveSceneries.Data
|
stationName: string
|
||||||
): StationTrain[] {
|
): StationTrain[] {
|
||||||
return trainList
|
return trainList
|
||||||
.filter(
|
.filter(
|
||||||
(train) =>
|
(train) =>
|
||||||
train?.region === region &&
|
train?.region === region && train.online && train.currentStationName === stationName
|
||||||
train.online &&
|
|
||||||
train.currentStationName === sceneryData.stationName
|
|
||||||
)
|
)
|
||||||
.map((train) => ({
|
.map((train) => ({
|
||||||
driverName: train.driverName,
|
driverName: train.driverName,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 90vh;
|
height: 90vh;
|
||||||
min-height: 550px;
|
min-height: 550px;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
|
||||||
padding-right: 0.2em;
|
padding-right: 0.2em;
|
||||||
}
|
}
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
text-align: end;
|
text-align: end;
|
||||||
|
|
||||||
padding: 0.25em;
|
padding: 0.25em;
|
||||||
margin: 0.5em 0;
|
margin-top: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.journal_warning {
|
.journal_warning {
|
||||||
@@ -53,9 +54,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--load-data {
|
.btn--load-data {
|
||||||
|
|||||||
@@ -2,24 +2,35 @@
|
|||||||
@import 'responsive.scss';
|
@import 'responsive.scss';
|
||||||
|
|
||||||
.stats-tab {
|
.stats-tab {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
transform: translateY(1em);
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
box-shadow: 0 0 5px 1px $accentCol;
|
box-shadow: 0 0 5px 1px $accentCol;
|
||||||
|
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
hr.header-separator {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
width: 100%;
|
hr.section-separator {
|
||||||
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-stats {
|
.info-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-badge {
|
.stat-badge {
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
top: calc(100% + 0.5em);
|
top: calc(100% + 0.5em);
|
||||||
|
|
||||||
background-color: $bgCol;
|
background-color: $bgCol;
|
||||||
box-shadow: 0 5px 10px 2px #0f0f0f;
|
// box-shadow: 0 5px 10px 2px #0f0f0f;
|
||||||
|
box-shadow: 0 0 5px 1px $accentCol;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 550px;
|
max-width: 550px;
|
||||||
|
|||||||
@@ -5,11 +5,6 @@
|
|||||||
.actions-bar {
|
.actions-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters-options {
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1.option-title {
|
h1.option-title {
|
||||||
@@ -57,6 +52,7 @@ h1.option-title {
|
|||||||
|
|
||||||
.sort-option[data-selected='true'] {
|
.sort-option[data-selected='true'] {
|
||||||
color: $accentCol;
|
color: $accentCol;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-option {
|
.filter-option {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Quicksand';
|
||||||
|
src:
|
||||||
|
url('/fonts/Quicksand-Bold.woff2') format('woff2'),
|
||||||
|
url('/fonts/Quicksand-Bold.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Quicksand';
|
||||||
|
src:
|
||||||
|
url('/fonts/Quicksand-SemiBold.woff2') format('woff2'),
|
||||||
|
url('/fonts/Quicksand-SemiBold.woff') format('woff');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Quicksand';
|
||||||
|
src:
|
||||||
|
url('/fonts/Quicksand-Medium.woff2') format('woff2'),
|
||||||
|
url('/fonts/Quicksand-Medium.woff') format('woff');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Quicksand';
|
||||||
|
src:
|
||||||
|
url('/fonts/Quicksand-Regular.woff2') format('woff2'),
|
||||||
|
url('/fonts/Quicksand-Regular.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Quicksand';
|
||||||
|
src:
|
||||||
|
url('/fonts/Quicksand-Light.woff2') format('woff2'),
|
||||||
|
url('/fonts/Quicksand-Light.woff') format('woff');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import 'fonts.scss';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--clr-primary: #ffc014;
|
--clr-primary: #ffc014;
|
||||||
--clr-secondary: #2f2f2f;
|
--clr-secondary: #2f2f2f;
|
||||||
@@ -11,7 +13,7 @@
|
|||||||
--clr-skr: #ff5100;
|
--clr-skr: #ff5100;
|
||||||
--clr-twr: #ffbb00;
|
--clr-twr: #ffbb00;
|
||||||
|
|
||||||
--clr-error: #df3e3e;
|
--clr-error: #fa3636;
|
||||||
--clr-warning: #c59429;
|
--clr-warning: #c59429;
|
||||||
|
|
||||||
--clr-donator: #f7a4ff;
|
--clr-donator: #f7a4ff;
|
||||||
@@ -158,6 +160,10 @@ ul {
|
|||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
color: var(--clr-error);
|
||||||
|
}
|
||||||
|
|
||||||
&--donator {
|
&--donator {
|
||||||
color: var(--clr-donator);
|
color: var(--clr-donator);
|
||||||
text-shadow: var(--clr-donator) 0 0 10px;
|
text-shadow: var(--clr-donator) 0 0 10px;
|
||||||
@@ -183,7 +189,7 @@ a.a-button {
|
|||||||
&[data-disabled='true'] {
|
&[data-disabled='true'] {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.85;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn--filled {
|
&.btn--filled {
|
||||||
|
|||||||
+90
-35
@@ -1,6 +1,24 @@
|
|||||||
import { Status } from './common';
|
import { Status } from './common';
|
||||||
|
|
||||||
|
export enum APIDataStatus {
|
||||||
|
OK = 'OK',
|
||||||
|
WARNING = 'WARNING'
|
||||||
|
}
|
||||||
export namespace API {
|
export namespace API {
|
||||||
|
export namespace ActiveData {
|
||||||
|
export interface APIStatuses {
|
||||||
|
stationsAPI: APIDataStatus;
|
||||||
|
trainsAPI: APIDataStatus;
|
||||||
|
dispatchersAPI: APIDataStatus;
|
||||||
|
sceneryRequirementsAPI: APIDataStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Response {
|
||||||
|
activeSceneries?: API.ActiveSceneries.Response;
|
||||||
|
trains?: API.ActiveTrains.Response;
|
||||||
|
apiStatuses?: APIStatuses;
|
||||||
|
}
|
||||||
|
}
|
||||||
export namespace DispatcherHistory {
|
export namespace DispatcherHistory {
|
||||||
export type Response = Data[];
|
export type Response = Data[];
|
||||||
|
|
||||||
@@ -25,7 +43,11 @@ export namespace API {
|
|||||||
|
|
||||||
export namespace DispatcherStats {
|
export namespace DispatcherStats {
|
||||||
export interface DistanceStat {
|
export interface DistanceStat {
|
||||||
routeDistance: number;
|
routeDistance: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DurationStat {
|
||||||
|
currentDuration: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Count {
|
export interface Count {
|
||||||
@@ -33,11 +55,18 @@ export namespace API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Response {
|
export interface Response {
|
||||||
_sum: DistanceStat;
|
services: {
|
||||||
_max: DistanceStat;
|
count: number;
|
||||||
_min: DistanceStat;
|
durationMax: number;
|
||||||
_avg: DistanceStat;
|
durationAvg: number;
|
||||||
_count: Count;
|
} | null;
|
||||||
|
|
||||||
|
issuedTimetables: {
|
||||||
|
count: number;
|
||||||
|
distanceMax: number;
|
||||||
|
distanceAvg: number;
|
||||||
|
distanceSum: number;
|
||||||
|
} | null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,9 +145,9 @@ export namespace API {
|
|||||||
driverLevel?: number;
|
driverLevel?: number;
|
||||||
|
|
||||||
currentStationName: string;
|
currentStationName: string;
|
||||||
currentStationHash: string;
|
currentStationHash?: string;
|
||||||
|
|
||||||
online: boolean;
|
online: number;
|
||||||
lastSeen: number;
|
lastSeen: number;
|
||||||
|
|
||||||
region: string;
|
region: string;
|
||||||
@@ -132,16 +161,16 @@ export namespace API {
|
|||||||
stopNameRAW: string;
|
stopNameRAW: string;
|
||||||
stopType: string;
|
stopType: string;
|
||||||
stopDistance: number;
|
stopDistance: number;
|
||||||
pointId: number;
|
pointId: string;
|
||||||
|
|
||||||
mainStop: boolean;
|
mainStop: boolean;
|
||||||
|
|
||||||
arrivalLine: string;
|
arrivalLine: string | null;
|
||||||
arrivalTimestamp: number;
|
arrivalTimestamp: number;
|
||||||
arrivalRealTimestamp: number;
|
arrivalRealTimestamp: number;
|
||||||
arrivalDelay: number;
|
arrivalDelay: number;
|
||||||
|
|
||||||
departureLine: string;
|
departureLine: string | null;
|
||||||
departureTimestamp: number;
|
departureTimestamp: number;
|
||||||
departureRealTimestamp: number;
|
departureRealTimestamp: number;
|
||||||
departureDelay: number;
|
departureDelay: number;
|
||||||
@@ -150,9 +179,9 @@ export namespace API {
|
|||||||
|
|
||||||
beginsHere: boolean;
|
beginsHere: boolean;
|
||||||
terminatesHere: boolean;
|
terminatesHere: boolean;
|
||||||
confirmed: boolean;
|
confirmed: number;
|
||||||
stopped: boolean;
|
stopped: number;
|
||||||
stopTime: number;
|
stopTime: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Timetable {
|
export interface Timetable {
|
||||||
@@ -257,21 +286,47 @@ export namespace API {
|
|||||||
distanceAvg: number;
|
distanceAvg: number;
|
||||||
maxTimetable: API.TimetableHistory.Data | null;
|
maxTimetable: API.TimetableHistory.Data | null;
|
||||||
|
|
||||||
mostActiveDispatchers: {
|
globalDiff: GlobalDiff;
|
||||||
name: string;
|
globalMax: GlobalMax;
|
||||||
count: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
mostActiveDrivers: {
|
mostActiveDispatchers: MostActiveDispatcher[];
|
||||||
name: string;
|
mostActiveDrivers: MostActiveDriver[];
|
||||||
distance: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
longestDuties: {
|
longestDuties: LongestDuty[];
|
||||||
name: string;
|
}
|
||||||
duration: number;
|
|
||||||
station: string;
|
export interface MostActiveDispatcher {
|
||||||
}[];
|
name: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MostActiveDriver {
|
||||||
|
name: string;
|
||||||
|
distance: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LongestDuty {
|
||||||
|
name: string;
|
||||||
|
duration: number;
|
||||||
|
station: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GlobalDiff {
|
||||||
|
rippedSwitches: number;
|
||||||
|
derailments: number;
|
||||||
|
skippedStopSignals: number;
|
||||||
|
radioStops: number;
|
||||||
|
kills: number;
|
||||||
|
drivenKilometers: number;
|
||||||
|
routedTrains: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GlobalMax {
|
||||||
|
_max: {
|
||||||
|
drivers: number;
|
||||||
|
dispatchers: number;
|
||||||
|
timetables: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,14 +335,6 @@ export namespace API {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace Websocket {
|
|
||||||
export interface ActiveData {
|
|
||||||
activeSceneries?: API.ActiveSceneries.Response;
|
|
||||||
trains?: API.ActiveTrains.Response;
|
|
||||||
connectedSocketCount: number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace GithubAPI {
|
export namespace GithubAPI {
|
||||||
export namespace Release {
|
export namespace Release {
|
||||||
export interface Author {
|
export interface Author {
|
||||||
@@ -333,3 +380,11 @@ export namespace GithubAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace Websocket {
|
||||||
|
export interface Payload {
|
||||||
|
activeSceneries: API.ActiveSceneries.Response;
|
||||||
|
activeTrains: API.ActiveTrains.Response;
|
||||||
|
connectedSocketCount: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export namespace Status {
|
export namespace Status {
|
||||||
export enum ActiveDispatcher {
|
export enum ActiveDispatcher {
|
||||||
|
FREE = -3,
|
||||||
INVALID = -2,
|
INVALID = -2,
|
||||||
UNKNOWN = -1,
|
UNKNOWN = -1,
|
||||||
NO_LIMIT = 0,
|
NO_LIMIT = 0,
|
||||||
@@ -11,7 +12,7 @@ export namespace Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum Data {
|
export enum Data {
|
||||||
Offline = 2,
|
Offline = -2,
|
||||||
Initialized = -1,
|
Initialized = -1,
|
||||||
Loading = 0,
|
Loading = 0,
|
||||||
Error = 1,
|
Error = 1,
|
||||||
|
|||||||
@@ -3,15 +3,19 @@
|
|||||||
<JournalHeader />
|
<JournalHeader />
|
||||||
|
|
||||||
<div class="journal_wrapper">
|
<div class="journal_wrapper">
|
||||||
<JournalOptions
|
<div class="journal_top-bar">
|
||||||
@on-search-confirm="fetchHistoryData"
|
<JournalOptions
|
||||||
@on-options-reset="resetOptions"
|
@on-search-confirm="fetchHistoryData"
|
||||||
@on-refresh-data="fetchHistoryData(true)"
|
@on-options-reset="resetOptions"
|
||||||
:sorter-option-ids="['timestampFrom', 'duration']"
|
@on-refresh-data="fetchHistoryData(true)"
|
||||||
:data-status="dataStatus"
|
:sorter-option-ids="['timestampFrom', 'duration']"
|
||||||
:current-options-active="currentOptionsActive"
|
:data-status="dataStatus"
|
||||||
optionsType="dispatchers"
|
:current-options-active="currentOptionsActive"
|
||||||
/>
|
optionsType="dispatchers"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<JournalStats :statsButtons="statsButtons" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||||
@@ -32,26 +36,34 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
import http from '../http';
|
||||||
import { URLs } from '../scripts/utils/apiURLs';
|
import { useMainStore } from '../store/mainStore';
|
||||||
import { useStore } from '../store/mainStore';
|
|
||||||
import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue';
|
|
||||||
|
|
||||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
|
||||||
import { LocationQuery } from 'vue-router';
|
import { LocationQuery } from 'vue-router';
|
||||||
import { Journal } from '../components/JournalView/typings';
|
import { Journal } from '../components/JournalView/typings';
|
||||||
import { API } from '../typings/api';
|
import { API } from '../typings/api';
|
||||||
import { Status } from '../typings/common';
|
import { Status } from '../typings/common';
|
||||||
|
|
||||||
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
|
import JournalDispatchersList from '../components/JournalView/JournalDispatchers/JournalDispatchersList.vue';
|
||||||
|
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||||
|
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||||
|
import JournalStats from '../components/JournalView/JournalStats.vue';
|
||||||
|
|
||||||
|
const statsButtons: Journal.StatsButton[] = [
|
||||||
|
{
|
||||||
|
tab: Journal.StatsTab.DISPATCHER_STATS,
|
||||||
|
localeKey: 'journal.dispatcher-stats.button',
|
||||||
|
iconName: 'user',
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
JournalOptions,
|
JournalOptions,
|
||||||
JournalDispatchersList,
|
JournalHeader,
|
||||||
JournalHeader
|
JournalStats,
|
||||||
|
JournalDispatchersList
|
||||||
},
|
},
|
||||||
name: 'JournalDispatchers',
|
name: 'JournalDispatchers',
|
||||||
|
|
||||||
@@ -68,6 +80,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
statsButtons,
|
||||||
|
|
||||||
currentQuery: '',
|
currentQuery: '',
|
||||||
currentQueryArray: [] as string[],
|
currentQueryArray: [] as string[],
|
||||||
dataRefreshedAt: null as Date | null,
|
dataRefreshedAt: null as Date | null,
|
||||||
@@ -92,7 +106,7 @@ export default defineComponent({
|
|||||||
'search-dispatcher': '',
|
'search-dispatcher': '',
|
||||||
'search-station': '',
|
'search-station': '',
|
||||||
'search-date': ''
|
'search-date': ''
|
||||||
} as Journal.DispatcherSearcher);
|
} as Journal.DispatcherSearchType);
|
||||||
|
|
||||||
const countFromIndex = ref(0);
|
const countFromIndex = ref(0);
|
||||||
const countLimit = 15;
|
const countLimit = 15;
|
||||||
@@ -105,7 +119,7 @@ export default defineComponent({
|
|||||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
store: useStore(),
|
mainStore: useMainStore(),
|
||||||
|
|
||||||
sorterActive,
|
sorterActive,
|
||||||
searchersValues,
|
searchersValues,
|
||||||
@@ -123,6 +137,15 @@ export default defineComponent({
|
|||||||
this.currentOptionsActive =
|
this.currentOptionsActive =
|
||||||
q.length > 2 ||
|
q.length > 2 ||
|
||||||
q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'timestampFrom');
|
q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'timestampFrom');
|
||||||
|
},
|
||||||
|
|
||||||
|
'mainStore.dispatcherStatsData'(stats) {
|
||||||
|
this.statsButtons.find((sb) => sb.tab == Journal.StatsTab.DISPATCHER_STATS)!.disabled =
|
||||||
|
stats === undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
async 'mainStore.dispatcherStatsName'() {
|
||||||
|
this.fetchDispatcherStats();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -145,6 +168,16 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
handleRouteParams() {
|
||||||
|
this.$router.push({
|
||||||
|
query: {
|
||||||
|
'search-date': this.searchersValues['search-date'] || undefined,
|
||||||
|
'search-station': this.searchersValues['search-station'] || undefined,
|
||||||
|
'search-dispatcher': this.searchersValues['search-dispatcher'] || undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
handleScroll(e: Event) {
|
handleScroll(e: Event) {
|
||||||
const listElement = e.target as HTMLElement;
|
const listElement = e.target as HTMLElement;
|
||||||
const scrollTop = listElement.scrollTop;
|
const scrollTop = listElement.scrollTop;
|
||||||
@@ -157,24 +190,44 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleQueries(query: LocationQuery) {
|
handleQueries(query: LocationQuery) {
|
||||||
const queryKeys = Object.keys(query);
|
this.setOptions(query as any);
|
||||||
|
|
||||||
if (queryKeys.includes('sceneryName')) this.setSearchers('', `${query.sceneryName}`, '');
|
|
||||||
if (queryKeys.includes('dispatcherName'))
|
|
||||||
this.setSearchers('', '', `${query.dispatcherName}`);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setSearchers(date: string, station: string, dispatcher: string) {
|
async fetchDispatcherStats() {
|
||||||
this.searchersValues['search-date'] = date;
|
if (!this.mainStore.dispatcherStatsName) {
|
||||||
this.searchersValues['search-station'] = station;
|
this.mainStore.dispatcherStatsData = undefined;
|
||||||
this.searchersValues['search-dispatcher'] = dispatcher;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const statsData: API.DispatcherStats.Response = await (
|
||||||
|
await http.get('api/getDispatcherStats', {
|
||||||
|
params: {
|
||||||
|
name: this.mainStore.dispatcherStatsName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
|
this.mainStore.dispatcherStatsData = statsData;
|
||||||
|
} catch (error) {
|
||||||
|
this.mainStore.dispatcherStatsData = undefined;
|
||||||
|
|
||||||
|
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk dyżurnego! :/');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setOptions(options: { [key: string]: string }) {
|
||||||
|
this.searchersValues['search-date'] = options['search-date'] ?? '';
|
||||||
|
this.searchersValues['search-station'] = options['search-station'] ?? '';
|
||||||
|
this.searchersValues['search-dispatcher'] = options['search-dispatcher'] ?? '';
|
||||||
|
|
||||||
|
this.sorterActive.id =
|
||||||
|
(options['sorter-active'] as Journal.DispatcherSorterKey) ?? 'timestampFrom';
|
||||||
},
|
},
|
||||||
|
|
||||||
resetOptions() {
|
resetOptions() {
|
||||||
this.setSearchers('', '', '');
|
this.setOptions({});
|
||||||
this.sorterActive.id = 'timestampFrom';
|
this.sorterActive.id = 'timestampFrom';
|
||||||
|
|
||||||
this.fetchHistoryData();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async addHistoryData() {
|
async addHistoryData() {
|
||||||
@@ -183,9 +236,7 @@ export default defineComponent({
|
|||||||
this.countFromIndex = this.historyList.length;
|
this.countFromIndex = this.historyList.length;
|
||||||
|
|
||||||
const responseData: API.DispatcherHistory.Response = await (
|
const responseData: API.DispatcherHistory.Response = await (
|
||||||
await axios.get(
|
await http.get(`api/getDispatchers?${this.currentQuery}&countFrom=${this.countFromIndex}`)
|
||||||
`${DISPATCHERS_API_URL}?${this.currentQuery}&countFrom=${this.countFromIndex}`
|
|
||||||
)
|
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
if (!responseData) return;
|
if (!responseData) return;
|
||||||
@@ -232,7 +283,7 @@ export default defineComponent({
|
|||||||
if (reset) this.dataStatus = Status.Data.Loading;
|
if (reset) this.dataStatus = Status.Data.Loading;
|
||||||
|
|
||||||
const responseData: API.DispatcherHistory.Response = await (
|
const responseData: API.DispatcherHistory.Response = await (
|
||||||
await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}`)
|
await http.get(`api/getDispatchers?${this.currentQuery}`)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
if (!responseData) {
|
if (!responseData) {
|
||||||
@@ -246,7 +297,7 @@ export default defineComponent({
|
|||||||
this.historyList = responseData;
|
this.historyList = responseData;
|
||||||
|
|
||||||
// Stats display
|
// Stats display
|
||||||
this.store.dispatcherStatsName =
|
this.mainStore.dispatcherStatsName =
|
||||||
this.historyList.length > 0 && this.searchersValues['search-dispatcher'].trim()
|
this.historyList.length > 0 && this.searchersValues['search-dispatcher'].trim()
|
||||||
? this.historyList[0].dispatcherName
|
? this.historyList[0].dispatcherName
|
||||||
: '';
|
: '';
|
||||||
|
|||||||
+139
-79
@@ -3,18 +3,19 @@
|
|||||||
<JournalHeader />
|
<JournalHeader />
|
||||||
|
|
||||||
<div class="journal_wrapper">
|
<div class="journal_wrapper">
|
||||||
<JournalOptions
|
<div class="journal_top-bar">
|
||||||
@on-search-confirm="fetchHistoryData"
|
<JournalOptions
|
||||||
@on-options-reset="resetOptions"
|
@onOptionsReset="resetOptions"
|
||||||
@on-refresh-data="fetchHistoryData"
|
@onRefreshData="fetchHistoryData"
|
||||||
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
||||||
:filters="journalTimetableFilters"
|
:filters="journalTimetableFilters"
|
||||||
:currentOptionsActive="currentOptionsActive"
|
:currentOptionsActive="currentOptionsActive"
|
||||||
:data-status="dataStatus"
|
:data-status="dataStatus"
|
||||||
optionsType="timetables"
|
optionsType="timetables"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<JournalStats />
|
<JournalStats :statsButtons="statsButtons" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||||
@@ -35,7 +36,6 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
import dateMixin from '../mixins/dateMixin';
|
import dateMixin from '../mixins/dateMixin';
|
||||||
import routerMixin from '../mixins/routerMixin';
|
import routerMixin from '../mixins/routerMixin';
|
||||||
@@ -45,8 +45,7 @@ import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
|||||||
import JournalStats from '../components/JournalView/JournalStats.vue';
|
import JournalStats from '../components/JournalView/JournalStats.vue';
|
||||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||||
|
|
||||||
import { URLs } from '../scripts/utils/apiURLs';
|
import { useMainStore } from '../store/mainStore';
|
||||||
import { useStore } from '../store/mainStore';
|
|
||||||
|
|
||||||
import { LocationQuery } from 'vue-router';
|
import { LocationQuery } from 'vue-router';
|
||||||
|
|
||||||
@@ -54,50 +53,62 @@ import JournalTimetablesList from '../components/JournalView/JournalTimetables/J
|
|||||||
import { Journal } from '../components/JournalView/typings';
|
import { Journal } from '../components/JournalView/typings';
|
||||||
import { Status } from '../typings/common';
|
import { Status } from '../typings/common';
|
||||||
import { API } from '../typings/api';
|
import { API } from '../typings/api';
|
||||||
|
import http from '../http';
|
||||||
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
|
|
||||||
|
|
||||||
export const journalTimetableFilters: Journal.TimetableFilter[] = [
|
export const journalTimetableFilters: Journal.TimetableFilter[] = [
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.ALL,
|
id: Journal.TimetableFilterId.ALL_STATUSES,
|
||||||
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
||||||
isActive: true
|
isActive: true,
|
||||||
|
default: true
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.ACTIVE,
|
id: Journal.TimetableFilterId.ACTIVE,
|
||||||
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
||||||
isActive: false
|
isActive: false,
|
||||||
|
default: false
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.FULFILLED,
|
id: Journal.TimetableFilterId.FULFILLED,
|
||||||
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
||||||
isActive: false
|
isActive: false,
|
||||||
|
default: false
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.ABANDONED,
|
id: Journal.TimetableFilterId.ABANDONED,
|
||||||
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
||||||
isActive: false
|
isActive: false,
|
||||||
|
default: false
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.TWR_SKR,
|
id: Journal.TimetableFilterId.ALL_SPECIALS,
|
||||||
filterSection: Journal.FilterSection.TWRSKR,
|
filterSection: Journal.FilterSection.SPECIAL,
|
||||||
isActive: true
|
isActive: true,
|
||||||
|
default: true
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.TWR,
|
id: Journal.TimetableFilterId.TWR,
|
||||||
filterSection: Journal.FilterSection.TWRSKR,
|
filterSection: Journal.FilterSection.SPECIAL,
|
||||||
isActive: false
|
isActive: false,
|
||||||
|
default: false
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: Journal.TimetableFilterId.SKR,
|
id: Journal.TimetableFilterId.SKR,
|
||||||
filterSection: Journal.FilterSection.TWRSKR,
|
filterSection: Journal.FilterSection.SPECIAL,
|
||||||
isActive: false
|
isActive: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: Journal.TimetableFilterId.TWR_SKR,
|
||||||
|
filterSection: Journal.FilterSection.SPECIAL,
|
||||||
|
isActive: false,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -107,8 +118,12 @@ interface TimetablesQueryParams {
|
|||||||
timetableId?: string;
|
timetableId?: string;
|
||||||
|
|
||||||
authorName?: string;
|
authorName?: string;
|
||||||
timestampFrom?: number;
|
// timestampFrom?: number;
|
||||||
timestampTo?: number;
|
// timestampTo?: number;
|
||||||
|
|
||||||
|
dateFrom?: string;
|
||||||
|
dateTo?: string;
|
||||||
|
|
||||||
issuedFrom?: string;
|
issuedFrom?: string;
|
||||||
|
|
||||||
countFrom?: number;
|
countFrom?: number;
|
||||||
@@ -141,6 +156,24 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
journalTimetableFilters,
|
||||||
|
mainStore: useMainStore(),
|
||||||
|
|
||||||
|
statsButtons: [
|
||||||
|
{
|
||||||
|
tab: Journal.StatsTab.DAILY_STATS,
|
||||||
|
localeKey: 'journal.daily-stats.button',
|
||||||
|
iconName: 'stats',
|
||||||
|
disabled: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: Journal.StatsTab.DRIVER_STATS,
|
||||||
|
localeKey: 'journal.driver-stats.button',
|
||||||
|
iconName: 'train',
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
currentQueryParams: {} as TimetablesQueryParams,
|
currentQueryParams: {} as TimetablesQueryParams,
|
||||||
dataRefreshedAt: null as Date | null,
|
dataRefreshedAt: null as Date | null,
|
||||||
|
|
||||||
@@ -152,7 +185,6 @@ export default defineComponent({
|
|||||||
currentOptionsActive: false,
|
currentOptionsActive: false,
|
||||||
|
|
||||||
timetableHistory: [] as API.TimetableHistory.Response,
|
timetableHistory: [] as API.TimetableHistory.Response,
|
||||||
journalTimetableFilters,
|
|
||||||
|
|
||||||
dataStatus: Status.Data.Loading,
|
dataStatus: Status.Data.Loading,
|
||||||
dataErrorMessage: ''
|
dataErrorMessage: ''
|
||||||
@@ -160,10 +192,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const sorterActive: Journal.TimetableSorter = reactive({ id: 'timetableId', dir: 'desc' });
|
const sorterActive: Journal.TimetableSorter = reactive({ id: 'timetableId', dir: 'desc' });
|
||||||
// const journalFilterActive = ref(journalTimetableFilters[0]);
|
|
||||||
const initFilters: readonly Journal.TimetableFilter[] = JSON.parse(
|
const initFilters: readonly Journal.TimetableFilter[] = JSON.parse(
|
||||||
JSON.stringify(journalTimetableFilters)
|
JSON.stringify(journalTimetableFilters)
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterList: Journal.TimetableFilter[] = reactive(JSON.parse(JSON.stringify(initFilters)));
|
const filterList: Journal.TimetableFilter[] = reactive(JSON.parse(JSON.stringify(initFilters)));
|
||||||
|
|
||||||
const searchersValues = reactive({
|
const searchersValues = reactive({
|
||||||
@@ -192,15 +225,22 @@ export default defineComponent({
|
|||||||
countFromIndex,
|
countFromIndex,
|
||||||
countLimit,
|
countLimit,
|
||||||
|
|
||||||
scrollElement,
|
scrollElement
|
||||||
|
|
||||||
store: useStore()
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
currentQueryParams(q: TimetablesQueryParams) {
|
currentQueryParams(q: TimetablesQueryParams) {
|
||||||
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
||||||
|
},
|
||||||
|
|
||||||
|
'mainStore.driverStatsData'(driverStats) {
|
||||||
|
this.statsButtons.find((sb) => sb.tab == Journal.StatsTab.DRIVER_STATS)!.disabled =
|
||||||
|
driverStats === undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
async 'mainStore.driverStatsName'() {
|
||||||
|
this.fetchDriverStats();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -228,42 +268,51 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleQueries(query: LocationQuery) {
|
handleQueries(query: LocationQuery) {
|
||||||
const queryKeys = Object.keys(query);
|
this.setOptions(query as any);
|
||||||
|
|
||||||
if (queryKeys.includes('timetableId'))
|
|
||||||
this.setSearchers('', '', `#${query.timetableId}`, '', '');
|
|
||||||
if (queryKeys.includes('issuedFrom'))
|
|
||||||
this.setSearchers('', '', '', '', `${query.issuedFrom}`);
|
|
||||||
if (queryKeys.includes('authorName'))
|
|
||||||
this.setSearchers('', '', '', `${query.authorName}`, '');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setSearchers(
|
async fetchDriverStats() {
|
||||||
date: string,
|
if (!this.mainStore.driverStatsName) {
|
||||||
driver: string,
|
this.mainStore.driverStatsData = undefined;
|
||||||
train: string,
|
this.mainStore.driverStatsStatus = Status.Data.Initialized;
|
||||||
dispatcher: string,
|
return;
|
||||||
issuedFrom: string
|
}
|
||||||
) {
|
|
||||||
this.searchersValues['search-date'] = date;
|
try {
|
||||||
this.searchersValues['search-driver'] = driver;
|
this.mainStore.driverStatsStatus = Status.Data.Loading;
|
||||||
this.searchersValues['search-train'] = train;
|
|
||||||
this.searchersValues['search-dispatcher'] = dispatcher;
|
const statsData: API.DriverStats.Response = await (
|
||||||
this.searchersValues['search-issuedFrom'] = issuedFrom;
|
await http.get(`api/getDriverInfo?name=${this.mainStore.driverStatsName}`)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
this.mainStore.driverStatsData = statsData;
|
||||||
|
this.mainStore.driverStatsStatus = Status.Data.Loaded;
|
||||||
|
} catch (error) {
|
||||||
|
this.mainStore.driverStatsData = undefined;
|
||||||
|
this.mainStore.driverStatsStatus = Status.Data.Error;
|
||||||
|
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setOptions(options: { [key: string]: string }) {
|
||||||
|
this.searchersValues['search-date'] = options['search-date'] ?? '';
|
||||||
|
this.searchersValues['search-driver'] = options['search-driver'] ?? '';
|
||||||
|
this.searchersValues['search-train'] = options['search-train'] ?? '';
|
||||||
|
this.searchersValues['search-dispatcher'] = options['search-dispatcher'] ?? '';
|
||||||
|
this.searchersValues['search-issuedFrom'] = options['search-issuedFrom'] ?? '';
|
||||||
|
|
||||||
|
this.sorterActive.id =
|
||||||
|
(options['sorter-active'] as Journal.TimetableSorterKey) ?? 'timetableId';
|
||||||
|
|
||||||
|
this.filterList.forEach((f) => {
|
||||||
|
f.isActive =
|
||||||
|
options[f.filterSection] === f.id ||
|
||||||
|
(options[f.filterSection] === undefined && f.default);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
resetOptions() {
|
resetOptions() {
|
||||||
this.setSearchers('', '', '', '', '');
|
this.setOptions({});
|
||||||
|
|
||||||
this.sorterActive.id = 'timetableId';
|
|
||||||
|
|
||||||
this.filterList.forEach(
|
|
||||||
(f) =>
|
|
||||||
(f.isActive =
|
|
||||||
this.initFilters.find((initFilter) => initFilter.id == f.id)?.isActive || false)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.fetchHistoryData();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async addHistoryData() {
|
async addHistoryData() {
|
||||||
@@ -272,7 +321,7 @@ export default defineComponent({
|
|||||||
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
||||||
|
|
||||||
const responseData: API.TimetableHistory.Response = await (
|
const responseData: API.TimetableHistory.Response = await (
|
||||||
await axios.get(`${TIMETABLES_API_URL}`, {
|
await http.get('api/getTimetables', {
|
||||||
params: { ...this.currentQueryParams }
|
params: { ...this.currentQueryParams }
|
||||||
})
|
})
|
||||||
).data;
|
).data;
|
||||||
@@ -292,13 +341,19 @@ export default defineComponent({
|
|||||||
const driverName = this.searchersValues['search-driver'].trim() || undefined;
|
const driverName = this.searchersValues['search-driver'].trim() || undefined;
|
||||||
const trainNo = this.searchersValues['search-train'].trim() || undefined;
|
const trainNo = this.searchersValues['search-train'].trim() || undefined;
|
||||||
const authorName = this.searchersValues['search-dispatcher'].trim() || undefined;
|
const authorName = this.searchersValues['search-dispatcher'].trim() || undefined;
|
||||||
const dateString = this.searchersValues['search-date'].trim() || undefined;
|
const dateFrom = this.searchersValues['search-date'].trim() || undefined;
|
||||||
const issuedFrom = this.searchersValues['search-issuedFrom'].trim() || undefined;
|
const issuedFrom = this.searchersValues['search-issuedFrom'].trim() || undefined;
|
||||||
|
|
||||||
const timestampFrom = dateString
|
let dateTo: string | undefined = undefined;
|
||||||
? Date.parse(new Date(dateString).toISOString()) - 120 * 60 * 1000
|
|
||||||
: undefined;
|
if (dateFrom) {
|
||||||
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
const d = new Date(dateFrom);
|
||||||
|
d.setDate(d.getDate() + 1);
|
||||||
|
|
||||||
|
dateTo = d.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
// const timestampFrom = dateString ? Date.parse(new Date(dateString).toISOString()) : undefined;
|
||||||
|
// const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
||||||
|
|
||||||
const queryParams: TimetablesQueryParams = {};
|
const queryParams: TimetablesQueryParams = {};
|
||||||
|
|
||||||
@@ -321,23 +376,28 @@ export default defineComponent({
|
|||||||
queryParams['fulfilled'] = 1;
|
queryParams['fulfilled'] = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Journal.TimetableFilterId.ALL:
|
case Journal.TimetableFilterId.ALL_STATUSES:
|
||||||
queryParams['terminated'] = undefined;
|
queryParams['terminated'] = undefined;
|
||||||
queryParams['fulfilled'] = undefined;
|
queryParams['fulfilled'] = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Journal.TimetableFilterId.TWR_SKR:
|
case Journal.TimetableFilterId.ALL_SPECIALS:
|
||||||
queryParams['twr'] = undefined;
|
queryParams['twr'] = undefined;
|
||||||
queryParams['skr'] = undefined;
|
queryParams['skr'] = undefined;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Journal.TimetableFilterId.TWR:
|
case Journal.TimetableFilterId.TWR:
|
||||||
queryParams['twr'] = 1;
|
queryParams['twr'] = 1;
|
||||||
queryParams['skr'] = undefined;
|
queryParams['skr'] = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Journal.TimetableFilterId.SKR:
|
case Journal.TimetableFilterId.SKR:
|
||||||
queryParams['twr'] = undefined;
|
queryParams['twr'] = 0;
|
||||||
|
queryParams['skr'] = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Journal.TimetableFilterId.TWR_SKR:
|
||||||
|
queryParams['twr'] = 1;
|
||||||
queryParams['skr'] = 1;
|
queryParams['skr'] = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -352,8 +412,8 @@ export default defineComponent({
|
|||||||
queryParams['countLimit'] = undefined;
|
queryParams['countLimit'] = undefined;
|
||||||
|
|
||||||
queryParams['authorName'] = authorName;
|
queryParams['authorName'] = authorName;
|
||||||
queryParams['timestampFrom'] = timestampFrom;
|
queryParams['dateFrom'] = dateFrom;
|
||||||
queryParams['timestampTo'] = timestampTo;
|
queryParams['dateTo'] = dateTo;
|
||||||
queryParams['issuedFrom'] = issuedFrom;
|
queryParams['issuedFrom'] = issuedFrom;
|
||||||
queryParams['sortBy'] =
|
queryParams['sortBy'] =
|
||||||
this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
|
this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
|
||||||
@@ -365,7 +425,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const responseData: API.TimetableHistory.Response = await (
|
const responseData: API.TimetableHistory.Response = await (
|
||||||
await axios.get(`${TIMETABLES_API_URL}`, {
|
await http.get('api/getTimetables', {
|
||||||
params: this.currentQueryParams
|
params: this.currentQueryParams
|
||||||
})
|
})
|
||||||
).data;
|
).data;
|
||||||
|
|||||||
+25
-22
@@ -1,14 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="scenery-view">
|
<div class="scenery-view">
|
||||||
<div class="scenery-offline" v-if="!stationInfo && store.dataStatuses.sceneries == 2">
|
<div class="scenery-wrapper" ref="card-wrapper">
|
||||||
<div>{{ $t('scenery.no-scenery') }}</div>
|
|
||||||
|
|
||||||
<action-button>
|
|
||||||
<router-link to="/">{{ $t('scenery.return-btn') }}</router-link>
|
|
||||||
</action-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="scenery-wrapper" v-if="stationInfo" ref="card-wrapper">
|
|
||||||
<div class="scenery-left">
|
<div class="scenery-left">
|
||||||
<div class="scenery-actions">
|
<div class="scenery-actions">
|
||||||
<button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')">
|
<button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')">
|
||||||
@@ -16,7 +8,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SceneryHeader :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
|
<SceneryHeader
|
||||||
|
:stationName="station"
|
||||||
|
:station="stationInfo"
|
||||||
|
:onlineScenery="onlineSceneryInfo"
|
||||||
|
/>
|
||||||
<SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
|
<SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -33,7 +29,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<keep-alive>
|
<div
|
||||||
|
v-if="
|
||||||
|
apiStore.dataStatuses.sceneries == Status.Loading ||
|
||||||
|
apiStore.dataStatuses.connection == Status.Loading
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<keep-alive v-else>
|
||||||
<component
|
<component
|
||||||
:is="currentMode"
|
:is="currentMode"
|
||||||
:onlineScenery="onlineSceneryInfo"
|
:onlineScenery="onlineSceneryInfo"
|
||||||
@@ -50,7 +53,7 @@
|
|||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import routerMixin from '../mixins/routerMixin';
|
import routerMixin from '../mixins/routerMixin';
|
||||||
import { useStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
|
|
||||||
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
|
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
|
||||||
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
|
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
|
||||||
@@ -58,6 +61,8 @@ import SceneryTimetable from '../components/SceneryView/SceneryTimetable.vue';
|
|||||||
import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue';
|
import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue';
|
||||||
import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue';
|
import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue';
|
||||||
import ActionButton from '../components/Global/ActionButton.vue';
|
import ActionButton from '../components/Global/ActionButton.vue';
|
||||||
|
import { Status } from '../typings/common';
|
||||||
|
import { useApiStore } from '../store/apiStore';
|
||||||
|
|
||||||
enum SceneryViewMode {
|
enum SceneryViewMode {
|
||||||
'TIMETABLES_ACTIVE',
|
'TIMETABLES_ACTIVE',
|
||||||
@@ -92,7 +97,9 @@ export default defineComponent({
|
|||||||
mixins: [routerMixin],
|
mixins: [routerMixin],
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
|
apiStore: useApiStore(),
|
||||||
|
|
||||||
viewModes: [
|
viewModes: [
|
||||||
{
|
{
|
||||||
id: 'scenery.option-active-timetables',
|
id: 'scenery.option-active-timetables',
|
||||||
@@ -110,7 +117,8 @@ export default defineComponent({
|
|||||||
sceneryViewMode: SceneryViewMode,
|
sceneryViewMode: SceneryViewMode,
|
||||||
selectedCheckpoint: '',
|
selectedCheckpoint: '',
|
||||||
currentViewCompontent: 'SceneryTimetable',
|
currentViewCompontent: 'SceneryTimetable',
|
||||||
onlineFrom: -1
|
onlineFrom: -1,
|
||||||
|
Status: Status.Data
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// activated() {
|
// activated() {
|
||||||
@@ -139,7 +147,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onlineSceneryInfo() {
|
onlineSceneryInfo() {
|
||||||
return this.store.onlineSceneryList.find(
|
return this.store.activeSceneryList.find(
|
||||||
(scenery) =>
|
(scenery) =>
|
||||||
scenery.name === this.station?.toString().replace(/_/g, ' ') &&
|
scenery.name === this.station?.toString().replace(/_/g, ' ') &&
|
||||||
scenery.region == this.store.region.id
|
scenery.region == this.store.region.id
|
||||||
@@ -161,11 +169,7 @@ export default defineComponent({
|
|||||||
loadSelectedCheckpoint() {
|
loadSelectedCheckpoint() {
|
||||||
if (!this.stationInfo?.generalInfo?.checkpoints) return;
|
if (!this.stationInfo?.generalInfo?.checkpoints) return;
|
||||||
if (this.stationInfo.generalInfo.checkpoints.length == 0) return;
|
if (this.stationInfo.generalInfo.checkpoints.length == 0) return;
|
||||||
this.selectedCheckpoint = this.stationInfo.generalInfo.checkpoints[0].checkpointName;
|
this.selectedCheckpoint = this.stationInfo.generalInfo.checkpoints[0];
|
||||||
},
|
|
||||||
|
|
||||||
selectCheckpoint(cp: { checkpointName: string }) {
|
|
||||||
this.selectedCheckpoint = cp.checkpointName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -185,8 +189,6 @@ button.back-btn {
|
|||||||
&-view {
|
&-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-offline {
|
&-offline {
|
||||||
@@ -215,6 +217,7 @@ button.back-btn {
|
|||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1700px;
|
max-width: 1700px;
|
||||||
|
min-height: 100vh;
|
||||||
|
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { defineComponent } from 'vue';
|
|||||||
import StationTable from '../components/StationsView/StationTable.vue';
|
import StationTable from '../components/StationsView/StationTable.vue';
|
||||||
import StationFilterCard from '../components/StationsView/StationFilterCard.vue';
|
import StationFilterCard from '../components/StationsView/StationFilterCard.vue';
|
||||||
import { useStationFiltersStore } from '../store/stationFiltersStore';
|
import { useStationFiltersStore } from '../store/stationFiltersStore';
|
||||||
import { useStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
import Donation from '../components/Global/Donation.vue';
|
import Donation from '../components/Global/Donation.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -37,7 +37,7 @@ export default defineComponent({
|
|||||||
STORAGE_KEY: 'options_saved',
|
STORAGE_KEY: 'options_saved',
|
||||||
focusedStationName: '',
|
focusedStationName: '',
|
||||||
filterStore: useStationFiltersStore(),
|
filterStore: useStationFiltersStore(),
|
||||||
store: useStore(),
|
store: useMainStore(),
|
||||||
|
|
||||||
isDonationModalOpen: false
|
isDonationModalOpen: false
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
|||||||
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
||||||
import modalTrainMixin from '../mixins/modalTrainMixin';
|
import modalTrainMixin from '../mixins/modalTrainMixin';
|
||||||
import Train from '../scripts/interfaces/Train';
|
import Train from '../scripts/interfaces/Train';
|
||||||
import { useStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
import { TrainFilter, trainFilters } from '../components/TrainsView/typings';
|
import { TrainFilter, trainFilters } from '../components/TrainsView/typings';
|
||||||
import { filteredTrainList } from '../managers/trainFilterManager';
|
import { filteredTrainList } from '../managers/trainFilterManager';
|
||||||
import TrainStats from '../components/TrainsView/TrainStats.vue';
|
import TrainStats from '../components/TrainsView/TrainStats.vue';
|
||||||
@@ -58,7 +58,7 @@ export default defineComponent({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useMainStore();
|
||||||
const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))];
|
const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))];
|
||||||
|
|
||||||
const sorterActive = reactive({ id: 'routeDistance', dir: -1 });
|
const sorterActive = reactive({ id: 'routeDistance', dir: -1 });
|
||||||
|
|||||||
+4
-2
@@ -6,20 +6,22 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
port: 5001
|
port: 5001
|
||||||
},
|
},
|
||||||
|
publicDir: 'public',
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
|
includeAssets: ['/images/*.png', '/fonts/*.woff', '/fonts/*.woff2'],
|
||||||
|
|
||||||
workbox: {
|
workbox: {
|
||||||
|
disableDevLogs: true,
|
||||||
globPatterns: ['**/*.{js,css,html,png,svg,jpg}'],
|
globPatterns: ['**/*.{js,css,html,png,svg,jpg}'],
|
||||||
runtimeCaching: [
|
runtimeCaching: [
|
||||||
{
|
{
|
||||||
urlPattern: new RegExp('^https://stacjownik.spythere.pl/api/getSceneries', 'i'),
|
urlPattern: new RegExp('^https://stacjownik.spythere.eu/api/getSceneries', 'i'),
|
||||||
handler: 'NetworkFirst',
|
handler: 'NetworkFirst',
|
||||||
options: {
|
options: {
|
||||||
cacheName: 'sceneries-cache',
|
cacheName: 'sceneries-cache',
|
||||||
|
|
||||||
cacheableResponse: {
|
cacheableResponse: {
|
||||||
statuses: [0, 200]
|
statuses: [0, 200]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz"
|
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz"
|
||||||
integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
|
integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
|
||||||
|
|
||||||
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.1", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.4.0-0":
|
"@babel/core@^7.11.1":
|
||||||
version "7.20.7"
|
version "7.20.7"
|
||||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.7.tgz"
|
resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.7.tgz"
|
||||||
integrity sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==
|
integrity sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==
|
||||||
@@ -929,11 +929,116 @@
|
|||||||
resolved "https://registry.npmjs.org/@canvas/image-data/-/image-data-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/@canvas/image-data/-/image-data-1.0.0.tgz"
|
||||||
integrity sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==
|
integrity sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==
|
||||||
|
|
||||||
|
"@esbuild/android-arm64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
|
||||||
|
integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==
|
||||||
|
|
||||||
|
"@esbuild/android-arm@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
|
||||||
|
integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==
|
||||||
|
|
||||||
|
"@esbuild/android-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
|
||||||
|
integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
|
||||||
|
integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
|
||||||
|
integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
|
||||||
|
integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
|
||||||
|
integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
|
||||||
|
integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
|
||||||
|
integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
|
||||||
|
integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
|
||||||
|
integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
|
||||||
|
integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
|
||||||
|
integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
|
||||||
|
integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
|
||||||
|
integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==
|
||||||
|
|
||||||
"@esbuild/linux-x64@0.18.20":
|
"@esbuild/linux-x64@0.18.20":
|
||||||
version "0.18.20"
|
version "0.18.20"
|
||||||
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz"
|
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz"
|
||||||
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
|
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
|
||||||
|
integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
|
||||||
|
integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
|
||||||
|
integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
|
||||||
|
integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
|
||||||
|
integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.18.20":
|
||||||
|
version "0.18.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
|
||||||
|
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
|
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
|
||||||
@@ -1025,7 +1130,7 @@
|
|||||||
"@firebase/util" "1.9.3"
|
"@firebase/util" "1.9.3"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/app-compat@0.2.19", "@firebase/app-compat@0.x":
|
"@firebase/app-compat@0.2.19":
|
||||||
version "0.2.19"
|
version "0.2.19"
|
||||||
resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.19.tgz"
|
resolved "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.19.tgz"
|
||||||
integrity sha512-QkJDqYqjhvs4fTMcRVXQkP9hbo5yfoJXDWkhU4VA5Vzs8Qsp76VPzYbqx5SD5OmBy+bz/Ot1UV8qySPGI4aKuw==
|
integrity sha512-QkJDqYqjhvs4fTMcRVXQkP9hbo5yfoJXDWkhU4VA5Vzs8Qsp76VPzYbqx5SD5OmBy+bz/Ot1UV8qySPGI4aKuw==
|
||||||
@@ -1036,12 +1141,12 @@
|
|||||||
"@firebase/util" "1.9.3"
|
"@firebase/util" "1.9.3"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/app-types@0.9.0", "@firebase/app-types@0.x":
|
"@firebase/app-types@0.9.0":
|
||||||
version "0.9.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz"
|
resolved "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz"
|
||||||
integrity sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==
|
integrity sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==
|
||||||
|
|
||||||
"@firebase/app@0.9.19", "@firebase/app@0.x":
|
"@firebase/app@0.9.19":
|
||||||
version "0.9.19"
|
version "0.9.19"
|
||||||
resolved "https://registry.npmjs.org/@firebase/app/-/app-0.9.19.tgz"
|
resolved "https://registry.npmjs.org/@firebase/app/-/app-0.9.19.tgz"
|
||||||
integrity sha512-t/SHyZ3xWkR77ZU9VMoobDNFLdDKQ5xqoCAn4o16gTsA1C8sJ6ZOMZ02neMOPxNHuQXVE4tA8ukilnDbnK7uJA==
|
integrity sha512-t/SHyZ3xWkR77ZU9VMoobDNFLdDKQ5xqoCAn4o16gTsA1C8sJ6ZOMZ02neMOPxNHuQXVE4tA8ukilnDbnK7uJA==
|
||||||
@@ -1326,7 +1431,7 @@
|
|||||||
node-fetch "2.6.7"
|
node-fetch "2.6.7"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/util@1.9.3", "@firebase/util@1.x":
|
"@firebase/util@1.9.3":
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz"
|
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz"
|
||||||
integrity sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==
|
integrity sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==
|
||||||
@@ -1431,7 +1536,7 @@
|
|||||||
"@jridgewell/gen-mapping" "^0.3.0"
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
"@jridgewell/trace-mapping" "^0.3.9"
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@1.4.14":
|
"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
|
||||||
version "1.4.14"
|
version "1.4.14"
|
||||||
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
|
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
|
||||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||||
@@ -1457,7 +1562,7 @@
|
|||||||
"@nodelib/fs.stat" "2.0.5"
|
"@nodelib/fs.stat" "2.0.5"
|
||||||
run-parallel "^1.1.9"
|
run-parallel "^1.1.9"
|
||||||
|
|
||||||
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
|
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
||||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||||
@@ -1602,26 +1707,16 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz"
|
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz"
|
||||||
integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==
|
integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0":
|
||||||
version "18.11.18"
|
version "18.11.18"
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz"
|
resolved "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz"
|
||||||
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
|
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
|
||||||
|
|
||||||
"@types/node@^20.6.2", "@types/node@>= 14":
|
"@types/node@^20.6.2":
|
||||||
version "20.6.2"
|
version "20.6.2"
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz"
|
resolved "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz"
|
||||||
integrity sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==
|
integrity sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==
|
||||||
|
|
||||||
"@types/node@>=12.12.47":
|
|
||||||
version "18.11.18"
|
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz"
|
|
||||||
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
|
|
||||||
|
|
||||||
"@types/node@>=13.7.0":
|
|
||||||
version "18.11.18"
|
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz"
|
|
||||||
integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
|
|
||||||
|
|
||||||
"@types/resolve@1.17.1":
|
"@types/resolve@1.17.1":
|
||||||
version "1.17.1"
|
version "1.17.1"
|
||||||
resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz"
|
resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz"
|
||||||
@@ -1656,7 +1751,7 @@
|
|||||||
semver "^7.5.4"
|
semver "^7.5.4"
|
||||||
ts-api-utils "^1.0.1"
|
ts-api-utils "^1.0.1"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.7.0":
|
"@typescript-eslint/parser@^6.7.0":
|
||||||
version "6.9.1"
|
version "6.9.1"
|
||||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz"
|
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz"
|
||||||
integrity sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==
|
integrity sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==
|
||||||
@@ -1746,14 +1841,14 @@
|
|||||||
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz"
|
resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz"
|
||||||
integrity sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==
|
integrity sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==
|
||||||
|
|
||||||
"@volar/language-core@~1.10.0", "@volar/language-core@1.10.1":
|
"@volar/language-core@1.10.1", "@volar/language-core@~1.10.0":
|
||||||
version "1.10.1"
|
version "1.10.1"
|
||||||
resolved "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz"
|
resolved "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz"
|
||||||
integrity sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==
|
integrity sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/source-map" "1.10.1"
|
"@volar/source-map" "1.10.1"
|
||||||
|
|
||||||
"@volar/source-map@~1.10.0", "@volar/source-map@1.10.1":
|
"@volar/source-map@1.10.1", "@volar/source-map@~1.10.0":
|
||||||
version "1.10.1"
|
version "1.10.1"
|
||||||
resolved "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz"
|
resolved "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz"
|
||||||
integrity sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==
|
integrity sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==
|
||||||
@@ -1777,7 +1872,7 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
"@vue/compiler-dom@^3.3.0", "@vue/compiler-dom@3.3.4":
|
"@vue/compiler-dom@3.3.4", "@vue/compiler-dom@^3.3.0":
|
||||||
version "3.3.4"
|
version "3.3.4"
|
||||||
resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz"
|
resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz"
|
||||||
integrity sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==
|
integrity sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==
|
||||||
@@ -1856,7 +1951,7 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.30.0"
|
magic-string "^0.30.0"
|
||||||
|
|
||||||
"@vue/reactivity@^3.3.0", "@vue/reactivity@3.3.4":
|
"@vue/reactivity@3.3.4", "@vue/reactivity@^3.3.0":
|
||||||
version "3.3.4"
|
version "3.3.4"
|
||||||
resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz"
|
resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz"
|
||||||
integrity sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==
|
integrity sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==
|
||||||
@@ -1888,7 +1983,7 @@
|
|||||||
"@vue/compiler-ssr" "3.3.4"
|
"@vue/compiler-ssr" "3.3.4"
|
||||||
"@vue/shared" "3.3.4"
|
"@vue/shared" "3.3.4"
|
||||||
|
|
||||||
"@vue/shared@^3.3.0", "@vue/shared@3.3.4":
|
"@vue/shared@3.3.4", "@vue/shared@^3.3.0":
|
||||||
version "3.3.4"
|
version "3.3.4"
|
||||||
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz"
|
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz"
|
||||||
integrity sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==
|
integrity sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==
|
||||||
@@ -1911,7 +2006,7 @@ acorn-jsx@^5.3.2:
|
|||||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
||||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||||
|
|
||||||
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.10.0, acorn@^8.5.0, acorn@^8.9.0:
|
acorn@^8.10.0, acorn@^8.5.0, acorn@^8.9.0:
|
||||||
version "8.10.0"
|
version "8.10.0"
|
||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz"
|
||||||
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
|
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
|
||||||
@@ -1926,7 +2021,7 @@ ajv@^6.12.4:
|
|||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ajv@^8.6.0, ajv@>=8:
|
ajv@^8.6.0:
|
||||||
version "8.11.2"
|
version "8.11.2"
|
||||||
resolved "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz"
|
resolved "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz"
|
||||||
integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==
|
integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==
|
||||||
@@ -2089,7 +2184,7 @@ braces@^3.0.2, braces@~3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fill-range "^7.0.1"
|
fill-range "^7.0.1"
|
||||||
|
|
||||||
browserslist@^4.21.3, browserslist@^4.21.4, "browserslist@>= 4.21.0":
|
browserslist@^4.21.3, browserslist@^4.21.4:
|
||||||
version "4.21.4"
|
version "4.21.4"
|
||||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
|
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz"
|
||||||
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
|
integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
|
||||||
@@ -2143,9 +2238,9 @@ callsites@^3.0.0:
|
|||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001400:
|
caniuse-lite@^1.0.30001400:
|
||||||
version "1.0.30001503"
|
version "1.0.30001565"
|
||||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz"
|
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz"
|
||||||
integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==
|
integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==
|
||||||
|
|
||||||
chalk@^2.4.2:
|
chalk@^2.4.2:
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
@@ -2156,15 +2251,7 @@ chalk@^2.4.2:
|
|||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
supports-color "^5.3.0"
|
supports-color "^5.3.0"
|
||||||
|
|
||||||
chalk@^4.0.0:
|
chalk@^4.0.0, chalk@^4.0.2:
|
||||||
version "4.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
|
|
||||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
|
||||||
dependencies:
|
|
||||||
ansi-styles "^4.1.0"
|
|
||||||
supports-color "^7.1.0"
|
|
||||||
|
|
||||||
chalk@^4.0.2:
|
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
|
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
|
||||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||||
@@ -2215,16 +2302,16 @@ color-convert@^2.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
color-name "~1.1.4"
|
color-name "~1.1.4"
|
||||||
|
|
||||||
color-name@^1.0.0, color-name@~1.1.4:
|
|
||||||
version "1.1.4"
|
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
|
||||||
|
|
||||||
color-name@1.1.3:
|
color-name@1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||||
|
|
||||||
|
color-name@^1.0.0, color-name@~1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||||
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
color-string@^1.9.0:
|
color-string@^1.9.0:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz"
|
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz"
|
||||||
@@ -2455,9 +2542,9 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
|||||||
once "^1.4.0"
|
once "^1.4.0"
|
||||||
|
|
||||||
engine.io-client@~6.5.2:
|
engine.io-client@~6.5.2:
|
||||||
version "6.5.2"
|
version "6.5.3"
|
||||||
resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz"
|
resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz"
|
||||||
integrity sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==
|
integrity sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@socket.io/component-emitter" "~3.1.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
@@ -2566,7 +2653,7 @@ eslint-plugin-prettier@^5.0.0:
|
|||||||
prettier-linter-helpers "^1.0.0"
|
prettier-linter-helpers "^1.0.0"
|
||||||
synckit "^0.8.5"
|
synckit "^0.8.5"
|
||||||
|
|
||||||
eslint-plugin-vue@^9.0.0, eslint-plugin-vue@^9.17.0:
|
eslint-plugin-vue@^9.17.0:
|
||||||
version "9.18.1"
|
version "9.18.1"
|
||||||
resolved "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.18.1.tgz"
|
resolved "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.18.1.tgz"
|
||||||
integrity sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==
|
integrity sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==
|
||||||
@@ -2592,7 +2679,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
|
|||||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
||||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||||
|
|
||||||
"eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.49.0, "eslint@>= 8.0.0", eslint@>=6.0.0, eslint@>=7.0.0, eslint@>=8.0.0:
|
eslint@^8.49.0:
|
||||||
version "8.52.0"
|
version "8.52.0"
|
||||||
resolved "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz"
|
resolved "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz"
|
||||||
integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==
|
integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==
|
||||||
@@ -2840,9 +2927,9 @@ flatted@^3.2.9:
|
|||||||
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
|
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
|
||||||
|
|
||||||
follow-redirects@^1.15.0:
|
follow-redirects@^1.15.0:
|
||||||
version "1.15.2"
|
version "1.15.5"
|
||||||
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
|
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz"
|
||||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==
|
||||||
|
|
||||||
form-data@^4.0.0:
|
form-data@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@@ -2873,6 +2960,11 @@ fs.realpath@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
|
fsevents@~2.3.2:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
function-bind@^1.1.1:
|
function-bind@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
|
||||||
@@ -3073,7 +3165,7 @@ ico-endec@*:
|
|||||||
resolved "https://registry.npmjs.org/ico-endec/-/ico-endec-0.1.6.tgz"
|
resolved "https://registry.npmjs.org/ico-endec/-/ico-endec-0.1.6.tgz"
|
||||||
integrity sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==
|
integrity sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==
|
||||||
|
|
||||||
idb@^7.0.1, idb@7.0.1:
|
idb@7.0.1, idb@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz"
|
resolved "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz"
|
||||||
integrity sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==
|
integrity sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==
|
||||||
@@ -3119,7 +3211,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@^2.0.3, inherits@^2.0.4, inherits@2:
|
inherits@2, inherits@^2.0.3, inherits@^2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@@ -3847,7 +3939,7 @@ prettier-linter-helpers@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-diff "^1.1.2"
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
prettier@^3.0.3, "prettier@>= 3.0.0", prettier@>=3.0.0:
|
prettier@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz"
|
resolved "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz"
|
||||||
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
|
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
|
||||||
@@ -4044,7 +4136,7 @@ rollup-plugin-terser@^7.0.0:
|
|||||||
serialize-javascript "^4.0.0"
|
serialize-javascript "^4.0.0"
|
||||||
terser "^5.0.0"
|
terser "^5.0.0"
|
||||||
|
|
||||||
"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.0.0, rollup@^2.43.1:
|
rollup@^2.43.1:
|
||||||
version "2.79.1"
|
version "2.79.1"
|
||||||
resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz"
|
resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz"
|
||||||
integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
|
integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
|
||||||
@@ -4072,7 +4164,7 @@ run-parallel@^1.1.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask "^1.2.2"
|
queue-microtask "^1.2.2"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0:
|
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
@@ -4091,7 +4183,7 @@ safe-regex-test@^1.0.0:
|
|||||||
get-intrinsic "^1.1.3"
|
get-intrinsic "^1.1.3"
|
||||||
is-regex "^1.1.4"
|
is-regex "^1.1.4"
|
||||||
|
|
||||||
sass@*, sass@^1.67.0:
|
sass@^1.67.0:
|
||||||
version "1.67.0"
|
version "1.67.0"
|
||||||
resolved "https://registry.npmjs.org/sass/-/sass-1.67.0.tgz"
|
resolved "https://registry.npmjs.org/sass/-/sass-1.67.0.tgz"
|
||||||
integrity sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A==
|
integrity sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A==
|
||||||
@@ -4105,28 +4197,7 @@ semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
|||||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.3.5:
|
semver@^7.3.5, semver@^7.3.6, semver@^7.3.8, semver@^7.5.4:
|
||||||
version "7.5.4"
|
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz"
|
|
||||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
|
||||||
dependencies:
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
semver@^7.3.6:
|
|
||||||
version "7.5.4"
|
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz"
|
|
||||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
|
||||||
dependencies:
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
semver@^7.3.8:
|
|
||||||
version "7.5.4"
|
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz"
|
|
||||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
|
||||||
dependencies:
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
semver@^7.5.4:
|
|
||||||
version "7.5.4"
|
version "7.5.4"
|
||||||
resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz"
|
resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz"
|
||||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||||
@@ -4215,10 +4286,10 @@ slash@^3.0.0:
|
|||||||
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
|
||||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||||
|
|
||||||
socket.io-client@^4.7.2:
|
socket.io-client@^4.7.4:
|
||||||
version "4.7.2"
|
version "4.7.4"
|
||||||
resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz"
|
resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz"
|
||||||
integrity sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==
|
integrity sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@socket.io/component-emitter" "~3.1.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~4.3.2"
|
debug "~4.3.2"
|
||||||
@@ -4233,7 +4304,7 @@ socket.io-parser@~4.2.4:
|
|||||||
"@socket.io/component-emitter" "~3.1.0"
|
"@socket.io/component-emitter" "~3.1.0"
|
||||||
debug "~4.3.1"
|
debug "~4.3.1"
|
||||||
|
|
||||||
source-map-js@^1.0.2, "source-map-js@>=0.6.2 <2.0.0":
|
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
|
||||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||||
@@ -4271,13 +4342,6 @@ streamx@^2.15.0:
|
|||||||
fast-fifo "^1.1.0"
|
fast-fifo "^1.1.0"
|
||||||
queue-tick "^1.0.1"
|
queue-tick "^1.0.1"
|
||||||
|
|
||||||
string_decoder@^1.1.1:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
|
||||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
|
||||||
dependencies:
|
|
||||||
safe-buffer "~5.2.0"
|
|
||||||
|
|
||||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||||
@@ -4319,6 +4383,13 @@ string.prototype.trimstart@^1.0.6:
|
|||||||
define-properties "^1.1.4"
|
define-properties "^1.1.4"
|
||||||
es-abstract "^1.20.4"
|
es-abstract "^1.20.4"
|
||||||
|
|
||||||
|
string_decoder@^1.1.1:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
||||||
|
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
stringify-object@^3.3.0:
|
stringify-object@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz"
|
resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz"
|
||||||
@@ -4441,7 +4512,7 @@ tempy@^0.6.0:
|
|||||||
type-fest "^0.16.0"
|
type-fest "^0.16.0"
|
||||||
unique-string "^2.0.0"
|
unique-string "^2.0.0"
|
||||||
|
|
||||||
terser@^5.0.0, terser@^5.4.0:
|
terser@^5.0.0:
|
||||||
version "5.16.1"
|
version "5.16.1"
|
||||||
resolved "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz"
|
resolved "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz"
|
||||||
integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==
|
integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==
|
||||||
@@ -4524,7 +4595,7 @@ type-fest@^0.20.2:
|
|||||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
|
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
|
||||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||||
|
|
||||||
typescript@*, typescript@^5.2.2, typescript@>=4.2.0, typescript@>=4.4.4:
|
typescript@^5.2.2:
|
||||||
version "5.2.2"
|
version "5.2.2"
|
||||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz"
|
resolved "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz"
|
||||||
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
|
integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==
|
||||||
@@ -4630,10 +4701,10 @@ vite-plugin-pwa@^0.16.5:
|
|||||||
workbox-build "^7.0.0"
|
workbox-build "^7.0.0"
|
||||||
workbox-window "^7.0.0"
|
workbox-window "^7.0.0"
|
||||||
|
|
||||||
"vite@^3.1.0 || ^4.0.0", vite@^4.0.0, vite@^4.4.9:
|
vite@^4.4.9:
|
||||||
version "4.4.9"
|
version "4.5.2"
|
||||||
resolved "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz"
|
resolved "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz"
|
||||||
integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==
|
integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.18.10"
|
esbuild "^0.18.10"
|
||||||
postcss "^8.4.27"
|
postcss "^8.4.27"
|
||||||
@@ -4692,7 +4763,7 @@ vue-tsc@^1.8.11:
|
|||||||
"@vue/typescript" "1.8.11"
|
"@vue/typescript" "1.8.11"
|
||||||
semver "^7.3.8"
|
semver "^7.3.8"
|
||||||
|
|
||||||
"vue@^2.6.14 || ^3.3.0", vue@^3.0.0, "vue@^3.0.0-0 || ^2.6.0", vue@^3.2.0, vue@^3.2.25, vue@^3.3.4, vue@3.3.4:
|
vue@^3.3.4:
|
||||||
version "3.3.4"
|
version "3.3.4"
|
||||||
resolved "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz"
|
resolved "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz"
|
||||||
integrity sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==
|
integrity sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==
|
||||||
@@ -4912,7 +4983,7 @@ workbox-sw@7.0.0:
|
|||||||
resolved "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.0.0.tgz"
|
resolved "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.0.0.tgz"
|
||||||
integrity sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==
|
integrity sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==
|
||||||
|
|
||||||
workbox-window@^7.0.0, workbox-window@7.0.0:
|
workbox-window@7.0.0, workbox-window@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.npmjs.org/workbox-window/-/workbox-window-7.0.0.tgz"
|
resolved "https://registry.npmjs.org/workbox-window/-/workbox-window-7.0.0.tgz"
|
||||||
integrity sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==
|
integrity sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==
|
||||||
|
|||||||
Reference in New Issue
Block a user