mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 13:28:11 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| febb22e1bc | |||
| 500f3c1223 | |||
| 221e0c7e82 | |||
| ca19f7e397 | |||
| a71ccd3e1a | |||
| d496c70fa8 | |||
| b9868ba52e | |||
| 59bd3fa2ef | |||
| e14d328ed9 | |||
| 36d71292bc | |||
| 2f6e2e7402 | |||
| e959eac6c5 | |||
| 8bedc4dfc6 | |||
| 73563d5db7 | |||
| 3f818069cd | |||
| cdf0b2a426 | |||
| c29ddeb78c | |||
| b81d98cab7 | |||
| 0e45bca5da | |||
| 715e66879f | |||
| 1747e15dc8 | |||
| 6a923a8e1d | |||
| 25a248e95e | |||
| aa7a6b220e |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.11.1",
|
"version": "1.12.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+5
-1
@@ -46,7 +46,11 @@
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
font-size: calc(0.5rem + 1.3vw);
|
font-size: calc(0.55rem + 1.1vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include screenLandscape() {
|
||||||
|
font-size: calc(0.45rem + 0.8vw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -21,7 +21,8 @@
|
|||||||
<footer class="app_footer">
|
<footer class="app_footer">
|
||||||
©
|
©
|
||||||
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
||||||
{{ new Date().getUTCFullYear() }} | <a :href="releaseURL" target="_blank">v{{ VERSION }}</a>
|
{{ new Date().getUTCFullYear() }} |
|
||||||
|
<a :href="releaseURL" target="_blank">v{{ VERSION }}{{ isOnProductionHost ? '' : 'dev' }}</a>
|
||||||
|
|
||||||
<div style="display: none">∫ ukryta taktyczna całka do programowania w HTMLu</div>
|
<div style="display: none">∫ ukryta taktyczna całka do programowania w HTMLu</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -86,6 +87,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
currentLang: 'pl',
|
currentLang: 'pl',
|
||||||
releaseURL: '',
|
releaseURL: '',
|
||||||
|
isOnProductionHost: location.hostname == 'stacjownik-td2.web.app',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<svg width="144" height="147" viewBox="0 0 144 147" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d_1343_19)">
|
||||||
|
<path d="M115.039 101.247C116.397 98.6665 115.405 95.4739 112.824 94.1167C110.243 92.7594 107.05 93.7514 105.693 96.3323L115.039 101.247ZM89.4447 44.0402L94.1179 46.4977L99.0329 37.1513L94.3597 34.6938L89.4447 44.0402ZM105.693 96.3323C95.7398 115.259 72.3278 122.534 53.4008 112.581L48.4858 121.927C72.5746 134.595 102.372 125.336 115.039 101.247L105.693 96.3323ZM53.4008 112.581C34.4739 102.627 27.1993 79.2155 37.1525 60.2885L27.8061 55.3735C15.1383 79.4623 24.397 109.259 48.4858 121.927L53.4008 112.581ZM37.1525 60.2885C47.1057 41.3616 70.5177 34.087 89.4447 44.0402L94.3597 34.6938C70.2709 22.026 40.4738 31.2846 27.8061 55.3735L37.1525 60.2885Z" fill="white"/>
|
||||||
|
<path d="M91.2258 38.7627L101.056 20.0698L116.15 51.8695L81.3956 57.4555L91.2258 38.7627Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1343_19" x="18.1328" y="20.0698" width="102.017" height="115.531" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="4"/>
|
||||||
|
<feGaussianBlur stdDeviation="2"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="out"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1343_19"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1343_19" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,7 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="journal-list">
|
<transition-group class="journal-list" tag="ul" name="list-anim">
|
||||||
<!-- <transition-group name="journal-list-anim"> -->
|
<li
|
||||||
<li v-for="item in computedDispatcherHistory" :class="{ sticky: typeof item == 'string' }">
|
v-for="item in computedDispatcherHistory"
|
||||||
|
:key="typeof item === 'string' ? item : item.timestampFrom + item.dispatcherId"
|
||||||
|
:class="{ sticky: typeof item == 'string' }"
|
||||||
|
>
|
||||||
<div v-if="typeof item == 'string'" class="journal_day">
|
<div v-if="typeof item == 'string'" class="journal_day">
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
@@ -14,10 +17,10 @@
|
|||||||
@keydown.enter="navigateToScenery(item.stationName, item.isOnline)"
|
@keydown.enter="navigateToScenery(item.stationName, item.isOnline)"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span>
|
<span class="item-general">
|
||||||
<b
|
<b
|
||||||
v-if="item.dispatcherLevel !== null"
|
v-if="item.dispatcherLevel !== null"
|
||||||
class="dispatcher-level"
|
class="level-badge dispatcher"
|
||||||
:style="calculateExpStyle(item.dispatcherLevel, item.dispatcherIsSupporter)"
|
:style="calculateExpStyle(item.dispatcherLevel, item.dispatcherIsSupporter)"
|
||||||
>
|
>
|
||||||
{{ item.dispatcherLevel >= 2 ? item.dispatcherLevel : 'L' }}
|
{{ item.dispatcherLevel >= 2 ? item.dispatcherLevel : 'L' }}
|
||||||
@@ -28,7 +31,7 @@
|
|||||||
<span class="region-badge" :class="item.region">PL1</span>
|
<span class="region-badge" :class="item.region">PL1</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span class="item-time">
|
||||||
<span :data-status="item.isOnline"> {{ item.isOnline ? $t('journal.online-since') : 'OFFLINE' }} </span>
|
<span :data-status="item.isOnline"> {{ item.isOnline ? $t('journal.online-since') : 'OFFLINE' }} </span>
|
||||||
<span>
|
<span>
|
||||||
{{ new Date(item.timestampFrom).toLocaleTimeString('pl-PL', { timeStyle: 'short' }) }}
|
{{ new Date(item.timestampFrom).toLocaleTimeString('pl-PL', { timeStyle: 'short' }) }}
|
||||||
@@ -44,8 +47,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<!-- </transition-group> -->
|
</transition-group>
|
||||||
</ul>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -95,19 +97,11 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '../../styles/animations.scss';
|
||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
|
@import '../../styles/badge.scss';
|
||||||
@import '../../styles/JournalSection.scss';
|
@import '../../styles/JournalSection.scss';
|
||||||
|
|
||||||
.region-badge {
|
|
||||||
padding: 0.1em 0.5em;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&.eu {
|
|
||||||
background-color: forestgreen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
li.sticky {
|
li.sticky {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -117,9 +111,12 @@ li.sticky {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
gap: 0.25em;
|
||||||
|
|
||||||
|
line-height: 1.7em;
|
||||||
padding: 0.75em;
|
padding: 0.75em;
|
||||||
|
|
||||||
&.online {
|
&.online {
|
||||||
@@ -135,6 +132,18 @@ li.sticky {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-general {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
|
||||||
|
.level-badge {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.journal_day {
|
.journal_day {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
@@ -151,27 +160,4 @@ li.sticky {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dispatcher-level {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 150%;
|
|
||||||
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
|
|
||||||
margin-right: 0.5em;
|
|
||||||
border-radius: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include smallScreen() {
|
|
||||||
.journal_item {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin-top: 0.25em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,11 +2,18 @@
|
|||||||
<div class="filters-options" @keydown.esc="showOptions = false">
|
<div class="filters-options" @keydown.esc="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="showOptions = !showOptions" ref="button">
|
<div class="actions-bar">
|
||||||
<img :src="getIcon('filter2')" alt="Open filters" />
|
<button class="filter-button btn--filled btn--image" @click="showOptions = !showOptions" ref="button">
|
||||||
{{ $t('options.filters') }} [F]
|
<img :src="getIcon('filter2')" alt="Open filters" />
|
||||||
<span class="active-indicator" v-if="currentOptionsActive"></span>
|
{{ $t('options.filters') }} [F]
|
||||||
</button>
|
<span class="active-indicator" v-if="currentOptionsActive"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="filter-button btn--filled btn--image" @click="refreshData">
|
||||||
|
<img :src="getIcon('refresh')" alt="Refresh data" />
|
||||||
|
{{ $t('general.refresh') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<datalist id="search-driver">
|
<datalist id="search-driver">
|
||||||
<option v-for="sugg in driverSuggestions" :value="sugg"></option>
|
<option v-for="sugg in driverSuggestions" :value="sugg"></option>
|
||||||
@@ -99,7 +106,7 @@ import SelectBox from '../Global/SelectBox.vue';
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { SelectBox, ActionButton },
|
components: { SelectBox, ActionButton },
|
||||||
emits: ['onSearchConfirm', 'onOptionsReset'],
|
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
|
||||||
mixins: [imageMixin, keyMixin],
|
mixins: [imageMixin, keyMixin],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@@ -206,6 +213,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
refreshData() {
|
||||||
|
this.$emit('onRefreshData');
|
||||||
|
},
|
||||||
|
|
||||||
startSearchTimeout(type: 'driver' | 'dispatcher', value: string) {
|
startSearchTimeout(type: 'driver' | 'dispatcher', value: string) {
|
||||||
if (this[`${type}Suggestions`].includes(value)) return;
|
if (this[`${type}Suggestions`].includes(value)) return;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
{{ $t(tab.titlePath) }}
|
{{ $t(tab.titlePath) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stats-tab" v-show="areStatsOpen">
|
<div class="stats-tab" v-show="areStatsOpen">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<JournalDailyStats v-if="store.currentStatsTab == 'daily'" ref="dailyStatsComp" />
|
<JournalDailyStats v-if="store.currentStatsTab == 'daily'" ref="dailyStatsComp" />
|
||||||
@@ -35,7 +35,8 @@ type TStatTab = 'daily' | 'driver';
|
|||||||
const store = useStore();
|
const store = useStore();
|
||||||
const dailyStatsComp: Ref<InstanceType<typeof JournalDailyStats> | null> = ref(null);
|
const dailyStatsComp: Ref<InstanceType<typeof JournalDailyStats> | null> = ref(null);
|
||||||
|
|
||||||
const areStatsOpen = ref(true);
|
const lastDailyStatsOpen = ref(false);
|
||||||
|
const areStatsOpen = ref(false);
|
||||||
const lastClickedTab = ref('daily');
|
const lastClickedTab = ref('daily');
|
||||||
|
|
||||||
let data = reactive({
|
let data = reactive({
|
||||||
@@ -54,9 +55,9 @@ let data = reactive({
|
|||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
function onTabButtonClick(tab: TStatTab) {
|
function onTabButtonClick(tab: TStatTab) {
|
||||||
if (lastClickedTab.value == tab || !areStatsOpen.value) {
|
if (lastClickedTab.value == tab || !areStatsOpen.value) areStatsOpen.value = !areStatsOpen.value;
|
||||||
areStatsOpen.value = !areStatsOpen.value;
|
|
||||||
}
|
if (tab == 'daily') lastDailyStatsOpen.value = areStatsOpen.value;
|
||||||
|
|
||||||
store.currentStatsTab = tab;
|
store.currentStatsTab = tab;
|
||||||
lastClickedTab.value = tab;
|
lastClickedTab.value = tab;
|
||||||
@@ -77,6 +78,7 @@ watch(
|
|||||||
|
|
||||||
lastClickedTab.value = statsData ? 'driver' : 'daily';
|
lastClickedTab.value = statsData ? 'driver' : 'daily';
|
||||||
if (statsData) areStatsOpen.value = true;
|
if (statsData) areStatsOpen.value = true;
|
||||||
|
if (!statsData) areStatsOpen.value = lastDailyStatsOpen.value;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,25 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="journal-list">
|
<transition-group class="journal-list" tag="ul" name="list-anim">
|
||||||
<li
|
<li
|
||||||
v-for="{ timetable, sceneryList, ...item } in computedTimetableHistory"
|
v-for="{ timetable, sceneryList, ...item } in computedTimetableHistory"
|
||||||
class="journal_item"
|
class="journal_item"
|
||||||
:key="timetable.timetableId"
|
:key="timetable.id"
|
||||||
>
|
>
|
||||||
<div class="journal_item-info">
|
<div class="journal_item-info">
|
||||||
<div class="info-top">
|
<div class="info-general">
|
||||||
<span
|
<span
|
||||||
|
class="general-train"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click="showTimetable(timetable)"
|
@click="showTimetable(timetable)"
|
||||||
@keydown.enter="showTimetable(timetable)"
|
@keydown.enter="showTimetable(timetable)"
|
||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
>
|
>
|
||||||
<b class="text--primary">{{ timetable.trainCategoryCode }} </b>
|
|
||||||
<b>{{ timetable.trainNo }}</b>
|
|
||||||
| <span>{{ timetable.driverName }}</span> |
|
|
||||||
<span class="text--grayed">#{{ timetable.id }}</span>
|
<span class="text--grayed">#{{ timetable.id }}</span>
|
||||||
|
<span>
|
||||||
|
<strong class="text--primary">
|
||||||
|
{{ timetable.trainCategoryCode }}
|
||||||
|
</strong>
|
||||||
|
<strong> {{ timetable.trainNo }}</strong>
|
||||||
|
</span>
|
||||||
|
•
|
||||||
|
<strong
|
||||||
|
v-if="timetable.driverLevel !== null"
|
||||||
|
class="level-badge driver"
|
||||||
|
:style="calculateExpStyle(timetable.driverLevel, timetable.driverIsSupporter)"
|
||||||
|
>
|
||||||
|
{{ timetable.driverLevel < 2 ? 'L' : `${timetable.driverLevel}` }}
|
||||||
|
</strong>
|
||||||
|
|
||||||
|
<strong>{{ timetable.driverName }}</strong>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span class="general-time">
|
||||||
<b class="info-date">{{ localeDay(timetable.beginDate, $i18n.locale) }}</b>
|
<b class="info-date">{{ localeDay(timetable.beginDate, $i18n.locale) }}</b>
|
||||||
<b
|
<b
|
||||||
class="info-status"
|
class="info-status"
|
||||||
@@ -39,26 +53,20 @@
|
|||||||
</b>
|
</b>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-route">
|
<div class="info-route">
|
||||||
<b>{{ timetable.route.replace('|', ' - ') }}</b>
|
<b>{{ timetable.route.replace('|', ' - ') }}</b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="scenery-list">
|
<div class="scenery-list">
|
||||||
<span v-for="(scenery, i) in sceneryList" :key="scenery.name" :class="{ confirmed: scenery.confirmed }">
|
<span v-for="(scenery, i) in sceneryList" :key="scenery.name" :class="{ confirmed: scenery.confirmed }">
|
||||||
<span v-if="i > 0"> ></span>
|
<span v-if="i > 0"> ></span>
|
||||||
{{ scenery.name }}
|
{{ scenery.name }}
|
||||||
|
|
||||||
<!-- Data odjazdu ze stacji początkowej -->
|
<!-- Data odjazdu ze stacji początkowej -->
|
||||||
<span v-if="i == 0" v-html="scenery.beginDateHTML"></span>
|
<span v-if="i == 0" v-html="scenery.beginDateHTML"></span>
|
||||||
|
|
||||||
<!-- Data przyjazdu do stacji końcowej -->
|
<!-- Data przyjazdu do stacji końcowej -->
|
||||||
<span v-if="i == sceneryList.length - 1" v-html="scenery.endDateHTML"> </span>
|
<span v-if="i == sceneryList.length - 1" v-html="scenery.endDateHTML"> </span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Status RJ -->
|
<!-- Status RJ -->
|
||||||
<div style="margin: 0.5em 0">
|
<div style="margin: 0.5em 0">
|
||||||
<span>
|
<span>
|
||||||
@@ -80,7 +88,6 @@
|
|||||||
</b>
|
</b>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Nick dyżurnego -->
|
<!-- Nick dyżurnego -->
|
||||||
<div v-if="timetable.authorName">
|
<div v-if="timetable.authorName">
|
||||||
<b class="text--grayed">{{ $t('journal.dispatcher-name') }} </b>
|
<b class="text--grayed">{{ $t('journal.dispatcher-name') }} </b>
|
||||||
@@ -100,24 +107,20 @@
|
|||||||
|
|
||||||
<div class="info-extended" v-if="timetable.stockString && item.showStock.value">
|
<div class="info-extended" v-if="timetable.stockString && item.showStock.value">
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span class="badge info-badge">
|
<span class="badge info-badge">
|
||||||
<span>{{ $t('journal.stock-max-speed') }}</span>
|
<span>{{ $t('journal.stock-max-speed') }}</span>
|
||||||
<span>{{ timetable.maxSpeed }}km/h</span>
|
<span>{{ timetable.maxSpeed }}km/h</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="badge info-badge">
|
<span class="badge info-badge">
|
||||||
<span>{{ $t('journal.stock-length') }}</span>
|
<span>{{ $t('journal.stock-length') }}</span>
|
||||||
<span>{{ timetable.stockLength }}m</span>
|
<span>{{ timetable.stockLength }}m</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="badge info-badge">
|
<span class="badge info-badge">
|
||||||
<span>{{ $t('journal.stock-mass') }}</span>
|
<span>{{ $t('journal.stock-mass') }}</span>
|
||||||
<span>{{ Math.floor(timetable.stockMass! / 1000) }}t</span>
|
<span>{{ Math.floor(timetable.stockMass! / 1000) }}t</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="stock-list">
|
<ul class="stock-list">
|
||||||
<li v-for="(car, i) in timetable.stockString.split(';')" :key="i">
|
<li v-for="(car, i) in timetable.stockString.split(';')" :key="i">
|
||||||
<img
|
<img
|
||||||
@@ -125,14 +128,13 @@
|
|||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${car.split(':')[0]}.png`"
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${car.split(':')[0]}.png`"
|
||||||
:alt="car"
|
:alt="car"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div>{{ car.replace(/_/g, ' ').split(':')[0] }}</div>
|
<div>{{ car.replace(/_/g, ' ').split(':')[0] }}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</transition-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -140,6 +142,7 @@ import { defineComponent, PropType, ref } from 'vue';
|
|||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||||
|
import styleMixin from '../../mixins/styleMixin';
|
||||||
import { TimetableHistory } from '../../scripts/interfaces/api/TimetablesAPIData';
|
import { TimetableHistory } from '../../scripts/interfaces/api/TimetablesAPIData';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -150,7 +153,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [dateMixin, imageMixin, modalTrainMixin],
|
mixins: [dateMixin, imageMixin, modalTrainMixin, styleMixin],
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedTimetableHistory() {
|
computedTimetableHistory() {
|
||||||
@@ -197,6 +200,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
showTimetable(timetable: TimetableHistory) {
|
showTimetable(timetable: TimetableHistory) {
|
||||||
|
if (!timetable) return;
|
||||||
if (timetable.terminated) return;
|
if (timetable.terminated) return;
|
||||||
|
|
||||||
this.selectModalTrain(timetable.driverName + timetable.trainNo.toString());
|
this.selectModalTrain(timetable.driverName + timetable.trainNo.toString());
|
||||||
@@ -211,6 +215,7 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '../../styles/animations.scss';
|
||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
@import '../../styles/badge.scss';
|
@import '../../styles/badge.scss';
|
||||||
@@ -242,10 +247,14 @@ hr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-top {
|
&-general {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
gap: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-route {
|
&-route {
|
||||||
@@ -257,6 +266,12 @@ hr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.general-train {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
ul.stock-list {
|
ul.stock-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
@@ -297,14 +312,9 @@ ul.stock-list {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
.info-top {
|
.info-general {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
span {
|
|
||||||
margin: 0.1em auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-extended {
|
.info-extended {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,25 +5,31 @@
|
|||||||
<div class="list-warning" v-else-if="dispatcherHistoryList.length == 0">{{ $t('scenery.history-list-empty') }}</div>
|
<div class="list-warning" v-else-if="dispatcherHistoryList.length == 0">{{ $t('scenery.history-list-empty') }}</div>
|
||||||
|
|
||||||
<ul class="history-list" v-else>
|
<ul class="history-list" v-else>
|
||||||
<li class="list-item" v-for="historyItem in dispatcherHistoryList">
|
<li class="list-item" v-for="item in dispatcherHistoryList">
|
||||||
<div>
|
<router-link class="item-general" :to="`/journal/dispatchers?dispatcherName=${item.dispatcherName}`">
|
||||||
<router-link :to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`">
|
<span class="text--grayed">#{{ item.stationHash }} </span>
|
||||||
<span class="text--grayed">#{{ historyItem.stationHash }} </span>
|
<b
|
||||||
<b>{{ historyItem.dispatcherName }}</b>
|
v-if="item.dispatcherLevel !== null"
|
||||||
</router-link>
|
class="level-badge dispatcher"
|
||||||
</div>
|
:style="calculateExpStyle(item.dispatcherLevel, item.dispatcherIsSupporter)"
|
||||||
|
>
|
||||||
|
{{ item.dispatcherLevel >= 2 ? item.dispatcherLevel : 'L' }}
|
||||||
|
</b>
|
||||||
|
|
||||||
<div v-if="historyItem.timestampTo">
|
<b>{{ item.dispatcherName }}</b>
|
||||||
<b>{{ $d(historyItem.timestampFrom) }}</b>
|
</router-link>
|
||||||
|
|
||||||
{{ timestampToString(historyItem.timestampFrom) }}
|
<div v-if="item.timestampTo">
|
||||||
- {{ timestampToString(historyItem.timestampTo) }} ({{ calculateDuration(historyItem.currentDuration) }})
|
<b>{{ $d(item.timestampFrom) }}</b>
|
||||||
|
|
||||||
|
{{ timestampToString(item.timestampFrom) }}
|
||||||
|
- {{ timestampToString(item.timestampTo) }} ({{ calculateDuration(item.currentDuration) }})
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dispatcher-online" v-else>
|
<div class="dispatcher-online" v-else>
|
||||||
{{ $t('journal.online-since') }}
|
{{ $t('journal.online-since') }}
|
||||||
<b>{{ timestampToString(historyItem.timestampFrom) }}</b>
|
<b>{{ timestampToString(item.timestampFrom) }}</b>
|
||||||
({{ calculateDuration(historyItem.currentDuration) }})
|
({{ calculateDuration(item.currentDuration) }})
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -39,10 +45,11 @@ import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIDa
|
|||||||
import Station from '../../scripts/interfaces/Station';
|
import Station from '../../scripts/interfaces/Station';
|
||||||
import { URLs } from '../../scripts/utils/apiURLs';
|
import { URLs } from '../../scripts/utils/apiURLs';
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
|
import styleMixin from '../../mixins/styleMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'SceneryDispatchersHistory',
|
name: 'SceneryDispatchersHistory',
|
||||||
mixins: [dateMixin],
|
mixins: [dateMixin, styleMixin],
|
||||||
props: {
|
props: {
|
||||||
station: {
|
station: {
|
||||||
type: Object as PropType<Station>,
|
type: Object as PropType<Station>,
|
||||||
@@ -55,7 +62,7 @@ export default defineComponent({
|
|||||||
dataStatus: DataStatus.Loading,
|
dataStatus: DataStatus.Loading,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
activated() {
|
||||||
this.fetchAPIData();
|
this.fetchAPIData();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -96,6 +103,13 @@ export default defineComponent({
|
|||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-general {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
.dispatcher-online {
|
.dispatcher-online {
|
||||||
color: springgreen;
|
color: springgreen;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="info-header">
|
<section class="info-header">
|
||||||
<a class="scenery-name" :href="station.generalInfo?.url">
|
<a class="scenery-name" :href="station.generalInfo?.url" target="_blank">
|
||||||
{{ station.name }}
|
{{ station.name }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="scenery-info">
|
<div class="scenery-info">
|
||||||
<section v-if="!timetableOnly">
|
<section v-if="!timetableOnly">
|
||||||
<div class="info-general" v-if="station.generalInfo">
|
<div class="scenery-info-general" v-if="station.generalInfo">
|
||||||
<scenery-info-icons :station="station" />
|
<scenery-info-icons :station="station" />
|
||||||
|
|
||||||
<div class="general-list">
|
<div class="scenery-general-list">
|
||||||
<span>
|
<span>
|
||||||
<b>{{ $t('availability.title') }}:</b> {{ $t(`availability.${station.generalInfo.availability}`) }}
|
<b>{{ $t('availability.title') }}:</b> {{ $t(`availability.${station.generalInfo.availability}`) }}
|
||||||
|
|
||||||
@@ -26,26 +26,32 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-if="station.generalInfo.project">
|
<span v-if="station.generalInfo.project">
|
||||||
• <b>{{ $t('scenery.project-title') }}: </b>
|
• <b>{{ $t('scenery.project-title') }}: </b>
|
||||||
<b style="color: salmon">{{ station.generalInfo.project }}</b>
|
<a
|
||||||
|
style="color: salmon; text-decoration: underline; font-weight: bold"
|
||||||
|
:href="station.generalInfo.projectUrl"
|
||||||
|
target="_blank"
|
||||||
|
>{{ station.generalInfo.project }}</a
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<scenery-info-routes :station="station" />
|
<scenery-info-routes :station="station" />
|
||||||
|
|
||||||
<div class="scenery-authors" v-if="station.generalInfo.authors && station.generalInfo.authors.length > 0">
|
<div class="scenery-authors" v-if="station.generalInfo.authors && station.generalInfo.authors.length > 0">
|
||||||
<b> {{ $t('scenery.authors-title', { authors: station.generalInfo.authors.length }, station.generalInfo.authors.length) }}: </b>
|
<b>
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
'scenery.authors-title',
|
||||||
|
{ authors: station.generalInfo.authors.length },
|
||||||
|
station.generalInfo.authors.length
|
||||||
|
)
|
||||||
|
}}:
|
||||||
|
</b>
|
||||||
{{ station.generalInfo.authors.join(', ') }}
|
{{ station.generalInfo.authors.join(', ') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
|
||||||
<div class="scenery-topic" v-if="station.generalInfo.url">
|
|
||||||
<a :href="station.generalInfo.url" target="_blank">
|
|
||||||
> {{ $t('scenery.forum-topic', { name: station.name }) }} <
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin: 2em 0; height: 2px; background-color: white" />
|
<div style="margin: 2em 0; height: 2px; background-color: white"></div>
|
||||||
|
|
||||||
<!-- info dispatcher -->
|
<!-- info dispatcher -->
|
||||||
<scenery-info-dispatcher :station="station" :onlineFrom="onlineFrom" />
|
<scenery-info-dispatcher :station="station" :onlineFrom="onlineFrom" />
|
||||||
@@ -124,11 +130,11 @@ h3.section-header {
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-general {
|
.scenery-info-general {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.general-list {
|
.scenery-general-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -1,114 +1,108 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="info-routes" v-if="station.generalInfo">
|
<section class="info-routes" v-if="station.generalInfo">
|
||||||
<div class="routes one-way" v-if="station.generalInfo.routes.oneWay.length > 0">
|
<div class="routes one-way" v-if="station.generalInfo.routes.oneWay.length > 0">
|
||||||
<b>{{ $t('scenery.one-way-routes') }}</b>
|
<b>{{ $t('scenery.one-way-routes') }}</b>
|
||||||
|
|
||||||
<ul class="routes-list">
|
<ul class="routes-list">
|
||||||
<li
|
<li v-for="route in station.generalInfo.routes.oneWay">
|
||||||
v-for="route in station.generalInfo.routes.oneWay"
|
<span :class="{ 'no-catenary': !route.catenary, internal: route.isInternal }"> {{ route.name }}</span>
|
||||||
:class="{ 'no-catenary': !route.catenary, internal: route.isInternal }"
|
<span v-if="route.speed" class="speed">{{ route.speed }}</span>
|
||||||
>
|
<span v-if="route.SBL" class="sbl">SBL</span>
|
||||||
{{ route.name }}
|
</li>
|
||||||
<b v-if="route.SBL">SBL</b>
|
</ul>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
|
||||||
</div>
|
<div class="routes two-way" v-if="station.generalInfo.routes.twoWay.length > 0">
|
||||||
|
<b>{{ $t('scenery.two-way-routes') }}</b>
|
||||||
<div class="routes two-way" v-if="station.generalInfo.routes.twoWay.length > 0">
|
|
||||||
<b>{{ $t('scenery.two-way-routes') }}</b>
|
<ul class="routes-list">
|
||||||
|
<li v-for="route in station.generalInfo.routes.twoWay">
|
||||||
<ul class="routes-list">
|
<span :class="{ 'no-catenary': !route.catenary, internal: route.isInternal }">{{ route.name }}</span>
|
||||||
<li
|
<span v-if="route.speed" class="speed">{{ route.speed }}</span>
|
||||||
v-for="route in station.generalInfo.routes.twoWay"
|
<span v-if="route.SBL" class="sbl">SBL</span>
|
||||||
:class="{ 'no-catenary': !route.catenary, internal: route.isInternal }"
|
</li>
|
||||||
>
|
</ul>
|
||||||
{{ route.name }} <b v-if="route.SBL">SBL</b>
|
</div>
|
||||||
</li>
|
</section>
|
||||||
</ul>
|
</template>
|
||||||
</div>
|
|
||||||
|
<script lang="ts">
|
||||||
<!-- <div
|
import { defineComponent } from 'vue';
|
||||||
class="route-info"
|
import Station from '../../../scripts/interfaces/Station';
|
||||||
:class="{ 'no-catenary': !route.catenary, internal: route.isInternal }"
|
|
||||||
v-for="route in [...station.generalInfo.routes.oneWay, ...station.generalInfo.routes.twoWay].filter(
|
export default defineComponent({
|
||||||
(route) => route.name != '-'
|
props: {
|
||||||
)"
|
station: {
|
||||||
:key="route.name"
|
type: Object as () => Station,
|
||||||
:title="`Szlak ${route.name}: ${route.isInternal ? 'wewnętrzny' : 'zewnętrzny'}, ${
|
default: {},
|
||||||
route.tracks == 2 ? 'dwutorowy' : 'jednotorowy'
|
},
|
||||||
}, ${route.catenary ? 'zelektryfikowany' : 'niezelektryfikowany'} z ${route.SBL ? 'SBL' : 'PBL'} ${
|
},
|
||||||
route.TWB ? 'i blokadą dwukierunkową' : ''
|
});
|
||||||
}`"
|
</script>
|
||||||
> -->
|
|
||||||
<!-- <span class="track-name">
|
<style lang="scss" scoped>
|
||||||
<b>{{ route.name }}</b>
|
.info-routes {
|
||||||
</span> -->
|
display: flex;
|
||||||
<!--
|
justify-content: center;
|
||||||
<span class="track-specs">
|
flex-wrap: wrap;
|
||||||
{{ route.tracks }}tor
|
|
||||||
<img v-if="route.catenary" :src="icons.trackCatenary" alt="icon track catenary" />
|
margin: 1em 0;
|
||||||
<img v-else :src="icons.trackNoCatenary" alt="icon track no catenary" />
|
}
|
||||||
|
|
||||||
<img v-if="route.TWB" :src="icons.trackTWB" alt="icon track twb" />
|
.routes {
|
||||||
<img v-if="route.SBL" :src="icons.trackSBL" alt="icon track sbl" />
|
display: flex;
|
||||||
</span> -->
|
justify-content: center;
|
||||||
<!-- </div> -->
|
align-items: center;
|
||||||
</section>
|
flex-wrap: wrap;
|
||||||
</template>
|
|
||||||
|
padding: 0.25em;
|
||||||
<script lang="ts">
|
}
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import Station from '../../../scripts/interfaces/Station';
|
ul.routes-list {
|
||||||
|
margin: 0.45em 0.25em;
|
||||||
export default defineComponent({
|
display: flex;
|
||||||
props: {
|
justify-content: center;
|
||||||
station: {
|
flex-wrap: wrap;
|
||||||
type: Object as () => Station,
|
|
||||||
default: {},
|
li {
|
||||||
},
|
margin: 0.5em 0.25em;
|
||||||
},
|
|
||||||
});
|
span {
|
||||||
</script>
|
padding: 0.2em 0.25em;
|
||||||
|
background-color: #007599;
|
||||||
<style lang="scss" scoped>
|
font-weight: bold;
|
||||||
.info-routes {
|
|
||||||
display: flex;
|
&.no-catenary {
|
||||||
justify-content: center;
|
background-color: #686868;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
|
|
||||||
margin: 1em 0;
|
&.internal {
|
||||||
}
|
text-decoration: underline;
|
||||||
|
}
|
||||||
.routes {
|
|
||||||
display: flex;
|
&.speed {
|
||||||
justify-content: center;
|
background-color: #404040;
|
||||||
align-items: center;
|
color: #cfcfcf;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
|
|
||||||
padding: 0.25em;
|
&.sbl {
|
||||||
}
|
color: var(--clr-primary);
|
||||||
|
background-color: #404040;
|
||||||
ul.routes-list {
|
}
|
||||||
margin: 0.45em 0.25em;
|
|
||||||
display: flex;
|
&:last-child {
|
||||||
|
border-radius: 0 0.5em 0.5em 0;
|
||||||
li {
|
}
|
||||||
background-color: #007599;
|
|
||||||
|
&:first-child {
|
||||||
padding: 0.2em 0.25em;
|
border-radius: 0.5em 0 0 0.5em;
|
||||||
margin-left: 0.25em;
|
}
|
||||||
|
|
||||||
&.no-catenary {
|
&:only-child {
|
||||||
background-color: #686868;
|
border-radius: 0.5em;
|
||||||
}
|
|
||||||
|
}
|
||||||
&.internal {
|
}
|
||||||
text-decoration: underline;
|
}
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
b {
|
|
||||||
color: var(--clr-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
{{ $t('scenery.no-timetables') }}
|
{{ $t('scenery.no-timetables') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<transition-group name="timetables-anim">
|
<transition-group name="list-anim">
|
||||||
<div
|
<div
|
||||||
class="timetable-item"
|
class="timetable-item"
|
||||||
v-for="(scheduledTrain, i) in computedScheduledTrains"
|
v-for="(scheduledTrain, i) in computedScheduledTrains"
|
||||||
@@ -272,22 +272,7 @@ export default defineComponent({
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
|
@import '../../styles/animations.scss';
|
||||||
.timetables-anim-move,
|
|
||||||
.timetables-anim-enter-active,
|
|
||||||
.timetables-anim-leave-active {
|
|
||||||
transition: all 250ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetables-anim-enter-from,
|
|
||||||
.timetables-anim-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.timetables-anim-leave-active {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenery-timetable {
|
.scenery-timetable {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -486,21 +471,6 @@ export default defineComponent({
|
|||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scenery-timetable-list-anim {
|
|
||||||
&-enter-from,
|
|
||||||
&-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter-active {
|
|
||||||
transition: all 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
transition: all 100ms ease-out 100ms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
.timetable-item {
|
.timetable-item {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -2,8 +2,46 @@
|
|||||||
<section class="scenery-timetables-history scenery-section">
|
<section class="scenery-timetables-history scenery-section">
|
||||||
<Loading v-if="dataStatus != 2" />
|
<Loading v-if="dataStatus != 2" />
|
||||||
|
|
||||||
<div class="list-warning" v-else-if="sceneryHistoryList.length == 0">{{ $t('scenery.history-list-empty') }}</div>
|
<table v-else-if="sceneryHistoryList.length">
|
||||||
<ul class="history-list" v-else>
|
<thead>
|
||||||
|
<th>{{ $t('scenery.timetables-history-id') }}</th>
|
||||||
|
<th>{{ $t('scenery.timetables-history-number')}}</th>
|
||||||
|
<th>{{ $t('scenery.timetables-history-route')}}</th>
|
||||||
|
<th>{{ $t('scenery.timetables-history-driver')}}</th>
|
||||||
|
<th>{{ $t('scenery.timetables-history-author')}}</th>
|
||||||
|
<th>{{ $t('scenery.timetables-history-date')}}</th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="historyItem in sceneryHistoryList" @click="test">
|
||||||
|
<td>
|
||||||
|
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">#{{ historyItem.id }}</router-link>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b class="text--primary">{{ historyItem.trainCategoryCode }}</b> <br />
|
||||||
|
{{ historyItem.trainNo }}
|
||||||
|
</td>
|
||||||
|
<td>{{ historyItem.route.replace('|', ' -> ') }}</td>
|
||||||
|
<td>{{ historyItem.driverName }}</td>
|
||||||
|
<td>
|
||||||
|
<router-link
|
||||||
|
v-if="historyItem.authorName"
|
||||||
|
:to="`/journal/dispatchers?dispatcherName=${historyItem.authorName}`"
|
||||||
|
>{{ historyItem.authorName }}
|
||||||
|
</router-link>
|
||||||
|
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b>{{ localeDay(historyItem.beginDate, $i18n.locale) }}</b>
|
||||||
|
{{ localeTime(historyItem.beginDate, $i18n.locale) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="list-warning" v-else>{{ $t('scenery.history-list-empty') }}</div>
|
||||||
|
|
||||||
|
<!-- <ul class="history-list" v-else>
|
||||||
<li class="list-item" v-for="historyItem in sceneryHistoryList">
|
<li class="list-item" v-for="historyItem in sceneryHistoryList">
|
||||||
<div>
|
<div>
|
||||||
<b>{{ localeDay(historyItem.beginDate, $i18n.locale) }}</b>
|
<b>{{ localeDay(historyItem.beginDate, $i18n.locale) }}</b>
|
||||||
@@ -19,16 +57,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>{{ historyItem.route.replace('|', ' -> ') }}</div>
|
<div>{{ historyItem.route.replace('|', ' -> ') }}</div>
|
||||||
<!-- <div>{{ historyItem.routeDistance }} km</div> -->
|
|
||||||
<div>
|
<div>
|
||||||
{{ $t('scenery.timetable-author-title') }}:
|
{{ $t('scenery.timetable-author-title') }}:
|
||||||
<b v-if="historyItem.authorName">{{ historyItem.authorName }}</b>
|
<b v-if="historyItem.authorName">{{ historyItem.authorName }}</b>
|
||||||
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
|
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div v-if="historyItem.authorId">{{ historyItem.authorName }}</div> -->
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul> -->
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -57,7 +93,7 @@ export default defineComponent({
|
|||||||
dataStatus: DataStatus.Loading,
|
dataStatus: DataStatus.Loading,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
activated() {
|
||||||
this.fetchAPIData();
|
this.fetchAPIData();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -72,6 +108,10 @@ export default defineComponent({
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
test() {
|
||||||
|
console.log('test');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: { Loading },
|
components: { Loading },
|
||||||
});
|
});
|
||||||
@@ -91,17 +131,29 @@ export default defineComponent({
|
|||||||
padding: 0 0.5em;
|
padding: 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-item {
|
table {
|
||||||
display: grid;
|
width: 100%;
|
||||||
grid-template-columns: 1fr 2fr 2fr 1fr;
|
border-collapse: collapse;
|
||||||
gap: 1em;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
background-color: #353535;
|
thead {
|
||||||
padding: 0.5em;
|
position: sticky;
|
||||||
margin: 0.5em 0;
|
top: 0;
|
||||||
|
background-color: #222222;
|
||||||
|
}
|
||||||
|
|
||||||
line-height: 1.5em;
|
th {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
background-color: #353535;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0.75em;
|
||||||
|
border-bottom: solid 5px #111;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
|
|||||||
@@ -2,22 +2,23 @@
|
|||||||
<div class="train-info" tabindex="0">
|
<div class="train-info" tabindex="0">
|
||||||
<section class="train-route">
|
<section class="train-route">
|
||||||
<div class="train_general">
|
<div class="train_general">
|
||||||
<span>
|
<b class="warning-timeout" v-if="train.isTimeout" :title="$t('trains.timeout')">?</b>
|
||||||
<span class="timetable-id" v-if="train.timetableData">#{{ train.timetableData.timetableId }}</span>
|
<span class="timetable-id" v-if="train.timetableData">#{{ train.timetableData.timetableId }}</span>
|
||||||
|
|
||||||
<span class="timetable_warnings">
|
<span class="timetable_warnings" v-if="train.timetableData?.TWR || train.timetableData?.SKR">
|
||||||
<span class="train-badge twr" v-if="train.timetableData?.TWR">TWR</span>
|
<span class="train-badge twr" v-if="train.timetableData?.TWR">TWR</span>
|
||||||
<span class="train-badge skr" v-if="train.timetableData?.SKR">SKR</span>
|
<span class="train-badge skr" v-if="train.timetableData?.SKR">SKR</span>
|
||||||
</span>
|
|
||||||
<strong class="timetable-category" v-if="train.timetableData">
|
|
||||||
{{ train.timetableData.category }}
|
|
||||||
</strong>
|
|
||||||
<strong class="train-number"> {{ train.trainNo }}</strong>
|
|
||||||
|
|
|
||||||
<span class="train-driver" :class="{ supporter: train.isSupporter }">{{ train.driverName }}</span>
|
|
||||||
|
|
||||||
<b class="warning-timeout" v-if="train.isTimeout" :title="$t('trains.timeout')">?</b>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<strong>
|
||||||
|
<span v-if="train.timetableData" class="text--primary">{{ train.timetableData.category }} </span>
|
||||||
|
<span class="train-number">{{ train.trainNo }}</span>
|
||||||
|
</strong>
|
||||||
|
<span>•</span>
|
||||||
|
<b class="level-badge driver" :style="calculateExpStyle(train.driverLevel, train.isSupporter)">
|
||||||
|
{{ train.driverLevel < 2 ? 'L' : `${train.driverLevel}` }}
|
||||||
|
</b>
|
||||||
|
<span>{{ train.driverName }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="timetable_route" v-if="train.timetableData">
|
<div class="timetable_route" v-if="train.timetableData">
|
||||||
@@ -40,9 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="timetable_progress" style="margin-top: 0.5em" v-if="train.timetableData">
|
<div class="timetable_progress" style="margin-top: 0.5em" v-if="train.timetableData">
|
||||||
<!-- <span> </span> -->
|
|
||||||
<span class="timetable_progress-bar">
|
<span class="timetable_progress-bar">
|
||||||
<!-- {{ confirmedPercentage(train.timetableData.followingStops) }}% -->
|
|
||||||
<span class="bar-bg"></span>
|
<span class="bar-bg"></span>
|
||||||
<span
|
<span
|
||||||
class="bar-fg"
|
class="bar-fg"
|
||||||
@@ -94,6 +93,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
import styleMixin from '../../mixins/styleMixin';
|
||||||
import trainInfoMixin from '../../mixins/trainInfoMixin';
|
import trainInfoMixin from '../../mixins/trainInfoMixin';
|
||||||
import Train from '../../scripts/interfaces/Train';
|
import Train from '../../scripts/interfaces/Train';
|
||||||
|
|
||||||
@@ -110,12 +110,14 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [trainInfoMixin, imageMixin],
|
mixins: [trainInfoMixin, imageMixin, styleMixin],
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
|
@import '../../styles/badge.scss';
|
||||||
|
|
||||||
|
|
||||||
.image-warning {
|
.image-warning {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
@@ -149,7 +151,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.timetable-id {
|
.timetable-id {
|
||||||
margin-right: 0.3em;
|
|
||||||
color: #d2d2d2;
|
color: #d2d2d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,11 +160,7 @@ export default defineComponent({
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
width: 1.25em;
|
padding: 0 0.25em;
|
||||||
height: 1.25em;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
margin-left: 0.25em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timetable_stops {
|
.timetable_stops {
|
||||||
@@ -174,16 +171,20 @@ export default defineComponent({
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
gap: 0.25em;
|
||||||
|
margin-right: 1.5em;
|
||||||
}
|
}
|
||||||
.train-status-badges {
|
.train-status-badges {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-badge {
|
.train-badge {
|
||||||
padding: 0.15em 0.35em;
|
padding: 0.1em 0.2em;
|
||||||
margin-right: 0.3em;
|
border-radius: 0.2em;
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
@@ -197,7 +198,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.offline {
|
&.offline {
|
||||||
background-color: #b83b2d;
|
background-color: #9c362b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +217,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.timetable_warnings {
|
.timetable_warnings {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.2em;
|
||||||
|
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ import dateMixin from '../../mixins/dateMixin';
|
|||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import Train from '../../scripts/interfaces/Train';
|
import Train from '../../scripts/interfaces/Train';
|
||||||
import TrainStop from '../../scripts/interfaces/TrainStop';
|
import TrainStop from '../../scripts/interfaces/TrainStop';
|
||||||
|
import { useStore } from '../../store/store';
|
||||||
import StopDate from '../Global/StopDate.vue';
|
import StopDate from '../Global/StopDate.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -106,6 +107,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
setup(props) {
|
setup(props) {
|
||||||
return {
|
return {
|
||||||
|
store: useStore(),
|
||||||
|
|
||||||
lastConfirmed: computed(() => {
|
lastConfirmed: computed(() => {
|
||||||
return props.train.timetableData!.followingStops.findIndex(
|
return props.train.timetableData!.followingStops.findIndex(
|
||||||
(stop, i, stops) => stop.confirmed && !stops[i + 1]?.confirmed && !stops[i + 1]?.stopped
|
(stop, i, stops) => stop.confirmed && !stops[i + 1]?.confirmed && !stops[i + 1]?.stopped
|
||||||
@@ -424,3 +427,4 @@ ul.stop_list > li.stop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,7 @@
|
|||||||
<b class="warning-timeout">?</b>
|
<b class="warning-timeout">?</b>
|
||||||
{{ $t('trains.timeout') }}
|
{{ $t('trains.timeout') }}
|
||||||
</div> -->
|
</div> -->
|
||||||
|
<transition-group name="list-anim" tag="ul" class="train-list" v-else>
|
||||||
<ul class="train-list" v-else>
|
|
||||||
<li
|
<li
|
||||||
class="train-row"
|
class="train-row"
|
||||||
v-for="train in currentTrains"
|
v-for="train in currentTrains"
|
||||||
@@ -27,7 +26,7 @@
|
|||||||
>
|
>
|
||||||
<TrainInfo :train="train" />
|
<TrainInfo :train="train" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,6 +97,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
|
@import '../../styles/animations.scss';
|
||||||
|
|
||||||
.anim {
|
.anim {
|
||||||
&-enter-from,
|
&-enter-from,
|
||||||
@@ -158,8 +158,8 @@ img.train-image {
|
|||||||
|
|
||||||
.train {
|
.train {
|
||||||
&-list {
|
&-list {
|
||||||
overflow: auto;
|
position: relative;
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ export const sorterOptions = [
|
|||||||
id: 'distance',
|
id: 'distance',
|
||||||
value: 'kilometraż',
|
value: 'kilometraż',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'id',
|
||||||
|
value: 'id rozkładu',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'progress',
|
id: 'progress',
|
||||||
value: 'przebyta trasa',
|
value: 'przebyta trasa',
|
||||||
|
|||||||
+10
-1
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"general": {
|
"general": {
|
||||||
"and": " and "
|
"and": " and ",
|
||||||
|
"refresh": "REFRESH"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"sceneries": "SCENERIES",
|
"sceneries": "SCENERIES",
|
||||||
@@ -102,6 +103,7 @@
|
|||||||
"sort-timetable": "train no.",
|
"sort-timetable": "train no.",
|
||||||
"sort-progress": "route progress",
|
"sort-progress": "route progress",
|
||||||
"sort-delay": "current delay",
|
"sort-delay": "current delay",
|
||||||
|
"sort-id": "timetable id",
|
||||||
|
|
||||||
"sort-total-stops": "total stops",
|
"sort-total-stops": "total stops",
|
||||||
"sort-beginDate": "date",
|
"sort-beginDate": "date",
|
||||||
@@ -311,6 +313,13 @@
|
|||||||
"timetable-author-title": "Issued by",
|
"timetable-author-title": "Issued by",
|
||||||
"timetable-author-unknown": "Author unknown",
|
"timetable-author-unknown": "Author unknown",
|
||||||
|
|
||||||
|
"timetables-history-id": "ID",
|
||||||
|
"timetables-history-number": "Number",
|
||||||
|
"timetables-history-route": "Route",
|
||||||
|
"timetables-history-driver": "Driver",
|
||||||
|
"timetables-history-author": "TT author",
|
||||||
|
"timetables-history-date": "Date",
|
||||||
|
|
||||||
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
|
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
|
||||||
"history-list-empty": "No recorded scenery history!",
|
"history-list-empty": "No recorded scenery history!",
|
||||||
|
|
||||||
|
|||||||
+11
-2
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"general": {
|
"general": {
|
||||||
"and": " oraz "
|
"and": " oraz ",
|
||||||
|
"refresh": "ODŚWIEŻ"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"sceneries": "SCENERIE",
|
"sceneries": "SCENERIE",
|
||||||
@@ -103,7 +104,8 @@
|
|||||||
"sort-timetableId": "ID rozkładu",
|
"sort-timetableId": "ID rozkładu",
|
||||||
"sort-timestampFrom": "data",
|
"sort-timestampFrom": "data",
|
||||||
"sort-duration": "czas dyżuru",
|
"sort-duration": "czas dyżuru",
|
||||||
|
"sort-id": "id rozkładu",
|
||||||
|
|
||||||
"sort-mass": "masa",
|
"sort-mass": "masa",
|
||||||
"sort-speed": "prędkość",
|
"sort-speed": "prędkość",
|
||||||
"sort-length": "długość",
|
"sort-length": "długość",
|
||||||
@@ -315,6 +317,13 @@
|
|||||||
"timetable-author-title": "Wydany przez",
|
"timetable-author-title": "Wydany przez",
|
||||||
"timetable-author-unknown": "Autor nieznany",
|
"timetable-author-unknown": "Autor nieznany",
|
||||||
|
|
||||||
|
"timetables-history-id": "ID",
|
||||||
|
"timetables-history-number": "Numer",
|
||||||
|
"timetables-history-route": "Trasa",
|
||||||
|
"timetables-history-driver": "Maszynista",
|
||||||
|
"timetables-history-author": "Autor RJ",
|
||||||
|
"timetables-history-date": "Data",
|
||||||
|
|
||||||
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
|
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
|
||||||
"history-list-empty": "Brak historii dla tej scenerii!",
|
"history-list-empty": "Brak historii dla tej scenerii!",
|
||||||
|
|
||||||
|
|||||||
+10
@@ -7,6 +7,7 @@ import plLang from './locales/pl.json';
|
|||||||
|
|
||||||
import { createI18n } from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia';
|
||||||
|
import { registerSW } from 'virtual:pwa-register';
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
locale: 'pl',
|
locale: 'pl',
|
||||||
@@ -19,6 +20,15 @@ const i18n = createI18n({
|
|||||||
enableLegacy: false,
|
enableLegacy: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerSW({
|
||||||
|
onRegistered(r) {
|
||||||
|
r &&
|
||||||
|
setInterval(() => {
|
||||||
|
r.update();
|
||||||
|
}, 60 * 60 * 1000);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const clickOutsideDirective: Directive = {
|
const clickOutsideDirective: Directive = {
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
el.clickOutsideEvent = (event: Event) => {
|
el.clickOutsideEvent = (event: Event) => {
|
||||||
|
|||||||
@@ -4,11 +4,17 @@ export default defineComponent({
|
|||||||
methods: {
|
methods: {
|
||||||
calculateExpStyle(exp: number, isSupporter = false): string {
|
calculateExpStyle(exp: number, isSupporter = false): string {
|
||||||
const bgColor = exp > -1 ? (exp < 2 ? '#26B0D9' : `hsl(${-exp * 5 + 100}, 85%, 50%)`) : '#666';
|
const bgColor = exp > -1 ? (exp < 2 ? '#26B0D9' : `hsl(${-exp * 5 + 100}, 85%, 50%)`) : '#666';
|
||||||
|
|
||||||
const fontColor = exp > 14 || exp == -1 ? 'white' : 'black';
|
const fontColor = exp > 14 || exp == -1 ? 'white' : 'black';
|
||||||
const boxShadow = isSupporter ? `box-shadow: 0 0 10px 2px ${bgColor};` : '';
|
const boxShadow = isSupporter ? `box-shadow: 0 0 6px 2px ${bgColor};` : '';
|
||||||
|
|
||||||
return `background-color: ${bgColor}; color: ${fontColor}; ${boxShadow}`;
|
return `background-color: ${bgColor}; color: ${fontColor}; ${boxShadow};`;
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateTextExpStyle(exp: number, isSupporter = false): string {
|
||||||
|
const textColor = exp > -1 ? (exp < 2 ? '#26B0D9' : `hsl(${-exp * 5 + 100}, 75%, 50%)`) : '#666';
|
||||||
|
|
||||||
|
return `color: ${textColor}; ${isSupporter ? 'text-shadow: 0 0 6px ' + textColor : ''};`;
|
||||||
},
|
},
|
||||||
|
|
||||||
statusClasses(occupiedTo: string) {
|
statusClasses(occupiedTo: string) {
|
||||||
@@ -41,6 +47,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return className;
|
return className;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ export default interface Station {
|
|||||||
generalInfo?: {
|
generalInfo?: {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
reqLevel: number;
|
reqLevel: number;
|
||||||
// supportersOnly: boolean;
|
// supportersOnly: boolean;
|
||||||
|
|
||||||
lines: string;
|
lines: string;
|
||||||
project: string;
|
project: string;
|
||||||
|
projectUrl?: string;
|
||||||
|
|
||||||
signalType: string;
|
signalType: string;
|
||||||
controlType: string;
|
controlType: string;
|
||||||
|
|||||||
@@ -1,27 +1,30 @@
|
|||||||
export default interface StationRoutes {
|
export default interface StationRoutes {
|
||||||
oneWay:
|
oneWay: {
|
||||||
{
|
name: string;
|
||||||
name: string;
|
catenary: boolean;
|
||||||
catenary: boolean;
|
SBL: boolean;
|
||||||
SBL: boolean;
|
TWB: boolean;
|
||||||
TWB: boolean;
|
isInternal: boolean;
|
||||||
isInternal: boolean;
|
tracks: number;
|
||||||
tracks: number;
|
speed: number;
|
||||||
}[];
|
length: number;
|
||||||
|
}[];
|
||||||
twoWay: {
|
|
||||||
name: string;
|
twoWay: {
|
||||||
catenary: boolean;
|
name: string;
|
||||||
SBL: boolean;
|
catenary: boolean;
|
||||||
TWB: boolean;
|
SBL: boolean;
|
||||||
isInternal: boolean;
|
TWB: boolean;
|
||||||
tracks: number;
|
isInternal: boolean;
|
||||||
}[];
|
tracks: number;
|
||||||
|
speed: number;
|
||||||
/* [catenary, noCatenary] */
|
length: number;
|
||||||
oneWayCatenaryRouteNames: string[];
|
}[];
|
||||||
oneWayNoCatenaryRouteNames: string[];
|
|
||||||
twoWayCatenaryRouteNames: string[];
|
/* [catenary, noCatenary] */
|
||||||
twoWayNoCatenaryRouteNames: string[];
|
oneWayCatenaryRouteNames: string[];
|
||||||
sblRouteNames: string[];
|
oneWayNoCatenaryRouteNames: string[];
|
||||||
}
|
twoWayCatenaryRouteNames: string[];
|
||||||
|
twoWayNoCatenaryRouteNames: string[];
|
||||||
|
sblRouteNames: string[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default interface Train {
|
|||||||
driverId: number;
|
driverId: number;
|
||||||
trainNo: number;
|
trainNo: number;
|
||||||
driverName: string;
|
driverName: string;
|
||||||
|
driverLevel: number;
|
||||||
currentStationName: string;
|
currentStationName: string;
|
||||||
currentStationHash: string;
|
currentStationHash: string;
|
||||||
locoURL: string;
|
locoURL: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export default interface TrainStop {
|
export default interface TrainStop {
|
||||||
stopName: string;
|
stopName: string;
|
||||||
stopNameRAW: string;
|
stopNameRAW: string;
|
||||||
stopType: string;
|
stopType: string;
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ export interface TimetableHistory {
|
|||||||
timetableId: number;
|
timetableId: number;
|
||||||
trainNo: number;
|
trainNo: number;
|
||||||
trainCategoryCode: string;
|
trainCategoryCode: string;
|
||||||
|
|
||||||
driverId: number;
|
driverId: number;
|
||||||
driverName: string;
|
driverName: string;
|
||||||
|
driverLevel: number | null;
|
||||||
|
driverIsSupporter: boolean;
|
||||||
|
|
||||||
route: string;
|
route: string;
|
||||||
twr: number;
|
twr: number;
|
||||||
skr: number;
|
skr: number;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default interface TrainAPIData {
|
|||||||
driverName: string;
|
driverName: string;
|
||||||
driverId: number;
|
driverId: number;
|
||||||
driverIsSupporter: boolean;
|
driverIsSupporter: boolean;
|
||||||
|
driverLevel?: number;
|
||||||
|
|
||||||
currentStationName: string;
|
currentStationName: string;
|
||||||
currentStationHash: string;
|
currentStationHash: string;
|
||||||
|
|||||||
@@ -1,115 +1,120 @@
|
|||||||
import { TrainFilter } from "../../types/Trains/TrainOptionsTypes";
|
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
|
||||||
import { TrainFilterType } from "../enums/TrainFilterType";
|
import { TrainFilterType } from '../enums/TrainFilterType';
|
||||||
import Train from "../interfaces/Train";
|
import Train from '../interfaces/Train';
|
||||||
import TrainStop from "../interfaces/TrainStop";
|
import TrainStop from '../interfaces/TrainStop';
|
||||||
|
|
||||||
function confirmedPercentage(stops: TrainStop[] | undefined) {
|
function confirmedPercentage(stops: TrainStop[] | undefined) {
|
||||||
if (!stops) return -1;
|
if (!stops) return -1;
|
||||||
|
|
||||||
return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
|
return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
|
||||||
};
|
}
|
||||||
|
|
||||||
function currentDelay(stops: TrainStop[] | undefined) {
|
function currentDelay(stops: TrainStop[] | undefined) {
|
||||||
if (!stops) return -Infinity;
|
if (!stops) return -Infinity;
|
||||||
|
|
||||||
const delay =
|
const delay =
|
||||||
stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
|
stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
|
||||||
?.departureDelay || 0;
|
?.departureDelay || 0;
|
||||||
|
|
||||||
return delay;
|
return delay;
|
||||||
};
|
}
|
||||||
|
|
||||||
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
|
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
|
||||||
return trainList.filter(
|
return trainList.filter((train) => {
|
||||||
(train) => {
|
const isFiltered = filters.every((f) => {
|
||||||
const isFiltered = filters.every(f => {
|
if (f.isActive) return true;
|
||||||
if (f.isActive) return true;
|
|
||||||
|
|
||||||
if (!train.timetableData) return filters.find(filter => filter.id == TrainFilterType.noTimetable)!.isActive;
|
if (!train.timetableData) return filters.find((filter) => filter.id == TrainFilterType.noTimetable)!.isActive;
|
||||||
|
|
||||||
switch (f.id) {
|
switch (f.id) {
|
||||||
case TrainFilterType.comments:
|
case TrainFilterType.comments:
|
||||||
return !train.timetableData.followingStops.some(stop => stop.comments);
|
return !train.timetableData.followingStops.some((stop) => stop.comments);
|
||||||
|
|
||||||
case TrainFilterType.twr:
|
|
||||||
return !train.timetableData.TWR;
|
|
||||||
|
|
||||||
case TrainFilterType.skr:
|
|
||||||
return !train.timetableData.SKR;
|
|
||||||
|
|
||||||
case TrainFilterType.passenger:
|
|
||||||
return !/^[AMRE]\D{2}$/.test(train.timetableData.category);
|
|
||||||
|
|
||||||
case TrainFilterType.freight:
|
|
||||||
return !train.timetableData.category.startsWith('T');
|
|
||||||
|
|
||||||
case TrainFilterType.other:
|
|
||||||
return !/^[PXZL]\D{2}$/.test(train.timetableData.category);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
|
case TrainFilterType.twr:
|
||||||
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) && isFiltered
|
return !train.timetableData.TWR;
|
||||||
}
|
|
||||||
|
|
||||||
|
case TrainFilterType.skr:
|
||||||
|
return !train.timetableData.SKR;
|
||||||
|
|
||||||
|
case TrainFilterType.passenger:
|
||||||
|
return !/^[AMRE]\D{2}$/.test(train.timetableData.category);
|
||||||
|
|
||||||
|
case TrainFilterType.freight:
|
||||||
|
return !train.timetableData.category.startsWith('T');
|
||||||
|
|
||||||
|
case TrainFilterType.other:
|
||||||
|
return !/^[PXZL]\D{2}$/.test(train.timetableData.category);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
(searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
|
||||||
|
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) &&
|
||||||
|
(!train.timetableData ? !train.online : true) &&
|
||||||
|
isFiltered
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: number }) {
|
function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: number }) {
|
||||||
return trainList.sort((a: Train, b: Train) => {
|
return trainList.sort((a: Train, b: Train) => {
|
||||||
switch (sorterActive.id) {
|
switch (sorterActive.id) {
|
||||||
case 'mass':
|
case 'id':
|
||||||
if (a.mass > b.mass) return sorterActive.dir;
|
if ((a.timetableData?.timetableId || -1) > (b.timetableData?.timetableId || -1)) return sorterActive.dir;
|
||||||
return -sorterActive.dir;
|
|
||||||
|
|
||||||
case 'distance':
|
return -sorterActive.dir;
|
||||||
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
|
||||||
|
|
||||||
return -sorterActive.dir;
|
case 'mass':
|
||||||
|
if (a.mass > b.mass) return sorterActive.dir;
|
||||||
|
return -sorterActive.dir;
|
||||||
|
|
||||||
case 'progress':
|
case 'distance':
|
||||||
if (confirmedPercentage(a.timetableData?.followingStops) > confirmedPercentage(b.timetableData?.followingStops))
|
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
||||||
return sorterActive.dir;
|
|
||||||
|
|
||||||
return -sorterActive.dir;
|
return -sorterActive.dir;
|
||||||
|
|
||||||
case 'delay':
|
case 'progress':
|
||||||
if (currentDelay(a.timetableData?.followingStops) > currentDelay(b.timetableData?.followingStops))
|
if (confirmedPercentage(a.timetableData?.followingStops) > confirmedPercentage(b.timetableData?.followingStops))
|
||||||
return sorterActive.dir;
|
return sorterActive.dir;
|
||||||
|
|
||||||
return -sorterActive.dir;
|
return -sorterActive.dir;
|
||||||
|
|
||||||
case 'speed':
|
case 'delay':
|
||||||
if (a.speed > b.speed) return sorterActive.dir;
|
if (currentDelay(a.timetableData?.followingStops) > currentDelay(b.timetableData?.followingStops))
|
||||||
return -sorterActive.dir;
|
return sorterActive.dir;
|
||||||
|
|
||||||
case 'timetable':
|
return -sorterActive.dir;
|
||||||
if (a.trainNo > b.trainNo) return sorterActive.dir;
|
|
||||||
return -sorterActive.dir;
|
|
||||||
|
|
||||||
case 'length':
|
case 'speed':
|
||||||
if (a.length > b.length) return sorterActive.dir;
|
if (a.speed > b.speed) return sorterActive.dir;
|
||||||
return -sorterActive.dir;
|
return -sorterActive.dir;
|
||||||
|
|
||||||
default:
|
case 'timetable':
|
||||||
break;
|
if (a.trainNo > b.trainNo) return sorterActive.dir;
|
||||||
}
|
return -sorterActive.dir;
|
||||||
|
|
||||||
return 0;
|
case 'length':
|
||||||
});
|
if (a.length > b.length) return sorterActive.dir;
|
||||||
|
return -sorterActive.dir;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filteredTrainList(
|
export function filteredTrainList(
|
||||||
trainList: Train[],
|
trainList: Train[],
|
||||||
searchedTrain: string,
|
searchedTrain: string,
|
||||||
searchedDriver: string,
|
searchedDriver: string,
|
||||||
sorterActive: { id: string; dir: number },
|
sorterActive: { id: string; dir: number },
|
||||||
filters: TrainFilter[]
|
filters: TrainFilter[]
|
||||||
) {
|
) {
|
||||||
|
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
|
||||||
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
|
return [...sortTrainList(filtered, sorterActive)];
|
||||||
return [...sortTrainList(filtered, sorterActive)];
|
}
|
||||||
};
|
|
||||||
|
|||||||
+66
-58
@@ -24,6 +24,7 @@ export const useStore = defineStore('store', {
|
|||||||
|
|
||||||
stationList: [],
|
stationList: [],
|
||||||
trainList: [],
|
trainList: [],
|
||||||
|
routesList: [],
|
||||||
|
|
||||||
sceneryData: [],
|
sceneryData: [],
|
||||||
lastDispatcherStatuses: [],
|
lastDispatcherStatuses: [],
|
||||||
@@ -101,6 +102,7 @@ export const useStore = defineStore('store', {
|
|||||||
isTimeout: train.isTimeout,
|
isTimeout: train.isTimeout,
|
||||||
|
|
||||||
isSupporter: train.driverIsSupporter,
|
isSupporter: train.driverIsSupporter,
|
||||||
|
driverLevel: train.driverLevel,
|
||||||
|
|
||||||
timetableData: timetable
|
timetableData: timetable
|
||||||
? {
|
? {
|
||||||
@@ -114,8 +116,8 @@ export const useStore = defineStore('store', {
|
|||||||
sceneries: timetable.sceneries,
|
sceneries: timetable.sceneries,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
} as Train;
|
||||||
}) as Train[];
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getDispatcherStatus(onlineStationData: StationAPIData) {
|
getDispatcherStatus(onlineStationData: StationAPIData) {
|
||||||
@@ -293,73 +295,78 @@ export const useStore = defineStore('store', {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stationList = sceneryData.map((scenery) => ({
|
this.stationList = sceneryData.map((scenery) => {
|
||||||
name: scenery.name,
|
return {
|
||||||
|
name: scenery.name,
|
||||||
|
|
||||||
generalInfo: {
|
generalInfo: {
|
||||||
...scenery,
|
...scenery,
|
||||||
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
||||||
routes:
|
routes:
|
||||||
scenery.routes
|
scenery.routes
|
||||||
?.split(';')
|
?.split(';')
|
||||||
.filter((routeString) => routeString)
|
.filter((routeString) => routeString)
|
||||||
.reduce(
|
.reduce(
|
||||||
(acc, routeString) => {
|
(acc, routeString) => {
|
||||||
const specs1 = routeString.split('_')[0];
|
const specs1 = routeString.split('_')[0];
|
||||||
const isInternal = specs1.startsWith('!');
|
const isInternal = specs1.startsWith('!');
|
||||||
const name = isInternal ? specs1.replace('!', '') : specs1;
|
const name = isInternal ? specs1.replace('!', '') : specs1;
|
||||||
|
|
||||||
const specs2 = routeString.split('_')[1].split('');
|
const specs2 = routeString.split('_')[1].split('');
|
||||||
const twoWay = specs2[0] == '2';
|
const twoWay = specs2[0] == '2';
|
||||||
const catenary = specs2[1] == 'E';
|
const catenary = specs2[1] == 'E';
|
||||||
const SBL = specs2[2] == 'S';
|
const SBL = specs2[2] == 'S';
|
||||||
const TWB = specs2[3] ? true : false;
|
const TWB = specs2[3] ? true : false;
|
||||||
|
const speed = Number(routeString.split(':')[1]) || 0;
|
||||||
|
const length = Number(routeString.split(':')[2]) || 0;
|
||||||
|
|
||||||
const propName = twoWay
|
const propName = twoWay
|
||||||
? catenary
|
? catenary
|
||||||
? 'twoWayCatenaryRouteNames'
|
? 'twoWayCatenaryRouteNames'
|
||||||
: 'twoWayNoCatenaryRouteNames'
|
: 'twoWayNoCatenaryRouteNames'
|
||||||
: catenary
|
: catenary
|
||||||
? 'oneWayCatenaryRouteNames'
|
? 'oneWayCatenaryRouteNames'
|
||||||
: 'oneWayNoCatenaryRouteNames';
|
: 'oneWayNoCatenaryRouteNames';
|
||||||
|
|
||||||
acc[twoWay ? 'twoWay' : 'oneWay'].push({
|
acc[twoWay ? 'twoWay' : 'oneWay'].push({
|
||||||
name,
|
name,
|
||||||
SBL,
|
SBL,
|
||||||
TWB,
|
TWB,
|
||||||
catenary,
|
catenary,
|
||||||
isInternal,
|
isInternal,
|
||||||
tracks: twoWay ? 2 : 1,
|
tracks: twoWay ? 2 : 1,
|
||||||
});
|
length,
|
||||||
if (!isInternal) acc[propName].push(name);
|
speed,
|
||||||
|
});
|
||||||
|
if (!isInternal) acc[propName].push(name);
|
||||||
|
|
||||||
if (SBL) acc['sblRouteNames'].push(name);
|
if (SBL) acc['sblRouteNames'].push(name);
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
oneWay: [],
|
oneWay: [],
|
||||||
twoWay: [],
|
twoWay: [],
|
||||||
sblRouteNames: [],
|
sblRouteNames: [],
|
||||||
oneWayCatenaryRouteNames: [],
|
oneWayCatenaryRouteNames: [],
|
||||||
oneWayNoCatenaryRouteNames: [],
|
oneWayNoCatenaryRouteNames: [],
|
||||||
twoWayCatenaryRouteNames: [],
|
twoWayCatenaryRouteNames: [],
|
||||||
twoWayNoCatenaryRouteNames: [],
|
twoWayNoCatenaryRouteNames: [],
|
||||||
} as StationRoutes
|
} as StationRoutes
|
||||||
) || {},
|
) || {},
|
||||||
checkpoints: scenery.checkpoints
|
checkpoints: scenery.checkpoints
|
||||||
? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
|
? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
|
||||||
: [],
|
: [],
|
||||||
},
|
},
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
connectToWebsocket() {
|
connectToWebsocket() {
|
||||||
const socket = io(URLs.stacjownikAPI, {
|
const socket = io(URLs.stacjownikAPI, {
|
||||||
transports: ['websocket', 'polling'],
|
// transports: ['websocket', 'polling'],
|
||||||
rememberUpgrade: true,
|
rememberUpgrade: true,
|
||||||
reconnection: true,
|
reconnection: true,
|
||||||
timeout: 2000,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connect_error', (err) => {
|
socket.on('connect_error', (err) => {
|
||||||
@@ -373,8 +380,9 @@ export const useStore = defineStore('store', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.emit('FETCH_DATA', {}, (data: APIData) => {
|
socket.emit('FETCH_DATA', {}, (data: APIData) => {
|
||||||
this.apiData = data;
|
|
||||||
this.dataStatuses.connection = DataStatus.Loaded;
|
this.dataStatuses.connection = DataStatus.Loaded;
|
||||||
|
|
||||||
|
this.apiData = data;
|
||||||
this.setOnlineData();
|
this.setOnlineData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export interface StationJSONData {
|
|||||||
url: string;
|
url: string;
|
||||||
lines: string;
|
lines: string;
|
||||||
project: string;
|
project: string;
|
||||||
|
projectUrl: string;
|
||||||
|
|
||||||
reqLevel: number;
|
reqLevel: number;
|
||||||
|
|
||||||
@@ -69,8 +70,9 @@ export interface StationJSONData {
|
|||||||
SUP: boolean;
|
SUP: boolean;
|
||||||
|
|
||||||
routes: string;
|
routes: string;
|
||||||
|
|
||||||
checkpoints: string | null;
|
checkpoints: string | null;
|
||||||
authors?: string;
|
authors?: string;
|
||||||
|
|
||||||
availability: Availability;
|
availability: Availability;
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,5 @@
|
|||||||
@import 'responsive.scss';
|
@import 'responsive.scss';
|
||||||
|
@import 'animations.scss';
|
||||||
// Animations
|
|
||||||
.warning {
|
|
||||||
&-enter-from,
|
|
||||||
&-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter-active {
|
|
||||||
transition: all 150ms 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
transition: all 150ms 100ms ease-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Styles
|
//Styles
|
||||||
|
|
||||||
.list_wrapper {
|
.list_wrapper {
|
||||||
@@ -26,6 +10,10 @@
|
|||||||
padding-right: 0.2em;
|
padding-right: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.journal-list {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.journal_wrapper {
|
.journal_wrapper {
|
||||||
max-width: 1350px;
|
max-width: 1350px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
.list-anim-move,
|
||||||
|
.list-anim-enter-active,
|
||||||
|
.list-anim-leave-active {
|
||||||
|
transition: all 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-anim-enter-from,
|
||||||
|
.list-anim-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-anim-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-anim {
|
||||||
|
&-enter-from,
|
||||||
|
&-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-active {
|
||||||
|
transition: all 100ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 100ms ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
-28
@@ -1,28 +1,58 @@
|
|||||||
.badge {
|
.badge {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
background: #585858;
|
background: #585858;
|
||||||
|
|
||||||
margin: 0.25em;
|
margin: 0.25em;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.2em 0.4em;
|
padding: 0.2em 0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-none {
|
&-none {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
padding: 0.2em 0.4em;
|
padding: 0.2em 0.4em;
|
||||||
background: firebrick;
|
background: firebrick;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.level-badge {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
&.driver {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 1.7em;
|
||||||
|
height: 1.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dispatcher {
|
||||||
|
border-radius: 0.25em;
|
||||||
|
|
||||||
|
width: 1.6em;
|
||||||
|
height: 1.6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.region-badge {
|
||||||
|
padding: 0 0.5em;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&.eu {
|
||||||
|
background-color: forestgreen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.actions-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.filter-button .active-indicator {
|
.filter-button .active-indicator {
|
||||||
width: 7px;
|
width: 7px;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
|
|||||||
@@ -18,19 +18,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 1rem;
|
width: 15px;
|
||||||
height: 1rem;
|
height: 15px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
&-track {
|
&-track {
|
||||||
border-radius: 0.5em;
|
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-thumb {
|
&-thumb {
|
||||||
border-radius: 0.5em;
|
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-corner {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -47,7 +49,7 @@ body {
|
|||||||
|
|
||||||
&.no-scroll {
|
&.no-scroll {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
padding-right: 1rem;
|
padding-right: 15px;
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
+16
-11
@@ -1,18 +1,23 @@
|
|||||||
@mixin smallScreen() {
|
@mixin smallScreen() {
|
||||||
@media only screen and (max-width: 700px) {
|
@media only screen and (max-width: 700px) {
|
||||||
@content;
|
@content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@mixin midScreen() {
|
@mixin midScreen() {
|
||||||
@media only screen and (max-width: 1150px) {
|
@media only screen and (max-width: 1150px) {
|
||||||
@content;
|
@content;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin screenLandscape() {
|
||||||
|
@media only screen and (orientation: landscape) and (max-device-height: 450px) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin bigScreen() {
|
@mixin bigScreen() {
|
||||||
@media only screen and (min-width: 2000px) {
|
@media only screen and (min-width: 2000px) {
|
||||||
@content;
|
@content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ export interface JournalTimetableFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface JournalTimetableSorter {
|
export interface JournalTimetableSorter {
|
||||||
id: 'beginDate' | 'distance' | 'total-stops';
|
id: 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
|
||||||
dir: -1 | 1;
|
dir: -1 | 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,41 +6,42 @@
|
|||||||
<JournalOptions
|
<JournalOptions
|
||||||
@on-search-confirm="fetchHistoryData"
|
@on-search-confirm="fetchHistoryData"
|
||||||
@on-options-reset="resetOptions"
|
@on-options-reset="resetOptions"
|
||||||
|
@on-refresh-data="fetchHistoryData"
|
||||||
:sorter-option-ids="['timestampFrom', 'duration']"
|
:sorter-option-ids="['timestampFrom', 'duration']"
|
||||||
:data-status="dataStatus"
|
:data-status="dataStatus"
|
||||||
:current-options-active="currentOptionsActive"
|
:current-options-active="currentOptionsActive"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="list_wrapper" @scroll="handleScroll">
|
<div class="list_wrapper" @scroll="handleScroll">
|
||||||
<!-- <transition name="warning" mode="out-in"> -->
|
<transition name="status-anim" mode="out-in">
|
||||||
<!-- <div :key="dataStatus"> -->
|
<div :key="dataStatus">
|
||||||
<div class="journal_warning" v-if="store.isOffline">
|
<div class="journal_warning" v-if="store.isOffline">
|
||||||
{{ $t('app.offline') }}
|
{{ $t('app.offline') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Loading v-else-if="dataStatus == DataStatus.Initialized || dataStatus == DataStatus.Loading" />
|
<Loading v-else-if="dataStatus == DataStatus.Loading" />
|
||||||
|
|
||||||
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
||||||
{{ $t('app.error') }}
|
{{ $t('app.error') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="journal_warning" v-else-if="historyList.length == 0">
|
<div class="journal_warning" v-else-if="historyList.length == 0">
|
||||||
{{ $t('app.no-result') }}
|
{{ $t('app.no-result') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<JournalDispatchersList :dispatcherHistory="computedHistoryList" />
|
<JournalDispatchersList :dispatcherHistory="computedHistoryList" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn--option btn--load-data"
|
class="btn btn--option btn--load-data"
|
||||||
v-if="!scrollNoMoreData && scrollDataLoaded && computedHistoryList.length > 15"
|
v-if="!scrollNoMoreData && scrollDataLoaded && computedHistoryList.length > 15"
|
||||||
@click="addHistoryData"
|
@click="addHistoryData"
|
||||||
>
|
>
|
||||||
{{ $t('journal.load-data') }}
|
{{ $t('journal.load-data') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- </div>
|
</div>
|
||||||
</transition> -->
|
</transition>
|
||||||
|
|
||||||
<div class="journal_warning" v-if="scrollNoMoreData">
|
<div class="journal_warning" v-if="scrollNoMoreData">
|
||||||
{{ $t('journal.no-further-data') }}
|
{{ $t('journal.no-further-data') }}
|
||||||
@@ -110,7 +111,7 @@ export default defineComponent({
|
|||||||
statsCardOpen: false,
|
statsCardOpen: false,
|
||||||
currentOptionsActive: false,
|
currentOptionsActive: false,
|
||||||
|
|
||||||
dataStatus: DataStatus.Initialized,
|
dataStatus: DataStatus.Loading,
|
||||||
DataStatus,
|
DataStatus,
|
||||||
|
|
||||||
historyList: [] as DispatcherHistory[],
|
historyList: [] as DispatcherHistory[],
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
<JournalOptions
|
<JournalOptions
|
||||||
@on-search-confirm="fetchHistoryData"
|
@on-search-confirm="fetchHistoryData"
|
||||||
@on-options-reset="resetOptions"
|
@on-options-reset="resetOptions"
|
||||||
:sorter-option-ids="[ 'beginDate', 'distance', 'total-stops']"
|
@on-refresh-data="fetchHistoryData"
|
||||||
|
:sorter-option-ids="['timetableId', 'beginDate', 'distance', 'total-stops']"
|
||||||
:filters="journalTimetableFilters"
|
:filters="journalTimetableFilters"
|
||||||
:currentOptionsActive="currentOptionsActive"
|
:currentOptionsActive="currentOptionsActive"
|
||||||
:data-status="dataStatus"
|
:data-status="dataStatus"
|
||||||
@@ -15,35 +16,35 @@
|
|||||||
<JournalStats />
|
<JournalStats />
|
||||||
|
|
||||||
<div class="list_wrapper" @scroll="handleScroll">
|
<div class="list_wrapper" @scroll="handleScroll">
|
||||||
<!-- <transition name="warning" mode="out-in"> -->
|
<transition name="status-anim" mode="out-in">
|
||||||
<!-- <div :key="dataStatus"> -->
|
<div :key="dataStatus">
|
||||||
<div class="journal_warning" v-if="store.isOffline">
|
<div class="journal_warning" v-if="store.isOffline">
|
||||||
{{ $t('app.offline') }}
|
{{ $t('app.offline') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Loading v-else-if="dataStatus == DataStatus.Initialized || dataStatus == DataStatus.Loading" />
|
<Loading v-else-if="dataStatus == DataStatus.Loading" />
|
||||||
|
|
||||||
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
||||||
{{ $t('app.error') }}
|
{{ $t('app.error') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="timetableHistory.length == 0" class="journal_warning">
|
<div v-else-if="timetableHistory.length == 0" class="journal_warning">
|
||||||
{{ $t('app.no-result') }}
|
{{ $t('app.no-result') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<JournalTimetablesList :timetableHistory="timetableHistory" />
|
<JournalTimetablesList :timetableHistory="timetableHistory" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn--option btn--load-data"
|
class="btn btn--option btn--load-data"
|
||||||
v-if="!scrollNoMoreData && scrollDataLoaded && timetableHistory.length >= 15"
|
v-if="!scrollNoMoreData && scrollDataLoaded && timetableHistory.length >= 15"
|
||||||
@click="addHistoryData"
|
@click="addHistoryData"
|
||||||
>
|
>
|
||||||
{{ $t('journal.load-data') }}
|
{{ $t('journal.load-data') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- </div> -->
|
</div>
|
||||||
<!-- </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">{{ $t('journal.loading-further-data') }}</div>
|
<div class="journal_warning" v-else-if="!scrollDataLoaded">{{ $t('journal.loading-further-data') }}</div>
|
||||||
@@ -53,12 +54,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, provide, reactive, Ref, ref, watch } from 'vue';
|
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import DriverStats from '../components/JournalView/JournalDriverStats.vue';
|
import DriverStats from '../components/JournalView/JournalDriverStats.vue';
|
||||||
import Loading from '../components/Global/Loading.vue';
|
import Loading from '../components/Global/Loading.vue';
|
||||||
import { JournalTimetableFilter, JournalTimetableSorter } from '../types/Journal/JournalTimetablesTypes';
|
import { JournalTimetableSorter } from '../types/Journal/JournalTimetablesTypes';
|
||||||
import dateMixin from '../mixins/dateMixin';
|
import dateMixin from '../mixins/dateMixin';
|
||||||
import routerMixin from '../mixins/routerMixin';
|
import routerMixin from '../mixins/routerMixin';
|
||||||
import { DataStatus } from '../scripts/enums/DataStatus';
|
import { DataStatus } from '../scripts/enums/DataStatus';
|
||||||
@@ -104,14 +105,14 @@ export default defineComponent({
|
|||||||
timetableHistory: [] as TimetableHistory[],
|
timetableHistory: [] as TimetableHistory[],
|
||||||
journalTimetableFilters,
|
journalTimetableFilters,
|
||||||
|
|
||||||
dataStatus: DataStatus.Initialized,
|
dataStatus: DataStatus.Loading,
|
||||||
dataErrorMessage: '',
|
dataErrorMessage: '',
|
||||||
|
|
||||||
DataStatus,
|
DataStatus,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const sorterActive: JournalTimetableSorter = reactive({ id: 'beginDate', dir: 1 });
|
const sorterActive: JournalTimetableSorter = reactive({ id: 'timetableId', dir: 1 });
|
||||||
const journalFilterActive = ref(journalTimetableFilters[0]);
|
const journalFilterActive = ref(journalTimetableFilters[0]);
|
||||||
|
|
||||||
const searchersValues = reactive({
|
const searchersValues = reactive({
|
||||||
@@ -145,9 +146,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
currentQueryArray(q: string[]) {
|
currentQueryArray(q: string[]) {
|
||||||
this.currentOptionsActive =
|
this.currentOptionsActive = q.length >= 2 || q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1]);
|
||||||
q.length > 2 || q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'beginDate');
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -162,6 +162,7 @@ export default defineComponent({
|
|||||||
this.fetchHistoryData();
|
this.fetchHistoryData();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleScroll(e: Event) {
|
handleScroll(e: Event) {
|
||||||
const listElement = e.target as HTMLElement;
|
const listElement = e.target as HTMLElement;
|
||||||
@@ -188,7 +189,7 @@ export default defineComponent({
|
|||||||
this.setSearchers('', '', '', '');
|
this.setSearchers('', '', '', '');
|
||||||
|
|
||||||
this.journalFilterActive = this.journalTimetableFilters[0];
|
this.journalFilterActive = this.journalTimetableFilters[0];
|
||||||
this.sorterActive.id = 'beginDate';
|
this.sorterActive.id = 'timetableId';
|
||||||
|
|
||||||
this.fetchHistoryData();
|
this.fetchHistoryData();
|
||||||
},
|
},
|
||||||
@@ -214,6 +215,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async fetchHistoryData() {
|
async fetchHistoryData() {
|
||||||
|
// if(this.dataStatus == DataStatus.Loading) return;
|
||||||
|
|
||||||
const queries: string[] = [];
|
const queries: string[] = [];
|
||||||
|
|
||||||
const driverName = this.searchersValues['search-driver'].trim();
|
const driverName = this.searchersValues['search-driver'].trim();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<section class="trains-view">
|
<section class="trains-view">
|
||||||
<div class="trains_wrapper">
|
<div class="trains_wrapper">
|
||||||
<TrainOptions
|
<TrainOptions
|
||||||
:sorter-option-ids="['distance', 'progress', 'delay', 'mass', 'speed', 'length']"
|
:sorter-option-ids="['distance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']"
|
||||||
:current-options-active="currentOptionsActive"
|
:current-options-active="currentOptionsActive"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user