Compare commits

...

52 Commits

Author SHA1 Message Date
Spythere 57e88b9abc Merge do wersji 1.15.1
Wersja 1.15.1
2023-06-02 20:09:43 +02:00
Spythere 44ebf53798 poprawki pwa 2023-06-02 20:06:25 +02:00
Spythere 145dc72b6b pwa: zmiana na autoUpdate 2023-06-02 19:41:33 +02:00
Spythere b7f3761940 Merge do wersji 1.15.0
Wersja 1.15.0
2023-06-02 01:16:37 +02:00
Spythere ea7c49dfb3 bump: 1.15.0 2023-06-02 01:10:01 +02:00
Spythere 5d6785813a fix: odznaki TWR/SKR; dodano do dziennika 2023-06-02 01:07:44 +02:00
Spythere a0054aed14 fix: optymalizacja zapytań historii RJ scenerii 2023-06-02 00:51:03 +02:00
Spythere 471e6f5216 Wersja 1.14.3
Wersja 1.14.3
2023-05-18 02:42:27 +02:00
Spythere a617eef00e bump 1.14.3 2023-05-18 02:38:13 +02:00
Spythere 38e700ecd6 migracja z route na routeNames 2023-05-18 02:37:27 +02:00
Spythere da1be0e10a Wersja 1.14.2 2023-05-17 02:24:05 +02:00
Spythere f49bb12948 bump 1.14.2 2023-05-17 02:21:13 +02:00
Spythere 02673a3d70 poprawki filtrów scenerii 2023-05-16 14:27:31 +02:00
Spythere 4ddc7345df poprawki filtrów RJ 2023-05-16 02:40:08 +02:00
Spythere 5d822684c0 feature: rozszerzone filtry RJ 2023-05-14 15:05:51 +02:00
Spythere 69fa15c70a v1.14.1
v1.14.1
2023-04-13 19:25:30 +02:00
Spythere 9192067388 1.14.1 2023-04-13 19:21:36 +02:00
Spythere 2b41e5b857 hotfix 2023-04-13 19:21:24 +02:00
Spythere 674680ff14 1.14: hotfixy
1.14 hotfixy
2023-04-13 19:15:38 +02:00
Spythere 475bd2ff10 przeniesienie skrótów do widoku scenerii 2023-04-13 19:11:44 +02:00
Spythere 074d1eb155 Hotfix tłumaczeń 2023-04-13 18:50:00 +02:00
Spythere 378393de89 Wersja 1.14.0
Wersja 1.14.0
2023-04-13 15:45:39 +02:00
Spythere 03e61083a7 tłumaczenie stopki 2023-04-13 15:37:34 +02:00
Spythere 0b746fce8c hotfix headera 2023-04-13 15:32:41 +02:00
Spythere 5883e710be bump wersji: 1.14 2023-04-13 15:26:22 +02:00
Spythere 3d0695a17b Poprawki w headerze i stopce 2023-04-13 15:25:39 +02:00
Spythere 4adb76eeb0 feature: podpisy status indicatorów dla RJ 2023-04-11 00:33:14 +02:00
Spythere 4c41076519 feature: skrót posterunku w tabelce 2023-04-09 01:37:11 +02:00
Spythere 77f61d17fd hotfix: wygląd badge'a 2023-04-09 00:19:53 +02:00
Spythere 032a84cbcf feature: historia zmian w zestawieniu pociągów 2023-03-20 22:39:23 +01:00
Spythere de9851ebcc Wersja 1.13.0
Wersja 1.13.0
2023-03-16 01:31:18 +01:00
Spythere ff78eba927 hotfixy filtrów 2023-03-15 18:15:01 +01:00
Spythere e4c5f6a322 filtry pociągów 2023-03-15 17:56:27 +01:00
Spythere 0a78761928 hotfix tłumaczeń 2023-03-15 15:34:36 +01:00
Spythere 4843043c29 bump wersji: 1.13.0 2023-03-15 15:20:28 +01:00
Spythere 9e1df1fb61 filtry pociągów 2023-03-15 15:19:50 +01:00
Spythere 021474cfb0 fix: tłumaczenia; hotfixy 2023-03-14 23:13:37 +01:00
Spythere 7d0e68862c filtry scenerii 2023-03-14 21:42:39 +01:00
Spythere 653d45dfc6 scenerie: remake filtrów 2023-03-13 22:26:00 +01:00
Spythere 4a4e1240a4 widok scenerii: odnośniki do tablic przy aktywnych RJ 2023-03-10 16:59:16 +01:00
Spythere 14ca48a90d hotfix 2023-03-10 16:30:36 +01:00
Spythere a02f9804b1 dziennik RJ: dodatkowe info 2023-03-10 16:30:31 +01:00
Spythere c5efc6fbac bump: wersja 1.12.2 2023-03-10 02:03:37 +01:00
Spythere cacd0a1e4e fix: data stworzenia RJ 2023-03-10 01:53:19 +01:00
Spythere 50375099ab update url api 2023-03-10 01:42:10 +01:00
Spythere 6af67ec741 fix: tłumaczenia filtrów 2023-03-09 14:35:03 +01:00
Spythere c64112c86a dziennik RJ: dodatkowe informacje 2023-03-09 14:28:16 +01:00
Spythere 0434702d3b update: URL api 2023-03-09 13:41:59 +01:00
Spythere dd7d1b0bb0 Wersja 1.12.1
Wersja 1.12.1
2023-02-26 14:20:40 +01:00
Spythere 68934a89a4 bump: wersja 1.12.1 2023-02-26 14:16:13 +01:00
Spythere b88a240ec1 feature: like count historii dyżurnych 2023-02-26 14:14:32 +01:00
Spythere eaa34f3359 hotfix: dostępność grubości czcionki 2023-02-26 13:50:47 +01:00
58 changed files with 1488 additions and 1109 deletions
+4 -3
View File
@@ -5,8 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="keywords" content="Stacjownik, TD2, Train Driver 2, stacjownik-td2" /> <meta name="keywords" content="Stacjownik, TD2, Train Driver 2, stacjownik-td2, stacjownik, td2.info.pl" />
<meta name="description" content="Automatycznie odświeżana strona wyświetlająca stacje w Train Driver 2!" /> <meta name="description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
<title>Stacjownik</title> <title>Stacjownik</title>
@@ -24,7 +24,7 @@
<link rel="icon" href="favicon-16.png" sizes="16x16" type="image/png" /> <link rel="icon" href="favicon-16.png" sizes="16x16" type="image/png" />
<link rel="icon" href="favicon.ico" /> <link rel="icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500;700&display=swap" rel="stylesheet" />
</head> </head>
<body> <body>
@@ -32,3 +32,4 @@
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.12.0", "version": "1.15.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+5
View File
@@ -91,6 +91,11 @@ footer.app_footer {
max-width: 100%; max-width: 100%;
padding: 0.5em; padding: 0.5em;
img {
width: 1.1em;
vertical-align: text-bottom;
}
z-index: 10; z-index: 10;
background: #111; background: #111;
+2
View File
@@ -23,6 +23,8 @@
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a> <a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
{{ new Date().getUTCFullYear() }} | {{ new Date().getUTCFullYear() }} |
<a :href="releaseURL" target="_blank">v{{ VERSION }}{{ isOnProductionHost ? '' : 'dev' }}</a> <a :href="releaseURL" target="_blank">v{{ VERSION }}{{ isOnProductionHost ? '' : 'dev' }}</a>
<br />
<a href="https://discord.gg/x2mpNN3svk"><img :src="getIcon('discord', 'png')" alt="">&nbsp;<b>{{ $t('footer.discord') }}</b></a>
<div style="display: none">&int; ukryta taktyczna całka do programowania w HTMLu</div> <div style="display: none">&int; ukryta taktyczna całka do programowania w HTMLu</div>
</footer> </footer>
+18 -16
View File
@@ -1,18 +1,20 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="60" height="60" fill="#898989"/> <rect y="-0.00012207" width="60" height="60" fill="#898989"/>
<path d="M30.5 6.04878H35.2195" stroke="#BFBFBF"/> <path d="M29.0126 32.4897V10.2511V9.52028H30.4337V10.2511V57.234H29.0126V32.4897Z" fill="#BFBFBF"/>
<path d="M27.9024 4.00303C25.2115 4.10008 24.2403 6.24494 24 7.41767H32.0488C31.8486 6.16406 30.5934 3.90598 27.9024 4.00303Z" fill="black"/> <path d="M26.955 29.3992V32.9949L29.7672 36.9105" stroke="black" stroke-width="0.61183"/>
<path d="M33.0244 29.6688V5.47793V4.68292H34.4878V5.47793V56.5854H33.0244V32.5H27.5V28.5V28.0163L28.5 28V31.5L31.9268 31.5447H33.0244V29.6688Z" fill="#BFBFBF"/> <rect x="29.0051" y="34.0686" width="1.42857" height="22.8196" fill="white"/>
<path d="M28.1463 29.2683C30.8373 29.1712 31.8085 27.0264 32.0488 25.8537H24C24.2002 27.1073 25.4554 29.3654 28.1463 29.2683Z" fill="black"/> <rect x="29.0051" y="34.0686" width="1.42857" height="5.18627" fill="#FF0000"/>
<path d="M32.0488 25.8537V7.86993V7.41464H24V25.8537H32.0488Z" fill="black"/> <rect x="29.0051" y="54.8137" width="1.42857" height="5.18627" fill="#FF0000"/>
<path d="M25 26V29.5L33.8781 44.9756" stroke="black"/> <rect x="29.0051" y="44.4412" width="1.42857" height="5.18627" fill="#FF0000"/>
<rect x="33.0244" y="31.5447" width="1.46341" height="25.0407" fill="white"/> <rect x="27.8749" y="31.8649" width="3.75" height="2.17823" fill="white"/>
<rect x="33.0244" y="31.5447" width="1.46341" height="5.69106" fill="#FF0000"/> <path d="M33.5 28.5111V8.61545V8.11176H26V28.5111H33.5Z" fill="black"/>
<rect x="33.0244" y="42.9268" width="1.46341" height="5.69106" fill="#FF0000"/> <path d="M29.6364 5.00276C27.1289 5.09112 26.2239 7.044 26 8.11176H33.5C33.3134 6.97036 32.1438 4.91439 29.6364 5.00276Z" fill="black"/>
<rect x="33.0244" y="54.3089" width="1.46341" height="5.69106" fill="#FF0000"/> <path d="M29.8636 31.6201C32.3711 31.5317 33.2761 29.5789 33.5 28.5111H26C26.1865 29.6525 27.3561 31.7085 29.8636 31.6201Z" fill="black"/>
<ellipse cx="27.9024" cy="7.40022" rx="1.46341" ry="1.40022" fill="#212121"/> <ellipse cx="29.887" cy="11.8168" rx="1.38696" ry="1.28474" fill="#212121"/>
<ellipse cx="27.9024" cy="11.8343" rx="1.46341" ry="1.40022" fill="#212121"/> <ellipse cx="29.887" cy="8.0135" rx="1.38696" ry="1.28474" fill="#212121"/>
<ellipse cx="27.9024" cy="16.2683" rx="1.46341" ry="1.40022" fill="#FF0000"/> <ellipse cx="29.887" cy="15.6151" rx="1.38696" ry="1.28474" fill="#212121"/>
<ellipse cx="27.9024" cy="20.7023" rx="1.46341" ry="1.40022" fill="#212121"/> <ellipse cx="29.887" cy="19.6834" rx="1.38696" ry="1.28474" fill="#212121"/>
<ellipse cx="27.9024" cy="25.1364" rx="1.46341" ry="1.40022" fill="#212121"/> <ellipse cx="29.887" cy="23.7518" rx="1.38696" ry="1.28474" fill="#212121"/>
<ellipse cx="29.887" cy="27.8201" rx="1.38696" ry="1.28474" fill="#00FF0A"/>
<ellipse cx="29.887" cy="19.769" rx="1.38696" ry="1.28474" fill="#00FF0A"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#F2E147"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#66FF6C"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#898989"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

+27 -65
View File
@@ -6,16 +6,6 @@
<img :src="getIcon('pl')" alt="icon-pl" @click="changeLang('en')" v-if="currentLang == 'pl'" /> <img :src="getIcon('pl')" alt="icon-pl" @click="changeLang('en')" v-if="currentLang == 'pl'" />
<img :src="getIcon('en', 'jpg')" alt="icon-en" @click="changeLang('pl')" v-else /> <img :src="getIcon('en', 'jpg')" alt="icon-en" @click="changeLang('pl')" v-else />
</span> </span>
<span class="icons-bottom">
<a href="https://www.paypal.com/paypalme/spythere" target="_blank">
<img :src="getIcon('dollar')" alt="icon paypal" />
</a>
<a href="https://discord.gg/x2mpNN3svk" target="_blank">
<img :src="getIcon('discord', 'png')" alt="icon discord" />
</a>
</span>
</div> </div>
<div class="header_body"> <div class="header_body">
@@ -33,6 +23,12 @@
<div class="info_counter"> <div class="info_counter">
<img :src="getIcon('dispatcher')" alt="icon dispatcher" /> <img :src="getIcon('dispatcher')" alt="icon dispatcher" />
<span class="text--primary">{{ onlineDispatchersCount }}</span> <span class="text--primary">{{ onlineDispatchersCount }}</span>
<!-- <span class="g-tooltip">
<b class="text--primary">{{ factorU }}U</b>
<div class="content">Test</div>
</span> -->
<span class="text--grayed"> / </span> <span class="text--grayed"> / </span>
<span class="text--primary">{{ onlineTrainsCount }}</span> <span class="text--primary">{{ onlineTrainsCount }}</span>
<img :src="getIcon('train')" alt="icon train" /> <img :src="getIcon('train')" alt="icon train" />
@@ -98,11 +94,17 @@ export default defineComponent({
onlineTrainsCount() { onlineTrainsCount() {
return this.store.trainList.filter((train) => train.online).length; return this.store.trainList.filter((train) => train.online).length;
}, },
onlineDispatchersCount() { onlineDispatchersCount() {
return this.store.stationList.filter( return this.store.stationList.filter(
(station) => station.onlineInfo && station.onlineInfo.region == this.store.region.id (station) => station.onlineInfo && station.onlineInfo.region == this.store.region.id
).length; ).length;
}, },
factorU() {
return this.onlineDispatchersCount == 0 ? '-' : (this.onlineTrainsCount / this.onlineDispatchersCount).toFixed(2);
},
computedRegions() { computedRegions() {
return options.regions.map((region) => { return options.regions.map((region) => {
const regionStationCount = const regionStationCount =
@@ -135,22 +137,20 @@ export default defineComponent({
.header { .header {
&_body { &_body {
max-width: 21em;
position: relative; position: relative;
max-width: 20em;
@include smallScreen {
max-width: 18em;
}
} }
&_container { &_container {
display: flex; display: flex;
justify-content: center; justify-content: center;
position: relative;
width: 1350px;
padding: 0.5em 0.3em 0 0.3em;
border-radius: 0 0 1em 1em; border-radius: 0 0 1em 1em;
@include smallScreen {
position: relative;
margin-top: 0.5em;
}
} }
&_brand { &_brand {
@@ -158,6 +158,7 @@ export default defineComponent({
img { img {
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
} }
} }
@@ -165,9 +166,7 @@ export default defineComponent({
&_info { &_info {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
max-width: 100%; font-size: 1.15em;
font-size: 1.2em;
} }
&_links { &_links {
@@ -184,57 +183,20 @@ export default defineComponent({
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
height: 100%;
display: flex; padding: 0.5em;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
padding: 0.5em 0.5em;
@include smallScreen() { @include smallScreen {
right: auto; transform: translateX(85%);
left: 0.75em;
padding: 0;
align-items: center;
} }
} }
} }
// ICONS // ICONS
.icons { .icons-top {
position: relative; img {
width: 2.5em;
&-top { cursor: pointer;
img {
width: 2.5em;
cursor: pointer;
}
margin-bottom: 0.5em;
}
&-bottom {
display: flex;
a {
margin-left: 0.6em;
user-select: none;
}
img {
width: 1.9em;
}
@include smallScreen() {
flex-direction: column;
a {
margin: 0.25em 0;
}
}
} }
} }
+12 -15
View File
@@ -164,7 +164,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { DataStatus } from '../../scripts/enums/DataStatus'; import { DataStatus } from '../../scripts/enums/DataStatus';
import { useStore } from '../../store/store'; import { useStore } from '../../store/store';
import { StoreState } from '../../store/storeTypes'; import { StoreState } from '../../scripts/interfaces/store/storeTypes';
export default defineComponent({ export default defineComponent({
data() { data() {
@@ -303,9 +303,11 @@ export default defineComponent({
.status-indicator { .status-indicator {
position: absolute; position: absolute;
left: 110%;
bottom: 0; bottom: 0;
right: 0;
z-index: 100; z-index: 100;
transform: translateX(1.5em);
} }
.indicator { .indicator {
@@ -330,7 +332,7 @@ export default defineComponent({
background-color: #171717; background-color: #171717;
border-radius: 0.75em; border-radius: 0.75em;
min-width: 13em; width: 13em;
text-align: center; text-align: center;
overflow: none; overflow: none;
@@ -354,22 +356,16 @@ export default defineComponent({
} }
@include midScreen() { @include midScreen() {
left: 50%; left: auto;
top: 100%; right: 200%;
transform: translate(-50%, 0);
margin-left: 0;
margin-top: 0.75em;
&::before { &::before {
border-left: 10px solid transparent;
border-right: 10px solid transparent; border-right: 10px solid transparent;
border-bottom: 10px solid #171717; border-left: 12px solid #171717;
right: 0;
left: auto;
top: 0; transform: translate(100%, -50%);
left: 50%;
transform: translate(-50%, -100%);
} }
} }
@@ -379,3 +375,4 @@ export default defineComponent({
} }
} }
</style> </style>
+6 -30
View File
@@ -3,6 +3,10 @@
<div class="select-box_content"> <div class="select-box_content">
<button class="selected" @click="toggleBox"> <button class="selected" @click="toggleBox">
<span>{{ computedSelectedItem.selectedValue || computedSelectedItem.value }}</span> <span>{{ computedSelectedItem.selectedValue || computedSelectedItem.value }}</span>
<div class="arrow">
<img :src="listOpen ? getIcon('arrow-asc') : getIcon('arrow-desc')" alt="arrow-icon" />
</div>
</button> </button>
<ul class="options" :ref="(el) => (listRef = el as Element)"> <ul class="options" :ref="(el) => (listRef = el as Element)">
@@ -21,10 +25,6 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="arrow">
<img :src="listOpen ? getIcon('arrow-asc') : getIcon('arrow-desc')" alt="arrow-icon" />
</div>
</div> </div>
</template> </template>
@@ -129,46 +129,22 @@ export default defineComponent({
} }
.select-box { .select-box {
position: relative; display: flex;
width: auto; align-items: center;
} }
.arrow { .arrow {
position: absolute;
top: 50%;
right: 0;
padding: 0;
img { img {
vertical-align: middle; vertical-align: middle;
width: 1.35em; width: 1.35em;
} }
transform: translateY(-50%);
pointer-events: none;
} }
button.selected { button.selected {
background-color: transparent;
color: paleturquoise; color: paleturquoise;
font-size: 1em;
font-weight: bold; font-weight: bold;
padding: 0.1em 0.5em; padding: 0.1em 0.5em;
margin-right: 2em;
display: flex;
width: 100%;
cursor: pointer;
border: none;
outline: none;
text-align: left;
&:focus { &:focus {
background-color: #262626; background-color: #262626;
@@ -29,6 +29,10 @@
<b class="text--primary">{{ item.dispatcherName }}</b> &bull; <b>{{ item.stationName }}</b> <b class="text--primary">{{ item.dispatcherName }}</b> &bull; <b>{{ item.stationName }}</b>
<span class="text--grayed">&nbsp;#{{ item.stationHash }}&nbsp;</span> <span class="text--grayed">&nbsp;#{{ item.stationHash }}&nbsp;</span>
<span class="region-badge" :class="item.region">PL1</span> <span class="region-badge" :class="item.region">PL1</span>
<span class="like-count" v-if="item.dispatcherRate">
<img :src="getIcon('like')" alt="like icon" />
{{ item.dispatcherRate }}
</span>
</span> </span>
<span class="item-time"> <span class="item-time">
@@ -55,6 +59,7 @@ import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData'; import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import imageMixin from '../../mixins/imageMixin';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -64,7 +69,7 @@ export default defineComponent({
}, },
}, },
mixins: [dateMixin, styleMixin], mixins: [dateMixin, styleMixin, imageMixin],
computed: { computed: {
computedDispatcherHistory() { computedDispatcherHistory() {
@@ -101,6 +106,7 @@ export default defineComponent({
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/badge.scss'; @import '../../styles/badge.scss';
@import '../../styles/JournalSection.scss'; @import '../../styles/JournalSection.scss';
@import '../../styles/variables.scss';
li.sticky { li.sticky {
position: sticky; position: sticky;
@@ -114,7 +120,7 @@ li.sticky {
flex-wrap: wrap; flex-wrap: wrap;
text-align: left; text-align: left;
gap: 0.25em; gap: 0.5em 1em;
line-height: 1.7em; line-height: 1.7em;
padding: 0.75em; padding: 0.75em;
@@ -134,11 +140,11 @@ li.sticky {
.item-general { .item-general {
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
gap: 0.25em; gap: 0.25em;
flex-wrap: wrap; flex-wrap: wrap;
.level-badge { .level-badge {
margin-right: 0.25em; margin-right: 0.25em;
} }
@@ -160,4 +166,18 @@ li.sticky {
font-weight: bold; font-weight: bold;
} }
} }
.like-count {
display: flex;
align-items: center;
gap: 0.25em;
font-size: 1.2em;
color: $accentCol;
}
@include smallScreen {
.journal_item {
flex-direction: column;
}
}
</style> </style>
@@ -29,7 +29,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">{{ $t('options.search-date') }}</label> <label v-if="propName == 'search-date'" for="date">{{ $t(`options.search-${optionsType}-date`) }}</label>
<div class="search-box"> <div class="search-box">
<input <input
@@ -129,6 +129,11 @@ export default defineComponent({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
optionsType: {
type: String,
required: true
}
}, },
data() { data() {
@@ -1,20 +1,27 @@
<template> <template>
<transition-group class="journal-list" tag="ul" name="list-anim"> <transition-group class="journal-list" tag="ul" name="list-anim">
<li <li
v-for="{ timetable, sceneryList, ...item } in computedTimetableHistory" v-for="{ timetable, sceneryList, stockHistoryComp, ...item } in computedTimetableHistory"
class="journal_item" class="journal_item"
:key="timetable.id" :key="timetable.id"
@click="item.showExtra.value = !item.showExtra.value"
> >
<div class="journal_item-info"> <div class="journal_item-info">
<div class="info-general"> <div class="info-general">
<span <span
class="general-train" class="general-train"
tabindex="0" tabindex="0"
@click="showTimetable(timetable)" @click.stop="showTimetable(timetable)"
@keydown.enter="showTimetable(timetable)" @keydown.enter="showTimetable(timetable)"
style="cursor: pointer" style="cursor: pointer"
> >
<span class="text--grayed">#{{ timetable.id }}</span> <span class="text--grayed">#{{ timetable.id }}</span>
<span class="badges" v-if="timetable.skr || timetable.twr">
<span class="train-badge twr" v-if="timetable.twr" :title="$t('general.TWR')">TWR</span>
<span class="train-badge skr" v-if="timetable.skr" :title="$t('general.SKR')">SKR</span>
</span>
<span> <span>
<strong class="text--primary"> <strong class="text--primary">
{{ timetable.trainCategoryCode }} {{ timetable.trainCategoryCode }}
@@ -53,20 +60,38 @@
</b> </b>
</span> </span>
</div> </div>
<div class="info-route"> <div class="info-route">
<b>{{ timetable.route.replace('|', ' - ') }}</b> <b>{{ timetable.route.replace('|', ' - ') }}</b>
</div> </div>
<hr /> <hr />
<div class="scenery-list"> <div class="scenery-list">
<span v-for="(scenery, i) in sceneryList" :key="scenery.name" :class="{ confirmed: scenery.confirmed }"> <span
<span v-if="i > 0"> &gt;</span> v-for="(scenery, i) in sceneryList.filter((_, i) =>
!item.showExtra.value ? i == 0 || i == sceneryList.length - 1 : true
)"
:key="scenery.name"
:class="{ confirmed: scenery.confirmed }"
>
<span v-if="i > 0">
&gt;
<span v-if="!item.showExtra.value && i == 1 && sceneryList.length > 2"
>... (+{{ sceneryList.length - 2 }}) &gt;</span
>
</span>
{{ scenery.name }} {{ scenery.name }}
<!-- Data odjazdu ze stacji początkowej --> <!-- Data odjazdu ze stacji początkowej -->
<span v-if="i == 0" v-html="scenery.beginDateHTML"></span> <span v-if="i == 0" v-html="scenery.beginDateHTML"></span>
<!-- Data przyjazdu do stacji końcowej --> <!-- Data przyjazdu do stacji końcowej -->
<span v-if="i == sceneryList.length - 1" v-html="scenery.endDateHTML"> </span> <span
v-if="i == sceneryList.length - 1 || (i == 1 && !item.showExtra.value)"
v-html="scenery.endDateHTML"
></span>
</span> </span>
</div> </div>
<!-- Status RJ --> <!-- Status RJ -->
<div style="margin: 0.5em 0"> <div style="margin: 0.5em 0">
<span> <span>
@@ -88,41 +113,80 @@
</b> </b>
</span> </span>
</div> </div>
<!-- Nick dyżurnego --> <!-- Nick dyżurnego -->
<div v-if="timetable.authorName"> <div v-if="timetable.authorName">
<b class="text--grayed">{{ $t('journal.dispatcher-name') }}&nbsp;</b> <b class="text--grayed">{{ $t('journal.dispatcher-name') }}&nbsp;</b>
<router-link class="dispatcher-link" :to="`/journal/dispatchers?dispatcherName=${timetable.authorName}`"> <router-link class="dispatcher-link" :to="`/journal/dispatchers?dispatcherName=${timetable.authorName}`">
<b>{{ timetable.authorName }}</b> <b>{{ timetable.authorName }}</b>
</router-link> </router-link>
<span class="text--grayed">
({{
(new Date(timetable.createdAt).getTime() - new Date(timetable.beginDate).getTime() < 0
? new Date(timetable.createdAt)
: new Date(timetable.beginDate)
).toLocaleString($i18n.locale, { timeStyle: 'short', dateStyle: 'full' })
}})
</span>
</div> </div>
<button <button class="btn--option btn--show">
v-if="timetable.stockString"
class="btn--option btn--show"
@click="item.showStock.value = !item.showStock.value"
>
{{ $t('journal.stock-info') }} {{ $t('journal.stock-info') }}
<img :src="getIcon(`arrow-${item.showStock.value ? 'asc' : 'desc'}`)" alt="Arrow" /> <img :src="getIcon(`arrow-${item.showExtra.value ? 'asc' : 'desc'}`)" alt="Arrow" />
</button> </button>
<div class="info-extended" v-if="timetable.stockString && item.showStock.value"> <!-- Dodatkowe informacje -->
<div class="info-extended" v-if="timetable.stockString && timetable.stockMass && item.showExtra.value">
<hr /> <hr />
<div>
<span class="badge info-badge"> <div class="stock-specs">
<span class="badge specs-badge">
<span>{{ $t('journal.stock-max-speed') }}</span> <span>{{ $t('journal.stock-max-speed') }}</span>
<span>{{ timetable.maxSpeed }}km/h</span> <span>{{ timetable.maxSpeed }}km/h</span>
</span> </span>
<span class="badge info-badge"> <span class="badge specs-badge">
<span>{{ $t('journal.stock-length') }}</span> <span>{{ $t('journal.stock-length') }}</span>
<span>{{ timetable.stockLength }}m</span> <span>
{{
item.currentHistoryIndex.value == 0
? timetable.stockLength
: stockHistoryComp[item.currentHistoryIndex.value].stockLength || timetable.stockLength
}}m
</span>
</span> </span>
<span class="badge info-badge"> <span class="badge specs-badge">
<span>{{ $t('journal.stock-mass') }}</span> <span>{{ $t('journal.stock-mass') }}</span>
<span>{{ Math.floor(timetable.stockMass! / 1000) }}t</span> <span>
{{
Math.floor(
(item.currentHistoryIndex.value == 0
? timetable.stockMass!
: stockHistoryComp[item.currentHistoryIndex.value].stockMass || timetable.stockMass) / 1000
)
}}t
</span>
</span> </span>
</div> </div>
<div class="stock-history" v-if="stockHistoryComp.length > 1">
<button
class="btn--action"
v-for="(sh, i) in stockHistoryComp"
:data-checked="i == item.currentHistoryIndex.value"
@click.stop="item.currentHistoryIndex.value = i"
>
{{ sh.updatedAt }}
</button>
</div>
<ul class="stock-list"> <ul class="stock-list">
<li v-for="(car, i) in timetable.stockString.split(';')" :key="i"> <li
v-for="(car, i) in (item.currentHistoryIndex.value == 0
? timetable.stockString
: stockHistoryComp[item.currentHistoryIndex.value].stockString
).split(';')"
:key="i"
>
<img <img
@error="onImageError" @error="onImageError"
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${car.split(':')[0]}.png`" :src="`https://rj.td2.info.pl/dist/img/thumbnails/${car.split(':')[0]}.png`"
@@ -160,7 +224,24 @@ export default defineComponent({
return this.timetableHistory.map((timetable) => ({ return this.timetableHistory.map((timetable) => ({
timetable, timetable,
sceneryList: this.getSceneryList(timetable), sceneryList: this.getSceneryList(timetable),
showStock: ref(false), stockHistoryComp: timetable.stockHistory
.slice()
.reverse()
.map((h) => {
const historyData = h.split('@');
return {
updatedAt: new Date(Number(historyData[0])).toLocaleTimeString(this.$i18n.locale, {
hour: '2-digit',
minute: '2-digit',
}),
stockString: historyData[1],
stockMass: Number(historyData[2]) || undefined,
stockLength: Number(historyData[3]) || undefined,
};
}),
showExtra: ref(false),
currentHistoryIndex: ref(0),
})); }));
}, },
}, },
@@ -190,12 +271,7 @@ export default defineComponent({
this.$i18n.locale this.$i18n.locale
)}</span>)`; )}</span>)`;
const abandonedDateHTML = ` (porz. ${this.localeTime( return { name, confirmed: i < timetable.confirmedStopsCount, beginDateHTML, endDateHTML };
timetable.fulfilled ? timetable.scheduledEndDate : timetable.endDate,
this.$i18n.locale
)})`;
return { name, confirmed: i < timetable.confirmedStopsCount, beginDateHTML, endDateHTML, abandonedDateHTML };
}); });
}, },
@@ -221,6 +297,10 @@ export default defineComponent({
@import '../../styles/badge.scss'; @import '../../styles/badge.scss';
@import '../../styles/JournalSection.scss'; @import '../../styles/JournalSection.scss';
.journal_item {
cursor: pointer;
}
hr { hr {
margin: 0.25em 0; margin: 0.25em 0;
} }
@@ -268,6 +348,7 @@ hr {
.general-train { .general-train {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center; align-items: center;
gap: 0.25em; gap: 0.25em;
} }
@@ -284,6 +365,45 @@ ul.stock-list {
color: #aaa; color: #aaa;
font-size: 0.9em; font-size: 0.9em;
} }
li > img {
vertical-align: text-bottom;
max-height: 60px;
}
}
.stock-specs {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
margin-top: 0.5em;
.specs-badge {
margin: 0;
span:last-child {
color: black;
background-color: $accentCol;
}
}
}
.badges {
display: flex;
gap: 0.25em;
// badge.scss
}
.stock-history {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
margin-top: 1em;
button[data-checked='true'] {
color: $accentCol;
}
} }
.scenery-list { .scenery-list {
@@ -304,13 +424,6 @@ ul.stock-list {
} }
} }
.info-badge {
span:last-child {
color: black;
background-color: $accentCol;
}
}
@include smallScreen { @include smallScreen {
.info-general { .info-general {
flex-direction: column; flex-direction: column;
@@ -319,6 +432,10 @@ ul.stock-list {
text-align: center; text-align: center;
} }
.general-train {
justify-content: center;
}
.info-route { .info-route {
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -327,5 +444,13 @@ ul.stock-list {
.btn--show { .btn--show {
margin: 1em auto 0 auto; margin: 1em auto 0 auto;
} }
.stock-specs {
justify-content: center;
}
.stock-history {
justify-content: center;
}
} }
</style> </style>
+9 -3
View File
@@ -4,6 +4,8 @@
{{ station.name }} {{ station.name }}
</a> </a>
<div class="scenery-abbrev">{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b></div>
<div class="scenery-hash" v-if="station.onlineInfo?.hash">#{{ station.onlineInfo.hash }}</div> <div class="scenery-hash" v-if="station.onlineInfo?.hash">#{{ station.onlineInfo.hash }}</div>
</section> </section>
</template> </template>
@@ -28,16 +30,20 @@ export default defineComponent({
.scenery-name { .scenery-name {
font-weight: bold; font-weight: bold;
position: relative;
font-size: 3em; font-size: 3em;
text-transform: uppercase; text-transform: uppercase;
} }
.scenery-abbrev {
font-size: 1.3em;
color: #aaa;
}
.scenery-hash { .scenery-hash {
margin-top: 0.5em;
color: #aaa; color: #aaa;
font-size: 1.2em; font-size: 1.2em;
} }
</style> </style>
+2 -1
View File
@@ -30,8 +30,9 @@
style="color: salmon; text-decoration: underline; font-weight: bold" style="color: salmon; text-decoration: underline; font-weight: bold"
:href="station.generalInfo.projectUrl" :href="station.generalInfo.projectUrl"
target="_blank" target="_blank"
>{{ station.generalInfo.project }}</a
> >
{{ station.generalInfo.project }}
</a>
</span> </span>
</div> </div>
+46 -14
View File
@@ -2,17 +2,37 @@
<section class="scenery-timetable"> <section class="scenery-timetable">
<div class="timetable-header"> <div class="timetable-header">
<h3> <h3>
<img :src="getIcon('timetable')" alt="icon-timetable" />&nbsp; <img :src="getIcon('timetable')" alt="icon-timetable" />
<span>{{ $t('scenery.timetables') }}</span> <span>{{ $t('scenery.timetables') }}</span>
&nbsp;
<span class="text--primary">{{ station.onlineInfo?.scheduledTrains?.length || '0' }}</span> <span>
<span>&nbsp;/&nbsp;</span> <span class="text--primary">{{ station.onlineInfo?.scheduledTrains?.length || '0' }}</span>
<span class="text--grayed"> <span> / </span>
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || '0' }} <span class="text--grayed">
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || '0' }}
</span>
</span>
<span class="header_links">
<a
:href="`https://pragotron-td2.web.app/board?name=${station.name}`"
target="_blank"
:title="$t('scenery.pragotron-link')"
>
<img :src="getIcon('pragotron')" alt="icon-pragotron" />
</a>
<a
:href="`https://tablice-td2.web.app/?station=${station.name}`"
target="_blank"
:title="$t('scenery.tablice-link')"
>
<img :src="getIcon('tablice', 'ico')" alt="icon-tablice" />
</a>
</span> </span>
</h3> </h3>
<div class="timetable-checkpoints" v-if="station && station.generalInfo?.checkpoints"> <div class="timetable-checkpoints" v-if="station?.generalInfo?.checkpoints">
<span v-for="(cp, i) in station.generalInfo.checkpoints" :key="i"> <span v-for="(cp, i) in station.generalInfo.checkpoints" :key="i">
{{ (i > 0 && '&bull;') || '' }} {{ (i > 0 && '&bull;') || '' }}
@@ -168,7 +188,6 @@ import { useStore } from '../../store/store';
import imageMixin from '../../mixins/imageMixin'; import imageMixin from '../../mixins/imageMixin';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import ScheduledTrainStatus from './ScheduledTrainStatus.vue'; import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
import ScheduledTrain from '../../scripts/interfaces/ScheduledTrain';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetable', name: 'SceneryTimetable',
@@ -281,24 +300,36 @@ export default defineComponent({
} }
.timetable-header { .timetable-header {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 99; z-index: 99;
background-color: #181818; background-color: #181818;
padding: 0.5em;
img {
width: 25px;
vertical-align: middle;
}
h3 { h3 {
display: flex; display: flex;
justify-content: center;
flex-wrap: wrap;
align-items: center; align-items: center;
gap: 0.5em;
font-size: 1.3em; font-size: 1.3em;
} }
} }
.header_links {
display: flex;
gap: 0.5em;
margin-left: 0.5em;
}
.timetable { .timetable {
&-count { &-count {
margin-left: 0.5em; margin-left: 0.5em;
@@ -355,7 +386,8 @@ export default defineComponent({
flex-wrap: wrap; flex-wrap: wrap;
font-size: 1.1em; font-size: 1.1em;
padding: 0.75em 0;
margin-top: 0.5em;
button.checkpoint_item { button.checkpoint_item {
color: #aaa; color: #aaa;
@@ -4,16 +4,16 @@
<table v-else-if="sceneryHistoryList.length"> <table v-else-if="sceneryHistoryList.length">
<thead> <thead>
<th>{{ $t('scenery.timetables-history-id') }}</th> <th>{{ $t('scenery.timetables-history-id') }}</th>
<th>{{ $t('scenery.timetables-history-number')}}</th> <th>{{ $t('scenery.timetables-history-number') }}</th>
<th>{{ $t('scenery.timetables-history-route')}}</th> <th>{{ $t('scenery.timetables-history-route') }}</th>
<th>{{ $t('scenery.timetables-history-driver')}}</th> <th>{{ $t('scenery.timetables-history-driver') }}</th>
<th>{{ $t('scenery.timetables-history-author')}}</th> <th>{{ $t('scenery.timetables-history-author') }}</th>
<th>{{ $t('scenery.timetables-history-date')}}</th> <th>{{ $t('scenery.timetables-history-date') }}</th>
</thead> </thead>
<tbody> <tbody>
<tr v-for="historyItem in sceneryHistoryList" @click="test"> <tr v-for="historyItem in sceneryHistoryList">
<td> <td>
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">#{{ historyItem.id }}</router-link> <router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">#{{ historyItem.id }}</router-link>
</td> </td>
@@ -40,31 +40,6 @@
</table> </table>
<div class="list-warning" v-else>{{ $t('scenery.history-list-empty') }}</div> <div class="list-warning" v-else>{{ $t('scenery.history-list-empty') }}</div>
<!-- <ul class="history-list" v-else>
<li class="list-item" v-for="historyItem in sceneryHistoryList">
<div>
<b>{{ localeDay(historyItem.beginDate, $i18n.locale) }}</b>
{{ localeTime(historyItem.beginDate, $i18n.locale) }}
</div>
<div>
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">
<span class="text--grayed"> #{{ historyItem.id }} </span>
<b class="text--primary">&nbsp;{{ historyItem.trainCategoryCode }} {{ historyItem.trainNo }}</b>
<div>{{ historyItem.driverName }}</div>
</router-link>
</div>
<div>{{ historyItem.route.replace('|', ' -> ') }}</div>
<div>
{{ $t('scenery.timetable-author-title') }}:
<b v-if="historyItem.authorName">{{ historyItem.authorName }}</b>
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
</div>
</li>
</ul> -->
</section> </section>
</template> </template>
@@ -99,19 +74,15 @@ export default defineComponent({
methods: { methods: {
async fetchAPIData(countFrom = 0, countLimit = 15) { async fetchAPIData(countFrom = 0, countLimit = 15) {
try { try {
const requestString = `${URLs.stacjownikAPI}/api/getSceneryTimetables?name=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`; const requestString = `${URLs.stacjownikAPI}/api/getIssuedTimetables?name=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
const historyAPIData: SceneryTimetableHistory = await (await axios.get(requestString)).data; const historyAPIData: SceneryTimetableHistory = await (await axios.get(requestString)).data;
this.sceneryHistoryList = historyAPIData.sceneryTimetables; this.sceneryHistoryList = historyAPIData.timetables;
this.dataStatus = DataStatus.Loaded; this.dataStatus = DataStatus.Loaded;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
}, },
test() {
console.log('test');
},
}, },
components: { Loading }, components: { Loading },
}); });
@@ -1,47 +1,19 @@
<template> <template>
<div class="general-status"> <div class="general-status">
<span :class="scheduledTrain.stopStatus"> <span :class="computedScheduledTrain.stopStatus" :title="computedScheduledTrain.stopStatusDescription">
<span v-if="scheduledTrain.stopStatus == 'arriving'"> {{ computedScheduledTrain.stopStatusIndicator }}
<span v-if="scheduledTrain.prevDepartureLine">({{ scheduledTrain.prevDepartureLine }})</span>
{{ scheduledTrain.prevStationName }}
&gt;<span v-if="scheduledTrain.nextArrivalLine"> ({{ scheduledTrain.nextArrivalLine }}) </span>
{{ scheduledTrain.nextStationName || '---' }}
</span>
<span v-else-if="scheduledTrain.stopStatus == 'departed'">
&gt;&gt; <span v-if="scheduledTrain.nextArrivalLine"> ({{ scheduledTrain.nextArrivalLine }}) </span>
{{ scheduledTrain.nextStationName }}
</span>
<span v-else-if="scheduledTrain.stopStatus == 'departed-away'">
&gt;&gt;&gt;
<span v-if="scheduledTrain.nextArrivalLine"> ({{ scheduledTrain.nextArrivalLine }}) </span>
{{ scheduledTrain.nextStationName }}
</span>
<span v-else-if="scheduledTrain.stopStatus == 'online'">
&gt;
<span v-if="scheduledTrain.nextArrivalLine">
({{ scheduledTrain.nextArrivalLine }}) {{ scheduledTrain.nextStationName }}
</span>
<span v-else-if="!scheduledTrain.nextStationName">{{ $t('timetables.end') }}</span>
<span v-else>{{ scheduledTrain.nextStationName }}</span>
</span>
<span v-else-if="scheduledTrain.stopStatus == 'stopped'">
&gt;
<span v-if="scheduledTrain.nextArrivalLine"> ({{ scheduledTrain.nextArrivalLine }}) </span>
{{ scheduledTrain.nextStationName }}
</span>
<span v-else-if="scheduledTrain.stopStatus == 'terminated'">X {{ $t('timetables.terminated') }}</span>
</span> </span>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import ScheduledTrain from '../../scripts/interfaces/ScheduledTrain'; import { ScheduledTrain, StopStatus } from '../../scripts/interfaces/ScheduledTrain';
interface ScheduledTrainComp extends ScheduledTrain {
stopStatusIndicator: string;
stopStatusDescription: string;
}
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -50,6 +22,58 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
computed: {
computedScheduledTrain(): ScheduledTrainComp {
const { prevDepartureLine, prevStationName, stopStatus, nextArrivalLine, nextStationName } = this.scheduledTrain;
const prevDepartureIndicator = prevDepartureLine ? `(${prevDepartureLine}) ${prevStationName}` : '---';
const nextArrivalIndicator = nextArrivalLine ? `(${nextArrivalLine}) ${nextStationName}` : '---';
let stopStatusDescription = '',
stopStatusIndicator = '';
switch (stopStatus) {
case StopStatus.arriving:
stopStatusIndicator = `${this.$t('timetables.from')}: ${prevDepartureIndicator}`;
stopStatusDescription = this.$t('timetables.desc-arriving', { prevStationName, prevDepartureLine });
break;
case StopStatus.online:
case StopStatus.stopped:
stopStatusIndicator = nextArrivalLine
? `${this.$t('timetables.to')}: ${nextArrivalIndicator}`
: `${this.$t('timetables.desc-end')}`;
stopStatusDescription = nextArrivalLine
? this.$t(`timetables.desc-${stopStatus}`, { nextStationName, nextArrivalLine })
: '';
break;
case StopStatus.departed:
stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`;
stopStatusDescription = this.$t('timetables.desc-departed', { nextStationName, nextArrivalLine });
break;
case StopStatus['departed-away']:
stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`;
stopStatusDescription = this.$t('timetables.desc-departed-away', { nextStationName, nextArrivalLine });
break;
case StopStatus.terminated:
stopStatusIndicator = `X ${this.$t('timetables.desc-terminated')}`;
stopStatusDescription = this.$t('timetables.desc-terminated');
break;
default:
break;
}
return {
...this.scheduledTrain,
stopStatusDescription,
stopStatusIndicator,
};
},
},
}); });
</script> </script>
@@ -86,3 +110,4 @@ export default defineComponent({
} }
} }
</style> </style>
+41 -46
View File
@@ -1,5 +1,11 @@
<template> <template>
<button class="btn--action" :class="option.section" :data-selected="option.value" @click="handleChange"> <button
class="btn--action"
:class="option.section"
:data-selected="option.value"
@click="handleLeftClick"
@dblclick="handleDbClick"
>
{{ $t(`filters.${option.id}`) }} {{ $t(`filters.${option.id}`) }}
</button> </button>
</template> </template>
@@ -29,20 +35,48 @@ export default defineComponent({
filterStore: useStationFiltersStore(), filterStore: useStationFiltersStore(),
}; };
}, },
methods: { methods: {
handleChange() { handleLeftClick() {
this.option.value = !this.option.value; this.option.value = !this.option.value;
this.filterStore.lastClickedFilterId = '';
this.filterStore.changeFilterValue({ this.filterStore.changeFilterValue({
name: this.option.name, name: this.option.name,
value: !this.option.value, value: !this.option.value,
}); });
}, },
handleDbClick(e: Event) {
e.preventDefault();
this.filterStore.lastClickedFilterId = this.option.id;
this.option.value = true;
this.filterStore.changeFilterValue({
name: this.option.name,
value: !this.option.value,
});
this.filterStore.inputs.options
.filter((option) => {
return option.section == this.option.section && option.id != this.option.id;
})
.forEach((option) => {
this.filterStore.changeFilterValue({
name: option.name,
value: this.option.value,
});
option.value = !this.option.value;
});
},
}, },
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$realityCol: #e03b07;
$accessCol: #e03b07; $accessCol: #e03b07;
$controlCol: #0085ff; $controlCol: #0085ff;
$signalCol: #bf7c00; $signalCol: #bf7c00;
@@ -51,56 +85,17 @@ $saveCol: #28a826;
$routesCol: #9049c0; $routesCol: #9049c0;
button { button {
width: 100%; padding: 0.25em;
padding: 0.4em; border-radius: 0;
border-radius: 0.4em;
&:focus-visible { &:focus-visible {
outline: 1px solid white; outline: 1px solid white;
} }
&[data-selected='true'] { &[data-selected='true'] {
&.access { background-color: forestgreen;
background-color: $accessCol; font-weight: bold;
box-shadow: 0 0 6px 1px $accessCol;
}
&.control {
background-color: $controlCol;
box-shadow: 0 0 6px 1px $controlCol;
}
&.signals {
background-color: $signalCol;
box-shadow: 0 0 6px 1px $signalCol;
}
&.routes {
background-color: $routesCol;
box-shadow: 0 0 6px 1px $routesCol;
}
&.status {
background-color: $statusCol;
box-shadow: 0 0 6px 1px $statusCol;
}
&.save {
background-color: $saveCol;
box-shadow: 0 0 6px 1px $saveCol;
}
&.troll {
background-color: firebrick;
box-shadow: 0 0 6px 1px firebrick;
}
&.mode {
background-color: lightgreen;
color: black;
font-weight: 500;
}
} }
} }
</style> </style>
@@ -26,15 +26,28 @@
<div class="card" v-if="isVisible" tabindex="0" ref="cardEl"> <div class="card" v-if="isVisible" tabindex="0" ref="cardEl">
<div class="card_content"> <div class="card_content">
<div class="card_title flex">{{ $t('filters.title') }}</div> <div class="card_title flex">{{ $t('filters.title') }}</div>
<p class="card_info" v-html="$t('filters.desc')"></p>
<section class="card_options"> <section class="card_options">
<filter-option <div class="option-section" v-for="section in filterStore.inputs.optionSections">
v-for="(option, i) in filterStore.inputs.options" <h3 class="text--primary">
:option="option" {{ $t(`filters.sections.${section}`) }}
:key="i"
@optionChange="handleChange" <button @click="filterStore.resetSectionOptions(section)">RESET</button>
/> </h3>
<hr />
<div class="section-inputs">
<filter-option
v-for="(option, i) in filterStore.inputs.options.filter((o) => o.section == section)"
:option="option"
:key="i"
/>
</div>
</div>
</section> </section>
<section class="card_timestamp" style="text-align: center"> <section class="card_timestamp" style="text-align: center">
<div>{{ $t('filters.minimum-hours-title') }}</div> <div>{{ $t('filters.minimum-hours-title') }}</div>
<span class="clock"> <span class="clock">
@@ -80,18 +93,25 @@
</div> </div>
</div> </div>
</section> </section>
<section class="card_actions">
<div class="action-buttons">
<button class="btn--action" style="width: 100%" @click="saveFilters" :data-selected="saveOptions">
{{ $t('filters.save') }}
</button>
<button class="btn--action" @click="resetFilters">{{ $t('filters.reset') }}</button>
<button class="btn--action" @click="closeCard">{{ $t('filters.close') }}</button>
</div>
</section>
</div> </div>
<section class="card_actions">
<div class="action-buttons">
<button class="btn--action" style="width: 100%" @click="saveFilters" :data-selected="saveOptions">
{{ $t('filters.save') }}
</button>
<button
class="btn--action"
@click="resetFilters"
:disabled="filterStore.areFiltersAtDefault"
:data-disabled="filterStore.areFiltersAtDefault"
>
{{ $t('filters.reset') }}
</button>
<button class="btn--action" @click="closeCard">{{ $t('filters.close') }}</button>
</div>
</section>
</div> </div>
</transition> </transition>
</section> </section>
@@ -181,15 +201,6 @@ export default defineComponent({
this.isVisible = !this.isVisible; this.isVisible = !this.isVisible;
}, },
handleChange(change: { name: string; value: boolean }) {
this.filterStore.changeFilterValue({
name: change.name,
value: !change.value,
});
if (this.saveOptions) StorageManager.setBooleanValue(change.name, change.value);
},
handleInput(e: Event) { handleInput(e: Event) {
const target = e.target as HTMLInputElement; const target = e.target as HTMLInputElement;
@@ -281,6 +292,14 @@ export default defineComponent({
} }
.card { .card {
display: grid;
grid-template-rows: 1fr auto;
&_info {
background-color: #111;
padding: 0.5em;
}
&_controls { &_controls {
display: flex; display: flex;
gap: 0.5em; gap: 0.5em;
@@ -292,13 +311,13 @@ export default defineComponent({
} }
&_content { &_content {
padding: 1em 0.5em;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1em; gap: 1em;
overflow: auto;
max-height: 90vh;
padding: 1em;
} }
&_title { &_title {
@@ -309,18 +328,6 @@ export default defineComponent({
text-align: center; text-align: center;
} }
&_options {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-rows: repeat(4, 1fr);
gap: 0.5em;
@include smallScreen() {
grid-template-columns: repeat(auto-fit, minmax(8em, 1fr));
grid-template-rows: auto;
}
}
&_regions { &_regions {
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -391,6 +398,9 @@ export default defineComponent({
} }
&_actions { &_actions {
width: 100%;
padding: 0.5em;
.filter-option { .filter-option {
max-width: 50%; max-width: 50%;
margin: 0 auto; margin: 0 auto;
@@ -409,14 +419,42 @@ export default defineComponent({
padding: 0.5em; padding: 0.5em;
&[data-selected='true'] { &[data-selected='true'] {
background-color: lightgreen; background-color: forestgreen;
color: black;
} }
} }
} }
} }
} }
.card_options {
.option-section h3 {
display: flex;
align-items: center;
margin-bottom: 0.25em;
gap: 0.5em;
button {
padding: 0.15em;
color: coral;
}
}
.section-inputs {
display: grid;
// flex-wrap: wrap;
grid-template-columns: repeat(3, minmax(0, 1fr));
// grid-template-rows: repeat(3, 1fr);
gap: 0.5em;
margin: 1em 0;
// @include smallScreen() {
// grid-template-columns: repeat(auto-fit, minmax(8em, 1fr));
// grid-template-rows: auto;
// }
}
}
.slider { .slider {
display: flex; display: flex;
align-items: center; align-items: center;
+49 -24
View File
@@ -8,26 +8,36 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<th v-for="(id, i) in headIds" :key="id" @click="() => changeSorter(i)"> <th
v-for="(headerName, i) in headIds"
:key="headerName"
@click="changeSorter(headerName)"
class="header-text"
>
<span class="header_wrapper"> <span class="header_wrapper">
<div v-html="$t(`sceneries.${id}`)"></div> <div v-html="$t(`sceneries.${headerName}`)"></div>
<img <img
class="sort-icon" class="sort-icon"
v-if="sorterActive.index == i" v-if="sorterActive.headerName == headerName"
:src="sorterActive.dir == 1 ? getIcon('arrow-asc') : getIcon('arrow-desc')" :src="sorterActive.dir == 1 ? getIcon('arrow-asc') : getIcon('arrow-desc')"
alt="sort icon" alt="sort icon"
/> />
</span> </span>
</th> </th>
<th v-for="(id, i) in headIconsIds" :key="id" @click="() => changeSorter(i + 7)"> <th
v-for="(headerName, i) in headIconsIds"
:key="headerName"
@click="changeSorter(headerName)"
class="header-image"
>
<span class="header_wrapper"> <span class="header_wrapper">
<img :src="getIcon(id)" :alt="id" :title="$t(`sceneries.${id}s`)" /> <img :src="getIcon(headerName)" :alt="headerName" :title="$t(`sceneries.${headerName}`)" />
<img <img
class="sort-icon" class="sort-icon"
v-if="sorterActive.index == i + 7" v-if="sorterActive.headerName == headerName"
:src="sorterActive.dir == 1 ? getIcon('arrow-asc') : getIcon('arrow-desc')" :src="sorterActive.dir == 1 ? getIcon('arrow-asc') : getIcon('arrow-desc')"
alt="sort icon" alt="sort icon"
/> />
@@ -190,25 +200,31 @@
<td class="station_users" :class="{ inactive: !station.onlineInfo }"> <td class="station_users" :class="{ inactive: !station.onlineInfo }">
<span> <span>
<span class="highlight">{{ station.onlineInfo?.currentUsers || '0' }}</span> <span class="highlight">{{ station.onlineInfo?.currentUsers || 0 }}</span>
/ /
<span>{{ station.onlineInfo?.maxUsers || '0' }}</span> <span class="highlight">{{ station.onlineInfo?.maxUsers || 0 }}</span>
</span> </span>
</td> </td>
<td class="station_spawns" :class="{ inactive: !station.onlineInfo }"> <td class="station_spawns" :class="{ inactive: !station.onlineInfo }">
<span class="highlight">{{ station.onlineInfo?.spawns.length || '0' }}</span> <span>{{ station.onlineInfo?.spawns.length || 0 }}</span>
</td> </td>
<td class="station_schedules" :class="{ inactive: !station.onlineInfo }"> <td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
<span> <span class="highlight">
<span class="highlight"> {{ station.onlineInfo?.scheduledTrains?.length || 0 }}
{{ station.onlineInfo?.scheduledTrains?.length || '0' }} </span>
</span> </td>
/
<span style="color: #bbb"> <td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || '0' }} <span style="color: #ccc">
</span> {{ station.onlineInfo?.scheduledTrains?.filter((train) => !train.stopInfo.confirmed).length || 0 }}
</span>
</td>
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
<span style="color: #66ff6c">
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || 0 }}
</span> </span>
</td> </td>
</tr> </tr>
@@ -236,6 +252,7 @@ import Station from '../../scripts/interfaces/Station';
import { useStationFiltersStore } from '../../store/stationFiltersStore'; import { useStationFiltersStore } from '../../store/stationFiltersStore';
import { useStore } from '../../store/store'; import { useStore } from '../../store/store';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -249,8 +266,8 @@ export default defineComponent({
mixins: [styleMixin, dateMixin, stationInfoMixin, returnBtnMixin, imageMixin], mixins: [styleMixin, dateMixin, stationInfoMixin, returnBtnMixin, imageMixin],
data: () => ({ data: () => ({
headIds: ['station', 'min-lvl', 'status', 'dispatcher', 'dispatcher-lvl', 'routes', 'general'], headIconsIds,
headIconsIds: ['user', 'spawn', 'timetable'], headIds,
lastSelectedStationName: '', lastSelectedStationName: '',
}), }),
@@ -291,8 +308,10 @@ export default defineComponent({
window.open(url, '_blank'); window.open(url, '_blank');
}, },
changeSorter(i: number) { changeSorter(headerName: HeadIdsTypes) {
this.stationFiltersStore.changeSorter(i); if (headerName == 'general' || headerName == 'routes') return;
this.stationFiltersStore.changeSorter(headerName);
}, },
}, },
}); });
@@ -349,9 +368,15 @@ table {
position: sticky; position: sticky;
top: 0; top: 0;
min-width: 75px; &.header-text {
min-width: 140px;
}
padding: 0.5em; &.header-image {
min-width: 60px;
}
padding: 0.5em 0.25em;
background-color: $bgCol; background-color: $bgCol;
white-space: pre-wrap; white-space: pre-wrap;
+3 -26
View File
@@ -6,8 +6,8 @@
<span class="timetable-id" v-if="train.timetableData">#{{ train.timetableData.timetableId }}</span> <span class="timetable-id" v-if="train.timetableData">#{{ train.timetableData.timetableId }}</span>
<span class="timetable_warnings" v-if="train.timetableData?.TWR || train.timetableData?.SKR"> <span class="timetable_warnings" v-if="train.timetableData?.TWR || train.timetableData?.SKR">
<span class="train-badge twr" v-if="train.timetableData?.TWR">TWR</span> <span class="train-badge twr" v-if="train.timetableData?.TWR" :title="$t('general.TWR')">TWR</span>
<span class="train-badge skr" v-if="train.timetableData?.SKR">SKR</span> <span class="train-badge skr" v-if="train.timetableData?.SKR" :title="$t('general.SKR')">SKR</span>
</span> </span>
<strong> <strong>
@@ -118,7 +118,6 @@ export default defineComponent({
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/badge.scss'; @import '../../styles/badge.scss';
.image-warning { .image-warning {
height: 1em; height: 1em;
@@ -182,26 +181,6 @@ export default defineComponent({
gap: 0.25em; gap: 0.25em;
} }
.train-badge {
padding: 0.1em 0.2em;
border-radius: 0.2em;
font-weight: bold;
font-size: 0.9em;
&.twr {
background-color: var(--clr-twr);
}
&.skr {
background-color: var(--clr-skr);
}
&.offline {
background-color: #9c362b;
}
}
.train-driver { .train-driver {
&.supporter { &.supporter {
color: orange; color: orange;
@@ -218,9 +197,7 @@ export default defineComponent({
.timetable_warnings { .timetable_warnings {
display: flex; display: flex;
gap: 0.2em; gap: 0.25em;
color: black;
} }
.timetable_progress { .timetable_progress {
+40 -22
View File
@@ -43,29 +43,34 @@
<h1 class="option-title">{{ $t('options.sort-title') }}</h1> <h1 class="option-title">{{ $t('options.sort-title') }}</h1>
<div class="options_sorters"> <div class="options_sorters">
<div v-for="opt in translatedSorterOptions"> <button
v-for="opt in translatedSorterOptions"
class="sort-option btn--option"
:data-selected="opt.id == sorterActive.id"
@click="onSorterChange(opt)"
>
{{ opt.value.toUpperCase() }}
</button>
</div>
<h1 class="option-title" v-if="trainFilterList.length != 0">{{ $t('options.filter-title') }}</h1>
<div class="options_filters">
<div v-for="section in Object.keys(TrainFilterSection)">
<button <button
class="sort-option btn--option" class="btn--option"
:data-selected="opt.id == sorterActive.id" v-for="filter in trainFilterList.filter((f) => f.section == section)"
@click="onSorterChange(opt)" :data-inactive="!filter.isActive"
@click="onFilterChange(filter)"
> >
{{ opt.value.toUpperCase() }} {{ $t(`options.filter-${filter.id}`) }}
</button> </button>
</div> </div>
</div> </div>
<h1 class="option-title" v-if="trainFilterList.length != 0">{{ $t('options.filter-title') }}</h1> <div class="filter-actions">
<div class="options_filters"> <div></div>
<div class="filter-option" v-for="filter in trainFilterList"> <button class="btn--action" @click="resetAllFilters">{{ $t('options.filter-reset') }}</button>
<button class="btn--option" :data-inactive="!filter.isActive" @click="onFilterChange(filter)">
{{ $t(`options.filter-${filter.id}`) }}
</button>
</div>
<div class="filter-actions">
<button class="btn--action" @click="clearAllFilters">{{ $t('options.filter-clear') }}</button>
<button class="btn--action" @click="resetAllFilters">{{ $t('options.filter-reset') }}</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -80,6 +85,7 @@ import keyMixin from '../../mixins/keyMixin';
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes'; import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
import ActionButton from '../Global/ActionButton.vue'; import ActionButton from '../Global/ActionButton.vue';
import SelectBox from '../Global/SelectBox.vue'; import SelectBox from '../Global/SelectBox.vue';
import { TrainFilterSection } from '../../scripts/enums/TrainFilterType';
export default defineComponent({ export default defineComponent({
components: { SelectBox, ActionButton }, components: { SelectBox, ActionButton },
@@ -101,6 +107,7 @@ export default defineComponent({
return { return {
showOptions: false, showOptions: false,
lastSelectedFilter: null as TrainFilter | null, lastSelectedFilter: null as TrainFilter | null,
TrainFilterSection,
}; };
}, },
@@ -183,13 +190,24 @@ export default defineComponent({
margin: 0 auto; margin: 0 auto;
} }
.filter-option { .options_sorters {
display: flex;
grid-template-columns: repeat(3, 1fr);
}
.options_filters > div {
display: flex;
width: 100%;
gap: 0.5em;
button { button {
color: white; width: 100%;
color: springgreen;
font-weight: bold; font-weight: bold;
&[data-disabled='true'] { &[data-inactive='true'] {
color: #888; color: #aaa;
} }
} }
} }
@@ -201,7 +219,7 @@ export default defineComponent({
margin-top: 1em; margin-top: 1em;
button { > * {
width: 100%; width: 100%;
} }
} }
@@ -202,7 +202,6 @@ ul.stock-list {
img { img {
max-height: 60px; max-height: 60px;
max-width: 320px;
} }
} }
-13
View File
@@ -12,10 +12,6 @@
{{ $t('trains.no-trains') }} {{ $t('trains.no-trains') }}
</div> </div>
<!-- <div class="timeouts-warning" v-if="trainNumbersWithTimeouts.length == 0">
<b class="warning-timeout">?</b>
{{ $t('trains.timeout') }}
</div> -->
<transition-group name="list-anim" tag="ul" class="train-list" v-else> <transition-group name="list-anim" tag="ul" class="train-list" v-else>
<li <li
class="train-row" class="train-row"
@@ -70,18 +66,9 @@ export default defineComponent({
id: string | number; id: string | number;
dir: number; dir: number;
}, },
distanceLimitExceeded: computed(
() => props.trains.findIndex(({ timetableData }) => timetableData && timetableData.routeDistance > 200) != -1
),
}; };
}, },
computed: {
trainNumbersWithTimeouts() {
return this.store.trainList.filter((train) => train.isTimeout).map((train) => train.trainNo);
},
},
activated() { activated() {
const query = this.$route.query; const query = this.$route.query;
if (query.trainNo && query.driverName) { if (query.trainNo && query.driverName) {
+27 -2
View File
@@ -1,33 +1,58 @@
import { TrainFilterType } from '../../scripts/enums/TrainFilterType'; import { TrainFilterSection, TrainFilterType } from '../../scripts/enums/TrainFilterType';
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes'; import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
export const trainFilters: TrainFilter[] = [ export const trainFilters: TrainFilter[] = [
{ {
id: TrainFilterType.twr, id: TrainFilterType.twr,
section: TrainFilterSection.TRAIN_TYPE,
isActive: true, isActive: true,
}, },
{ {
id: TrainFilterType.skr, id: TrainFilterType.skr,
section: TrainFilterSection.TRAIN_TYPE,
isActive: true, isActive: true,
}, },
{
id: TrainFilterType.common,
section: TrainFilterSection.TRAIN_TYPE,
isActive: true,
},
{ {
id: TrainFilterType.passenger, id: TrainFilterType.passenger,
section: TrainFilterSection.TIMETABLE_TYPE,
isActive: true, isActive: true,
}, },
{ {
id: TrainFilterType.freight, id: TrainFilterType.freight,
section: TrainFilterSection.TIMETABLE_TYPE,
isActive: true, isActive: true,
}, },
{ {
id: TrainFilterType.other, id: TrainFilterType.other,
section: TrainFilterSection.TIMETABLE_TYPE,
isActive: true,
},
{
id: TrainFilterType.withComments,
section: TrainFilterSection.COMMENTS,
isActive: true, isActive: true,
}, },
{ {
id: TrainFilterType.comments, id: TrainFilterType.noComments,
section: TrainFilterSection.COMMENTS,
isActive: true,
},
{
id: TrainFilterType.withTimetable,
section: TrainFilterSection.TIMETABLE,
isActive: true, isActive: true,
}, },
{ {
id: TrainFilterType.noTimetable, id: TrainFilterType.noTimetable,
section: TrainFilterSection.TIMETABLE,
isActive: true, isActive: true,
}, },
]; ];
+65 -46
View File
@@ -1,41 +1,38 @@
{ {
"optionSections": ["reality", "package-access", "access", "control", "addons", "blockades", "signals", "status"],
"options": [ "options": [
{
"id": "default",
"name": "default",
"iconName": "td2",
"section": "access",
"value": true,
"defaultValue": true
},
{
"id": "not-default",
"name": "notDefault",
"iconName": "",
"section": "access",
"value": true,
"defaultValue": true
},
{ {
"id": "real", "id": "real",
"name": "real", "name": "real",
"iconName": "lock", "section": "reality",
"section": "access",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
}, },
{ {
"id": "fictional", "id": "fictional",
"name": "fictional", "name": "fictional",
"iconName": "user", "section": "reality",
"section": "access", "value": true,
"defaultValue": true
},
{
"id": "default",
"name": "default",
"section": "package-access",
"value": true,
"defaultValue": true
},
{
"id": "not-default",
"name": "notDefault",
"section": "package-access",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
}, },
{ {
"id": "non-public", "id": "non-public",
"name": "nonPublic", "name": "nonPublic",
"iconName": "user",
"section": "access", "section": "access",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -43,7 +40,6 @@
{ {
"id": "unavailable", "id": "unavailable",
"name": "unavailable", "name": "unavailable",
"iconName": "user",
"section": "access", "section": "access",
"value": false, "value": false,
"defaultValue": false "defaultValue": false
@@ -51,7 +47,6 @@
{ {
"id": "abandoned", "id": "abandoned",
"name": "abandoned", "name": "abandoned",
"iconName": "user",
"section": "access", "section": "access",
"value": false, "value": false,
"defaultValue": false "defaultValue": false
@@ -59,7 +54,6 @@
{ {
"id": "SPK", "id": "SPK",
"name": "SPK", "name": "SPK",
"iconName": "SPK",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -67,7 +61,6 @@
{ {
"id": "SCS", "id": "SCS",
"name": "SCS", "name": "SCS",
"iconName": "SCS",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -75,15 +68,21 @@
{ {
"id": "SPE", "id": "SPE",
"name": "SPE", "name": "SPE",
"iconName": "SPE", "section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SPK-M",
"name": "mechaniczne+SPK",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
}, },
{ {
"id": "manual", "id": "SCS-M",
"name": "ręczne", "name": "mechaniczne+SCS",
"iconName": "ręczne",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -91,7 +90,27 @@
{ {
"id": "mechanical", "id": "mechanical",
"name": "mechaniczne", "name": "mechaniczne",
"iconName": "mechaniczne", "section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SPK-R",
"name": "ręczne+SPK",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SCS-R",
"name": "ręczne+SCS",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "manual",
"name": "ręczne",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -99,23 +118,34 @@
{ {
"id": "SUP", "id": "SUP",
"name": "SUP", "name": "SUP",
"iconName": "SUP", "section": "addons",
"section": "control", "value": true,
"defaultValue": true
},
{
"id": "noSUP",
"name": "noSUP",
"section": "addons",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
}, },
{ {
"id": "SBL", "id": "SBL",
"name": "SBL", "name": "SBL",
"iconName": "SBL", "section": "blockades",
"section": "routes", "value": true,
"defaultValue": true
},
{
"id": "PBL",
"name": "PBL",
"section": "blockades",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
}, },
{ {
"id": "modern", "id": "modern",
"name": "współczesna", "name": "współczesna",
"iconName": "współczesna",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -123,7 +153,6 @@
{ {
"id": "semaphores", "id": "semaphores",
"name": "kształtowa", "name": "kształtowa",
"iconName": "kształtowa",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -131,7 +160,6 @@
{ {
"id": "mixed", "id": "mixed",
"name": "mieszana", "name": "mieszana",
"iconName": "mieszana",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -139,7 +167,6 @@
{ {
"id": "historical", "id": "historical",
"name": "historyczna", "name": "historyczna",
"iconName": "historyczna",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -148,7 +175,6 @@
{ {
"id": "free", "id": "free",
"name": "free", "name": "free",
"iconName": "",
"section": "status", "section": "status",
"value": false, "value": false,
@@ -157,7 +183,6 @@
{ {
"id": "occupied", "id": "occupied",
"name": "occupied", "name": "occupied",
"iconName": "",
"section": "status", "section": "status",
"value": true, "value": true,
@@ -166,7 +191,6 @@
{ {
"id": "endingStatus", "id": "endingStatus",
"name": "endingStatus", "name": "endingStatus",
"iconName": "",
"section": "status", "section": "status",
"value": true, "value": true,
@@ -175,7 +199,6 @@
{ {
"id": "afkStatus", "id": "afkStatus",
"name": "afkStatus", "name": "afkStatus",
"iconName": "",
"section": "status", "section": "status",
"value": true, "value": true,
@@ -184,7 +207,6 @@
{ {
"id": "noSpaceStatus", "id": "noSpaceStatus",
"name": "noSpaceStatus", "name": "noSpaceStatus",
"iconName": "",
"section": "status", "section": "status",
"value": true, "value": true,
@@ -193,7 +215,6 @@
{ {
"id": "unavailableStatus", "id": "unavailableStatus",
"name": "unavailableStatus", "name": "unavailableStatus",
"iconName": "",
"section": "status", "section": "status",
"value": true, "value": true,
@@ -254,7 +275,6 @@
{ {
"id": "include-selected", "id": "include-selected",
"name": "include-selected", "name": "include-selected",
"iconName": "",
"section": "mode", "section": "mode",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
@@ -262,7 +282,6 @@
{ {
"id": "save", "id": "save",
"name": "save", "name": "save",
"iconName": "",
"section": "mode", "section": "mode",
"value": true, "value": true,
"defaultValue": true "defaultValue": true
+71 -21
View File
@@ -1,7 +1,9 @@
{ {
"general": { "general": {
"and": " and ", "and": " and ",
"refresh": "REFRESH" "refresh": "REFRESH",
"TWR": "High risk freight train",
"SKR": "Train with exceeded gauge"
}, },
"app": { "app": {
"sceneries": "SCENERIES", "sceneries": "SCENERIES",
@@ -15,6 +17,9 @@
"migration-confirm": "Roger that!", "migration-confirm": "Roger that!",
"offline": "App is in the offline mode!" "offline": "App is in the offline mode!"
}, },
"footer": {
"discord": "Stacjownik Discord server"
},
"update": { "update": {
"title": "New version of the app is available!", "title": "New version of the app is available!",
"paragraph1": "Enjoy the application and may the green signal be with you!", "paragraph1": "Enjoy the application and may the green signal be with you!",
@@ -35,7 +40,7 @@
"desc": { "desc": {
"control-type": "Control type: ", "control-type": "Control type: ",
"signals-type": "Signals type: ", "signals-type": "Signals type: ",
"SBL": "This scenery has automatic line blockade system on following routes: ", "SBL": "This scenery has automatic block signalling (ABS/SBL) system on following routes: ",
"SUP": "Requires the SUP application (level crossing remote control simulator)", "SUP": "Requires the SUP application (level crossing remote control simulator)",
"TWB-all": "This scenery has two-way route blockade on all routes", "TWB-all": "This scenery has two-way route blockade on all routes",
"TWB-routes": "This scenery has two-way route blockade on following routes: ", "TWB-routes": "This scenery has two-way route blockade on following routes: ",
@@ -94,7 +99,8 @@
"search-dispatcher": "Dispatcher name", "search-dispatcher": "Dispatcher name",
"search-station": "Scenery name", "search-station": "Scenery name",
"search-author": "Timetable author name", "search-author": "Timetable author name",
"search-date": "Timetable date (CEST / GMT+2)", "search-timetables-date": "Timetable date (CEST / GMT+2)",
"search-dispatchers-date": "Service date (CEST / GMT+2)",
"sort-mass": "mass", "sort-mass": "mass",
"sort-speed": "speed", "sort-speed": "speed",
@@ -111,13 +117,16 @@
"sort-timestampFrom": "date", "sort-timestampFrom": "date",
"sort-duration": "duration", "sort-duration": "duration",
"filter-comments": "COMMENTS", "filter-noComments": "NO COMMENTS",
"filter-twr": "TWR", "filter-withComments": "COMMENTS",
"filter-skr": "SKR", "filter-twr": "HIGH RISK CARGO",
"filter-skr": "EXCEEDED GAUGE",
"filter-common": "NO WARNINGS",
"filter-passenger": "PASSENGER", "filter-passenger": "PASSENGER",
"filter-freight": "FREIGHT", "filter-freight": "FREIGHT",
"filter-other": "OTHER", "filter-other": "OTHER",
"filter-noTimetable": "NO TIMETABLE", "filter-noTimetable": "NO TIMETABLE",
"filter-withTimetable": "TIMETABLE",
"filter-reset": "RESET FILTERS", "filter-reset": "RESET FILTERS",
"filter-clear": "CLEAR FILTERS", "filter-clear": "CLEAR FILTERS",
@@ -128,14 +137,27 @@
"filter-active": "ACTIVE" "filter-active": "ACTIVE"
}, },
"filters": { "filters": {
"desc": " &bull; Left mouse click: select / unselect chosen filter <br /> &bull; Double left click: unselect all filters but chosen from a <b class='text--primary'>group</b> <br /> &bull; <span style='color: coral'>RESET</span>: reset all filters from a <b class='text--primary'>group</b>",
"sections": {
"reality": "SCENERY REALITY",
"package-access": "IN-GAME AVAILABILITY",
"access": "GENERAL AVAILABILITY",
"control": "CONTROLS",
"signals": "SIGNALLING",
"addons": "ADDITIONAL PROGRAMS",
"blockades": "BLOCK SIGNALLING",
"status": "ONLINE STATUS"
},
"endingStatus": "ENDS SOON", "endingStatus": "ENDS SOON",
"afkStatus": "AFK", "afkStatus": "AFK",
"noSpaceStatus": "NO SPACE", "noSpaceStatus": "NO SPACE",
"unavailableStatus": "UNAVAILABLE", "unavailableStatus": "UNAVAILABLE",
"title": "STATION FILTER", "title": "STATION FILTERS",
"default": "DEFAULT", "default": "IN-GAME",
"not-default": "OTHER", "not-default": "ADDITIONAL",
"real": "REAL", "real": "REAL",
"fictional": "FICTIONAL", "fictional": "FICTIONAL",
"unavailable": "UNSUPPORTED", "unavailable": "UNSUPPORTED",
@@ -143,12 +165,22 @@
"abandoned": "ABANDONED", "abandoned": "ABANDONED",
"SPK": "SPK", "SPK": "SPK",
"SPK-R": "SPK + MANUAL",
"SPK-M": "SPK + MECH.",
"SCS": "SCS", "SCS": "SCS",
"SCS-R": "SCS + MANUAL",
"SCS-M": "SCS + MECH.",
"SPE": "SPE", "SPE": "SPE",
"manual": "MANUAL", "manual": "MANUAL",
"mechanical": "MECHANICAL", "mechanical": "MECHANICAL",
"SUP": "SUP",
"SBL": "SBL", "SUP": "SUP (RASP-UZK)",
"noSUP": "WITHOUT SUP",
"SBL": "AUTOMATIC (SBL)",
"PBL": "SEMIAUTOMATIC (PBL)",
"modern": "MODERN", "modern": "MODERN",
"semaphores": "SEMAPHORES", "semaphores": "SEMAPHORES",
"mixed": "MIXED", "mixed": "MIXED",
@@ -169,7 +201,7 @@
"hour": "h", "hour": "h",
"no-limit": "NO LIMIT", "no-limit": "NO LIMIT",
"include-selected": "INCLUDE SELECTED", "include-selected": "INCLUDE SELECTED",
"save": "SAVE FILTERS", "save": "REMEMBER FILTERS",
"reset": "RESET FILTERS", "reset": "RESET FILTERS",
"close": "CLOSE FILTERS" "close": "CLOSE FILTERS"
}, },
@@ -181,9 +213,11 @@
"dispatcher-lvl": "Dispatcher\nlevel", "dispatcher-lvl": "Dispatcher\nlevel",
"routes": "Routes\ndouble / single", "routes": "Routes\ndouble / single",
"general": "General info", "general": "General info",
"users": "Drivers online", "user": "Drivers online",
"spawns": "Spawns online", "spawn": "Spawns online",
"timetables": "Active timetables", "timetableAll": "Active timetables",
"timetableConfirmed": "Confirmed timetables",
"timetableUnconfirmed": "Unconfirmed timetables",
"no-stations": "No stations to show here!", "no-stations": "No stations to show here!",
"scenery-search": "Search for scenery..." "scenery-search": "Search for scenery..."
}, },
@@ -254,10 +288,10 @@
"minutes": "{minutes} mins", "minutes": "{minutes} mins",
"hours": "{hours}h {minutes} mins", "hours": "{hours}h {minutes} mins",
"stock-info": "STOCK INFO", "stock-info": "EXTRA INFO",
"stock-length": "Length", "stock-length": "Length",
"stock-mass": "Mass", "stock-mass": "Mass",
"stock-max-speed": "Maximum registered speed", "stock-max-speed": "Max. speed",
"load-data": "Load further data...", "load-data": "Load further data...",
@@ -301,14 +335,15 @@
"history-btn": "View the dispatcher history", "history-btn": "View the dispatcher history",
"info-btn": "Return to the scenery view", "info-btn": "Return to the scenery view",
"authors-title": "Scenery author | Scenery authors", "authors-title": "Scenery author | Scenery authors",
"abbrev": "Station symbol:",
"lines-title": "Real lines", "lines-title": "Real lines",
"project-title": "Project name", "project-title": "Project name",
"one-way-routes": "One way routes", "one-way-routes": "One way routes",
"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": "Scenery timetables history", "option-timetables-history": "Timetables history",
"option-dispatchers-history": "Scenery dispatchers history", "option-dispatchers-history": "Dispatchers history",
"timetable-author-title": "Issued by", "timetable-author-title": "Issued by",
"timetable-author-unknown": "Author unknown", "timetable-author-unknown": "Author unknown",
@@ -323,7 +358,10 @@
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required", "req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
"history-list-empty": "No recorded scenery history!", "history-list-empty": "No recorded scenery history!",
"forum-topic": "Official {name} forum topic" "forum-topic": "Official {name} forum topic",
"pragotron-link": "Timetable pallet board (beta)",
"tablice-link": "Timetable summary board (by Thundo)"
}, },
"availability": { "availability": {
"title": "Availability", "title": "Availability",
@@ -338,7 +376,19 @@
"end": "Timetable terminates here", "end": "Timetable terminates here",
"terminated": "Timetable terminated", "terminated": "Timetable terminated",
"begins": "BEGINS HERE", "begins": "BEGINS HERE",
"terminates": "TERMINATES\nHERE" "terminates": "TERMINATES\nHERE",
"from": "FROM",
"to": "TO",
"desc-arriving": "The train is not here yet. It's going to come from: {prevStationName} (szlak {prevDepartureLine})",
"desc-online": "The train is at the station. It's going to leave to: {nextStationName} (szlak {nextArrivalLine})",
"desc-stopped": "The train is at the station and is stopped. It's going to leave towards: {nextStationName} (szlak {nextArrivalLine})",
"desc-next-arrival": "Leaves towards: {nextStationName} (szlak {nextArrivalLine})",
"desc-departed": "The train is at the station and it's been departed. Leaves towards: {nextStationName} (szlak {nextArrivalLine})",
"desc-departed-away": "The train has been departed to: {nextStationName} (szlak {nextArrivalLine})",
"desc-end": "The train terminates here",
"desc-terminated": "The train has been terminated"
}, },
"history": { "history": {
"title": "TIMETABLE JOURNAL", "title": "TIMETABLE JOURNAL",
+71 -20
View File
@@ -1,7 +1,9 @@
{ {
"general": { "general": {
"and": " oraz ", "and": " oraz ",
"refresh": "ODŚWIEŻ" "refresh": "ODŚWIEŻ",
"TWR": "Towar niebezpieczny wysokiego ryzyka",
"SKR": "Przekroczona skrajnia"
}, },
"app": { "app": {
"sceneries": "SCENERIE", "sceneries": "SCENERIE",
@@ -15,7 +17,9 @@
"migration-confirm": "Przyjąłem!", "migration-confirm": "Przyjąłem!",
"offline": "Aplikacja w trybie offline!" "offline": "Aplikacja w trybie offline!"
}, },
"footer": {
"discord": "Serwer Discord Stacjownika"
},
"update": { "update": {
"title": "Nowa wersja Stacjownika jest dostępna!", "title": "Nowa wersja Stacjownika jest dostępna!",
"paragraph1": "Miłego korzystania z aplikacji i niech S2 będzie z wami!", "paragraph1": "Miłego korzystania z aplikacji i niech S2 będzie z wami!",
@@ -23,7 +27,6 @@
"confirm-button": "ZAKTUALIZUJ", "confirm-button": "ZAKTUALIZUJ",
"later-button": "PÓŹNIEJ" "later-button": "PÓŹNIEJ"
}, },
"data-status": { "data-status": {
"S1-offline": "<b>Sygnał S1</b> <br> Aplikacja działa w trybie offline!", "S1-offline": "<b>Sygnał S1</b> <br> Aplikacja działa w trybie offline!",
"S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z API Stacjownika!", "S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z API Stacjownika!",
@@ -96,7 +99,8 @@
"search-dispatcher": "Nick dyżurnego", "search-dispatcher": "Nick dyżurnego",
"search-station": "Nazwa scenerii", "search-station": "Nazwa scenerii",
"search-author": "Nick autora rozkładu jazdy", "search-author": "Nick autora rozkładu jazdy",
"search-date": "Data rozkładu jazdy (czas polski)", "search-timetables-date": "Data rozkładu jazdy (czas polski)",
"search-dispatchers-date": "Data służby (czas polski)",
"sort-distance": "kilometraż", "sort-distance": "kilometraż",
"sort-total-stops": "stacje", "sort-total-stops": "stacje",
@@ -114,13 +118,16 @@
"sort-delay": "opóźnienie", "sort-delay": "opóźnienie",
"sort-comments": "uwagi ekspl.", "sort-comments": "uwagi ekspl.",
"filter-comments": "UWAGI EKSPLOATACYJNE", "filter-withComments": "UWAGI EKSPLOATACYJNE",
"filter-twr": "TWR", "filter-noComments": "BEZ UWAG",
"filter-skr": "PRZEKR. SKRAJNIA", "filter-twr": "WYS. RYZYKA",
"filter-skr": "SKRAJNIA",
"filter-common": "ZWYKŁE",
"filter-passenger": "PASAŻERSKIE", "filter-passenger": "PASAŻERSKIE",
"filter-freight": "TOWAROWE", "filter-freight": "TOWAROWE",
"filter-other": "INNE", "filter-other": "INNE",
"filter-noTimetable": "BEZ RJ", "filter-noTimetable": "BEZ RJ",
"filter-withTimetable": "ROZKŁAD JAZDY",
"filter-reset": "ZRESETUJ FILTRY", "filter-reset": "ZRESETUJ FILTRY",
"filter-clear": "WYŁĄCZ FILTRY", "filter-clear": "WYŁĄCZ FILTRY",
@@ -131,6 +138,19 @@
"filter-active": "AKTYWNE" "filter-active": "AKTYWNE"
}, },
"filters": { "filters": {
"desc": " &bull; Kliknięcie: zaznaczenie / odznaczenie filtru <br /> &bull; Podwójne kliknięcie: odznaczenie reszty filtrów z <b class='text--primary'>grupy</b> <br /> &bull; <span style='color: coral'>RESET</span>: zresetowanie filtrów z <b class='text--primary'>grupy</b>",
"sections": {
"reality": "FIKCYJNOŚĆ SCENERII",
"package-access": "DOSTĘPNOŚĆ W PACZCE",
"access": "DOSTĘPNOŚĆ OGÓLNA",
"control": "TYP STEROWANIA",
"signals": "TYP SYGNALIZACJI",
"addons": "DODATKOWE PROGRAMY",
"blockades": "BLOKADY LINIOWE",
"status": "STATUS ONLINE"
},
"endingStatus": "KOŃCZY", "endingStatus": "KOŃCZY",
"afkStatus": "Z/W", "afkStatus": "Z/W",
"noSpaceStatus": "BRAK MIEJSCA", "noSpaceStatus": "BRAK MIEJSCA",
@@ -146,18 +166,29 @@
"abandoned": "WYCOFANA", "abandoned": "WYCOFANA",
"SPK": "SPK", "SPK": "SPK",
"SPK-R": "SPK + RĘCZNE",
"SPK-M": "SPK + MECH.",
"SCS": "SCS", "SCS": "SCS",
"SCS-R": "SCS + RĘCZNE",
"SCS-M": "SCS + MECH.",
"SPE": "SPE", "SPE": "SPE",
"manual": "RĘCZNE", "manual": "RĘCZNE",
"SUP": "SUP",
"SBL": "SBL", "SUP": "SUP (RASP-UZK)",
"noSUP": "BEZ SUP",
"SBL": "SAMOCZYNNA",
"PBL": "PÓŁSAMOCZYNNA",
"mechanical": "MECHANICZNE", "mechanical": "MECHANICZNE",
"modern": "WSPÓŁCZESNA", "modern": "WSPÓŁCZESNA",
"semaphores": "KSZTAŁTOWA", "semaphores": "KSZTAŁTOWA",
"mixed": "MIESZANA", "mixed": "MIESZANA",
"historical": "HISTORYCZNA", "historical": "HISTORYCZNA",
"free": "WOLNA", "free": "WOLNA",
"occupied": "ZAJĘTA", "occupied": "ZAJĘTA",
"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",
@@ -166,27 +197,31 @@
"routes-2t-cat": "SZLAKI DWUTOROWE ZELEKTR. (MINIMUM)", "routes-2t-cat": "SZLAKI DWUTOROWE ZELEKTR. (MINIMUM)",
"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)",
"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.",
"no-limit": "BEZ LIMITU", "no-limit": "BEZ LIMITU",
"include-selected": "POKAŻ ZAZNACZONE", "include-selected": "POKAŻ ZAZNACZONE",
"save": "ZAPISZ FILTRY", "save": "ZAPAMIĘTAJ FILTRY",
"reset": "RESETUJ FILTRY", "reset": "RESETUJ FILTRY",
"close": "ZAMKNIJ FILTRY" "close": "ZAMKNIJ FILTRY"
}, },
"sceneries": { "sceneries": {
"station": "Stacja", "station": "Stacja",
"abbr": "Skrót\nposterunku",
"min-lvl": "Min. poziom\ndyżurnego", "min-lvl": "Min. poziom\ndyżurnego",
"status": "Status", "status": "Status",
"dispatcher": "Dyżurny", "dispatcher": "Dyżurny",
"dispatcher-lvl": "Poziom\ndyżurnego", "dispatcher-lvl": "Poziom\ndyżurnego",
"routes": "Szlaki\n2tor / 1tor", "routes": "Szlaki\n2tor / 1tor",
"general": "Informacje\nogólne", "general": "Informacje\nogólne",
"users": "Maszyniści online", "user": "Maszyniści online",
"spawns": "Otwarte spawny", "spawn": "Otwarte spawny",
"timetables": "Aktywne rozkłady jazdy", "timetableAll": "Aktywne rozkłady jazdy",
"timetableConfirmed": "Zatwierdzone rozkłady jazdy",
"timetableUnconfirmed": "Niezatwierdzone rozkłady jazdy",
"no-stations": "Brak stacji do wyświetlenia!", "no-stations": "Brak stacji do wyświetlenia!",
"scenery-search": "Wyszukaj scenerię..." "scenery-search": "Wyszukaj scenerię..."
}, },
@@ -258,10 +293,10 @@
"timetable-fulfilled": "WYPEŁNIONY", "timetable-fulfilled": "WYPEŁNIONY",
"timetable-abandoned": "PORZUCONY", "timetable-abandoned": "PORZUCONY",
"stock-info": "INFORMACJE O SKŁADZIE", "stock-info": "DODATKOWE INFORMACJE",
"stock-length": "Długość", "stock-length": "Długość",
"stock-mass": "Masa", "stock-mass": "Masa",
"stock-max-speed": "Maks. zarejestrowana prędkość", "stock-max-speed": "Prędkość maks.",
"load-data": "Pobierz dalszą historię...", "load-data": "Pobierz dalszą historię...",
@@ -303,16 +338,17 @@
"no-scenery": "Ups! Ta sceneria nie istnieje!", "no-scenery": "Ups! Ta sceneria nie istnieje!",
"return-btn": "Wróć na stronę główną", "return-btn": "Wróć na stronę główną",
"history-btn": "Przejdź do widoku historii dyżurnych ruchu", "history-btn": "Przejdź do widoku historii dyżurnych ruchu",
"info-btn": "Wróc do widoku scenerii", "info-btn": "Wróć do widoku scenerii",
"authors-title": "Autor scenerii | Autorzy scenerii", "authors-title": "Autor scenerii | Autorzy scenerii",
"abbrev": "Skrót posterunku:",
"lines-title": "Rzeczywiste linie", "lines-title": "Rzeczywiste linie",
"project-title": "Projekt", "project-title": "Projekt",
"one-way-routes": "Szlaki jednotorowe", "one-way-routes": "Szlaki jednotorowe",
"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 scenerii", "option-timetables-history": "Historia rozkładów",
"option-dispatchers-history": "Historia dyżurów scenerii", "option-dispatchers-history": "Historia dyżurów",
"timetable-author-title": "Wydany przez", "timetable-author-title": "Wydany przez",
"timetable-author-unknown": "Autor nieznany", "timetable-author-unknown": "Autor nieznany",
@@ -327,7 +363,10 @@
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego", "req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
"history-list-empty": "Brak historii dla tej scenerii!", "history-list-empty": "Brak historii dla tej scenerii!",
"forum-topic": "Oficjalny wątek scenerii {name}" "forum-topic": "Oficjalny wątek scenerii {name}",
"pragotron-link": "Paletowa tablica informacyjna (beta)",
"tablice-link": "Tablica informacyjna zbiorcza (autorstwa Thundo)"
}, },
"availability": { "availability": {
"title": "Dostępność", "title": "Dostępność",
@@ -342,7 +381,19 @@
"end": "Koniec rozkładu jazdy", "end": "Koniec rozkładu jazdy",
"terminated": "Rozkład jazdy zakończony", "terminated": "Rozkład jazdy zakończony",
"begins": "ROZPOCZYNA\nBIEG", "begins": "ROZPOCZYNA\nBIEG",
"terminates": "KOŃCZY BIEG" "terminates": "KOŃCZY BIEG",
"from": "Z",
"to": "DO",
"desc-arriving": "Pociągu nie ma jeszcze na tej scenerii. Przyjedzie z: {prevStationName} (szlak {prevDepartureLine})",
"desc-online": "Pociąg jest na tej scenerii. Odjedzie do: {nextStationName} (szlak {nextArrivalLine})",
"desc-stopped": "Pociąg jest na tej scenerii i odbywa postój. Odjedzie do: {nextStationName} (szlak {nextArrivalLine})",
"desc-next-arrival": "Odjeżdża do: {nextStationName} (szlak {nextArrivalLine})",
"desc-departed": "Pociąg jest na tej scenerii i został odprawiony. Odjeżdża do: {nextStationName} (szlak {nextArrivalLine})",
"desc-departed-away": "Pociąg został odprawiony i odjechał do: {nextStationName} (szlak {nextArrivalLine})",
"desc-end": "Pociąg kończy bieg",
"desc-terminated": "Pociąg skończył bieg"
}, },
"history": { "history": {
"title": "DZIENNIK ROZKŁADÓW JAZDY" "title": "DZIENNIK ROZKŁADÓW JAZDY"
+1 -10
View File
@@ -7,11 +7,11 @@ import plLang from './locales/pl.json';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import { registerSW } from 'virtual:pwa-register';
const i18n = createI18n({ const i18n = createI18n({
locale: 'pl', locale: 'pl',
legacy: false, legacy: false,
warnHtmlMessage: false,
fallbackLocale: 'pl', fallbackLocale: 'pl',
messages: { messages: {
en: enLang, en: enLang,
@@ -20,15 +20,6 @@ const i18n = createI18n({
enableLegacy: false, enableLegacy: false,
}); });
registerSW({
onRegistered(r) {
r &&
setInterval(() => {
r.update();
}, 60 * 60 * 1000);
},
});
const clickOutsideDirective: Directive = { const clickOutsideDirective: Directive = {
mounted(el, binding) { mounted(el, binding) {
el.clickOutsideEvent = (event: Event) => { el.clickOutsideEvent = (event: Event) => {
+5
View File
@@ -0,0 +1,5 @@
export const headIds = ['station', 'min-lvl', 'status', 'dispatcher', 'dispatcher-lvl', 'routes', 'general'] as const;
export const headIconsIds = ['user', 'spawn', 'timetableAll', 'timetableUnconfirmed', 'timetableConfirmed'] as const;
export type HeadIdsTypes = typeof headIds[number] | typeof headIconsIds[number];
+20 -8
View File
@@ -1,9 +1,21 @@
export const enum TrainFilterType { export enum TrainFilterSection {
comments = "comments", TRAIN_TYPE = 'TRAIN_TYPE',
twr = "twr", TIMETABLE_TYPE = 'TIMETABLE_TYPE',
skr = "skr", COMMENTS = 'COMMENTS',
passenger = "passenger", TIMETABLE = 'TIMETABLE',
freight = "freight", }
other = "other",
noTimetable = "noTimetable" export const enum TrainFilterType {
noComments = 'noComments',
withComments = 'withComments',
twr = 'twr',
skr = 'skr',
common = 'common',
passenger = 'passenger',
freight = 'freight',
other = 'other',
noTimetable = 'noTimetable',
withTimetable = 'withTimetable',
} }
+12 -6
View File
@@ -1,16 +1,22 @@
export default interface Filter { export default interface Filter {
[key: string]: (boolean | number | string), [key: string]: boolean | number | string;
default: boolean; default: boolean;
notDefault: boolean; notDefault: boolean;
real: boolean; real: boolean;
fictional: boolean; fictional: boolean;
"SPK": boolean; SPK: boolean;
"SCS": boolean; SCS: boolean;
"SPE": boolean; SPE: boolean;
"SUP": boolean; SUP: boolean;
noSUP: boolean;
ręczne: boolean; ręczne: boolean;
'ręczne+SPK': boolean;
'ręczne+SCS': boolean;
mechaniczne: boolean; mechaniczne: boolean;
"SBL": boolean; 'mechaniczne+SPK': boolean;
'mechaniczne+SCS': boolean;
SBL: boolean;
PBL: boolean;
współczesna: boolean; współczesna: boolean;
kształtowa: boolean; kształtowa: boolean;
historyczna: boolean; historyczna: boolean;
+39 -30
View File
@@ -1,32 +1,41 @@
import TrainStop from "./TrainStop"; import TrainStop from './TrainStop';
export default interface ScheduledTrain { export enum StopStatus {
trainId: string; 'arriving' = 'arriving',
trainNo: number; 'departed' = 'departed',
'departed-away' = 'departed-away',
driverName: string; 'online' = 'online',
driverId: number; 'stopped' = 'stopped',
currentStationName: string; 'terminated' = 'terminated',
currentStationHash: string; }
category: string;
stopInfo: TrainStop; export interface ScheduledTrain {
trainId: string;
terminatesAt: string; trainNo: number;
beginsAt: string;
driverName: string;
prevStationName: string; driverId: number;
nextStationName: string; currentStationName: string;
currentStationHash: string;
arrivingLine: string | null; category: string;
departureLine: string | null; stopInfo: TrainStop;
prevDepartureLine: string | null; terminatesAt: string;
nextArrivalLine: string | null; beginsAt: string;
signal: string; prevStationName: string;
connectedTrack: string; nextStationName: string;
stopLabel: string; arrivingLine: string | null;
stopStatus: string; departureLine: string | null;
stopStatusID: number;
prevDepartureLine: string | null;
nextArrivalLine: string | null;
signal: string;
connectedTrack: string;
stopLabel: string;
stopStatus: StopStatus;
stopStatusID: number;
} }
+4 -2
View File
@@ -1,5 +1,5 @@
import { Availability } from '../../store/storeTypes'; import { Availability } from './store/storeTypes';
import ScheduledTrain from './ScheduledTrain'; import {ScheduledTrain} from './ScheduledTrain';
import StationRoutes from './StationRoutes'; import StationRoutes from './StationRoutes';
export default interface Station { export default interface Station {
@@ -8,10 +8,12 @@ export default interface Station {
generalInfo?: { generalInfo?: {
name: string; name: string;
url: string; url: string;
abbr: string;
reqLevel: number; reqLevel: number;
// supportersOnly: boolean; // supportersOnly: boolean;
lines: string; lines: string;
project: string; project: string;
projectUrl?: string; projectUrl?: string;
@@ -5,6 +5,7 @@ export interface DispatcherHistory {
dispatcherId: number; dispatcherId: number;
dispatcherName: string; dispatcherName: string;
dispatcherLevel: number | null; dispatcherLevel: number | null;
dispatcherRate: number;
dispatcherIsSupporter: boolean; dispatcherIsSupporter: boolean;
isOnline: boolean; isOnline: boolean;
lastOnlineTimestamp: number; lastOnlineTimestamp: number;
@@ -1,5 +1,7 @@
export interface TimetableHistory { export interface TimetableHistory {
id: number; id: number;
createdAt: string;
updatedAt: string;
timetableId: number; timetableId: number;
trainNo: number; trainNo: number;
@@ -33,7 +35,11 @@ export interface TimetableHistory {
authorName?: string; authorName?: string;
authorId?: number; authorId?: number;
stopsString?: string;
stockString?: string; stockString?: string;
stockHistory: string[];
stockMass?: number; stockMass?: number;
stockLength?: number; stockLength?: number;
maxSpeed?: number; maxSpeed?: number;
@@ -41,10 +47,12 @@ export interface TimetableHistory {
hashesString?: string; hashesString?: string;
currentSceneryName?: string; currentSceneryName?: string;
currentSceneryHash?: string; currentSceneryHash?: string;
routeSceneries?: string;
} }
export interface SceneryTimetableHistory { export interface SceneryTimetableHistory {
sceneryTimetables: TimetableHistory[]; timetables: TimetableHistory[];
totalCount: number; // totalCount: number;
sceneryName: string; // sceneryName: string;
} }
+42 -38
View File
@@ -1,4 +1,44 @@
export default interface TrainAPIData { export interface TimetableStop {
stopName: string;
stopNameRAW: string;
stopType: string;
stopDistance: number;
pointId: number;
mainStop: boolean;
arrivalLine: string;
arrivalTimestamp: number;
arrivalRealTimestamp: number;
arrivalDelay: number;
departureLine: string;
departureTimestamp: number;
departureRealTimestamp: number;
departureDelay: number;
comments?: any;
beginsHere: boolean;
terminatesHere: boolean;
confirmed: boolean;
stopped: boolean;
stopTime: number;
}
export interface TrainTimetable {
timetableId: number;
category: string;
route: string;
stopList: TimetableStop[];
TWR: boolean;
SKR: boolean;
sceneries: string[];
}
export interface TrainAPIData {
trainNo: number; trainNo: number;
mass: number; mass: number;
@@ -24,41 +64,5 @@ export default interface TrainAPIData {
region: string; region: string;
isTimeout: boolean; isTimeout: boolean;
timetable?: { timetable?: TrainTimetable;
timetableId: number;
category: string;
route: string;
stopList: {
stopName: string;
stopNameRAW: string;
stopType: string;
stopDistance: number;
pointId: number;
mainStop: boolean;
arrivalLine: string;
arrivalTimestamp: number;
arrivalRealTimestamp: number;
arrivalDelay: number;
departureLine: string;
departureTimestamp: number;
departureRealTimestamp: number;
departureDelay: number;
comments?: any;
beginsHere: boolean;
terminatesHere: boolean;
confirmed: boolean;
stopped: boolean;
stopTime: number;
}[];
TWR: boolean;
SKR: boolean;
sceneries: string[];
};
} }
@@ -1,11 +1,11 @@
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { DataStatus } from '../scripts/enums/DataStatus'; import { DataStatus } from '../../enums/DataStatus';
import StationAPIData from '../scripts/interfaces/api/StationAPIData'; import StationAPIData from '../api/StationAPIData';
import TrainAPIData from '../scripts/interfaces/api/TrainAPIData'; import { TrainAPIData } from '../api/TrainAPIData';
import Station from '../scripts/interfaces/Station'; import Station from '../Station';
import Train from '../scripts/interfaces/Train'; import Train from '../Train';
import { DispatcherStatsAPIData } from '../scripts/interfaces/api/DispatcherStatsAPIData'; import { DispatcherStatsAPIData } from '../api/DispatcherStatsAPIData';
import { DriverStatsAPIData } from '../scripts/interfaces/api/DriverStatsAPIData'; import { DriverStatsAPIData } from '../api/DriverStatsAPIData';
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault'; export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
@@ -55,8 +55,19 @@ export interface APIData {
connectedSocketCount: number; connectedSocketCount: number;
} }
export interface StationRoutesInfo {
routeName: string;
isElectric: boolean;
isInternal: boolean;
isRouteSBL: boolean;
routeLength: number;
routeSpeed: number;
routeTracks: number;
}
export interface StationJSONData { export interface StationJSONData {
name: string; name: string;
abbr: string;
url: string; url: string;
lines: string; lines: string;
project: string; project: string;
@@ -69,7 +80,8 @@ export interface StationJSONData {
SUP: boolean; SUP: boolean;
routes: string; // routes: string;
routesInfo: StationRoutesInfo[];
checkpoints: string | null; checkpoints: string | null;
authors?: string; authors?: string;
+20 -10
View File
@@ -24,26 +24,36 @@ function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriv
const isFiltered = filters.every((f) => { const isFiltered = filters.every((f) => {
if (f.isActive) return true; if (f.isActive) return true;
if (!train.timetableData) return filters.find((filter) => filter.id == TrainFilterType.noTimetable)!.isActive;
switch (f.id) { switch (f.id) {
case TrainFilterType.comments: case TrainFilterType.noTimetable:
return !train.timetableData.followingStops.some((stop) => stop.comments); return train.timetableData;
case TrainFilterType.withTimetable:
return !train.timetableData;
case TrainFilterType.withComments:
return !train.timetableData?.followingStops.some((stop) => stop.comments);
case TrainFilterType.noComments:
return train.timetableData?.followingStops.some((stop) => stop.comments);
case TrainFilterType.twr: case TrainFilterType.twr:
return !train.timetableData.TWR; return !train.timetableData?.TWR;
case TrainFilterType.skr: case TrainFilterType.skr:
return !train.timetableData.SKR; return !train.timetableData?.SKR;
case TrainFilterType.common:
return train.timetableData?.SKR || train.timetableData?.TWR;
case TrainFilterType.passenger: case TrainFilterType.passenger:
return !/^[AMRE]\D{2}$/.test(train.timetableData.category); return !/^[AMRE]\D{2}$/.test(train.timetableData?.category || '');
case TrainFilterType.freight: case TrainFilterType.freight:
return !train.timetableData.category.startsWith('T'); return !train.timetableData?.category.startsWith('T');
case TrainFilterType.other: case TrainFilterType.other:
return !/^[PXZL]\D{2}$/.test(train.timetableData.category); return !/^[PXZL]\D{2}$/.test(train.timetableData?.category || '');
default: default:
return true; return true;
@@ -53,7 +63,7 @@ function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriv
return ( return (
(searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) && (searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) && (searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) &&
(!train.timetableData ? !train.online : true) && (!train.timetableData ? train.online : train.timetableData) &&
isFiltered isFiltered
); );
}); });
+3 -1
View File
@@ -1,5 +1,7 @@
export const URLs = { export const URLs = {
stacjownikAPI: stacjownikAPI:
import.meta.env.VITE_APP_API_DEV == 1 && !import.meta.env.PROD ? 'http://localhost:3000' : 'https://spythere.pl', import.meta.env.VITE_APP_API_DEV == 1 && !import.meta.env.PROD
? 'http://localhost:3001'
: 'https://spythere.pl',
stacjownikAPIDev: 'localhost:3000', stacjownikAPIDev: 'localhost:3000',
}; };
+10 -11
View File
@@ -1,4 +1,4 @@
import ScheduledTrain from '../interfaces/ScheduledTrain'; import { ScheduledTrain, StopStatus } from '../interfaces/ScheduledTrain';
import Train from '../interfaces/Train'; import Train from '../interfaces/Train';
import TrainStop from '../interfaces/TrainStop'; import TrainStop from '../interfaces/TrainStop';
@@ -74,32 +74,32 @@ export const parseSpawns = (spawnString: string) => {
export const getTimestamp = (date: string | null): number => (date ? new Date(date).getTime() : 0); export const getTimestamp = (date: string | null): number => (date ? new Date(date).getTime() : 0);
export const getTrainStopStatus = (stopInfo: TrainStop, currentStationName: string, stationName: string) => { export const getTrainStopStatus = (stopInfo: TrainStop, currentStationName: string, stationName: string) => {
let stopStatus = '', let stopStatus = StopStatus['arriving'],
stopLabel = '', stopLabel = '',
stopStatusID = -1; stopStatusID = -1;
if (stopInfo.terminatesHere && stopInfo.confirmed) { if (stopInfo.terminatesHere && stopInfo.confirmed) {
stopStatus = 'terminated'; stopStatus = StopStatus['terminated'];
stopLabel = 'Skończył bieg'; stopLabel = 'Skończył bieg';
stopStatusID = 5; stopStatusID = 5;
} else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == stationName) { } else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == stationName) {
stopStatus = 'departed'; stopStatus = StopStatus['departed'];
stopLabel = 'Odprawiony'; stopLabel = 'Odprawiony';
stopStatusID = 2; stopStatusID = 2;
} else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != stationName) { } else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != stationName) {
stopStatus = 'departed-away'; stopStatus = StopStatus['departed-away'];
stopLabel = 'Odjechał'; stopLabel = 'Odjechał';
stopStatusID = 4; stopStatusID = 4;
} else if (currentStationName == stationName && !stopInfo.stopped) { } else if (currentStationName == stationName && !stopInfo.stopped) {
stopStatus = 'online'; stopStatus = StopStatus['online'];
stopLabel = 'Na stacji'; stopLabel = 'Na stacji';
stopStatusID = 0; stopStatusID = 0;
} else if (currentStationName == stationName && stopInfo.stopped) { } else if (currentStationName == stationName && stopInfo.stopped) {
stopStatus = 'stopped'; stopStatus = StopStatus['stopped'];
stopLabel = 'Postój'; stopLabel = 'Postój';
stopStatusID = 1; stopStatusID = 1;
} else if (currentStationName != stationName) { } else if (currentStationName != stationName) {
stopStatus = 'arriving'; stopStatus = StopStatus['arriving'];
stopLabel = 'W drodze'; stopLabel = 'W drodze';
stopStatusID = 3; stopStatusID = 3;
} }
@@ -122,7 +122,7 @@ export function getScheduledTrain(train: Train, trainStopIndex: number, stationN
for (let i = trainStopIndex - 1; i >= 0; i--) { for (let i = trainStopIndex - 1; i >= 0; i--) {
if (/strong|podg/g.test(followingStops[i].stopName)) { if (/strong|podg/g.test(followingStops[i].stopName)) {
prevStationName = followingStops[i].stopNameRAW.replace(/,.*/g,""); prevStationName = followingStops[i].stopNameRAW.replace(/,.*/g, '');
break; break;
} }
@@ -130,7 +130,7 @@ export function getScheduledTrain(train: Train, trainStopIndex: number, stationN
for (let i = trainStopIndex + 1; i < followingStops.length; i++) { for (let i = trainStopIndex + 1; i < followingStops.length; i++) {
if (/strong|podg/g.test(followingStops[i].stopName)) { if (/strong|podg/g.test(followingStops[i].stopName)) {
nextStationName = followingStops[i].stopNameRAW.replace(/,.*/g,""); nextStationName = followingStops[i].stopNameRAW.replace(/,.*/g, '');
break; break;
} }
@@ -172,7 +172,6 @@ export function getScheduledTrain(train: Train, trainStopIndex: number, stationN
signal: train.signal, signal: train.signal,
connectedTrack: train.connectedTrack, connectedTrack: train.connectedTrack,
driverName: train.driverName, driverName: train.driverName,
driverId: train.driverId, driverId: train.driverId,
currentStationName: train.currentStationName, currentStationName: train.currentStationName,
+49
View File
@@ -0,0 +1,49 @@
import Filter from "../../scripts/interfaces/Filter";
export const filterInitStates: Filter = {
default: false,
notDefault: false,
real: false,
fictional: false,
SPK: false,
SCS: false,
SPE: false,
SUP: false,
noSUP: false,
ręczne: false,
'ręczne+SPK': false,
'ręczne+SCS': false,
mechaniczne: false,
'mechaniczne+SPK': false,
'mechaniczne+SCS': false,
współczesna: false,
kształtowa: false,
historyczna: false,
mieszana: false,
SBL: false,
PBL: false,
minLevel: 0,
maxLevel: 20,
minOneWayCatenary: 0,
minOneWay: 0,
minTwoWayCatenary: 0,
minTwoWay: 0,
'include-selected': false,
'no-1track': false,
'no-2track': false,
free: true,
occupied: false,
ending: false,
nonPublic: false,
unavailable: true,
abandoned: true,
afkStatus: false,
endingStatus: false,
noSpaceStatus: false,
unavailableStatus: false,
unsignedStatus: false,
authors: '',
onlineFromHours: 0,
};
+28 -237
View File
@@ -1,246 +1,29 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import inputData from '../data/options.json'; import inputData from '../data/options.json';
import Filter from '../scripts/interfaces/Filter';
import Station from '../scripts/interfaces/Station'; import Station from '../scripts/interfaces/Station';
import StorageManager from '../scripts/managers/storageManager'; import StorageManager from '../scripts/managers/storageManager';
import { useStore } from './store'; import { useStore } from './store';
import { filterInitStates } from './constants/initFilterStates';
const sortStations = (a: Station, b: Station, sorter: { index: number; dir: number }) => { import { filterStations, sortStations } from './utils/filterUtils';
switch (sorter.index) { import { HeadIdsTypes } from '../scripts/data/stationHeaderNames';
case 0:
return sorter.dir == 1 ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
case 1:
if ((a.generalInfo?.reqLevel || 0) > (b.generalInfo?.reqLevel || 0)) return sorter.dir;
if ((a.generalInfo?.reqLevel || 0) < (b.generalInfo?.reqLevel || 0)) return -sorter.dir;
break;
case 2:
if ((a.onlineInfo?.statusTimestamp || 0) > (b.onlineInfo?.statusTimestamp || 0)) return sorter.dir;
if ((a.onlineInfo?.statusTimestamp || 0) < (b.onlineInfo?.statusTimestamp || 0)) return -sorter.dir;
break;
case 3:
if ((a.onlineInfo?.dispatcherName.toLowerCase() || '') > (b.onlineInfo?.dispatcherName.toLowerCase() || ''))
return sorter.dir;
if ((a.onlineInfo?.dispatcherName.toLowerCase() || '') < (b.onlineInfo?.dispatcherName.toLowerCase() || ''))
return -sorter.dir;
break;
case 4:
if ((a.onlineInfo?.dispatcherExp || 0) > (b.onlineInfo?.dispatcherExp || 0)) return sorter.dir;
if ((a.onlineInfo?.dispatcherExp || 0) < (b.onlineInfo?.dispatcherExp || 0)) return -sorter.dir;
break;
case 7:
if ((a.onlineInfo?.currentUsers || 0) > (b.onlineInfo?.currentUsers || 0)) return sorter.dir;
if ((a.onlineInfo?.currentUsers || 0) < (b.onlineInfo?.currentUsers || 0)) return -sorter.dir;
if ((a.onlineInfo?.maxUsers || 0) > (b.onlineInfo?.maxUsers || 0)) return sorter.dir;
if ((a.onlineInfo?.maxUsers || 0) < (b.onlineInfo?.maxUsers || 0)) return -sorter.dir;
break;
case 8:
if ((a.onlineInfo?.spawns.length || 0) > (b.onlineInfo?.spawns.length || 0)) return sorter.dir;
if ((a.onlineInfo?.spawns.length || 0) < (b.onlineInfo?.spawns.length || 0)) return -sorter.dir;
break;
case 9:
if ((a.onlineInfo?.scheduledTrains?.length || 0) > (b.onlineInfo?.scheduledTrains?.length || 0))
return sorter.dir;
if ((a.onlineInfo?.scheduledTrains?.length || 0) < (b.onlineInfo?.scheduledTrains?.length || 0))
return -sorter.dir;
default:
break;
}
return a.name.localeCompare(b.name);
};
const filterStations = (station: Station, filters: Filter, isOffline = false) => {
const returnMode = false;
if ((station.generalInfo?.availability == 'nonPublic' || !station.generalInfo) && filters['nonPublic'])
return returnMode;
if (station.onlineInfo?.statusID == 'ending' && filters['ending']) return returnMode;
if (
station.onlineInfo &&
station.onlineInfo.statusTimestamp > 0 &&
filters['onlineFromHours'] < 8 &&
station.onlineInfo.statusTimestamp <= Date.now() + filters['onlineFromHours'] * 3600000
)
return returnMode;
if (filters['onlineFromHours'] > 0 && station.onlineInfo && station.onlineInfo.statusTimestamp <= 0)
return returnMode;
if (filters['onlineFromHours'] == 8 && station.onlineInfo?.statusID != 'no-limit') return returnMode;
if (station.onlineInfo?.statusID == 'ending' && filters['endingStatus']) return returnMode;
if (
(station.onlineInfo?.statusID == 'not-signed' || station.onlineInfo?.statusID == 'unavailable') &&
filters['unavailableStatus']
)
return returnMode;
if (station.onlineInfo?.statusID == 'brb' && filters['afkStatus']) return returnMode;
if (station.onlineInfo?.statusID == 'no-space' && filters['noSpaceStatus']) return returnMode;
if (station.onlineInfo && filters['occupied']) return returnMode;
if (!station.onlineInfo && filters['free']) return returnMode;
if (station.generalInfo?.availability == 'unavailable' && filters['unavailable'] && !station.onlineInfo)
return returnMode;
if (station.generalInfo) {
const routes = station.generalInfo.routes;
const availability = station.generalInfo.availability;
if (filters['abandoned'] && availability == 'abandoned' && !station.onlineInfo) return returnMode;
if (availability == 'default' && filters['default']) return returnMode;
if (
availability != 'default' &&
filters['notDefault'] &&
!(availability == 'abandoned' || availability == 'unavailable')
)
return returnMode;
if (filters['real'] && station.generalInfo.lines != '') return returnMode;
if (
filters['fictional'] &&
station.generalInfo.lines == '' &&
availability != 'abandoned' &&
availability != 'unavailable'
)
return returnMode;
if (
station.generalInfo.reqLevel +
(availability == 'nonPublic' || availability == 'unavailable' || availability == 'abandoned' ? 1 : 0) <
filters['minLevel']
)
return returnMode;
if (
station.generalInfo.reqLevel +
(availability == 'nonPublic' || availability == 'unavailable' || availability == 'abandoned' ? 1 : 0) >
filters['maxLevel']
)
return returnMode;
if (
filters['no-1track'] &&
(routes.oneWayCatenaryRouteNames.length != 0 || routes.oneWayNoCatenaryRouteNames.length != 0)
)
return returnMode;
if (
filters['no-2track'] &&
(routes.twoWayCatenaryRouteNames.length != 0 || routes.twoWayNoCatenaryRouteNames.length != 0)
)
return returnMode;
if (routes.oneWayCatenaryRouteNames.length < filters['minOneWayCatenary']) return returnMode;
if (routes.oneWayNoCatenaryRouteNames.length < filters['minOneWay']) return returnMode;
if (routes.twoWayCatenaryRouteNames.length < filters['minTwoWayCatenary']) return returnMode;
if (routes.twoWayNoCatenaryRouteNames.length < filters['minTwoWay']) return returnMode;
if (filters[station.generalInfo.controlType]) return returnMode;
if (filters[station.generalInfo.signalType]) return returnMode;
if (
filters['SPK'] &&
(station.generalInfo.controlType === 'SPK' || station.generalInfo.controlType.includes('+SPK'))
)
return returnMode;
if (
filters['SCS'] &&
(station.generalInfo.controlType === 'SCS' || station.generalInfo.controlType.includes('+SCS'))
)
return returnMode;
if (
filters['SPE'] &&
(station.generalInfo.controlType === 'SPE' || station.generalInfo.controlType.includes('+SPE'))
)
return returnMode;
if (filters['SUP'] && station.generalInfo.SUP) return returnMode;
if (
filters['SCS'] &&
filters['SPK'] &&
(station.generalInfo.controlType.includes('SPK') || station.generalInfo.controlType.includes('SCS'))
)
return returnMode;
if (filters['mechaniczne'] && station.generalInfo.controlType.includes('mechaniczne')) return returnMode;
if (filters['ręczne'] && station.generalInfo.controlType.includes('ręczne')) return returnMode;
if (filters['SBL'] && routes.sblRouteNames.length > 0) return returnMode;
if (
filters['authors'].length > 3 &&
!station.generalInfo.authors?.map((a) => a.toLocaleLowerCase()).includes(filters['authors'].toLocaleLowerCase())
)
return returnMode;
}
return true;
};
const filterInitStates: Filter = {
default: false,
notDefault: false,
real: false,
fictional: false,
SPK: false,
SCS: false,
SPE: false,
SUP: false,
ręczne: false,
mechaniczne: false,
współczesna: false,
kształtowa: false,
historyczna: false,
mieszana: false,
SBL: false,
minLevel: 0,
maxLevel: 20,
minOneWayCatenary: 0,
minOneWay: 0,
minTwoWayCatenary: 0,
minTwoWay: 0,
'include-selected': false,
'no-1track': false,
'no-2track': false,
free: true,
occupied: false,
ending: false,
nonPublic: false,
unavailable: true,
abandoned: true,
afkStatus: false,
endingStatus: false,
noSpaceStatus: false,
unavailableStatus: false,
unsignedStatus: false,
authors: '',
onlineFromHours: 0,
};
export const useStationFiltersStore = defineStore('stationFiltersStore', { export const useStationFiltersStore = defineStore('stationFiltersStore', {
state() { state() {
return { return {
inputs: inputData, inputs: inputData,
filters: { ...filterInitStates }, filters: { ...filterInitStates },
sorterActive: { index: 0, dir: 1 }, sorterActive: { headerName: 'station' as HeadIdsTypes, dir: 1 },
store: useStore(), store: useStore(),
lastClickedFilterId: '',
}; };
}, },
getters: {
areFiltersAtDefault(state) {
return Object.keys(state.filters).every((f) => state.filters[f] === filterInitStates[f]);
},
},
actions: { actions: {
getFilteredStationList(stationList: Station[], region: string): Station[] { getFilteredStationList(stationList: Station[], region: string): Station[] {
return stationList return stationList
@@ -251,7 +34,7 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
return station; return station;
}) })
.filter((station) => filterStations(station, this.filters, this.store.isOffline)) .filter((station) => filterStations(station, this.filters))
.sort((a, b) => sortStations(a, b, this.sorterActive)); .sort((a, b) => sortStations(a, b, this.sorterActive));
}, },
@@ -259,10 +42,10 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
if (!StorageManager.isRegistered('options_saved')) return; if (!StorageManager.isRegistered('options_saved')) return;
this.inputs.options.forEach((option) => { this.inputs.options.forEach((option) => {
if (!StorageManager.isRegistered(option.id)) return; if (!StorageManager.isRegistered(option.name)) return;
const savedValue = StorageManager.getBooleanValue(option.id); const savedValue = StorageManager.getBooleanValue(option.name);
this.filters[option.id] = savedValue; this.filters[option.name] = savedValue;
option.value = !savedValue; option.value = !savedValue;
}); });
@@ -295,14 +78,22 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
}); });
}, },
changeSorter(index: number) { resetSectionOptions(section: string) {
if (index > 4 && index < 7) return; this.inputs.options.forEach((option) => {
if (option.section != section) return;
if (index == this.sorterActive.index) this.sorterActive.dir = -1 * this.sorterActive.dir; option.value = option.defaultValue;
this.filters[option.id] = !option.defaultValue;
StorageManager.setBooleanValue(option.name, !option.defaultValue);
});
},
changeSorter(headerName: HeadIdsTypes) {
if (headerName == this.sorterActive.headerName) this.sorterActive.dir = -1 * this.sorterActive.dir;
else this.sorterActive.dir = 1; else this.sorterActive.dir = 1;
this.sorterActive.index = index; this.sorterActive.headerName = headerName;
}, },
}, },
}); });
+31 -49
View File
@@ -3,7 +3,7 @@ import { defineStore } from 'pinia';
import { io } from 'socket.io-client'; import { io } from 'socket.io-client';
import { DataStatus } from '../scripts/enums/DataStatus'; import { DataStatus } from '../scripts/enums/DataStatus';
import StationAPIData from '../scripts/interfaces/api/StationAPIData'; import StationAPIData from '../scripts/interfaces/api/StationAPIData';
import ScheduledTrain from '../scripts/interfaces/ScheduledTrain'; import { ScheduledTrain } from '../scripts/interfaces/ScheduledTrain';
import Station from '../scripts/interfaces/Station'; import Station from '../scripts/interfaces/Station';
import StationRoutes from '../scripts/interfaces/StationRoutes'; import StationRoutes from '../scripts/interfaces/StationRoutes';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
@@ -15,7 +15,7 @@ import {
getScheduledTrain, getScheduledTrain,
parseSpawns, parseSpawns,
} from '../scripts/utils/storeUtils'; } from '../scripts/utils/storeUtils';
import { APIData, StationJSONData, StoreState } from './storeTypes'; import { APIData, StationJSONData, StoreState } from '../scripts/interfaces/store/storeTypes';
export const useStore = defineStore('store', { export const useStore = defineStore('store', {
state: () => state: () =>
@@ -303,57 +303,39 @@ export const useStore = defineStore('store', {
...scenery, ...scenery,
authors: scenery.authors?.split(',').map((a) => a.trim()), authors: scenery.authors?.split(',').map((a) => a.trim()),
routes: routes:
scenery.routes scenery.routesInfo.reduce(
?.split(';') (acc, route) => {
.filter((routeString) => routeString) const propName: keyof StationRoutes = `${route.routeTracks == 2 ? 'twoWay' : 'oneWay'}${
.reduce( route.isElectric ? '' : 'No'
(acc, routeString) => { }CatenaryRouteNames`;
const specs1 = routeString.split('_')[0];
const isInternal = specs1.startsWith('!');
const name = isInternal ? specs1.replace('!', '') : specs1;
const specs2 = routeString.split('_')[1].split(''); acc[route.routeTracks == 2 ? 'twoWay' : 'oneWay'].push({
const twoWay = specs2[0] == '2'; name: route.routeName,
const catenary = specs2[1] == 'E'; SBL: route.isRouteSBL,
const SBL = specs2[2] == 'S'; TWB: false,
const TWB = specs2[3] ? true : false; catenary: route.isElectric,
const speed = Number(routeString.split(':')[1]) || 0; isInternal: route.isInternal,
const length = Number(routeString.split(':')[2]) || 0; tracks: route.routeTracks,
length: route.routeLength,
speed: route.routeSpeed,
});
const propName = twoWay if (!route.isInternal) acc[propName].push(route.routeName);
? catenary
? 'twoWayCatenaryRouteNames'
: 'twoWayNoCatenaryRouteNames'
: catenary
? 'oneWayCatenaryRouteNames'
: 'oneWayNoCatenaryRouteNames';
acc[twoWay ? 'twoWay' : 'oneWay'].push({ if (route.isRouteSBL) acc['sblRouteNames'].push(route.routeName);
name,
SBL,
TWB,
catenary,
isInternal,
tracks: twoWay ? 2 : 1,
length,
speed,
});
if (!isInternal) acc[propName].push(name);
if (SBL) acc['sblRouteNames'].push(name); return acc;
},
return acc; {
}, oneWay: [],
{ twoWay: [],
oneWay: [], sblRouteNames: [],
twoWay: [], oneWayCatenaryRouteNames: [],
sblRouteNames: [], oneWayNoCatenaryRouteNames: [],
oneWayCatenaryRouteNames: [], twoWayCatenaryRouteNames: [],
oneWayNoCatenaryRouteNames: [], twoWayNoCatenaryRouteNames: [],
twoWayCatenaryRouteNames: [], } as StationRoutes
twoWayNoCatenaryRouteNames: [], ) || {},
} as StationRoutes
) || {},
checkpoints: scenery.checkpoints checkpoints: scenery.checkpoints
? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] })) ? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
: [], : [],
+156
View File
@@ -0,0 +1,156 @@
import { HeadIdsTypes } from '../../scripts/data/stationHeaderNames';
import Filter from '../../scripts/interfaces/Filter';
import Station from '../../scripts/interfaces/Station';
export const sortStations = (a: Station, b: Station, sorter: { headerName: HeadIdsTypes; dir: number }) => {
let diff = 0;
switch (sorter.headerName) {
case 'station':
return sorter.dir == 1 ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
case 'min-lvl':
diff = (a.generalInfo?.reqLevel || 0) - (b.generalInfo?.reqLevel || 0);
break;
case 'status':
diff = (a.onlineInfo?.statusTimestamp || 0) - (b.onlineInfo?.statusTimestamp || 0);
break;
case 'dispatcher':
if ((a.onlineInfo?.dispatcherName.toLowerCase() || '') > (b.onlineInfo?.dispatcherName.toLowerCase() || ''))
return sorter.dir;
if ((a.onlineInfo?.dispatcherName.toLowerCase() || '') < (b.onlineInfo?.dispatcherName.toLowerCase() || ''))
return -sorter.dir;
break;
case 'dispatcher-lvl':
diff = (a.onlineInfo?.dispatcherExp || 0) - (b.onlineInfo?.dispatcherExp || 0);
break;
case 'user':
diff = (b.onlineInfo ? b.onlineInfo.currentUsers : -1) - (a.onlineInfo ? a.onlineInfo.currentUsers : -1);
break;
case 'spawn':
diff = (a.onlineInfo ? a.onlineInfo.spawns.length : -1) - (b.onlineInfo ? b.onlineInfo.spawns.length : -1);
break;
case 'timetableConfirmed':
diff =
(a.onlineInfo?.scheduledTrains
? a.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
: -1) -
(b.onlineInfo?.scheduledTrains
? b.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
: -1);
break;
case 'timetableUnconfirmed':
diff =
(a.onlineInfo?.scheduledTrains
? a.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
: -1) -
(b.onlineInfo?.scheduledTrains
? b.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
: -1);
break;
case 'timetableAll':
diff =
(a.onlineInfo?.scheduledTrains ? a.onlineInfo.scheduledTrains.length : -1) -
(b.onlineInfo?.scheduledTrains ? b.onlineInfo.scheduledTrains.length : -1);
break;
default:
break;
}
if (diff != 0) return Math.sign(diff) * sorter.dir;
return a.name.localeCompare(b.name);
};
export const filterStations = (station: Station, filters: Filter) => {
if (!station.onlineInfo && filters['free']) return false;
if (station.onlineInfo) {
const { statusID, statusTimestamp } = station.onlineInfo;
const isEnding = statusID == 'ending' && filters['endingStatus'];
const isNotSigned = (statusID == 'not-signed' || statusID == 'unavailable') && filters['unavailableStatus'];
const isAFK = statusID == 'brb' && filters['afkStatus'];
const isNoSpace = statusID == 'no-space' && filters['noSpaceStatus'];
const isOccupied = station.onlineInfo && filters['occupied'];
const isOnlineInBounds =
(filters['onlineFromHours'] < 8 &&
statusTimestamp > 0 &&
statusTimestamp <= Date.now() + filters['onlineFromHours'] * 3600000) ||
(filters['onlineFromHours'] > 0 && statusTimestamp <= 0) ||
(filters['onlineFromHours'] == 8 && statusID != 'no-limit');
if (isEnding || isOnlineInBounds || isNotSigned || isAFK || isNoSpace || isOccupied) return false;
}
if ((station.generalInfo?.availability == 'nonPublic' || !station.generalInfo) && filters['nonPublic']) return false;
if (station.generalInfo) {
const { routes, availability, controlType, lines, reqLevel, signalType, SUP, authors } = station.generalInfo;
if (availability == 'unavailable' && filters['unavailable'] && !station.onlineInfo) return false;
if (availability == 'abandoned' && filters['abandoned'] && !station.onlineInfo) return false;
if (availability == 'default' && filters['default']) return false;
if (
availability != 'default' &&
filters['notDefault'] &&
!(availability == 'abandoned' || availability == 'unavailable')
)
return false;
if (filters['real'] && lines) return false;
if (filters['fictional'] && !lines) return false;
const otherAvailability =
availability == 'nonPublic' || availability == 'unavailable' || availability == 'abandoned';
if (reqLevel + (otherAvailability ? 1 : 0) < filters['minLevel']) return false;
if (reqLevel + (otherAvailability ? 1 : 0) > filters['maxLevel']) return false;
if (
filters['no-1track'] &&
(routes.oneWayCatenaryRouteNames.length != 0 || routes.oneWayNoCatenaryRouteNames.length != 0)
)
return false;
if (
filters['no-2track'] &&
(routes.twoWayCatenaryRouteNames.length != 0 || routes.twoWayNoCatenaryRouteNames.length != 0)
)
return false;
if (routes.oneWayCatenaryRouteNames.length < filters['minOneWayCatenary']) return false;
if (routes.oneWayNoCatenaryRouteNames.length < filters['minOneWay']) return false;
if (routes.twoWayCatenaryRouteNames.length < filters['minTwoWayCatenary']) return false;
if (routes.twoWayNoCatenaryRouteNames.length < filters['minTwoWay']) return false;
if (filters[controlType]) return false;
if (filters[signalType]) return false;
if (filters['SUP'] && SUP) return false;
if (filters['noSUP'] && !SUP) return false;
if (filters['SBL'] && routes.sblRouteNames.length > 0) return false;
if (filters['PBL'] && routes.sblRouteNames.length == 0) return false;
if (
filters['authors'].length > 3 &&
!authors?.map((a) => a.toLocaleLowerCase()).includes(filters['authors'].toLocaleLowerCase())
)
return false;
}
return true;
};
+24 -2
View File
@@ -4,12 +4,11 @@
display: inline-block; display: inline-block;
padding: 0; padding: 0;
background: #585858;
margin: 0.25em; margin: 0.25em;
span { span {
display: inline-block; display: inline-block;
background: #585858;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
} }
@@ -56,3 +55,26 @@
background-color: forestgreen; background-color: forestgreen;
} }
} }
.train-badge {
padding: 0.1em 0.2em;
border-radius: 0.2em;
font-weight: bold;
font-size: 0.9em;
&.twr {
background-color: var(--clr-twr);
box-shadow: 0 0 5px 1px var(--clr-twr);
color: black;
}
&.skr {
background-color: var(--clr-skr);
box-shadow: 0 0 5px 1px var(--clr-skr);
}
&.offline {
background-color: #be3728;
}
}
+13 -8
View File
@@ -21,17 +21,16 @@
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
overflow-x: hidden; overflow: hidden;
background: #202020da; background: #202020e8;
box-shadow: 0 0 15px 5px #303030; box-shadow: 0 0 15px 0 black;
border: 1px solid #202020e8;
width: 600px; width: 95%;
max-width: 700px;
@include smallScreen { max-height: 95vh;
width: 100%;
height: 80vh;
}
&-exit { &-exit {
position: absolute; position: absolute;
@@ -46,3 +45,9 @@
cursor: pointer; cursor: pointer;
} }
} }
@include smallScreen {
.card {
max-height: 85vh;
}
}
+5 -3
View File
@@ -67,7 +67,7 @@ h1.option-title {
box-shadow: 0 5px 10px 2px #0f0f0f; box-shadow: 0 5px 10px 2px #0f0f0f;
width: 97%; width: 97%;
max-width: 500px; max-width: 550px;
padding: 1em; padding: 1em;
z-index: 100; z-index: 100;
@@ -77,6 +77,7 @@ h1.option-title {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5em;
padding: 0.25em 0.25em 0 0; padding: 0.25em 0.25em 0 0;
} }
@@ -84,17 +85,18 @@ h1.option-title {
.options_filters { .options_filters {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5em;
margin: 0.5em 0 0 0; margin: 0.5em 0 0 0;
} }
.sort-option, .sort-option,
.filter-option { .filter-option {
margin: 0.25em 0.25em 0.25em 0; padding: 0.25em 0.5em;
} }
.sort-option[data-selected='true'] { .sort-option[data-selected='true'] {
color: $accentCol; color: $accentCol;
font-weight: bold;
} }
.filter-option { .filter-option {
+1 -18
View File
@@ -45,6 +45,7 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: 'Quicksand', sans-serif; font-family: 'Quicksand', sans-serif;
font-weight: 500;
overflow-y: scroll; overflow-y: scroll;
&.no-scroll { &.no-scroll {
@@ -81,22 +82,9 @@ body {
transition: opacity 0.3s; transition: opacity 0.3s;
padding: 0.25em; padding: 0.25em;
// @include smallScreen() {
// right: 0;
// left: 0;
// &::after {
// left: 75%;
// }
// }
} }
&:hover > .content { &:hover > .content {
// @include smallScreen() {
// display: none;
// }
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
} }
@@ -105,7 +93,6 @@ body {
button, button,
input, input,
select { select {
// font-family: "Open Sans", sans-serif;
border: none; border: none;
font-family: 'Quicksand', sans-serif; font-family: 'Quicksand', sans-serif;
font-size: 1em; font-size: 1em;
@@ -215,10 +202,6 @@ button {
pointer-events: none; pointer-events: none;
opacity: 0.85; opacity: 0.85;
} }
&[data-inactive='true'] {
opacity: 0.55;
}
} }
button.btn--filled { button.btn--filled {
+2 -1
View File
@@ -1,6 +1,7 @@
import { TrainFilterType } from "../../scripts/enums/TrainFilterType"; import { TrainFilterSection, TrainFilterType } from '../../scripts/enums/TrainFilterType';
export interface TrainFilter { export interface TrainFilter {
id: TrainFilterType; id: TrainFilterType;
section: TrainFilterSection;
isActive: boolean; isActive: boolean;
} }
+1
View File
@@ -10,6 +10,7 @@
:sorter-option-ids="['timestampFrom', 'duration']" :sorter-option-ids="['timestampFrom', 'duration']"
:data-status="dataStatus" :data-status="dataStatus"
:current-options-active="currentOptionsActive" :current-options-active="currentOptionsActive"
optionsType="dispatchers"
/> />
<div class="list_wrapper" @scroll="handleScroll"> <div class="list_wrapper" @scroll="handleScroll">
+1
View File
@@ -11,6 +11,7 @@
:filters="journalTimetableFilters" :filters="journalTimetableFilters"
:currentOptionsActive="currentOptionsActive" :currentOptionsActive="currentOptionsActive"
:data-status="dataStatus" :data-status="dataStatus"
optionsType="timetables"
/> />
<JournalStats /> <JournalStats />
-11
View File
@@ -52,17 +52,6 @@ export default defineComponent({
mounted() { mounted() {
this.filterStore.setupFilters(); this.filterStore.setupFilters();
// this.filterStore.inputs.options.forEach((option) => {
// const value = StorageManager.getBooleanValue(option.name);
// option.value = value;
// this.filterStore.changeFilterValue({ name: option.name, value: value });
// });
// this.filterStore.inputs.sliders.forEach((slider) => {
// const value = StorageManager.getNumericValue(slider.name);
// slider.value = value;
// this.filterStore.changeFilterValue({ name: slider.name, value: value });
// });
}, },
}); });
</script> </script>
+2 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
plugins: [ plugins: [
vue(), vue(),
VitePWA({ VitePWA({
registerType: 'prompt', registerType: 'autoUpdate',
workbox: { workbox: {
globPatterns: ['**/*.{js,css,html,png,svg,jpg}'], globPatterns: ['**/*.{js,css,html,png,svg,jpg}'],
@@ -52,3 +52,4 @@ export default defineConfig({
}); });