mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 13:28:11 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbc9785341 | |||
| 9fd02c2336 | |||
| c031dd55c1 | |||
| b0870699a4 | |||
| a8da634b0e | |||
| 8920b1e5e8 | |||
| 4fa1c05831 | |||
| ecef2d5ee4 | |||
| 1749871d08 | |||
| 5545616706 | |||
| b35bb03868 | |||
| b9521918cb | |||
| 7ad17fc2c5 | |||
| a80144cb1c | |||
| 1227cdb94a | |||
| b33594fd6f | |||
| 9f8656e590 | |||
| 81cd165fe7 | |||
| 41e4b45599 | |||
| 462dd7dd7a | |||
| 9837ae97e1 | |||
| a818cd980b | |||
| 573ebc233b | |||
| aae47c6abd | |||
| 24c9b62162 | |||
| 481d43b6d8 | |||
| 4969a433cc | |||
| 8a2b453dc6 | |||
| 86d178ef56 | |||
| 7769477508 | |||
| 551b60c733 | |||
| 80a5b56785 | |||
| 6bd62f13a1 | |||
| 42591f6e76 | |||
| 4ca0f09e75 | |||
| 02c3629c00 | |||
| 9c4c806f0e | |||
| 58d6a97762 | |||
| cd71c78eb4 | |||
| 300e70dcfe | |||
| 09b31f7914 | |||
| c29c3c6abe | |||
| 916f6070ac | |||
| a74ab6eb2a | |||
| 985c699ced | |||
| 7e0e9146a5 | |||
| 30a0f05922 | |||
| a30e04ca96 | |||
| 1852d3e234 | |||
| a17bf6c03f | |||
| 766b08bc15 | |||
| cd1a4fa734 | |||
| acbe761068 | |||
| 44f6cf4232 |
@@ -0,0 +1,17 @@
|
|||||||
|
name: Deploy to Firebase Hosting on merge
|
||||||
|
'on':
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
build_and_deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: yarn && yarn build
|
||||||
|
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||||
|
with:
|
||||||
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_STACJOWNIK_TD2 }}'
|
||||||
|
channelId: live
|
||||||
|
projectId: stacjownik-td2
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.26.0",
|
"version": "1.27.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -6,13 +6,6 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Tooltip />
|
<Tooltip />
|
||||||
|
|
||||||
<transition name="modal-anim">
|
|
||||||
<keep-alive>
|
|
||||||
<TrainModal />
|
|
||||||
</keep-alive>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<AppHeader :current-lang="currentLang" @change-lang="changeLang" />
|
<AppHeader :current-lang="currentLang" @change-lang="changeLang" />
|
||||||
|
|
||||||
<main class="app_main">
|
<main class="app_main">
|
||||||
@@ -54,7 +47,6 @@ import { useTooltipStore } from './store/tooltipStore';
|
|||||||
import Clock from './components/App/Clock.vue';
|
import Clock from './components/App/Clock.vue';
|
||||||
import StatusIndicator from './components/App/StatusIndicator.vue';
|
import StatusIndicator from './components/App/StatusIndicator.vue';
|
||||||
import AppHeader from './components/App/AppHeader.vue';
|
import AppHeader from './components/App/AppHeader.vue';
|
||||||
import TrainModal from './components/TrainsView/TrainModal.vue';
|
|
||||||
import Tooltip from './components/Tooltip/Tooltip.vue';
|
import Tooltip from './components/Tooltip/Tooltip.vue';
|
||||||
import UpdateCard from './components/App/UpdateCard.vue';
|
import UpdateCard from './components/App/UpdateCard.vue';
|
||||||
|
|
||||||
@@ -67,7 +59,6 @@ export default defineComponent({
|
|||||||
Clock,
|
Clock,
|
||||||
StatusIndicator,
|
StatusIndicator,
|
||||||
AppHeader,
|
AppHeader,
|
||||||
TrainModal,
|
|
||||||
UpdateCard,
|
UpdateCard,
|
||||||
Tooltip
|
Tooltip
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
width: 6em;
|
width: 6em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
margin: 0.5em 0;
|
|
||||||
|
|
||||||
.bar-fg,
|
.bar-fg,
|
||||||
.bar-bg {
|
.bar-bg {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
<div class="stock-list">
|
<div class="stock-list">
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="({ vehicleName, vehicleCargo, images, imagesFallbacks }, i) in thumbnailNames"
|
v-for="(
|
||||||
|
{ vehicleName, vehicleCargo, images, imagesFallbacks, vehicleString }, i
|
||||||
|
) in thumbnailNames"
|
||||||
:key="i"
|
:key="i"
|
||||||
>
|
>
|
||||||
<div class="stock-text">
|
<div class="stock-text">
|
||||||
@@ -13,7 +15,7 @@
|
|||||||
<span>
|
<span>
|
||||||
<VehicleThumbnail
|
<VehicleThumbnail
|
||||||
v-for="(thumbnailImage, imageIndex) in images"
|
v-for="(thumbnailImage, imageIndex) in images"
|
||||||
:vehicle-name="vehicleName"
|
:vehicle-name="vehicleString"
|
||||||
:img-name="thumbnailImage"
|
:img-name="thumbnailImage"
|
||||||
:fallback-name="imagesFallbacks[imageIndex]"
|
:fallback-name="imagesFallbacks[imageIndex]"
|
||||||
/>
|
/>
|
||||||
@@ -63,7 +65,8 @@ export default defineComponent({
|
|||||||
images: [] as string[],
|
images: [] as string[],
|
||||||
imagesFallbacks: [] as string[],
|
imagesFallbacks: [] as string[],
|
||||||
vehicleName,
|
vehicleName,
|
||||||
vehicleCargo
|
vehicleCargo,
|
||||||
|
vehicleString
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generowanie członów EN57
|
// Generowanie członów EN57
|
||||||
|
|||||||
@@ -5,10 +5,9 @@
|
|||||||
:src="`https://static.spythere.eu/thumbnails/v2/${imgName}.png`"
|
:src="`https://static.spythere.eu/thumbnails/v2/${imgName}.png`"
|
||||||
height="60"
|
height="60"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
:data-mouseover="vehicleName"
|
data-tooltip-type="VehiclePreviewTooltip"
|
||||||
:data-tooltip-content="vehicleName"
|
:data-tooltip-content="vehicleName"
|
||||||
:data-load-status="imgStatus"
|
:data-load-status="imgStatus"
|
||||||
data-tooltip-type="VehiclePreviewTooltip"
|
|
||||||
@error="onImageError"
|
@error="onImageError"
|
||||||
@load="onImageLoad"
|
@load="onImageLoad"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="daily-stats">
|
<section class="daily-stats">
|
||||||
<span :data-active="statsStatus">
|
<span :data-active="statsStatus">
|
||||||
<span class="stats-list">
|
|
||||||
<h3>
|
<h3>
|
||||||
{{ $t('journal.daily-stats.title') }}
|
{{ $t('journal.daily-stats.title') }}
|
||||||
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
||||||
@@ -22,8 +21,8 @@
|
|||||||
</b>
|
</b>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div v-if="stats.totalTimetables">
|
<ul class="stats-list">
|
||||||
•
|
<li v-if="stats.totalTimetables">
|
||||||
<i18n-t keypath="journal.daily-stats.total">
|
<i18n-t keypath="journal.daily-stats.total">
|
||||||
<template #count>
|
<template #count>
|
||||||
<b class="text--primary">
|
<b class="text--primary">
|
||||||
@@ -36,10 +35,9 @@
|
|||||||
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
<div v-if="stats.maxTimetable">
|
<li v-if="stats.maxTimetable">
|
||||||
•
|
|
||||||
<i18n-t keypath="journal.daily-stats.longest">
|
<i18n-t keypath="journal.daily-stats.longest">
|
||||||
<template #id>
|
<template #id>
|
||||||
<router-link :to="`/journal/timetables?search-train=%23${stats.maxTimetable.id}`">
|
<router-link :to="`/journal/timetables?search-train=%23${stats.maxTimetable.id}`">
|
||||||
@@ -60,10 +58,9 @@
|
|||||||
<b class="text--primary">{{ stats.maxTimetable.routeDistance }} km</b>
|
<b class="text--primary">{{ stats.maxTimetable.routeDistance }} km</b>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
<div v-if="topDispatchers.length == 1">
|
<li v-if="topDispatchers.length == 1">
|
||||||
•
|
|
||||||
<i18n-t keypath="journal.daily-stats.most-active-dr">
|
<i18n-t keypath="journal.daily-stats.most-active-dr">
|
||||||
<template #dispatcher>
|
<template #dispatcher>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -79,10 +76,9 @@
|
|||||||
</b>
|
</b>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
<div v-if="topDispatchers.length > 1">
|
<li v-if="topDispatchers.length > 1">
|
||||||
•
|
|
||||||
<i18n-t keypath="journal.daily-stats.most-active-dr-many">
|
<i18n-t keypath="journal.daily-stats.most-active-dr-many">
|
||||||
<template #dispatchers>
|
<template #dispatchers>
|
||||||
<span v-for="(disp, i) in topDispatchers" :key="i">
|
<span v-for="(disp, i) in topDispatchers" :key="i">
|
||||||
@@ -103,10 +99,9 @@
|
|||||||
</b>
|
</b>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
<div v-if="stats.longestDuties.length > 0">
|
<li v-if="stats.longestDuties.length > 0">
|
||||||
•
|
|
||||||
<i18n-t keypath="journal.daily-stats.longest-duties">
|
<i18n-t keypath="journal.daily-stats.longest-duties">
|
||||||
<template #dispatcher>
|
<template #dispatcher>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -122,10 +117,9 @@
|
|||||||
{{ calculateDuration(stats.longestDuties[0].duration) }}
|
{{ calculateDuration(stats.longestDuties[0].duration) }}
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</li>
|
||||||
|
|
||||||
<div v-if="stats.mostActiveDrivers.length > 0">
|
<li v-if="stats.mostActiveDrivers.length > 0">
|
||||||
•
|
|
||||||
<i18n-t keypath="journal.daily-stats.most-active-driver">
|
<i18n-t keypath="journal.daily-stats.most-active-driver">
|
||||||
<template #driver>
|
<template #driver>
|
||||||
<router-link
|
<router-link
|
||||||
@@ -138,13 +132,14 @@
|
|||||||
<b class="text--primary">{{ stats.mostActiveDrivers[0].distance.toFixed(2) }} km</b>
|
<b class="text--primary">{{ stats.mostActiveDrivers[0].distance.toFixed(2) }} km</b>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<hr class="section-separator" />
|
<hr class="section-separator" />
|
||||||
|
|
||||||
<div class="stats-badges">
|
<div class="stats-badges">
|
||||||
<span
|
<span
|
||||||
class="stat-badge"
|
class="badge stat-badge"
|
||||||
v-for="key in [
|
v-for="key in [
|
||||||
'rippedSwitches',
|
'rippedSwitches',
|
||||||
'derailments',
|
'derailments',
|
||||||
@@ -155,14 +150,13 @@
|
|||||||
:key="key"
|
:key="key"
|
||||||
>
|
>
|
||||||
<span>{{ $t(`journal.daily-stats.${key}`) }}</span>
|
<span>{{ $t(`journal.daily-stats.${key}`) }}</span>
|
||||||
<span>{{
|
<span>
|
||||||
Object.entries(stats.globalDiff).find(([k, v]) => k == key)?.[1] || '--'
|
{{ Object.entries(stats.globalDiff).find(([k, v]) => k == key)?.[1] || '--' }}
|
||||||
}}</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -178,7 +172,6 @@ export default defineComponent({
|
|||||||
name: 'journal-daily-stats',
|
name: 'journal-daily-stats',
|
||||||
|
|
||||||
mixins: [dateMixin],
|
mixins: [dateMixin],
|
||||||
// emits: ['toggleStatsOpen'],
|
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -193,7 +186,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
this.startFetchingDailyStats();
|
this.startFetchingDailyStats();
|
||||||
// this.$emit('toggleStatsOpen', true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivated() {
|
deactivated() {
|
||||||
@@ -249,14 +241,24 @@ export default defineComponent({
|
|||||||
.daily-stats {
|
.daily-stats {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.daily-stats > span[data-active='0'] {
|
.daily-stats > span[data-active='0'] {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.stats-list {
|
||||||
|
list-style: disc;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.stats-list a {
|
.stats-list a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats-list > li {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
.stats-badges {
|
.stats-badges {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -16,17 +16,17 @@
|
|||||||
<hr class="header-separator" />
|
<hr class="header-separator" />
|
||||||
|
|
||||||
<div class="info-stats">
|
<div class="info-stats">
|
||||||
<span class="stat-badge" v-if="stats.services">
|
<span class="badge stat-badge" v-if="stats.services">
|
||||||
<span>{{ $t('journal.dispatcher-stats.services-count') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.services-count') }}</span>
|
||||||
<span>{{ stats.services.count }}</span>
|
<span>{{ stats.services.count }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge" v-if="stats.services">
|
<span class="badge stat-badge" v-if="stats.services">
|
||||||
<span>{{ $t('journal.dispatcher-stats.service-max') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.service-max') }}</span>
|
||||||
<span>{{ calculateDuration(stats.services.durationMax) }}</span>
|
<span>{{ calculateDuration(stats.services.durationMax) }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge" v-if="stats.services">
|
<span class="badge stat-badge" v-if="stats.services">
|
||||||
<span>{{ $t('journal.dispatcher-stats.service-avg') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.service-avg') }}</span>
|
||||||
<span>{{ calculateDuration(stats.services.durationAvg) }}</span>
|
<span>{{ calculateDuration(stats.services.durationAvg) }}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -35,22 +35,22 @@
|
|||||||
<hr class="section-separator" />
|
<hr class="section-separator" />
|
||||||
|
|
||||||
<div class="info-stats">
|
<div class="info-stats">
|
||||||
<span class="stat-badge" v-if="stats.issuedTimetables">
|
<span class="badge stat-badge" v-if="stats.issuedTimetables">
|
||||||
<span>{{ $t('journal.dispatcher-stats.timetables-count') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.timetables-count') }}</span>
|
||||||
<span>{{ stats.issuedTimetables.count }}</span>
|
<span>{{ stats.issuedTimetables.count }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge" v-if="stats.issuedTimetables">
|
<span class="badge stat-badge" v-if="stats.issuedTimetables">
|
||||||
<span>{{ $t('journal.dispatcher-stats.timetables-sum') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.timetables-sum') }}</span>
|
||||||
<span>{{ stats.issuedTimetables.distanceSum.toFixed(2) }}km</span>
|
<span>{{ stats.issuedTimetables.distanceSum.toFixed(2) }}km</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge" v-if="stats.issuedTimetables">
|
<span class="badge stat-badge" v-if="stats.issuedTimetables">
|
||||||
<span>{{ $t('journal.dispatcher-stats.timetables-max') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.timetables-max') }}</span>
|
||||||
<span>{{ stats.issuedTimetables.distanceMax.toFixed(2) }}km</span>
|
<span>{{ stats.issuedTimetables.distanceMax.toFixed(2) }}km</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge" v-if="stats.issuedTimetables">
|
<span class="badge stat-badge" v-if="stats.issuedTimetables">
|
||||||
<span>{{ $t('journal.dispatcher-stats.timetables-avg') }}</span>
|
<span>{{ $t('journal.dispatcher-stats.timetables-avg') }}</span>
|
||||||
<span>{{ stats.issuedTimetables.distanceAvg.toFixed(2) }}km</span>
|
<span>{{ stats.issuedTimetables.distanceAvg.toFixed(2) }}km</span>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -99,11 +99,4 @@ export default defineComponent({
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../../styles/variables.scss';
|
@import '../../../styles/variables.scss';
|
||||||
@import '../../../styles/JournalSection.scss';
|
@import '../../../styles/JournalSection.scss';
|
||||||
|
|
||||||
.journal-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<hr class="header-separator" />
|
<hr class="header-separator" />
|
||||||
|
|
||||||
<div class="info-stats">
|
<div class="info-stats">
|
||||||
<span class="stat-badge">
|
<span class="badge stat-badge">
|
||||||
<span>{{ $t('journal.driver-stats.timetables') }}</span>
|
<span>{{ $t('journal.driver-stats.timetables') }}</span>
|
||||||
<span
|
<span
|
||||||
>{{ store.driverStatsData._count.fulfilled }} /
|
>{{ store.driverStatsData._count.fulfilled }} /
|
||||||
@@ -20,17 +20,17 @@
|
|||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="badge stat-badge">
|
||||||
<span>{{ $t('journal.driver-stats.longest-timetable') }}</span>
|
<span>{{ $t('journal.driver-stats.longest-timetable') }}</span>
|
||||||
<span> {{ store.driverStatsData._max.routeDistance.toFixed(2) }}km </span>
|
<span> {{ store.driverStatsData._max.routeDistance.toFixed(2) }}km </span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="badge stat-badge">
|
||||||
<span>{{ $t('journal.driver-stats.avg-timetable') }}</span>
|
<span>{{ $t('journal.driver-stats.avg-timetable') }}</span>
|
||||||
<span> {{ store.driverStatsData._avg.routeDistance.toFixed(2) }}km </span>
|
<span> {{ store.driverStatsData._avg.routeDistance.toFixed(2) }}km </span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="badge stat-badge">
|
||||||
<span>{{ $t('journal.driver-stats.distance') }}</span>
|
<span>{{ $t('journal.driver-stats.distance') }}</span>
|
||||||
<span>
|
<span>
|
||||||
{{ store.driverStatsData._sum.currentDistance.toFixed(2) }} /
|
{{ store.driverStatsData._sum.currentDistance.toFixed(2) }} /
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="stat-badge">
|
<span class="badge stat-badge">
|
||||||
<span>{{ $t('journal.driver-stats.stations') }}</span>
|
<span>{{ $t('journal.driver-stats.stations') }}</span>
|
||||||
<span>
|
<span>
|
||||||
{{ store.driverStatsData._sum.confirmedStopsCount }} /
|
{{ store.driverStatsData._sum.confirmedStopsCount }} /
|
||||||
|
|||||||
@@ -17,17 +17,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<ul class="journal-list">
|
<transition-group name="list-anim" tag="ul" class="journal-list">
|
||||||
<transition-group name="list-anim">
|
<li v-for="timetable in timetableHistory" class="journal_item" :key="timetable.id">
|
||||||
<li
|
|
||||||
v-for="{ timetable, showExtraInfo } in computedTimetableHistory"
|
|
||||||
class="journal_item"
|
|
||||||
:key="timetable.id"
|
|
||||||
@click="showExtraInfo.value = !showExtraInfo.value"
|
|
||||||
>
|
|
||||||
<div class="journal_item-info">
|
<div class="journal_item-info">
|
||||||
<!-- General -->
|
<!-- General -->
|
||||||
<TimetableGeneral :timetable="timetable" />
|
<TimetableGeneral :timetable="timetable" />
|
||||||
|
|
||||||
|
<div @click="toggleExtraInfo(timetable.id)" style="cursor: pointer">
|
||||||
<!-- Route -->
|
<!-- Route -->
|
||||||
<span class="item-route">
|
<span class="item-route">
|
||||||
<b>{{ timetable.route.replace('|', ' - ') }}</b>
|
<b>{{ timetable.route.replace('|', ' - ') }}</b>
|
||||||
@@ -35,16 +31,23 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<!-- Stops -->
|
<!-- Stops -->
|
||||||
<TimetableStops :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
|
<TimetableStops
|
||||||
|
:timetable="timetable"
|
||||||
|
:showExtraInfo="extraInfoIndexes.includes(timetable.id)"
|
||||||
|
/>
|
||||||
<!-- Status -->
|
<!-- Status -->
|
||||||
<TimetableStatus :timetable="timetable" />
|
<TimetableStatus :timetable="timetable" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Extra -->
|
<!-- Extra -->
|
||||||
<TimetableDetails :timetable="timetable" :showExtraInfo="showExtraInfo.value" />
|
<TimetableDetails
|
||||||
|
:timetable="timetable"
|
||||||
|
:showExtraInfo="extraInfoIndexes.includes(timetable.id)"
|
||||||
|
@toggle-extra-info="toggleExtraInfo"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</ul>
|
|
||||||
|
|
||||||
<AddDataButton
|
<AddDataButton
|
||||||
:list="timetableHistory"
|
:list="timetableHistory"
|
||||||
@@ -57,6 +60,7 @@
|
|||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
|
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
|
||||||
|
|
||||||
<div class="journal_warning" v-else-if="!scrollDataLoaded">
|
<div class="journal_warning" v-else-if="!scrollDataLoaded">
|
||||||
{{ $t('journal.loading-further-data') }}
|
{{ $t('journal.loading-further-data') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -64,7 +68,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType, ref } from 'vue';
|
import { defineComponent, Prop, PropType, ref } from 'vue';
|
||||||
|
|
||||||
import Loading from '../../Global/Loading.vue';
|
import Loading from '../../Global/Loading.vue';
|
||||||
import AddDataButton from '../../Global/AddDataButton.vue';
|
import AddDataButton from '../../Global/AddDataButton.vue';
|
||||||
@@ -110,16 +114,17 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
Status,
|
Status,
|
||||||
store: useMainStore()
|
store: useMainStore(),
|
||||||
|
extraInfoIndexes: [] as number[]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
methods: {
|
||||||
computedTimetableHistory() {
|
toggleExtraInfo(id: number) {
|
||||||
return this.timetableHistory.map((timetable) => ({
|
const existingIdx = this.extraInfoIndexes.indexOf(id);
|
||||||
timetable,
|
|
||||||
showExtraInfo: ref(false)
|
if (existingIdx != -1) this.extraInfoIndexes.splice(existingIdx, 1);
|
||||||
}));
|
else this.extraInfoIndexes.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="details-actions">
|
<div class="details-actions">
|
||||||
<button class="btn--action">
|
<button class="btn--action" @click="toggleExtraInfo">
|
||||||
<b>{{ $t('journal.stock-info') }}</b>
|
<b>{{ $t('journal.stock-info') }}</b>
|
||||||
<img :src="`/images/icon-arrow-${showExtraInfo ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
|
<img :src="`/images/icon-arrow-${showExtraInfo ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
v-if="driverRouteLocation !== null"
|
||||||
|
class="a-button btn--action btn-timetable"
|
||||||
|
:to="driverRouteLocation"
|
||||||
|
>
|
||||||
|
<img src="/images/icon-train.svg" alt="train icon" />
|
||||||
|
<b>{{ $t('journal.timetable-online-button') }}</b>
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="details-body" v-if="timetable.stockString && timetable.stockMass && showExtraInfo">
|
<div class="details-body" v-if="timetable.stockString && timetable.stockMass && showExtraInfo">
|
||||||
@@ -77,9 +86,13 @@
|
|||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import StockList from '../../Global/StockList.vue';
|
import StockList from '../../Global/StockList.vue';
|
||||||
import { API } from '../../../typings/api';
|
import { API } from '../../../typings/api';
|
||||||
|
import { RouteLocationRaw } from 'vue-router';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { StockList },
|
components: { StockList },
|
||||||
|
|
||||||
|
emits: ['toggleExtraInfo'],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
showExtraInfo: {
|
showExtraInfo: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -112,12 +125,25 @@ export default defineComponent({
|
|||||||
stockLength: Number(historyData[3]) || undefined
|
stockLength: Number(historyData[3]) || undefined
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
driverRouteLocation(): RouteLocationRaw | null {
|
||||||
|
if (this.timetable.terminated) return null;
|
||||||
|
return {
|
||||||
|
name: 'DriverView',
|
||||||
|
query: {
|
||||||
|
trainId: `${this.timetable.driverId}|${this.timetable.trainNo}|eu`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onImageError(e: Event) {
|
onImageError(e: Event) {
|
||||||
const imageEl = e.target as HTMLImageElement;
|
const imageEl = e.target as HTMLImageElement;
|
||||||
imageEl.src = '/images/icon-unknown.png';
|
imageEl.src = '/images/icon-unknown.png';
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleExtraInfo() {
|
||||||
|
this.$emit('toggleExtraInfo', this.timetable.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -134,6 +160,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
.details-actions {
|
.details-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
button img {
|
button img {
|
||||||
height: 1.25em;
|
height: 1.25em;
|
||||||
|
|||||||
@@ -3,13 +3,15 @@
|
|||||||
<span class="general-train">
|
<span class="general-train">
|
||||||
<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 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 class="train-badge skr" v-if="timetable.skr" :title="$t('general.SKR')">SKR</span>
|
||||||
</span>
|
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<strong class="text--primary">
|
<strong
|
||||||
|
data-tooltip-type="BaseTooltip"
|
||||||
|
:data-tooltip-content="getCategoryExplanation(timetable.trainCategoryCode)"
|
||||||
|
class="text--primary tooltip-help"
|
||||||
|
>
|
||||||
{{ timetable.trainCategoryCode }}
|
{{ timetable.trainCategoryCode }}
|
||||||
</strong>
|
</strong>
|
||||||
<strong> {{ timetable.trainNo }}</strong>
|
<strong> {{ timetable.trainNo }}</strong>
|
||||||
@@ -23,18 +25,19 @@
|
|||||||
{{ timetable.driverLevel < 2 ? 'L' : `${timetable.driverLevel}` }}
|
{{ timetable.driverLevel < 2 ? 'L' : `${timetable.driverLevel}` }}
|
||||||
</strong>
|
</strong>
|
||||||
|
|
||||||
<strong
|
<router-link
|
||||||
v-if="apiStore.donatorsData.includes(timetable.driverName)"
|
v-if="apiStore.donatorsData.includes(timetable.driverName)"
|
||||||
class="text--donator"
|
class="text--donator"
|
||||||
data-tooltip-type="DonatorTooltip"
|
data-tooltip-type="DonatorTooltip"
|
||||||
:data-tooltip-content="$t('donations.driver-message')"
|
:data-tooltip-content="$t('donations.driver-message')"
|
||||||
|
:to="`/journal/timetables?search-driver=${timetable.driverName}`"
|
||||||
>
|
>
|
||||||
{{ timetable.driverName }}
|
<strong>{{ timetable.driverName }}</strong>
|
||||||
</strong>
|
</router-link>
|
||||||
|
|
||||||
<strong v-else>
|
<router-link v-else :to="`/journal/timetables?search-driver=${timetable.driverName}`">
|
||||||
{{ timetable.driverName }}
|
<strong>{{ timetable.driverName }}</strong>
|
||||||
</strong>
|
</router-link>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="general-time">
|
<span class="general-time">
|
||||||
@@ -62,15 +65,6 @@
|
|||||||
: `${$t('journal.timetable-abandoned')} ${localeTime(timetable.endDate, $i18n.locale)}`
|
: `${$t('journal.timetable-abandoned')} ${localeTime(timetable.endDate, $i18n.locale)}`
|
||||||
}}
|
}}
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<button
|
|
||||||
v-if="timetable.terminated == false"
|
|
||||||
class="btn--action btn-timetable"
|
|
||||||
@click.stop="showTimetable(timetable, $event.currentTarget)"
|
|
||||||
>
|
|
||||||
<img src="/images/icon-train.svg" alt="train icon" />
|
|
||||||
<b>{{ $t('journal.timetable-online-button') }}</b>
|
|
||||||
</button>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -80,12 +74,12 @@ import { PropType, defineComponent } from 'vue';
|
|||||||
|
|
||||||
import { API } from '../../../typings/api';
|
import { API } from '../../../typings/api';
|
||||||
import dateMixin from '../../../mixins/dateMixin';
|
import dateMixin from '../../../mixins/dateMixin';
|
||||||
import modalTrainMixin from '../../../mixins/modalTrainMixin';
|
|
||||||
import styleMixin from '../../../mixins/styleMixin';
|
import styleMixin from '../../../mixins/styleMixin';
|
||||||
import { useApiStore } from '../../../store/apiStore';
|
import { useApiStore } from '../../../store/apiStore';
|
||||||
|
import trainCategoryMixin from '../../../mixins/trainCategoryMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [dateMixin, modalTrainMixin, styleMixin],
|
mixins: [dateMixin, styleMixin, trainCategoryMixin],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -98,14 +92,6 @@ export default defineComponent({
|
|||||||
type: Object as PropType<API.TimetableHistory.Data>,
|
type: Object as PropType<API.TimetableHistory.Data>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
showTimetable(timetable: API.TimetableHistory.Data, target: EventTarget | null) {
|
|
||||||
if (timetable?.terminated) return;
|
|
||||||
|
|
||||||
this.selectModalTrainById(`${timetable.driverName}${timetable.trainNo}`, target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -132,7 +118,6 @@ export default defineComponent({
|
|||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
line-height: 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.general-time {
|
.general-time {
|
||||||
@@ -175,6 +160,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
.item-general {
|
.item-general {
|
||||||
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,13 @@
|
|||||||
<li
|
<li
|
||||||
v-for="{ train, status } in stationTrains"
|
v-for="{ train, status } in stationTrains"
|
||||||
class="badge user"
|
class="badge user"
|
||||||
tabindex="0"
|
|
||||||
:key="train.id"
|
:key="train.id"
|
||||||
:data-status="status"
|
:data-status="status"
|
||||||
@click.prevent="selectModalTrain(train, $event.currentTarget)"
|
|
||||||
@keydown.enter="selectModalTrain(train, $event.currentTarget)"
|
|
||||||
>
|
>
|
||||||
|
<router-link :to="train.driverRouteLocation" class="a-block">
|
||||||
<span class="user_train">{{ train.trainNo }}</span>
|
<span class="user_train">{{ train.trainNo }}</span>
|
||||||
<span class="user_name">{{ train.driverName }}</span>
|
<span class="user_name">{{ train.driverName }}</span>
|
||||||
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</section>
|
</section>
|
||||||
@@ -30,14 +29,13 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PropType, defineComponent } from 'vue';
|
import { PropType, defineComponent } from 'vue';
|
||||||
import modalTrainMixin from '../../../mixins/modalTrainMixin';
|
|
||||||
import routerMixin from '../../../mixins/routerMixin';
|
import routerMixin from '../../../mixins/routerMixin';
|
||||||
import { ActiveScenery, Station, StopStatus } from '../../../typings/common';
|
import { ActiveScenery, Station } from '../../../typings/common';
|
||||||
import { getTrainStopStatus } from '../utils';
|
import { getTrainStopStatus } from '../utils';
|
||||||
import { useMainStore } from '../../../store/mainStore';
|
import { useMainStore } from '../../../store/mainStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [routerMixin, modalTrainMixin],
|
mixins: [routerMixin],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
onlineScenery: {
|
onlineScenery: {
|
||||||
@@ -99,38 +97,27 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user {
|
.user {
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&_train {
|
|
||||||
color: black;
|
|
||||||
background-color: $no-timetable;
|
|
||||||
|
|
||||||
transition: background-color 200ms;
|
|
||||||
-ms-transition: background-color 200ms;
|
|
||||||
-webkit-transition: background-color 200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-status='no-timetable'] .user_train {
|
&[data-status='no-timetable'] .user_train {
|
||||||
background-color: $no-timetable;
|
background-color: $no-timetable;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-status='departed'] > &_train {
|
&[data-status='departed'] .user_train {
|
||||||
background-color: $departed;
|
background-color: $departed;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-status='stopped'] > &_train {
|
&[data-status='stopped'] .user_train {
|
||||||
background-color: $stopped;
|
background-color: $stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-status='online'] > &_train {
|
&[data-status='online'] .user_train {
|
||||||
background-color: $online;
|
background-color: $online;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-status='terminated'] > &_train {
|
&[data-status='terminated'] .user_train {
|
||||||
background-color: $terminated;
|
background-color: $terminated;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-status='disconnected'] > &_train {
|
&[data-status='disconnected'] .user_train {
|
||||||
background-color: $disconnected;
|
background-color: $disconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +126,16 @@ ul {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user_train {
|
||||||
|
color: black;
|
||||||
|
background-color: $no-timetable;
|
||||||
|
|
||||||
|
transition: background-color 200ms;
|
||||||
|
-ms-transition: background-color 200ms;
|
||||||
|
-webkit-transition: background-color 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
.users-anim {
|
.users-anim {
|
||||||
&-move,
|
&-move,
|
||||||
&-enter-active,
|
&-enter-active,
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="header_links" v-if="station">
|
<span class="header_links" v-if="station">
|
||||||
|
<a :href="pragotronHref" target="_blank" :title="$t('scenery.pragotron-link')">
|
||||||
|
<img src="/images/icon-pragotron.svg" alt="icon-pragotron" />
|
||||||
|
</a>
|
||||||
|
|
||||||
<a :href="tabliceZbiorczeHref" target="_blank" :title="$t('scenery.tablice-link')">
|
<a :href="tabliceZbiorczeHref" target="_blank" :title="$t('scenery.tablice-link')">
|
||||||
<img src="/images/icon-tablice.ico" alt="icon-tablice" />
|
<img src="/images/icon-tablice.ico" alt="icon-tablice" />
|
||||||
</a>
|
</a>
|
||||||
@@ -21,18 +25,15 @@
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="timetable-checkpoints" v-if="station?.generalInfo?.checkpoints">
|
<div class="timetable-checkpoints" v-if="station?.generalInfo?.checkpoints">
|
||||||
<span v-for="(cp, i) in station.generalInfo.checkpoints" :key="i">
|
<template v-for="(ch, i) in station.generalInfo.checkpoints" :key="i">
|
||||||
{{ (i > 0 && '•') || '' }}
|
<template v-if="i > 0">•</template>
|
||||||
|
<router-link
|
||||||
<button
|
class="checkpoint-item"
|
||||||
:key="cp"
|
:class="{ current: chosenCheckpoint === ch }"
|
||||||
class="checkpoint_item"
|
:to="`/scenery?station=${station.name}&checkpoint=${ch}`"
|
||||||
:class="{ current: chosenCheckpoint === cp }"
|
>{{ ch }}</router-link
|
||||||
@click="setCheckpoint(cp)"
|
|
||||||
>
|
>
|
||||||
{{ cp }}
|
</template>
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -62,29 +63,36 @@
|
|||||||
{{ $t('scenery.no-timetables') }}
|
{{ $t('scenery.no-timetables') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<router-link
|
||||||
class="timetable-item"
|
class="timetable-item a-block"
|
||||||
v-else
|
v-else
|
||||||
v-for="(row, i) in sceneryTimetables"
|
v-for="(row, i) in sceneryTimetables"
|
||||||
:key="row.train.id + i"
|
:key="row.train.id + i"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click.prevent.stop="selectModalTrain(row.train, $event.currentTarget)"
|
:to="row.train.driverRouteLocation"
|
||||||
@keydown.enter.prevent="selectModalTrain(row.train, $event.currentTarget)"
|
|
||||||
>
|
>
|
||||||
<span class="timetable-general">
|
<span class="timetable-general">
|
||||||
<span class="general-info">
|
<span class="general-info">
|
||||||
<span class="info-number">
|
<div class="info-train">
|
||||||
<strong>{{ row.train.timetableData!.category }}</strong>
|
<b
|
||||||
{{ row.train.trainNo }}
|
data-tooltip-type="BaseTooltip"
|
||||||
|
:data-tooltip-content="getCategoryExplanation(row.train.timetableData!.category)"
|
||||||
<span v-if="row.checkpointStop.comments" :title="row.checkpointStop.comments">
|
class="text--primary tooltip-help"
|
||||||
|
>
|
||||||
|
{{ row.train.timetableData!.category }}
|
||||||
|
</b>
|
||||||
|
<span> </span>
|
||||||
|
<b>{{ row.train.trainNo }}</b>
|
||||||
|
<span> • </span>
|
||||||
|
<span>{{ row.train.driverName }}</span>
|
||||||
|
<span
|
||||||
|
v-if="row.checkpointStop.comments"
|
||||||
|
data-tooltip-type="BaseTooltip"
|
||||||
|
:data-tooltip-content="row.checkpointStop.comments"
|
||||||
|
>
|
||||||
<img src="/images/icon-warning.svg" />
|
<img src="/images/icon-warning.svg" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
|
|
|
||||||
<span>
|
|
||||||
{{ row.train.driverName }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="info-route">
|
<div class="info-route">
|
||||||
<strong>{{ row.train.timetableData!.route.replace('|', ' - ') }}</strong>
|
<strong>{{ row.train.timetableData!.route.replace('|', ' - ') }}</strong>
|
||||||
@@ -160,7 +168,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</router-link>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -173,12 +181,12 @@ import { useRoute } from 'vue-router';
|
|||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import routerMixin from '../../mixins/routerMixin';
|
import routerMixin from '../../mixins/routerMixin';
|
||||||
|
import trainCategoryMixin from '../../mixins/trainCategoryMixin';
|
||||||
import { useMainStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
|
||||||
import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
|
|
||||||
import { useApiStore } from '../../store/apiStore';
|
import { useApiStore } from '../../store/apiStore';
|
||||||
import { ActiveScenery, Station } from '../../typings/common';
|
import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
|
||||||
import { SceneryTimetableRow } from './typings';
|
import { SceneryTimetableRow } from './typings';
|
||||||
|
import { ActiveScenery, Station } from '../../typings/common';
|
||||||
import { getTrainStopStatus, stopStatusPriority } from './utils';
|
import { getTrainStopStatus, stopStatusPriority } from './utils';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -186,7 +194,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
components: { Loading, ScheduledTrainStatus },
|
components: { Loading, ScheduledTrainStatus },
|
||||||
|
|
||||||
mixins: [dateMixin, routerMixin, modalTrainMixin],
|
mixins: [dateMixin, routerMixin, trainCategoryMixin],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
@@ -205,6 +213,12 @@ export default defineComponent({
|
|||||||
this.loadSelectedOption();
|
this.loadSelectedOption();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
currentURL() {
|
||||||
|
this.loadSelectedOption();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const currentURL = computed(() => `${location.origin}${route.fullPath}`);
|
const currentURL = computed(() => `${location.origin}${route.fullPath}`);
|
||||||
@@ -235,6 +249,13 @@ export default defineComponent({
|
|||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pragotronHref() {
|
||||||
|
let url = `https://pragotron-td2.web.app/board?name=${this.station!.name}®ion=${this.mainStore.region.id}`;
|
||||||
|
if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`;
|
||||||
|
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
sceneryTimetables(): SceneryTimetableRow[] {
|
sceneryTimetables(): SceneryTimetableRow[] {
|
||||||
if (!this.onlineScenery) return [];
|
if (!this.onlineScenery) return [];
|
||||||
|
|
||||||
@@ -243,7 +264,7 @@ export default defineComponent({
|
|||||||
return this.onlineScenery.scheduledTrains
|
return this.onlineScenery.scheduledTrains
|
||||||
.filter(
|
.filter(
|
||||||
(ct) =>
|
(ct) =>
|
||||||
ct.timetablePathElement.stationName == sceneryName &&
|
// ct.timetablePathElement.stationName == sceneryName &&
|
||||||
ct.train.region == this.mainStore.region.id &&
|
ct.train.region == this.mainStore.region.id &&
|
||||||
this.chosenCheckpoint &&
|
this.chosenCheckpoint &&
|
||||||
ct.checkpointStop.stopNameRAW.toLowerCase() == this.chosenCheckpoint.toLowerCase()
|
ct.checkpointStop.stopNameRAW.toLowerCase() == this.chosenCheckpoint.toLowerCase()
|
||||||
@@ -286,7 +307,19 @@ export default defineComponent({
|
|||||||
loadSelectedOption() {
|
loadSelectedOption() {
|
||||||
if (!this.station) return;
|
if (!this.station) return;
|
||||||
|
|
||||||
this.chosenCheckpoint = this.station.generalInfo?.checkpoints[0] ?? this.station.name;
|
if (!this.station.generalInfo) {
|
||||||
|
this.chosenCheckpoint = this.station.name;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryCheckpoint = this.$route.query['checkpoint']?.toString();
|
||||||
|
|
||||||
|
this.chosenCheckpoint =
|
||||||
|
this.station.generalInfo.checkpoints.find(
|
||||||
|
(ch) => ch.toLocaleLowerCase() === queryCheckpoint?.toLocaleLowerCase()
|
||||||
|
) ??
|
||||||
|
this.station.generalInfo.checkpoints[0] ??
|
||||||
|
this.station.name;
|
||||||
},
|
},
|
||||||
|
|
||||||
setCheckpoint(cp: string) {
|
setCheckpoint(cp: string) {
|
||||||
@@ -356,7 +389,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
background: #353535;
|
background: #353535;
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
@@ -386,30 +418,35 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.timetable-list {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetable-checkpoints {
|
.timetable-checkpoints {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
|
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
button.checkpoint_item {
|
.checkpoint-item {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
display: inline;
|
display: inline;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkpoint_item.current {
|
&.current {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $accentCol;
|
color: $accentCol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.timetable-list {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.general-info {
|
.general-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -423,7 +460,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 1.1em;
|
height: 0.9em;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 0 0.25em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,30 +6,11 @@
|
|||||||
<p>[F] {{ $t('options.filters') }}</p>
|
<p>[F] {{ $t('options.filters') }}</p>
|
||||||
<span class="active-indicator" v-if="changedFilters.length != 0"></span>
|
<span class="active-indicator" v-if="changedFilters.length != 0"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<label for="scenery-search">
|
|
||||||
<input
|
|
||||||
id="scenery-search"
|
|
||||||
list="sceneries"
|
|
||||||
:placeholder="$t('sceneries.scenery-search')"
|
|
||||||
@focus="preventKeyDown = true"
|
|
||||||
@blur="preventKeyDown = false"
|
|
||||||
v-model="chosenSearchScenery"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<datalist id="sceneries">
|
|
||||||
<option
|
|
||||||
v-for="scenery in sortedStationList"
|
|
||||||
:key="scenery.name"
|
|
||||||
:value="scenery.name"
|
|
||||||
></option>
|
|
||||||
</datalist>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<transition name="card-anim">
|
<transition name="card-anim">
|
||||||
<div class="card" v-if="isVisible" tabindex="0" ref="cardRef" @keydown.r="resetFilters">
|
<div class="card" v-if="isVisible" ref="cardRef" @keydown.r="resetFilters">
|
||||||
<div class="card_content" @scroll="onScroll" ref="cardContentRef">
|
<div class="card_content" tabindex="0" @scroll="onScroll" ref="cardContentRef">
|
||||||
<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>
|
<p class="card_info" v-html="$t('filters.desc')"></p>
|
||||||
|
|
||||||
@@ -40,6 +21,31 @@
|
|||||||
<template v-else>{{ $t('filters.no-changed-filters') }}</template>
|
<template v-else>{{ $t('filters.no-changed-filters') }}</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section class="card_sceneries-search">
|
||||||
|
<h3 class="section-header">{{ $t('filters.sceneries-search') }}</h3>
|
||||||
|
|
||||||
|
<datalist id="sceneries">
|
||||||
|
<option
|
||||||
|
v-for="scenery in sortedStationList"
|
||||||
|
:key="scenery.name"
|
||||||
|
:value="scenery.name"
|
||||||
|
></option>
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<form action="javascript:void(0);" @submit="handleSceneriesInput">
|
||||||
|
<input
|
||||||
|
v-model="chosenSearchScenery"
|
||||||
|
id="scenery-search"
|
||||||
|
list="sceneries"
|
||||||
|
:placeholder="$t('filters.sceneries-placeholder')"
|
||||||
|
@focus="preventKeyDown = true"
|
||||||
|
@blur="preventKeyDown = false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button class="btn--action">{{ $t('filters.search-button-title') }}</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="card_options">
|
<section class="card_options">
|
||||||
<div
|
<div
|
||||||
class="option-section"
|
class="option-section"
|
||||||
@@ -57,16 +63,15 @@
|
|||||||
<div class="section-filters">
|
<div class="section-filters">
|
||||||
<label
|
<label
|
||||||
v-for="filterKey in sectionFilters"
|
v-for="filterKey in sectionFilters"
|
||||||
@click="() => (filters[filterKey] = !filters[filterKey])"
|
|
||||||
@dblclick="setSingleSectionFilter(sectionKey, filterKey)"
|
@dblclick="setSingleSectionFilter(sectionKey, filterKey)"
|
||||||
:for="filterKey"
|
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
type="checkbox"
|
||||||
:checked="filters[filterKey]"
|
:checked="filters[filterKey]"
|
||||||
v-model="filters[filterKey]"
|
v-model="filters[filterKey]"
|
||||||
type="checkbox"
|
|
||||||
:class="sectionKey"
|
:class="sectionKey"
|
||||||
:name="filterKey"
|
:name="filterKey"
|
||||||
|
:id="filterKey"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
{{ $t(`filters.${filterKey}`) }}
|
{{ $t(`filters.${filterKey}`) }}
|
||||||
@@ -111,7 +116,7 @@
|
|||||||
@blur="preventKeyDown = false"
|
@blur="preventKeyDown = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button class="btn--action">{{ $t('filters.authors-button-title') }}</button>
|
<button class="btn--action">{{ $t('filters.search-button-title') }}</button>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -269,20 +274,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
chosenSearchScenery(value: string) {
|
|
||||||
const chosenStation = this.store.stationList.find(({ name }) => name == value);
|
|
||||||
|
|
||||||
if (chosenStation) {
|
|
||||||
this.$router.push(`/scenery?station=${chosenStation.name.replace(/ /g, '_')}`);
|
|
||||||
this.chosenSearchScenery = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
isVisible(value: boolean) {
|
isVisible(value: boolean) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (value) {
|
if (value) {
|
||||||
(this.$refs['cardRef'] as HTMLDivElement).focus();
|
|
||||||
(this.$refs['cardContentRef'] as HTMLDivElement).scrollTop = this.scrollTop;
|
(this.$refs['cardContentRef'] as HTMLDivElement).scrollTop = this.scrollTop;
|
||||||
|
(this.$refs['cardContentRef'] as HTMLDivElement).focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -300,7 +296,18 @@ export default defineComponent({
|
|||||||
|
|
||||||
handleAuthorsInput() {
|
handleAuthorsInput() {
|
||||||
this.filters['authors'] = this.authors;
|
this.filters['authors'] = this.authors;
|
||||||
// if (this.saveOptions) StorageManager.setStringValue('authors', target.value);
|
},
|
||||||
|
|
||||||
|
handleSceneriesInput() {
|
||||||
|
const chosenStation = this.store.stationList.find(
|
||||||
|
({ name }) => name == this.chosenSearchScenery
|
||||||
|
);
|
||||||
|
|
||||||
|
if (chosenStation) {
|
||||||
|
this.$router.push(`/scenery?station=${chosenStation.name.replace(/ /g, '_')}`);
|
||||||
|
this.chosenSearchScenery = '';
|
||||||
|
this.isVisible = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
subHour() {
|
subHour() {
|
||||||
@@ -329,6 +336,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
resetFilters() {
|
resetFilters() {
|
||||||
|
if (this.preventKeyDown) return;
|
||||||
|
|
||||||
// Reset local model values
|
// Reset local model values
|
||||||
this.minimumHours = 0;
|
this.minimumHours = 0;
|
||||||
this.authors = '';
|
this.authors = '';
|
||||||
@@ -353,7 +362,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
setSingleSectionFilter(sectionKey: StationFilterSection, chosenKey: string) {
|
setSingleSectionFilter(sectionKey: StationFilterSection, chosenKey: string) {
|
||||||
filtersSections[sectionKey].forEach((filterKey) => {
|
filtersSections[sectionKey].forEach((filterKey) => {
|
||||||
if (filterKey != chosenKey) this.filters[filterKey] = initFilters[filterKey];
|
if (typeof this.filters[filterKey] === 'boolean')
|
||||||
|
this.filters[filterKey] = filterKey != chosenKey;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -382,6 +392,7 @@ h3.section-header {
|
|||||||
.card {
|
.card {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr auto;
|
grid-template-rows: 1fr auto;
|
||||||
|
padding: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card_info {
|
.card_info {
|
||||||
@@ -451,8 +462,12 @@ h3.section-header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card_authors-search {
|
.card_authors-search,
|
||||||
|
.card_sceneries-search {
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -642,10 +657,6 @@ h3.section-header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
.card_controls > button.card-button > p {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="station-stats">
|
<div
|
||||||
<div class="separator" />
|
class="dropdown"
|
||||||
|
@keydown.esc="showDropdown = false"
|
||||||
|
v-click-outside="() => (showDropdown = false)"
|
||||||
|
>
|
||||||
|
<div class="bg" v-if="showDropdown" @click="showDropdown = false"></div>
|
||||||
|
|
||||||
<div class="stats-row">
|
<button class="filter-button btn--filled btn--image" @click="toggleDropdown" ref="button">
|
||||||
|
<img src="/images/icon-stats.svg" alt="Open filters icon" />
|
||||||
|
<!-- {{ $t('train-stats.stats-button') }} -->
|
||||||
|
<span>STATYSTYKI</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<transition name="dropdown-anim">
|
||||||
|
<div class="dropdown_wrapper" v-if="showDropdown">
|
||||||
<div>
|
<div>
|
||||||
<span
|
<h1 class="text--primary">
|
||||||
>{{ $t('station-stats.u-factor') }}
|
<img src="/images/icon-stats.svg" alt="Open filters icon" />
|
||||||
|
{{ $t('train-stats.title') }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<hr style="margin: 0.5em 0" />
|
||||||
|
|
||||||
|
<ul class="stats-list">
|
||||||
|
<li>
|
||||||
|
<span>
|
||||||
|
{{ $t('station-stats.u-factor') }}
|
||||||
<a
|
<a
|
||||||
href="https://td2.info.pl/dyskusje/wspolczynnik-ugla-czy-to-ma-sens/msg81011/#msg81011"
|
href="https://td2.info.pl/dyskusje/wspolczynnik-ugla-czy-to-ma-sens/msg81011/#msg81011"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -13,44 +33,38 @@
|
|||||||
>(?)</a
|
>(?)</a
|
||||||
>:
|
>:
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<b class="u-factor" :style="calculateFactorStyle()">
|
<b class="u-factor" :style="calculateFactorStyle()">
|
||||||
{{ uFactor.toFixed(2) }}
|
{{ uFactor.toFixed(2) }}
|
||||||
</b>
|
</b>
|
||||||
</div>
|
</li>
|
||||||
|
<li>
|
||||||
<div>
|
|
||||||
•
|
|
||||||
{{ $t('station-stats.avg-timetable-count') }}
|
{{ $t('station-stats.avg-timetable-count') }}
|
||||||
<b>{{ avgTimetableCount.toFixed(2) }}</b>
|
<b>{{ avgTimetableCount.toFixed(2) }}</b>
|
||||||
</div>
|
</li>
|
||||||
|
<li>
|
||||||
<div>
|
|
||||||
•
|
|
||||||
{{ $t('station-stats.single-track-count') }}
|
{{ $t('station-stats.single-track-count') }}
|
||||||
<b>{{ trackCount.oneWay }}</b> (<b>{{ trackCount.oneWayElectric }} ⚡</b>)
|
<b>{{ trackCount.oneWay }}</b> (<b>{{ trackCount.oneWayElectric }} ⚡</b>)
|
||||||
</div>
|
</li>
|
||||||
|
<li>
|
||||||
<div>
|
|
||||||
•
|
|
||||||
{{ $t('station-stats.double-track-count') }}
|
{{ $t('station-stats.double-track-count') }}
|
||||||
<b>{{ trackCount.twoWay }}</b>
|
<b>{{ trackCount.twoWay }}</b>
|
||||||
(<b>{{ trackCount.twoWayElectric }} ⚡</b>)
|
(<b>{{ trackCount.twoWayElectric }} ⚡</b>)
|
||||||
</div>
|
</li>
|
||||||
|
<li>
|
||||||
<div>
|
{{ $t('station-stats.cross-sceneries') }}
|
||||||
• {{ $t('station-stats.cross-sceneries') }} <b>{{ trackCount.crossTrack }}</b> (<b
|
<b>{{ trackCount.crossTrack }}</b> (<b>{{ trackCount.crossTrackElectric }} ⚡</b>)
|
||||||
>{{ trackCount.crossTrackElectric }} ⚡</b
|
</li>
|
||||||
>)
|
<li>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
•
|
|
||||||
{{ $t('station-stats.open-spawns') }} <b>{{ spawnCount.passenger }}</b> - PAS /
|
{{ $t('station-stats.open-spawns') }} <b>{{ spawnCount.passenger }}</b> - PAS /
|
||||||
<b>{{ spawnCount.freight }}</b> - TOW / <b>{{ spawnCount.loco }}</b> - LUZ /
|
<b>{{ spawnCount.freight }}</b> - TOW / <b>{{ spawnCount.loco }}</b> - LUZ /
|
||||||
<b>{{ spawnCount.all }}</b> - ALL
|
<b>{{ spawnCount.all }}</b> - ALL
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div tabindex="0" @focus="() => (showDropdown = false)"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -61,11 +75,16 @@ import { useMainStore } from '../../store/mainStore';
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
mainStore: useMainStore()
|
mainStore: useMainStore(),
|
||||||
|
showDropdown: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleDropdown() {
|
||||||
|
this.showDropdown = !this.showDropdown;
|
||||||
|
},
|
||||||
|
|
||||||
calculateFactorStyle() {
|
calculateFactorStyle() {
|
||||||
if (this.uFactor == 0) return '';
|
if (this.uFactor == 0) return '';
|
||||||
|
|
||||||
@@ -171,25 +190,15 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.separator {
|
@import '../../styles/dropdown.scss';
|
||||||
width: 100%;
|
@import '../../styles/badge.scss';
|
||||||
height: 2px;
|
|
||||||
|
h1 img {
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
background-color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.station-stats {
|
|
||||||
text-align: center;
|
|
||||||
color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
text-wrap: pretty;
|
|
||||||
gap: 0.25em;
|
|
||||||
margin-top: 0.25em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.u-factor {
|
.u-factor {
|
||||||
@@ -209,4 +218,20 @@ export default defineComponent({
|
|||||||
color: rgb(22, 245, 22);
|
color: rgb(22, 245, 22);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.stats-list {
|
||||||
|
list-style: disc;
|
||||||
|
padding-left: 1em;
|
||||||
|
margin-top: 1em;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include smallScreen {
|
||||||
|
.filter-button span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -52,15 +52,14 @@
|
|||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr
|
<router-link
|
||||||
v-for="station in filteredStationList"
|
v-for="station in filteredStationList"
|
||||||
:class="{ 'last-selected': lastSelectedStationName == station.name }"
|
class="a-row"
|
||||||
|
role="row"
|
||||||
:key="station.name"
|
:key="station.name"
|
||||||
@click.left="setScenery(station.name)"
|
@click.right.prevent="openForumSite($event, station.generalInfo?.url)"
|
||||||
@click.right="openForumSite($event, station.generalInfo?.url)"
|
@keydown.space.prevent="openForumSite($event, station.generalInfo?.url)"
|
||||||
@keydown.enter="setScenery(station.name)"
|
:to="getSceneryRoute(station)"
|
||||||
@keydown.space="openForumSite($event, station.generalInfo?.url)"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
>
|
||||||
<td class="station-name" :class="station.generalInfo?.availability">
|
<td class="station-name" :class="station.generalInfo?.availability">
|
||||||
<b v-if="station.generalInfo?.project" style="color: salmon">{{
|
<b v-if="station.generalInfo?.project" style="color: salmon">{{
|
||||||
@@ -121,7 +120,7 @@
|
|||||||
<span v-if="station.onlineInfo?.dispatcherName">
|
<span v-if="station.onlineInfo?.dispatcherName">
|
||||||
<b
|
<b
|
||||||
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
||||||
@click.stop="openDonationCard"
|
@click.prevent="openDonationCard"
|
||||||
data-tooltip-type="DonatorTooltip"
|
data-tooltip-type="DonatorTooltip"
|
||||||
:data-tooltip-content="$t('donations.dispatcher-message')"
|
:data-tooltip-content="$t('donations.dispatcher-message')"
|
||||||
>
|
>
|
||||||
@@ -294,7 +293,7 @@
|
|||||||
>
|
>
|
||||||
{{ station.onlineInfo?.scheduledTrainCount.confirmed ?? '-' }}
|
{{ station.onlineInfo?.scheduledTrainCount.confirmed ?? '-' }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</router-link>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -319,7 +318,7 @@ import dateMixin from '../../mixins/dateMixin';
|
|||||||
import styleMixin from '../../mixins/styleMixin';
|
import styleMixin from '../../mixins/styleMixin';
|
||||||
import { useApiStore } from '../../store/apiStore';
|
import { useApiStore } from '../../store/apiStore';
|
||||||
import { useMainStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import { Status } from '../../typings/common';
|
import { Station, Status } from '../../typings/common';
|
||||||
import { useTooltipStore } from '../../store/tooltipStore';
|
import { useTooltipStore } from '../../store/tooltipStore';
|
||||||
import { getChangedFilters } from '../../managers/stationFilterManager';
|
import { getChangedFilters } from '../../managers/stationFilterManager';
|
||||||
import { ActiveSorter, HeadIdsType, headIconsIds, headIds } from './typings';
|
import { ActiveSorter, HeadIdsType, headIconsIds, headIds } from './typings';
|
||||||
@@ -334,7 +333,6 @@ export default defineComponent({
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
headIconsIds,
|
headIconsIds,
|
||||||
headIds,
|
headIds,
|
||||||
lastSelectedStationName: '',
|
|
||||||
getChangedFilters
|
getChangedFilters
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -364,21 +362,16 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setScenery(name: string) {
|
getSceneryRoute(station: Station) {
|
||||||
const station = this.filteredStationList.find((station) => station.name === name);
|
// TODO: Hide tooltips when navigating away
|
||||||
|
|
||||||
if (!station) return;
|
return {
|
||||||
|
|
||||||
this.lastSelectedStationName = station.name;
|
|
||||||
this.tooltipStore.hide();
|
|
||||||
|
|
||||||
this.$router.push({
|
|
||||||
name: 'SceneryView',
|
name: 'SceneryView',
|
||||||
query: {
|
query: {
|
||||||
station: station.name.replaceAll(' ', '_'),
|
station: station.name,
|
||||||
region: this.$route.query.region || undefined
|
region: this.$route.query.region || undefined
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
openDonationCard(e: Event) {
|
openDonationCard(e: Event) {
|
||||||
@@ -414,7 +407,7 @@ export default defineComponent({
|
|||||||
$rowCol: #424242;
|
$rowCol: #424242;
|
||||||
|
|
||||||
.station_table {
|
.station_table {
|
||||||
height: 80vh;
|
height: 90vh;
|
||||||
max-height: 2000px;
|
max-height: 2000px;
|
||||||
min-height: 700px;
|
min-height: 700px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -503,8 +496,10 @@ table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tr {
|
tr,
|
||||||
|
.a-row {
|
||||||
background-color: $rowCol;
|
background-color: $rowCol;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
&:nth-child(even) {
|
&:nth-child(even) {
|
||||||
background-color: lighten($rowCol, 5);
|
background-color: lighten($rowCol, 5);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default defineComponent({
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
white-space: pre-line;
|
||||||
|
|
||||||
padding: 0.25em 0.5em;
|
padding: 0.25em 0.5em;
|
||||||
border-radius: 0.25em;
|
border-radius: 0.25em;
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
class="stop-label"
|
class="stop-label"
|
||||||
:data-minor="stop.isSBL || (stop.nameRaw.endsWith(', po.') && !stop.duration)"
|
:data-minor="stop.isSBL || (stop.nameRaw.endsWith(', po') && !stop.duration)"
|
||||||
>
|
>
|
||||||
|
<router-link v-if="/(, podg$|<strong>)/.test(stop.nameHtml)" :to="sceneryHref">
|
||||||
<span class="name" v-html="stop.nameHtml"></span>
|
<span class="name" v-html="stop.nameHtml"></span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<span v-else class="name" v-html="stop.nameHtml"></span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-if="stop.position != 'begin'"
|
v-if="stop.position != 'begin'"
|
||||||
@@ -76,6 +80,12 @@ export default defineComponent({
|
|||||||
type: Object as PropType<TrainScheduleStop>,
|
type: Object as PropType<TrainScheduleStop>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
sceneryHref() {
|
||||||
|
return `/scenery?station=${this.stop.sceneryName}&checkpoint=${this.stop.nameRaw}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -97,18 +107,6 @@ s {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&[data-minor='true'] {
|
|
||||||
.date {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
background: none;
|
|
||||||
color: #aaa;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
background: $stopNameClr;
|
background: $stopNameClr;
|
||||||
border-radius: 0.5em 0 0 0.5em;
|
border-radius: 0.5em 0 0 0.5em;
|
||||||
@@ -131,6 +129,18 @@ s {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[data-minor='true'] {
|
||||||
|
.date {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
background: none;
|
||||||
|
color: #aaa;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.stop {
|
.stop {
|
||||||
&[data-stop-types='ph'],
|
&[data-stop-types='ph'],
|
||||||
&[data-stop-types='ph-pm'],
|
&[data-stop-types='ph-pm'],
|
||||||
@@ -146,6 +156,10 @@ s {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stop-label > a {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.stop .arrival {
|
.stop .arrival {
|
||||||
&[data-status='confirmed'][data-status-delayed='true'] {
|
&[data-status='confirmed'][data-status-delayed='true'] {
|
||||||
span {
|
span {
|
||||||
|
|||||||
@@ -2,39 +2,32 @@
|
|||||||
<div class="train-info" :data-extended="extended">
|
<div class="train-info" :data-extended="extended">
|
||||||
<section class="train-general">
|
<section class="train-general">
|
||||||
<div class="general-top-bar">
|
<div class="general-top-bar">
|
||||||
<div>
|
<div class="top-bar-header">
|
||||||
<b class="warning-timeout" v-if="train.isTimeout" :title="$t('trains.timeout')">?</b>
|
<b class="warning-timeout" v-if="train.isTimeout" :title="$t('trains.timeout')">?</b>
|
||||||
<span class="timetable-id" v-if="train.timetableData">
|
<span class="timetable-id" v-if="train.timetableData">
|
||||||
#{{ train.timetableData.timetableId }}
|
#{{ train.timetableData.timetableId }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span class="train-badge twr" v-if="train.timetableData?.TWR" :title="$t('general.TWR')">
|
||||||
class="timetable-warnings"
|
|
||||||
v-if="train.timetableData?.TWR || train.timetableData?.SKR"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="train-badge twr"
|
|
||||||
v-if="train.timetableData?.TWR"
|
|
||||||
:title="$t('general.TWR')"
|
|
||||||
>
|
|
||||||
TWR
|
TWR
|
||||||
</span>
|
</span>
|
||||||
<span
|
|
||||||
class="train-badge skr"
|
<span class="train-badge skr" v-if="train.timetableData?.SKR" :title="$t('general.SKR')">
|
||||||
v-if="train.timetableData?.SKR"
|
|
||||||
:title="$t('general.SKR')"
|
|
||||||
>
|
|
||||||
SKR
|
SKR
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
|
|
||||||
<strong>
|
<b
|
||||||
<span v-if="train.timetableData" class="text--primary"
|
v-if="train.timetableData"
|
||||||
>{{ train.timetableData.category }} </span
|
data-tooltip-type="BaseTooltip"
|
||||||
|
:data-tooltip-content="getCategoryExplanation(train.timetableData.category)"
|
||||||
|
class="text--primary tooltip-help"
|
||||||
>
|
>
|
||||||
<span class="train-number">{{ train.trainNo }}</span>
|
{{ train.timetableData.category }}
|
||||||
</strong>
|
</b>
|
||||||
|
<b class="train-number">{{ train.trainNo }}</b>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
|
|
||||||
|
<div class="train-driver">
|
||||||
<b
|
<b
|
||||||
class="level-badge driver"
|
class="level-badge driver"
|
||||||
:style="calculateExpStyle(train.driverLevel, train.isSupporter)"
|
:style="calculateExpStyle(train.driverLevel, train.isSupporter)"
|
||||||
@@ -42,7 +35,6 @@
|
|||||||
{{ train.driverLevel < 2 ? 'L' : `${train.driverLevel}` }}
|
{{ train.driverLevel < 2 ? 'L' : `${train.driverLevel}` }}
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<div class="train-driver">
|
|
||||||
<b
|
<b
|
||||||
v-if="apiStore.donatorsData.includes(train.driverName)"
|
v-if="apiStore.donatorsData.includes(train.driverName)"
|
||||||
data-tooltip-type="DonatorTooltip"
|
data-tooltip-type="DonatorTooltip"
|
||||||
@@ -55,19 +47,6 @@
|
|||||||
<span v-else>{{ train.driverName }}</span>
|
<span v-else>{{ train.driverName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="extended">
|
|
||||||
<button class="btn-timetable btn--image btn--action" @click="navigateToJournal">
|
|
||||||
<img src="/images/icon-train.svg" alt="train icon" />
|
|
||||||
<span>
|
|
||||||
{{ $t('trains.journal-button') }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn-exit btn--image btn--action" @click="closeModal">
|
|
||||||
<img src="/images/icon-exit.svg" alt="modal exit icon" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="general-timetable" v-if="train.timetableData">
|
<div class="general-timetable" v-if="train.timetableData">
|
||||||
@@ -88,7 +67,7 @@
|
|||||||
<div class="general-stops" v-if="train.timetableData">
|
<div class="general-stops" v-if="train.timetableData">
|
||||||
<span v-if="train.timetableData.followingStops.length > 2">
|
<span v-if="train.timetableData.followingStops.length > 2">
|
||||||
{{ $t('trains.via-title') }}
|
{{ $t('trains.via-title') }}
|
||||||
<span v-html="displayStopList(train.timetableData.followingStops)"></span>
|
<span v-html="getTrainStopsHtml(train.timetableData.followingStops)"></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -97,21 +76,22 @@
|
|||||||
<ProgressBar :progressPercent="confirmedPercentage(train.timetableData.followingStops)" />
|
<ProgressBar :progressPercent="confirmedPercentage(train.timetableData.followingStops)" />
|
||||||
|
|
||||||
<span class="progress-distance">
|
<span class="progress-distance">
|
||||||
{{ currentDistance(train.timetableData.followingStops) }} km /
|
<span>{{ currentDistance(train.timetableData.followingStops) }} km</span>
|
||||||
<span class="text--primary"> {{ train.timetableData.routeDistance }} km </span>
|
<span>/</span>
|
||||||
|
|
<span class="text--primary">{{ train.timetableData.routeDistance }} km </span>
|
||||||
|
<span>|</span>
|
||||||
<span v-html="currentDelay(train.timetableData.followingStops)"></span>
|
<span v-html="currentDelay(train.timetableData.followingStops)"></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="status-badges">
|
<div class="status-badges">
|
||||||
<div v-if="!train.currentStationHash" class="train-badge offline">
|
<div v-if="!train.currentStationHash" class="train-badge offline">
|
||||||
<img src="/images/icon-offline.svg" alt="" />
|
<img src="/images/icon-offline.svg" alt="offline train icon" />
|
||||||
{{ $t('trains.scenery-offline') }}
|
{{ $t('trains.scenery-offline') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!train.online" class="train-badge offline">
|
<div v-if="!train.online" class="train-badge offline">
|
||||||
<img src="/images/icon-offline.svg" alt="" />
|
<img src="/images/icon-offline.svg" alt="offline train icon" />
|
||||||
Offline {{ lastSeenMessage(train.lastSeen) }}
|
Offline {{ lastSeenMessage(train.lastSeen) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -179,11 +159,11 @@ import ProgressBar from '../Global/ProgressBar.vue';
|
|||||||
import { useMainStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import { useApiStore } from '../../store/apiStore';
|
import { useApiStore } from '../../store/apiStore';
|
||||||
import StockList from '../Global/StockList.vue';
|
import StockList from '../Global/StockList.vue';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
|
||||||
import { Train } from '../../typings/common';
|
import { Train } from '../../typings/common';
|
||||||
|
import trainCategoryMixin from '../../mixins/trainCategoryMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [trainInfoMixin, styleMixin, modalTrainMixin],
|
mixins: [trainInfoMixin, styleMixin, trainCategoryMixin],
|
||||||
components: { ProgressBar, StockList },
|
components: { ProgressBar, StockList },
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@@ -212,19 +192,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
return Math.min(vehicleSpeed, acc);
|
return Math.min(vehicleSpeed, acc);
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
journalRouteLocation() {
|
||||||
methods: {
|
return {
|
||||||
navigateToJournal() {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/journal/timetables',
|
path: '/journal/timetables',
|
||||||
query: {
|
query: {
|
||||||
'search-driver': this.train.driverName
|
'search-driver': this.train.driverName
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
this.closeModal();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -266,6 +241,12 @@ export default defineComponent({
|
|||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.train-driver {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
.train-driver img {
|
.train-driver img {
|
||||||
max-height: 20px;
|
max-height: 20px;
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
@@ -297,24 +278,15 @@ export default defineComponent({
|
|||||||
.general-top-bar {
|
.general-top-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
& > div {
|
.top-bar-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-timetable {
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-exit {
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.general-status {
|
.general-status {
|
||||||
@@ -361,10 +333,14 @@ export default defineComponent({
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
padding: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-distance {
|
.progress-distance {
|
||||||
margin-right: 0.25em;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timetable-warnings {
|
.timetable-warnings {
|
||||||
@@ -377,9 +353,5 @@ export default defineComponent({
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1em 0;
|
gap: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-timetable > span {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="train-modal" v-if="chosenTrain" @keydown.esc="closeModal">
|
|
||||||
<div class="modal-background" @click="closeModal"></div>
|
|
||||||
<div class="modal-content" ref="content" tabindex="0">
|
|
||||||
<TrainInfo :train="chosenTrain" :extended="true" ref="trainInfo" />
|
|
||||||
<TrainSchedule :train="chosenTrain" tabindex="0" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
|
||||||
import TrainInfo from './TrainInfo.vue';
|
|
||||||
import TrainSchedule from './TrainSchedule.vue';
|
|
||||||
import { Train } from '../../typings/common';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { TrainInfo, TrainSchedule },
|
|
||||||
mixins: [modalTrainMixin],
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
chosenTrain() {
|
|
||||||
return this.store.trainList.find((train) => train.modalId == this.store.chosenModalTrainId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
chosenTrain(train: Train | undefined) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (train) {
|
|
||||||
document.body.classList.add('no-scroll');
|
|
||||||
const contentEl = this.$refs['content'] as HTMLElement;
|
|
||||||
contentEl.focus();
|
|
||||||
} else {
|
|
||||||
(this.store.modalLastClickedTarget as any)?.focus();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
document.body.classList.remove('no-scroll');
|
|
||||||
}, 90);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../styles/responsive.scss';
|
|
||||||
|
|
||||||
.train-modal {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
color: white;
|
|
||||||
z-index: 200;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, 0.55);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
position: relative;
|
|
||||||
overflow-y: scroll;
|
|
||||||
|
|
||||||
width: 95vw;
|
|
||||||
max-height: 95vh;
|
|
||||||
max-height: 95dvh;
|
|
||||||
margin-top: 1em;
|
|
||||||
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
box-shadow: 0 0 15px 10px #0e0e0e;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include midScreen {
|
|
||||||
.exit {
|
|
||||||
margin: 0.5em;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 1.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -15,12 +15,14 @@
|
|||||||
<div class="search_content">
|
<div class="search_content">
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<input
|
<input
|
||||||
|
v-model="searchedTrain"
|
||||||
class="search-input"
|
class="search-input"
|
||||||
ref="initFocusedElement"
|
ref="initFocusedElement"
|
||||||
|
id="train-search"
|
||||||
|
name="train-search"
|
||||||
@focus="preventKeyDown = true"
|
@focus="preventKeyDown = true"
|
||||||
@blur="preventKeyDown = false"
|
@blur="preventKeyDown = false"
|
||||||
:placeholder="$t(`options.search-train`)"
|
:placeholder="$t(`options.search-train`)"
|
||||||
v-model="searchedTrain"
|
|
||||||
/>
|
/>
|
||||||
<button class="search-exit">
|
<button class="search-exit">
|
||||||
<img
|
<img
|
||||||
@@ -33,11 +35,13 @@
|
|||||||
|
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<input
|
<input
|
||||||
|
v-model="searchedDriver"
|
||||||
class="search-input"
|
class="search-input"
|
||||||
|
id="driver-search"
|
||||||
|
name="driver-search"
|
||||||
@focus="preventKeyDown = true"
|
@focus="preventKeyDown = true"
|
||||||
@blur="preventKeyDown = false"
|
@blur="preventKeyDown = false"
|
||||||
:placeholder="$t(`options.search-driver`)"
|
:placeholder="$t(`options.search-driver`)"
|
||||||
v-model="searchedDriver"
|
|
||||||
/>
|
/>
|
||||||
<button class="search-exit">
|
<button class="search-exit">
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
v-else
|
v-else
|
||||||
src="/images/icon-we4a.png"
|
src="/images/icon-we4a.png"
|
||||||
:title="$t('trains.we4a-tooltip')"
|
:title="$t('trains.we4a-tooltip')"
|
||||||
width="12"
|
width="10"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
v-else
|
v-else
|
||||||
src="/images/icon-we4a.png"
|
src="/images/icon-we4a.png"
|
||||||
:title="$t('trains.we4a-tooltip')"
|
:title="$t('trains.we4a-tooltip')"
|
||||||
width="12"
|
width="10"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,20 +165,18 @@ export default defineComponent({
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
scheduleStops(): TrainScheduleStop[] {
|
scheduleStops(): TrainScheduleStop[] {
|
||||||
let currentSceneryIndex = 0;
|
if (!this.train.timetableData) return [];
|
||||||
|
|
||||||
|
const { timetablePath } = this.train.timetableData;
|
||||||
|
let currentPathIndex = 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.train.timetableData?.followingStops.map((stop, i, arr) => {
|
this.train.timetableData?.followingStops.map((stop, i, arr) => {
|
||||||
const isExternal =
|
const isExternal =
|
||||||
i > 0 &&
|
i < arr.length - 1 &&
|
||||||
stop.arrivalLine != null &&
|
stop.departureLine === timetablePath[currentPathIndex].departureRouteExt;
|
||||||
(stop.arrivalLine != arr[i - 1].departureLine ||
|
|
||||||
(stop.arrivalLine == arr[i - 1].departureLine &&
|
|
||||||
!/-|_|(^it\d+)|(^sbl)/gi.test(stop.arrivalLine)));
|
|
||||||
|
|
||||||
if (isExternal) currentSceneryIndex++;
|
const sceneryName = timetablePath[currentPathIndex].stationName;
|
||||||
|
|
||||||
const sceneryName = this.train.timetableData!.sceneryNames[currentSceneryIndex];
|
|
||||||
const sceneryInfo = this.apiStore.sceneryData.find((st) => st.name == sceneryName);
|
const sceneryInfo = this.apiStore.sceneryData.find((st) => st.name == sceneryName);
|
||||||
|
|
||||||
const arrivalLineInfo = sceneryInfo?.routesInfo.find(
|
const arrivalLineInfo = sceneryInfo?.routesInfo.find(
|
||||||
@@ -189,6 +187,8 @@ export default defineComponent({
|
|||||||
(r) => r.routeName == stop.departureLine
|
(r) => r.routeName == stop.departureLine
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (isExternal) currentPathIndex++;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nameHtml: stop.stopName,
|
nameHtml: stop.stopName,
|
||||||
nameRaw: stop.stopNameRAW,
|
nameRaw: stop.stopNameRAW,
|
||||||
@@ -249,7 +249,7 @@ export default defineComponent({
|
|||||||
i < this.train.timetableData!.followingStops.length;
|
i < this.train.timetableData!.followingStops.length;
|
||||||
i++
|
i++
|
||||||
) {
|
) {
|
||||||
if (/po\.|sbl/gi.test(this.train.timetableData!.followingStops[i].stopNameRAW))
|
if (/(, po$|sbl|, pe$)/gi.test(this.train.timetableData!.followingStops[i].stopNameRAW))
|
||||||
activeMinorStopList.push(i);
|
activeMinorStopList.push(i);
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
@@ -286,7 +286,7 @@ $blinkAnim: 0.5s ease-in-out alternate infinite blink;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.train-schedule {
|
.train-schedule {
|
||||||
padding: 0 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schedule-wrapper {
|
.schedule-wrapper {
|
||||||
@@ -523,8 +523,17 @@ $blinkAnim: 0.5s ease-in-out alternate infinite blink;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scenery-route {
|
.scenery-route {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25em;
|
||||||
|
|
||||||
|
span:nth-child(2) {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25em;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
vertical-align: middle;
|
width: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dropdown" @keydown.esc="showOptions = false" @focusout="showOptions = false">
|
<div
|
||||||
|
class="dropdown"
|
||||||
|
@keydown.esc="showOptions = false"
|
||||||
|
v-click-outside="() => (showOptions = false)"
|
||||||
|
>
|
||||||
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
|
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
|
||||||
|
|
||||||
<button class="filter-button btn--filled btn--image" @click="toggleShowOptions" ref="button">
|
<button class="filter-button btn--filled btn--image" @click="toggleShowOptions" ref="button">
|
||||||
@@ -19,21 +23,21 @@
|
|||||||
<div v-if="apiStore.dataStatuses.connection == Status.Loaded && regionTrains.length > 0">
|
<div v-if="apiStore.dataStatuses.connection == Status.Loaded && regionTrains.length > 0">
|
||||||
<div class="top-list general">
|
<div class="top-list general">
|
||||||
<transition-group tag="ul" name="stats-anim">
|
<transition-group tag="ul" name="stats-anim">
|
||||||
<li class="badge" key="timetable-count">
|
<li class="badge stat-badge" key="timetable-count">
|
||||||
<span>{{ $t('train-stats.timetable-count') }}</span>
|
<span>{{ $t('train-stats.timetable-count') }}</span>
|
||||||
<span>
|
<span>
|
||||||
<b>{{ regionTrainsWithTT.length }}</b>
|
<b>{{ regionTrainsWithTT.length }}</b>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="badge" key="avg-speed">
|
<li class="badge stat-badge" key="avg-speed">
|
||||||
<span>{{ $t('train-stats.avg-speed') }}</span>
|
<span>{{ $t('train-stats.avg-speed') }}</span>
|
||||||
<span>
|
<span>
|
||||||
<b>{{ stats.avgSpeed.toFixed(1) }} km/h</b>
|
<b>{{ stats.avgSpeed.toFixed(1) }} km/h</b>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="badge" key="avg-distance">
|
<li class="badge stat-badge" key="avg-distance">
|
||||||
<span>{{ $t('train-stats.avg-timetable') }}</span>
|
<span>{{ $t('train-stats.avg-timetable') }}</span>
|
||||||
<span>
|
<span>
|
||||||
<b>{{ stats.avgDistance.toFixed(1) }} km</b>
|
<b>{{ stats.avgDistance.toFixed(1) }} km</b>
|
||||||
@@ -46,7 +50,7 @@
|
|||||||
<h3>{{ $t('train-stats.top-categories') }}</h3>
|
<h3>{{ $t('train-stats.top-categories') }}</h3>
|
||||||
|
|
||||||
<transition-group tag="ul" name="stats-anim">
|
<transition-group tag="ul" name="stats-anim">
|
||||||
<li class="badge" v-for="top in stats.topCategories" :key="top.name">
|
<li class="badge stat-badge" v-for="top in stats.topCategories" :key="top.name">
|
||||||
<span>{{ top.name }}</span>
|
<span>{{ top.name }}</span>
|
||||||
<span>{{ top.count }}</span>
|
<span>{{ top.count }}</span>
|
||||||
</li>
|
</li>
|
||||||
@@ -61,7 +65,7 @@
|
|||||||
<h3>{{ $t('train-stats.top-vehicles') }}</h3>
|
<h3>{{ $t('train-stats.top-vehicles') }}</h3>
|
||||||
|
|
||||||
<transition-group tag="ul" name="stats-anim">
|
<transition-group tag="ul" name="stats-anim">
|
||||||
<li class="badge" v-for="top in stats.topVehicles" :key="top.name">
|
<li class="badge stat-badge" v-for="top in stats.topVehicles" :key="top.name">
|
||||||
<span>{{ top.name }}</span>
|
<span>{{ top.name }}</span>
|
||||||
<span>{{ top.count }}</span>
|
<span>{{ top.count }}</span>
|
||||||
</li>
|
</li>
|
||||||
@@ -76,7 +80,7 @@
|
|||||||
<h3>{{ $t('train-stats.top-units') }}</h3>
|
<h3>{{ $t('train-stats.top-units') }}</h3>
|
||||||
|
|
||||||
<transition-group tag="ul" name="stats-anim">
|
<transition-group tag="ul" name="stats-anim">
|
||||||
<li class="badge" v-for="top in stats.topUnits.slice(0, 7)" :key="top.name">
|
<li class="badge stat-badge" v-for="top in stats.topUnits.slice(0, 7)" :key="top.name">
|
||||||
<span>{{ top.name }}</span>
|
<span>{{ top.name }}</span>
|
||||||
<span>{{ top.count }}</span>
|
<span>{{ top.count }}</span>
|
||||||
</li>
|
</li>
|
||||||
@@ -95,6 +99,8 @@
|
|||||||
<div class="no-data" v-else>
|
<div class="no-data" v-else>
|
||||||
{{ $t('train-stats.no-stats') }}
|
{{ $t('train-stats.no-stats') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div tabindex="0" @focus="() => (showOptions = false)"></div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@@ -236,43 +242,12 @@ h3 {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
// @include smallScreen {
|
|
||||||
// justify-content: center;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
& > span:first-child {
|
|
||||||
background-color: $accentCol;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown_wrapper {
|
.dropdown_wrapper {
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-anim {
|
|
||||||
&-move,
|
|
||||||
&-enter-active,
|
|
||||||
&-leave-active {
|
|
||||||
transition: all 250ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter-from,
|
|
||||||
&-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
h1,
|
h1,
|
||||||
.no-data {
|
.no-data {
|
||||||
|
|||||||
@@ -17,11 +17,10 @@
|
|||||||
class="train-row"
|
class="train-row"
|
||||||
v-for="train in trains"
|
v-for="train in trains"
|
||||||
:key="train.id"
|
:key="train.id"
|
||||||
tabindex="0"
|
|
||||||
@click.stop="selectModalTrain(train, $event.currentTarget)"
|
|
||||||
@keydown.enter="selectModalTrain(train, $event.currentTarget)"
|
|
||||||
>
|
>
|
||||||
|
<router-link class="a-block" :to="train.driverRouteLocation">
|
||||||
<TrainInfo :train="train" :extended="false" />
|
<TrainInfo :train="train" :extended="false" />
|
||||||
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,7 +29,6 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, inject, PropType, Ref } from 'vue';
|
import { defineComponent, inject, PropType, Ref } from 'vue';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
|
||||||
import { useMainStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import TrainInfo from './TrainInfo.vue';
|
import TrainInfo from './TrainInfo.vue';
|
||||||
@@ -47,8 +45,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [modalTrainMixin],
|
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useMainStore();
|
const store = useMainStore();
|
||||||
const apiStore = useApiStore();
|
const apiStore = useApiStore();
|
||||||
@@ -107,7 +103,5 @@ li.train-row {
|
|||||||
background-color: var(--clr-secondary);
|
background-color: var(--clr-secondary);
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+54
-4
@@ -48,7 +48,48 @@
|
|||||||
"footer": {
|
"footer": {
|
||||||
"discord": "Stacjownik Discord server"
|
"discord": "Stacjownik Discord server"
|
||||||
},
|
},
|
||||||
|
"categories": {
|
||||||
|
"EI": "domestic express",
|
||||||
|
"EC": "international express",
|
||||||
|
"EN": "domestic night express",
|
||||||
|
|
||||||
|
"MP": "intervoivodeship bullet",
|
||||||
|
"MO": "intervoivodeship regio",
|
||||||
|
"MM": "international bullet",
|
||||||
|
"MH": "intervoivodeship night bullet",
|
||||||
|
|
||||||
|
"RP": "voivodeship bullet",
|
||||||
|
"RM": "international voivodeship regio",
|
||||||
|
"RO": "voivodeship regio",
|
||||||
|
"RA": "voivodeship regio (urban)",
|
||||||
|
|
||||||
|
"PW": "empty passenger",
|
||||||
|
"PX": "empty passenger test drive",
|
||||||
|
|
||||||
|
"TC": "international freight (intermodal)",
|
||||||
|
"TG": "international freight (organized cargo)",
|
||||||
|
"TR": "international freight (unorganized cargo)",
|
||||||
|
"TD": "domestic freight (intermodal)",
|
||||||
|
"TM": "domestic freight (organized cargo)",
|
||||||
|
"TN": "domestic freight (unorganized cargo)",
|
||||||
|
"TK": "freight (for stations & sidings)",
|
||||||
|
"TS": "empty freight test drive",
|
||||||
|
"TH": "locomotive rolling stock (over 3 vehicles)",
|
||||||
|
|
||||||
|
"LT": "freight locomotive only",
|
||||||
|
"LP": "passenger locomotive only",
|
||||||
|
"LS": "shunting locomotive only",
|
||||||
|
"LZ": "shunting locomotive only",
|
||||||
|
|
||||||
|
"ZN": "inspection / diagnostic type",
|
||||||
|
"ZU": "other maintenance type",
|
||||||
|
"ZG": "emergency (deprecated category)",
|
||||||
|
|
||||||
|
"E": "electric loco",
|
||||||
|
"J": "EMU",
|
||||||
|
"S": "diesel loco",
|
||||||
|
"M": "DMU"
|
||||||
|
},
|
||||||
"vehicle-preview": {
|
"vehicle-preview": {
|
||||||
"loading": "Loading preview...",
|
"loading": "Loading preview...",
|
||||||
"error": "Oops! The vehicle preview seems to be missing! :/"
|
"error": "Oops! The vehicle preview seems to be missing! :/"
|
||||||
@@ -123,7 +164,7 @@
|
|||||||
"search-train": "Train no.",
|
"search-train": "Train no.",
|
||||||
"search-driver": "Driver name",
|
"search-driver": "Driver name",
|
||||||
"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-issuedFrom": "Issuing scenery name",
|
"search-issuedFrom": "Issuing scenery name",
|
||||||
"search-via": "Via scenery name",
|
"search-via": "Via scenery name",
|
||||||
@@ -252,9 +293,11 @@
|
|||||||
"minTwoWay": "MIN. OTHER DOUBLE TRACK ROUTES"
|
"minTwoWay": "MIN. OTHER DOUBLE TRACK ROUTES"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"sceneries-search": "SCENERY SEARCH:",
|
||||||
|
"sceneries-placeholder": "Enter scenery name...",
|
||||||
"authors-search": "SEARCH BY AUTHOR NAME (other filters apply):",
|
"authors-search": "SEARCH BY AUTHOR NAME (other filters apply):",
|
||||||
"authors-placeholder": "Enter the author nickname...",
|
"authors-placeholder": "Enter the author nickname...",
|
||||||
"authors-button-title": "Search",
|
"search-button-title": "SEARCH",
|
||||||
|
|
||||||
"minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:",
|
"minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:",
|
||||||
"now": "NOW",
|
"now": "NOW",
|
||||||
@@ -360,7 +403,14 @@
|
|||||||
"scenery-offline": "Offline ride",
|
"scenery-offline": "Offline ride",
|
||||||
"timeout": "An error occured while trying to refresh SWDR timetable data!",
|
"timeout": "An error occured while trying to refresh SWDR timetable data!",
|
||||||
|
|
||||||
"journal-button": "DRIVER'S JOURNAL"
|
"driver-journal-link": "DRIVER JOURNAL",
|
||||||
|
"driver-return-link": "GO BACK",
|
||||||
|
|
||||||
|
"driver-not-found-header": "Train not found! :/",
|
||||||
|
"driver-not-found-desc-1": "This train has already been terminated or it's offline.",
|
||||||
|
"driver-not-found-desc-2": "You can browse timetable history in the",
|
||||||
|
"driver-not-found-journal": "TIMETABLES JOURNAL",
|
||||||
|
"driver-not-found-return": "GO BACK TO THE MAIN SITE"
|
||||||
},
|
},
|
||||||
"train-stats": {
|
"train-stats": {
|
||||||
"stats-button": "STATISTICS",
|
"stats-button": "STATISTICS",
|
||||||
@@ -512,7 +562,7 @@
|
|||||||
|
|
||||||
"forum-topic": "Official {name} forum topic",
|
"forum-topic": "Official {name} forum topic",
|
||||||
|
|
||||||
"pragotron-link": "Timetable pallet board (beta)",
|
"pragotron-link": "Timetable pallet board",
|
||||||
"tablice-link": "Timetable summary board (by Thundo)",
|
"tablice-link": "Timetable summary board (by Thundo)",
|
||||||
|
|
||||||
"bottom-info": "Show full history in the Journal tab"
|
"bottom-info": "Show full history in the Journal tab"
|
||||||
|
|||||||
+56
-5
@@ -45,6 +45,48 @@
|
|||||||
"footer": {
|
"footer": {
|
||||||
"discord": "Serwer Discord Stacjownika"
|
"discord": "Serwer Discord Stacjownika"
|
||||||
},
|
},
|
||||||
|
"categories": {
|
||||||
|
"EI": "ekspres krajowy",
|
||||||
|
"EC": "ekspres międzynarodowy",
|
||||||
|
"EN": "ekspres krajowy nocny",
|
||||||
|
|
||||||
|
"MP": "międzywojewódzki pospieszny",
|
||||||
|
"MO": "międzywojewódzki osobowy",
|
||||||
|
"MM": "międzynarodowy pospieszny",
|
||||||
|
"MH": "międzywojewódzki pospieszny (nocny)",
|
||||||
|
|
||||||
|
"RP": "wojewódzki pospieszny",
|
||||||
|
"RO": "wojewódzki osobowy",
|
||||||
|
"RM": "wojewódzki osobowy międzynarodowy",
|
||||||
|
"RA": "wojewódzki osobowy algomeracyjny",
|
||||||
|
|
||||||
|
"PW": "pasażerski próżny - służbowy",
|
||||||
|
"PX": "pasażerski próżny próbny",
|
||||||
|
|
||||||
|
"TC": "towarowy międzynarodowy intermodalny",
|
||||||
|
"TG": "towarowy międzynarodowy masowy",
|
||||||
|
"TR": "towarowy międzynarodowy niemasowy",
|
||||||
|
"TD": "towarowy krajowy intermodalny",
|
||||||
|
"TM": "towarowy krajowy masowy",
|
||||||
|
"TN": "towarowy krajowy niemasowy",
|
||||||
|
"TK": "towarowy zdawczy",
|
||||||
|
"TS": "towarowy próżny próbny",
|
||||||
|
"TH": "skład lokomotyw (powyżej 3 pojazdów)",
|
||||||
|
|
||||||
|
"LT": "lokomotywa towarowa luzem",
|
||||||
|
"LP": "lokomotywa pasażerska luzem",
|
||||||
|
"LS": "lokomotywa manewrowa luzem",
|
||||||
|
"LZ": "lokomotywa dla poc. utrzymaniowo-naprawczych",
|
||||||
|
|
||||||
|
"ZN": "inspekcyjny / diagnostyczny",
|
||||||
|
"ZU": "inny utrzymaniowy",
|
||||||
|
"ZG": "ratunkowy (kat. wycofana)",
|
||||||
|
|
||||||
|
"E": "elektrowóz",
|
||||||
|
"J": "EZT",
|
||||||
|
"S": "spalinowóz",
|
||||||
|
"M": "SZT"
|
||||||
|
},
|
||||||
"vehicle-preview": {
|
"vehicle-preview": {
|
||||||
"loading": "Ładowanie podglądu...",
|
"loading": "Ładowanie podglądu...",
|
||||||
"error": "Ups! Nie znaleziono podglądu pojazdu! :/"
|
"error": "Ups! Nie znaleziono podglądu pojazdu! :/"
|
||||||
@@ -119,7 +161,7 @@
|
|||||||
"search-train": "Nr pociągu / #",
|
"search-train": "Nr pociągu / #",
|
||||||
"search-driver": "Nick maszynisty",
|
"search-driver": "Nick maszynisty",
|
||||||
"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-issuedFrom": "Sceneria początkowa",
|
"search-issuedFrom": "Sceneria początkowa",
|
||||||
"search-via": "Przez scenerię",
|
"search-via": "Przez scenerię",
|
||||||
@@ -249,9 +291,11 @@
|
|||||||
"minTwoWay": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)"
|
"minTwoWay": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)"
|
||||||
},
|
},
|
||||||
|
|
||||||
"authors-search": "SZUKAJ AUTORA (uwzględnia inne filtry):",
|
"sceneries-search": "WYSZUKAJ SCENERIĘ:",
|
||||||
|
"sceneries-placeholder": "Wpisz nazwę scenerii...",
|
||||||
|
"authors-search": "WYSZUKAJ AUTORA (uwzględnia inne filtry):",
|
||||||
"authors-placeholder": "Wpisz nick autora...",
|
"authors-placeholder": "Wpisz nick autora...",
|
||||||
"authors-button-title": "Szukaj",
|
"search-button-title": "SZUKAJ",
|
||||||
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
|
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
|
||||||
"now": "TERAZ",
|
"now": "TERAZ",
|
||||||
"hour": " godz.",
|
"hour": " godz.",
|
||||||
@@ -345,7 +389,14 @@
|
|||||||
|
|
||||||
"timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR",
|
"timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR",
|
||||||
|
|
||||||
"journal-button": "DZIENNIK MASZYNISTY"
|
"driver-journal-link": "DZIENNIK MASZYNISTY",
|
||||||
|
"driver-return-link": "POWRÓT",
|
||||||
|
|
||||||
|
"driver-not-found-header": "Nie znaleziono pociągu! :/",
|
||||||
|
"driver-not-found-desc-1": "Ten pociąg prawdopodobnie zakończył już swój bieg lub jest offline.",
|
||||||
|
"driver-not-found-desc-2": "Historię rozkładów jazdy możesz przejrzeć w",
|
||||||
|
"driver-not-found-journal": "DZIENNIKU RJ",
|
||||||
|
"driver-not-found-return": "WRÓĆ NA STRONĘ GŁÓWNĄ"
|
||||||
},
|
},
|
||||||
"train-stats": {
|
"train-stats": {
|
||||||
"stats-button": "STATYSTYKI",
|
"stats-button": "STATYSTYKI",
|
||||||
@@ -495,7 +546,7 @@
|
|||||||
|
|
||||||
"forum-topic": "Oficjalny wątek scenerii {name}",
|
"forum-topic": "Oficjalny wątek scenerii {name}",
|
||||||
|
|
||||||
"pragotron-link": "Paletowa tablica informacyjna (beta)",
|
"pragotron-link": "Paletowa tablica informacyjna",
|
||||||
"tablice-link": "Tablica informacyjna zbiorcza (autorstwa Thundo)",
|
"tablice-link": "Tablica informacyjna zbiorcza (autorstwa Thundo)",
|
||||||
|
|
||||||
"bottom-info": "Pokaż pełną historię w zakładce Dziennika"
|
"bottom-info": "Pokaż pełną historię w zakładce Dziennika"
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { defineComponent } from 'vue';
|
|
||||||
import { useMainStore } from '../store/mainStore';
|
|
||||||
import { useTooltipStore } from '../store/tooltipStore';
|
|
||||||
import { Train } from '../typings/common';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
store: useMainStore(),
|
|
||||||
tooltipStore: useTooltipStore()
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
selectModalTrain(train: Train, target?: EventTarget | null) {
|
|
||||||
this.store.chosenModalTrainId = train.modalId;
|
|
||||||
if (target) this.store.modalLastClickedTarget = target;
|
|
||||||
},
|
|
||||||
|
|
||||||
selectModalTrainById(modalId: string, target?: EventTarget | null) {
|
|
||||||
this.store.chosenModalTrainId = modalId;
|
|
||||||
if (target) this.store.modalLastClickedTarget = target;
|
|
||||||
},
|
|
||||||
|
|
||||||
closeModal() {
|
|
||||||
this.store.chosenModalTrainId = undefined;
|
|
||||||
this.tooltipStore.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
methods: {
|
||||||
|
getCategoryExplanation(categoryCode: string) {
|
||||||
|
const categoryKey = categoryCode.slice(0, 2);
|
||||||
|
const vehicleTypeKey = categoryCode.slice(-1);
|
||||||
|
|
||||||
|
return `${this.$t('categories.' + categoryKey)}\n(${this.$t('categories.' + vehicleTypeKey)})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -75,18 +75,18 @@ export default defineComponent({
|
|||||||
return positionString.charAt(0).toUpperCase() + positionString.slice(1);
|
return positionString.charAt(0).toUpperCase() + positionString.slice(1);
|
||||||
},
|
},
|
||||||
|
|
||||||
displayStopList(stops: TrainStop[]): string | undefined {
|
getTrainStopsHtml(stops: TrainStop[]): string {
|
||||||
if (!stops) return '';
|
if (!stops) return '';
|
||||||
|
|
||||||
return stops
|
return stops
|
||||||
.reduce((acc: string[], stop: TrainStop, i: number) => {
|
.reduce((acc: string[], stop: TrainStop, i: number) => {
|
||||||
if (stop.stopType.includes('ph') && !stop.stopNameRAW.includes('po.'))
|
if (stop.stopType.includes('ph'))
|
||||||
acc.push(
|
acc.push(
|
||||||
`<strong style='color:${stop.confirmed ? 'springgreen' : 'white'}'>${
|
`<strong style='color:${stop.confirmed ? 'springgreen' : 'white'}'>${
|
||||||
stop.stopName
|
stop.stopName
|
||||||
}</strong>`
|
}</strong>`
|
||||||
);
|
);
|
||||||
else if (i > 0 && i < stops.length - 1 && !/po\.|sbl/gi.test(stop.stopNameRAW))
|
else if (i > 0 && i < stops.length - 1 && !/(, po$|sbl)/gi.test(stop.stopNameRAW))
|
||||||
acc.push(
|
acc.push(
|
||||||
`<span style='color:${stop.confirmed ? 'springgreen' : 'lightgray'}'>${
|
`<span style='color:${stop.confirmed ? 'springgreen' : 'lightgray'}'>${
|
||||||
stop.stopName
|
stop.stopName
|
||||||
|
|||||||
+20
-1
@@ -20,6 +20,15 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
region: route.query.region
|
region: route.query.region
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/driver',
|
||||||
|
name: 'DriverView',
|
||||||
|
component: () => import('../views/DriverView.vue'),
|
||||||
|
props: (route) => ({
|
||||||
|
trainId: route.query.trainId,
|
||||||
|
modalId: route.query.modalId
|
||||||
|
})
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/scenery',
|
path: '/scenery',
|
||||||
name: 'SceneryView',
|
name: 'SceneryView',
|
||||||
@@ -57,7 +66,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
scrollBehavior(to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
if (to.name == 'SceneryView' && from.name !== to.name && from.query['view'] === undefined)
|
if (
|
||||||
|
(to.name == 'SceneryView' || to.name == 'DriverView') &&
|
||||||
|
from.name !== to.name &&
|
||||||
|
from.query['view'] === undefined
|
||||||
|
)
|
||||||
return { el: `.app_main`, top: -15 };
|
return { el: `.app_main`, top: -15 };
|
||||||
|
|
||||||
if (savedPosition) return savedPosition;
|
if (savedPosition) return savedPosition;
|
||||||
@@ -66,4 +79,10 @@ const router = createRouter({
|
|||||||
routes
|
routes
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
next((vm) => {
|
||||||
|
(vm as any)['xd'] = 'xd';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
+21
-16
@@ -50,15 +50,6 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
|
|
||||||
const timetable = train.timetable;
|
const timetable = train.timetable;
|
||||||
|
|
||||||
const sceneryNames =
|
|
||||||
train.timetable?.sceneries?.map(
|
|
||||||
(sceneryHash) =>
|
|
||||||
apiStore.activeData?.activeSceneries?.find((st) => st.stationHash === sceneryHash)
|
|
||||||
?.stationName ??
|
|
||||||
apiStore.sceneryData.find((sd) => sd.hash === sceneryHash)?.name ??
|
|
||||||
sceneryHash
|
|
||||||
) ?? [];
|
|
||||||
|
|
||||||
const trainObj = {
|
const trainObj = {
|
||||||
id: train.id,
|
id: train.id,
|
||||||
modalId: `${train.driverName}${train.trainNo}`, // simplified id for train modal
|
modalId: `${train.driverName}${train.trainNo}`, // simplified id for train modal
|
||||||
@@ -86,6 +77,13 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
isSupporter: train.driverIsSupporter,
|
isSupporter: train.driverIsSupporter,
|
||||||
driverLevel: train.driverLevel,
|
driverLevel: train.driverLevel,
|
||||||
|
|
||||||
|
driverRouteLocation: {
|
||||||
|
name: 'DriverView',
|
||||||
|
query: {
|
||||||
|
trainId: train.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
timetableData: timetable
|
timetableData: timetable
|
||||||
? {
|
? {
|
||||||
timetableId: timetable.timetableId,
|
timetableId: timetable.timetableId,
|
||||||
@@ -96,7 +94,7 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
followingStops: timetable.stopList,
|
followingStops: timetable.stopList,
|
||||||
routeDistance: timetable.stopList[timetable.stopList.length - 1].stopDistance,
|
routeDistance: timetable.stopList[timetable.stopList.length - 1].stopDistance,
|
||||||
sceneries: timetable.sceneries,
|
sceneries: timetable.sceneries,
|
||||||
sceneryNames: sceneryNames.reverse(),
|
// sceneryNames: sceneryNames.reverse(),
|
||||||
timetablePath: timetable.path.split(';').map((pathElementString) => {
|
timetablePath: timetable.path.split(';').map((pathElementString) => {
|
||||||
const [arrival, station, departure] = pathElementString.split(',');
|
const [arrival, station, departure] = pathElementString.split(',');
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
arrivalRouteExt: arrival,
|
arrivalRouteExt: arrival,
|
||||||
departureRouteExt: departure,
|
departureRouteExt: departure,
|
||||||
stationName: station.split(' ').slice(0, -1).join(' '),
|
stationName: station.split(' ').slice(0, -1).join(' '),
|
||||||
stationHash: station.split(' ').slice(-1).join(' ')
|
stationHash: station.split(' ').slice(-1).join(' ').replace('.sc', '')
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -169,12 +167,15 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
const offlineActiveSceneries = this.trainList.reduce((acc, train) => {
|
const offlineActiveSceneries = this.trainList.reduce((acc, train) => {
|
||||||
if (!train.timetableData) return acc;
|
if (!train.timetableData) return acc;
|
||||||
|
|
||||||
train.timetableData.sceneryNames.forEach((name) => {
|
train.timetableData.timetablePath.forEach((p) => {
|
||||||
if (
|
if (
|
||||||
acc.findIndex((v) => v.name == name && v.region == train.region) != -1 ||
|
acc.findIndex(
|
||||||
|
(v) =>
|
||||||
|
(v.name == p.stationName || v.hash == p.stationHash) && v.region == train.region
|
||||||
|
) != -1 ||
|
||||||
apiStore.activeData?.activeSceneries?.findIndex(
|
apiStore.activeData?.activeSceneries?.findIndex(
|
||||||
(sc) =>
|
(sc) =>
|
||||||
sc.stationName === name &&
|
(sc.stationName == p.stationName || sc.stationHash == p.stationHash) &&
|
||||||
sc.region == train.region &&
|
sc.region == train.region &&
|
||||||
Date.now() - sc.lastSeen < 1000 * 60 * 2
|
Date.now() - sc.lastSeen < 1000 * 60 * 2
|
||||||
) != -1
|
) != -1
|
||||||
@@ -182,7 +183,7 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
return acc;
|
return acc;
|
||||||
|
|
||||||
acc.push({
|
acc.push({
|
||||||
name: name,
|
name: p.stationName,
|
||||||
hash: '',
|
hash: '',
|
||||||
region: train.region,
|
region: train.region,
|
||||||
maxUsers: 0,
|
maxUsers: 0,
|
||||||
@@ -281,7 +282,11 @@ export const useMainStore = defineStore('mainStore', {
|
|||||||
if (!scheduledTrains) return;
|
if (!scheduledTrains) return;
|
||||||
|
|
||||||
scheduledTrains.forEach(({ train, checkpointStop, timetablePathElement, ...v }) => {
|
scheduledTrains.forEach(({ train, checkpointStop, timetablePathElement, ...v }) => {
|
||||||
if (scenery.name != timetablePathElement.stationName) return;
|
if (
|
||||||
|
scenery.name != timetablePathElement.stationName &&
|
||||||
|
scenery.hash != timetablePathElement.stationHash
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
scenery.scheduledTrains.push({ train, checkpointStop, timetablePathElement, ...v });
|
scenery.scheduledTrains.push({ train, checkpointStop, timetablePathElement, ...v });
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
@import 'responsive.scss';
|
@import 'responsive.scss';
|
||||||
@import 'animations.scss';
|
@import 'animations.scss';
|
||||||
|
|
||||||
|
.journal-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.list_wrapper {
|
.list_wrapper {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 90vh;
|
height: 90vh;
|
||||||
@@ -38,16 +46,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.journal_item {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.journal_item,
|
.journal_item,
|
||||||
.journal_warning {
|
.journal_warning {
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin-bottom: 1em;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.journal_top-bar {
|
.journal_top-bar {
|
||||||
|
|||||||
@@ -33,22 +33,6 @@ hr.section-separator {
|
|||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-badge {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
span {
|
|
||||||
background-color: $accentCol;
|
|
||||||
color: black;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.2em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
span:first-child {
|
|
||||||
background-color: #333;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
.journal-stats {
|
.journal-stats {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
+13
-3
@@ -1,3 +1,5 @@
|
|||||||
|
@import 'variables.scss';
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
@@ -78,14 +80,12 @@
|
|||||||
.train-badge {
|
.train-badge {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5em;
|
gap: 0.3em;
|
||||||
|
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
font-size: 0.9em;
|
|
||||||
|
|
||||||
&.twr {
|
&.twr {
|
||||||
background-color: var(--clr-twr);
|
background-color: var(--clr-twr);
|
||||||
box-shadow: 0 0 5px 1px var(--clr-twr);
|
box-shadow: 0 0 5px 1px var(--clr-twr);
|
||||||
@@ -114,3 +114,13 @@
|
|||||||
background-color: #007599;
|
background-color: #007599;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stat-badge {
|
||||||
|
margin: 0;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
& > span:first-child {
|
||||||
|
background-color: $accentCol;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+20
-4
@@ -124,10 +124,12 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:not(.a-block):not(.a-button):not(.a-row) {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
transition: color 0.3s;
|
transition: color 0.3s;
|
||||||
|
|
||||||
@@ -138,6 +140,14 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.a-block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.a-row {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -184,6 +194,7 @@ a.a-button {
|
|||||||
color: white;
|
color: white;
|
||||||
background: none;
|
background: none;
|
||||||
border-radius: 0.25em;
|
border-radius: 0.25em;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -214,7 +225,7 @@ a.a-button {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #555;
|
background-color: #424242;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,6 +301,7 @@ a.a-button {
|
|||||||
// Basic tooltip
|
// Basic tooltip
|
||||||
[data-tooltip] {
|
[data-tooltip] {
|
||||||
cursor: help;
|
cursor: help;
|
||||||
|
line-height: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-tooltip]:hover::after,
|
[data-tooltip]:hover::after,
|
||||||
@@ -308,6 +320,10 @@ a.a-button {
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tooltip-help {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 0.5em;
|
width: 0.5em;
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ export enum APIDataStatus {
|
|||||||
OK = 'OK',
|
OK = 'OK',
|
||||||
WARNING = 'WARNING'
|
WARNING = 'WARNING'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface APICache {
|
||||||
|
lastModified: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export namespace API {
|
export namespace API {
|
||||||
export namespace ActiveData {
|
export namespace ActiveData {
|
||||||
export interface APIStatuses {
|
export interface APIStatuses {
|
||||||
@@ -11,6 +17,7 @@ export namespace API {
|
|||||||
trainsAPI: APIDataStatus;
|
trainsAPI: APIDataStatus;
|
||||||
dispatchersAPI: APIDataStatus;
|
dispatchersAPI: APIDataStatus;
|
||||||
sceneryRequirementsAPI: APIDataStatus;
|
sceneryRequirementsAPI: APIDataStatus;
|
||||||
|
caches: APICache[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Response {
|
export interface Response {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { RouteLocationRaw } from 'vue-router';
|
||||||
|
|
||||||
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
|
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
|
||||||
export type ScenerySpawnType = 'passenger' | 'freight' | 'loco' | 'all';
|
export type ScenerySpawnType = 'passenger' | 'freight' | 'loco' | 'all';
|
||||||
|
|
||||||
@@ -70,6 +72,8 @@ export interface Train {
|
|||||||
isTimeout: boolean;
|
isTimeout: boolean;
|
||||||
isSupporter: boolean;
|
isSupporter: boolean;
|
||||||
|
|
||||||
|
driverRouteLocation: RouteLocationRaw,
|
||||||
|
|
||||||
timetableData?: {
|
timetableData?: {
|
||||||
timetableId: number;
|
timetableId: number;
|
||||||
category: string;
|
category: string;
|
||||||
@@ -79,7 +83,6 @@ export interface Train {
|
|||||||
SKR: boolean;
|
SKR: boolean;
|
||||||
routeDistance: number;
|
routeDistance: number;
|
||||||
sceneries: string[];
|
sceneries: string[];
|
||||||
sceneryNames: string[];
|
|
||||||
timetablePath: TimetablePathElement[];
|
timetablePath: TimetablePathElement[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<section class="driver-view">
|
||||||
|
<div class="view-wrapper">
|
||||||
|
<div v-if="chosenTrain">
|
||||||
|
<div class="actions">
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/timetables?search-driver=${chosenTrain.driverName}`"
|
||||||
|
class="a-button btn--image"
|
||||||
|
>
|
||||||
|
<span class="hidable">
|
||||||
|
{{ $t('trains.driver-journal-link') }}
|
||||||
|
</span>
|
||||||
|
<img src="/images/icon-train.svg" alt="train icon" />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="train-card">
|
||||||
|
<TrainInfo :train="chosenTrain" :extended="true" ref="trainInfo" />
|
||||||
|
<TrainSchedule :train="chosenTrain" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Loading v-else-if="apiStore.dataStatuses.connection == Status.Data.Loading" />
|
||||||
|
|
||||||
|
<div v-else class="driver-not-found">
|
||||||
|
<h2>⦻ {{ $t('trains.driver-not-found-header') }}</h2>
|
||||||
|
<p>
|
||||||
|
{{ $t('trains.driver-not-found-desc-1') }} <br />
|
||||||
|
{{ $t('trains.driver-not-found-desc-2') }}
|
||||||
|
<router-link to="/journal/timetables"
|
||||||
|
>{{ $t('trains.driver-not-found-journal') }} </router-link
|
||||||
|
>!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<router-link to="/"><< {{ $t('trains.driver-not-found-return') }}</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onActivated, onMounted, useAttrs } from 'vue';
|
||||||
|
import TrainInfo from '../components/TrainsView/TrainInfo.vue';
|
||||||
|
import TrainSchedule from '../components/TrainsView/TrainSchedule.vue';
|
||||||
|
import Loading from '../components/Global/Loading.vue';
|
||||||
|
import { useMainStore } from '../store/mainStore';
|
||||||
|
import { useApiStore } from '../store/apiStore';
|
||||||
|
import { Status } from '../typings/common';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
trainId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
|
||||||
|
modalId: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainStore = useMainStore();
|
||||||
|
const apiStore = useApiStore();
|
||||||
|
|
||||||
|
const chosenTrain = computed(() =>
|
||||||
|
mainStore.trainList.find((train) => train.id == props.trainId || train.modalId == props.modalId)
|
||||||
|
);
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
console.log();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import '../styles/responsive';
|
||||||
|
|
||||||
|
$viewBgCol: #1a1a1a;
|
||||||
|
|
||||||
|
.driver-view {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1em 0;
|
||||||
|
max-width: 2000px;
|
||||||
|
min-height: 95vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions > a {
|
||||||
|
background-color: $viewBgCol;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.5em 0.5em 0 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($viewBgCol, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.train-card {
|
||||||
|
background-color: $viewBgCol;
|
||||||
|
border-radius: 0 0 0.5em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.driver-not-found {
|
||||||
|
background-color: $viewBgCol;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 1em 0;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include smallScreen {
|
||||||
|
.actions > a > span.hidable {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -267,7 +267,10 @@ export default defineComponent({
|
|||||||
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
||||||
|
|
||||||
if (dispatcher) queries.push(`dispatcherName=${dispatcher}`);
|
if (dispatcher) queries.push(`dispatcherName=${dispatcher}`);
|
||||||
if (station) queries.push(`stationName=${station}`);
|
|
||||||
|
if (station.startsWith("#")) queries.push(`stationHash=${station.slice(1)}`);
|
||||||
|
else if (station.length > 0) queries.push(`stationName=${station}`);
|
||||||
|
|
||||||
if (timestampFrom && timestampTo)
|
if (timestampFrom && timestampTo)
|
||||||
queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
|||||||
|
|
||||||
import dateMixin from '../mixins/dateMixin';
|
import dateMixin from '../mixins/dateMixin';
|
||||||
import routerMixin from '../mixins/routerMixin';
|
import routerMixin from '../mixins/routerMixin';
|
||||||
import modalTrainMixin from '../mixins/modalTrainMixin';
|
|
||||||
|
|
||||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||||
import JournalStats from '../components/JournalView/JournalStats.vue';
|
import JournalStats from '../components/JournalView/JournalStats.vue';
|
||||||
@@ -148,7 +147,7 @@ export default defineComponent({
|
|||||||
JournalHeader,
|
JournalHeader,
|
||||||
JournalTimetablesList
|
JournalTimetablesList
|
||||||
},
|
},
|
||||||
mixins: [dateMixin, routerMixin, modalTrainMixin],
|
mixins: [dateMixin, routerMixin],
|
||||||
|
|
||||||
name: 'JournalTimetables',
|
name: 'JournalTimetables',
|
||||||
|
|
||||||
@@ -462,7 +461,7 @@ export default defineComponent({
|
|||||||
this.timetableHistory = responseData;
|
this.timetableHistory = responseData;
|
||||||
|
|
||||||
// Stats display
|
// Stats display
|
||||||
this.store.driverStatsName =
|
this.mainStore.driverStatsName =
|
||||||
this.timetableHistory.length > 0 && this.searchersValues['search-driver'].trim()
|
this.timetableHistory.length > 0 && this.searchersValues['search-driver'].trim()
|
||||||
? this.timetableHistory[0].driverName
|
? this.timetableHistory[0].driverName
|
||||||
: '';
|
: '';
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
:station="stationInfo"
|
:station="stationInfo"
|
||||||
:onlineScenery="onlineSceneryInfo"
|
:onlineScenery="onlineSceneryInfo"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
|
<SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -229,6 +230,7 @@ button.back-btn {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
background-color: #181818;
|
background-color: #181818;
|
||||||
|
border-radius: 0.5em;
|
||||||
padding: 1em 0.5em;
|
padding: 1em 0.5em;
|
||||||
|
|
||||||
height: calc(100vh - 0.5em);
|
height: calc(100vh - 0.5em);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
ref="filterCardRef"
|
ref="filterCardRef"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<StationStats />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn-donation btn--image"
|
class="btn-donation btn--image"
|
||||||
ref="btn"
|
ref="btn"
|
||||||
@@ -21,7 +23,6 @@
|
|||||||
|
|
||||||
<DonationCard :is-card-open="isDonationCardOpen" @toggle-card="toggleDonationCard" />
|
<DonationCard :is-card-open="isDonationCardOpen" @toggle-card="toggleDonationCard" />
|
||||||
<StationTable @toggle-donation-card="toggleDonationCard" />
|
<StationTable @toggle-donation-card="toggleDonationCard" />
|
||||||
<StationStats />
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -96,14 +97,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
.stations-options {
|
.stations-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.btn-donation {
|
button.btn-donation {
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
$btnColor: #254069;
|
$btnColor: #254069;
|
||||||
|
|
||||||
background-color: $btnColor;
|
background-color: $btnColor;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
|
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
|
||||||
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
||||||
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
||||||
import modalTrainMixin from '../mixins/modalTrainMixin';
|
|
||||||
import { useMainStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
import { TrainFilter, trainFilters } from '../components/TrainsView/typings';
|
import { TrainFilter, trainFilters } from '../components/TrainsView/typings';
|
||||||
import { filteredTrainList } from '../managers/trainFilterManager';
|
import { filteredTrainList } from '../managers/trainFilterManager';
|
||||||
@@ -33,8 +32,6 @@ export default defineComponent({
|
|||||||
TrainStats
|
TrainStats
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [modalTrainMixin],
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
train: {
|
train: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -102,16 +99,16 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
|
// Backwards compatibility with external links leading to train modal
|
||||||
|
if (this.trainId) {
|
||||||
|
this.$router.replace(`/driver?modalId=${this.trainId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.train) {
|
if (this.train) {
|
||||||
this.searchedTrain = this.train;
|
this.searchedTrain = this.train;
|
||||||
this.searchedDriver = this.driver || '';
|
this.searchedDriver = this.driver || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.trainId) {
|
|
||||||
this.selectModalTrainById(this.trainId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user