format & lint

This commit is contained in:
2023-10-04 15:01:01 +02:00
parent 800fc35e63
commit 45c1d83512
125 changed files with 15006 additions and 13222 deletions
+25 -21
View File
@@ -54,42 +54,42 @@
</i18n-t>
</div>
<div v-if="firstPlaceDispatchers.length == 1">
<div v-if="topDispatchers.length == 1">
&bull;
<i18n-t keypath="journal.timetable-stats-most-active-dr">
<template #dispatcher>
<router-link :to="`/journal/dispatchers?dispatcherName=${firstPlaceDispatchers[0].name}`">
<b>{{ firstPlaceDispatchers[0].name }}</b>
<router-link :to="`/journal/dispatchers?dispatcherName=${topDispatchers[0].name}`">
<b>{{ topDispatchers[0].name }}</b>
</router-link>
</template>
<template #count>
<b class="text--primary">
{{ firstPlaceDispatchers[0].count }}
{{ $t('journal.timetable-count', firstPlaceDispatchers[0].count) }}
{{ topDispatchers[0].count }}
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
</b>
</template>
</i18n-t>
</div>
<div v-if="firstPlaceDispatchers.length > 1">
<div v-if="topDispatchers.length > 1">
&bull;
<i18n-t keypath="journal.timetable-stats-most-active-dr-many">
<template #dispatchers>
<span v-for="(disp, i) in firstPlaceDispatchers">
<span v-if="i == firstPlaceDispatchers.length - 1"> {{ $t('general.and') }} </span>
<span v-for="(disp, i) in topDispatchers" :key="i">
<span v-if="i == topDispatchers.length - 1"> {{ $t('general.and') }} </span>
<router-link :to="`/journal/dispatchers?dispatcherName=${disp.name}`">
<b>{{ disp.name }}</b>
</router-link>
<span v-if="i < firstPlaceDispatchers.length - 2">, </span>
<span v-if="i < topDispatchers.length - 2">, </span>
</span>
</template>
<template #count>
<b class="text--primary">
{{ firstPlaceDispatchers[0].count }}
{{ $t('journal.timetable-count', firstPlaceDispatchers[0].count) }}
{{ topDispatchers[0].count }}
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
</b>
</template>
</i18n-t>
@@ -99,7 +99,9 @@
&bull;
<i18n-t keypath="journal.timetable-stats-longest-duties">
<template #dispatcher>
<router-link :to="`/journal/dispatchers?dispatcherName=${stats.longestDuties[0].name}`">
<router-link
:to="`/journal/dispatchers?dispatcherName=${stats.longestDuties[0].name}`"
>
<b>{{ stats.longestDuties[0].name }}</b>
</router-link>
</template>
@@ -133,7 +135,10 @@ import axios from 'axios';
import { defineComponent } from 'vue';
import dateMixin from '../../mixins/dateMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import { ITimetablesDailyStats, ITimetablesDailyStatsResponse } from '../../scripts/interfaces/api/StatsAPIData';
import {
ITimetablesDailyStats,
ITimetablesDailyStatsResponse
} from '../../scripts/interfaces/api/StatsAPIData';
import { URLs } from '../../scripts/utils/apiURLs';
export default defineComponent({
@@ -156,8 +161,8 @@ export default defineComponent({
timetableRouteDistance: 0,
longestDuties: [],
mostActiveDrivers: [],
mostActiveDispatchers: [],
} as ITimetablesDailyStats,
mostActiveDispatchers: []
} as ITimetablesDailyStats
};
},
@@ -171,12 +176,12 @@ export default defineComponent({
},
computed: {
firstPlaceDispatchers() {
topDispatchers() {
if (this.stats.mostActiveDispatchers.length == 0) return [];
const maxCount = this.stats.mostActiveDispatchers[0].count;
return this.stats.mostActiveDispatchers.filter((disp) => disp.count === maxCount);
},
}
},
methods: {
@@ -197,7 +202,7 @@ export default defineComponent({
mostActiveDispatchers: res.mostActiveDispatchers,
mostActiveDrivers: res.mostActiveDrivers,
longestDuties: res.longestDuties,
longestDuties: res.longestDuties
};
this.statsStatus = DataStatus.Loaded;
@@ -218,8 +223,8 @@ export default defineComponent({
stopFetchingDailyStats() {
clearInterval(this.intervalId);
this.intervalId = -1;
},
},
}
}
});
</script>
@@ -247,4 +252,3 @@ export default defineComponent({
}
}
</style>
+15 -22
View File
@@ -14,7 +14,7 @@
<div v-else>
<h3>STATYSTYKI WYSTAWIONYCH ROZKŁADÓW</h3>
<div class="info-stats" v-if="store.dispatcherStatsData._count._all">
<span class="stat-badge">
<span>LICZBA</span>
@@ -36,8 +36,9 @@
<h3>OSTATNIE WYSTAWIONE ROZKŁADY</h3>
<div class="last-timetables">
<div class="timetable-row" v-for="timetable in timetables">
#{{ timetable.timetableId }} | <b>{{ timetable.trainCategoryCode }} {{ timetable.trainNo }}</b> |
<div class="timetable-row" v-for="timetable in timetables" :key="timetable.id">
#{{ timetable.timetableId }} |
<b>{{ timetable.trainCategoryCode }} {{ timetable.trainNo }}</b> |
{{ timetable.driverName }} ({{ timetable.routeDistance }}km)
<div>{{ timetable.route.replace('|', ' > ') }}</div>
</div>
@@ -49,9 +50,8 @@
</template>
<script lang="ts">
import axios from 'axios';
import { computed, defineComponent } from 'vue';
import { defineComponent } from 'vue';
import { DispatcherStatsAPIData } from '../../scripts/interfaces/api/DispatcherStatsAPIData';
import { TimetableHistory } from '../../scripts/interfaces/api/TimetablesAPIData';
import { URLs } from '../../scripts/utils/apiURLs';
@@ -64,15 +64,8 @@ export default defineComponent({
setup() {
const store = useStore();
const statsData2 = computed(async () => {
return await (
await axios.get(`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${store.dispatcherStatsName}`)
).data;
});
return {
store,
statsData2,
store
};
},
@@ -80,7 +73,7 @@ export default defineComponent({
return {
cardVisible: false,
lastDispatcherName: '',
timetables: [] as TimetableHistory[],
timetables: [] as TimetableHistory[]
};
},
@@ -98,18 +91,22 @@ export default defineComponent({
}
const statsData: DispatcherStatsAPIData = await (
await axios.get(`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`)
await axios.get(
`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`
)
).data;
const timetables: TimetableHistory[] = await (
await axios.get(`${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}`)
await axios.get(
`${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}`
)
).data;
this.timetables = timetables;
this.store.dispatcherStatsData = statsData;
this.lastDispatcherName = this.store.dispatcherStatsName;
},
},
}
}
});
</script>
@@ -163,11 +160,7 @@ h3 {
text-align: center;
}
.last-timetables {
overflow-y: auto;
}
</style>
@@ -1,241 +1,254 @@
<template>
<div>
<transition name="status-anim" mode="out-in">
<div :key="dataStatus">
<div class="journal_warning" v-if="store.isOffline">
{{ $t('app.offline') }}
</div>
<Loading v-else-if="dataStatus == DataStatus.Loading" />
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
{{ $t('app.error') }}
</div>
<div class="journal_warning" v-else-if="dispatcherHistory.length == 0">
{{ $t('app.no-result') }}
</div>
<div v-else>
<table class="scenery-history-table">
<thead>
<th>{{ $t('journal.history-name') }}</th>
<th>{{ $t('journal.history-hash') }}</th>
<th>{{ $t('journal.history-dispatcher') }}</th>
<th>{{ $t('journal.history-level') }}</th>
<th>{{ $t('journal.history-rate') }}</th>
<th>{{ $t('journal.history-region') }}</th>
<th>{{ $t('journal.history-date') }}</th>
</thead>
<tbody>
<transition-group name="list-anim">
<tr v-for="historyItem in dispatcherHistory" :key="historyItem.id">
<td>
<router-link :to="`/journal/dispatchers?sceneryName=${historyItem.stationName}`">
<b>{{ historyItem.stationName }}</b>
</router-link>
</td>
<td>#{{ historyItem.stationHash }}</td>
<td>
<router-link :to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`">
<b>{{ historyItem.dispatcherName }}</b>
</router-link>
</td>
<td>
<b
v-if="historyItem.dispatcherLevel !== null"
class="level-badge dispatcher"
:style="calculateExpStyle(historyItem.dispatcherLevel, historyItem.dispatcherIsSupporter)"
>
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
</b>
</td>
<td class="text--primary">
<b>{{ historyItem.dispatcherRate }}</b>
</td>
<td>
<b class="region-badge" :aria-describedby="historyItem.region">{{
regions.find((r) => r.id == historyItem.region)?.value || '???'
}}</b>
</td>
<td style="min-width: 200px" class="time">
<span v-if="historyItem.timestampTo" class="text--offline">
<b>{{ $d(historyItem.timestampFrom) }}</b>
{{ timestampToString(historyItem.timestampFrom) }}
- {{ timestampToString(historyItem.timestampTo) }} ({{
calculateDuration(historyItem.currentDuration)
}})
</span>
<span class="dispatcher-online" v-else>
<b class="text--online">
<router-link :to="`/scenery?station=${historyItem.stationName}`">{{
$t('journal.online-since')
}}</router-link>
{{ timestampToString(historyItem.timestampFrom) }}
</b>
({{ calculateDuration(historyItem.currentDuration) }})
</span>
</td>
</tr>
</transition-group>
</tbody>
</table>
<AddDataButton
:list="dispatcherHistory"
:scrollDataLoaded="scrollDataLoaded"
:scrollNoMoreData="scrollNoMoreData"
@addHistoryData="addHistoryData"
/>
</div>
</div>
</transition>
<div class="journal_warning" v-if="scrollNoMoreData">
{{ $t('journal.no-further-data') }}
</div>
<div class="journal_warning" v-else-if="!scrollDataLoaded">
{{ $t('journal.loading-further-data') }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin';
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
import styleMixin from '../../mixins/styleMixin';
import imageMixin from '../../mixins/imageMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import { useStore } from '../../store/store';
import Loading from '../Global/Loading.vue';
import { regions } from '../../data/options.json';
import AddDataButton from '../Global/AddDataButton.vue';
export default defineComponent({
components: { Loading, AddDataButton },
mixins: [dateMixin, styleMixin, imageMixin],
props: {
dispatcherHistory: {
type: Array as PropType<DispatcherHistory[]>,
required: true,
},
scrollNoMoreData: {
type: Boolean,
},
scrollDataLoaded: {
type: Boolean,
},
addHistoryData: {
type: Function as PropType<() => void>,
},
dataStatus: {
type: Number as PropType<DataStatus>,
},
},
data() {
return {
DataStatus,
store: useStore(),
regions,
};
},
computed: {
computedDispatcherHistory() {
console.log(this.dispatcherHistory.length);
return this.dispatcherHistory.reduce((acc, historyItem, i) => {
if (this.isAnotherDay(i - 1, i)) acc.push(new Date(historyItem.timestampFrom).toLocaleDateString('pl-PL'));
acc.push(historyItem);
return acc;
}, [] as (DispatcherHistory | string)[]);
},
},
methods: {
navigateToScenery(name: string, isOnline: boolean) {
if (!isOnline) return;
this.$router.push(`/scenery?station=${name.trim().replace(/ /g, '_')}`);
},
isAnotherDay(prevIndex: number, currIndex: number) {
if (currIndex == 0) return true;
return (
new Date(this.dispatcherHistory[prevIndex].timestampFrom).getDate() !=
new Date(this.dispatcherHistory[currIndex].timestampFrom).getDate()
);
},
},
});
</script>
<style lang="scss" scoped>
@import '../../styles/animations.scss';
@import '../../styles/responsive.scss';
@import '../../styles/badge.scss';
@import '../../styles/variables.scss';
@import '../../styles/JournalSection.scss';
table.scenery-history-table {
--_bg-table: #111;
--_bg-head: #101010;
--_bg-row: #2f2f2f;
width: 100%;
border-collapse: collapse;
position: relative;
text-align: center;
margin-bottom: 1em;
thead {
position: sticky;
top: 0;
background-color: var(--_bg-head);
}
th {
padding: 0.5em;
}
tr {
background-color: var(--_bg-row);
border-bottom: 2px solid black;
&:last-child {
border: none;
}
}
td {
padding: 0.75em;
.level-badge {
margin: 0 auto;
}
}
@media screen and (max-width: 550px) {
font-size: 0.9em;
}
}
.text {
&--online {
color: springgreen;
}
&--offline {
color: #ddd;
}
}
</style>
<template>
<div>
<transition name="status-anim" mode="out-in">
<div :key="dataStatus">
<div class="journal_warning" v-if="store.isOffline">
{{ $t('app.offline') }}
</div>
<Loading v-else-if="dataStatus == DataStatus.Loading" />
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
{{ $t('app.error') }}
</div>
<div class="journal_warning" v-else-if="dispatcherHistory.length == 0">
{{ $t('app.no-result') }}
</div>
<div v-else>
<table class="scenery-history-table">
<thead>
<th>{{ $t('journal.history-name') }}</th>
<th>{{ $t('journal.history-hash') }}</th>
<th>{{ $t('journal.history-dispatcher') }}</th>
<th>{{ $t('journal.history-level') }}</th>
<th>{{ $t('journal.history-rate') }}</th>
<th>{{ $t('journal.history-region') }}</th>
<th>{{ $t('journal.history-date') }}</th>
</thead>
<tbody>
<transition-group name="list-anim">
<tr v-for="historyItem in dispatcherHistory" :key="historyItem.id">
<td>
<router-link
:to="`/journal/dispatchers?sceneryName=${historyItem.stationName}`"
>
<b>{{ historyItem.stationName }}</b>
</router-link>
</td>
<td>#{{ historyItem.stationHash }}</td>
<td>
<router-link
:to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`"
>
<b>{{ historyItem.dispatcherName }}</b>
</router-link>
</td>
<td>
<b
v-if="historyItem.dispatcherLevel !== null"
class="level-badge dispatcher"
:style="
calculateExpStyle(
historyItem.dispatcherLevel,
historyItem.dispatcherIsSupporter
)
"
>
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
</b>
</td>
<td class="text--primary">
<b>{{ historyItem.dispatcherRate }}</b>
</td>
<td>
<b class="region-badge" :aria-describedby="historyItem.region">{{
regions.find((r) => r.id == historyItem.region)?.value || '???'
}}</b>
</td>
<td style="min-width: 200px" class="time">
<span v-if="historyItem.timestampTo" class="text--offline">
<b>{{ $d(historyItem.timestampFrom) }}</b>
{{ timestampToString(historyItem.timestampFrom) }}
- {{ timestampToString(historyItem.timestampTo) }} ({{
calculateDuration(historyItem.currentDuration)
}})
</span>
<span class="dispatcher-online" v-else>
<b class="text--online">
<router-link :to="`/scenery?station=${historyItem.stationName}`">{{
$t('journal.online-since')
}}</router-link>
{{ timestampToString(historyItem.timestampFrom) }}
</b>
({{ calculateDuration(historyItem.currentDuration) }})
</span>
</td>
</tr>
</transition-group>
</tbody>
</table>
<AddDataButton
:list="dispatcherHistory"
:scrollDataLoaded="scrollDataLoaded"
:scrollNoMoreData="scrollNoMoreData"
@addHistoryData="addHistoryData"
/>
</div>
</div>
</transition>
<div class="journal_warning" v-if="scrollNoMoreData">
{{ $t('journal.no-further-data') }}
</div>
<div class="journal_warning" v-else-if="!scrollDataLoaded">
{{ $t('journal.loading-further-data') }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin';
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
import styleMixin from '../../mixins/styleMixin';
import imageMixin from '../../mixins/imageMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import { useStore } from '../../store/store';
import Loading from '../Global/Loading.vue';
import { regions } from '../../data/options.json';
import AddDataButton from '../Global/AddDataButton.vue';
export default defineComponent({
components: { Loading, AddDataButton },
mixins: [dateMixin, styleMixin, imageMixin],
props: {
dispatcherHistory: {
type: Array as PropType<DispatcherHistory[]>,
required: true
},
scrollNoMoreData: {
type: Boolean
},
scrollDataLoaded: {
type: Boolean
},
addHistoryData: {
type: Function as PropType<() => void>
},
dataStatus: {
type: Number as PropType<DataStatus>
}
},
data() {
return {
DataStatus,
store: useStore(),
regions
};
},
computed: {
computedDispatcherHistory() {
console.log(this.dispatcherHistory.length);
return this.dispatcherHistory.reduce(
(acc, historyItem, i) => {
if (this.isAnotherDay(i - 1, i))
acc.push(new Date(historyItem.timestampFrom).toLocaleDateString('pl-PL'));
acc.push(historyItem);
return acc;
},
[] as (DispatcherHistory | string)[]
);
}
},
methods: {
navigateToScenery(name: string, isOnline: boolean) {
if (!isOnline) return;
this.$router.push(`/scenery?station=${name.trim().replace(/ /g, '_')}`);
},
isAnotherDay(prevIndex: number, currIndex: number) {
if (currIndex == 0) return true;
return (
new Date(this.dispatcherHistory[prevIndex].timestampFrom).getDate() !=
new Date(this.dispatcherHistory[currIndex].timestampFrom).getDate()
);
}
}
});
</script>
<style lang="scss" scoped>
@import '../../styles/animations.scss';
@import '../../styles/responsive.scss';
@import '../../styles/badge.scss';
@import '../../styles/variables.scss';
@import '../../styles/JournalSection.scss';
table.scenery-history-table {
--_bg-table: #111;
--_bg-head: #101010;
--_bg-row: #2f2f2f;
width: 100%;
border-collapse: collapse;
position: relative;
text-align: center;
margin-bottom: 1em;
thead {
position: sticky;
top: 0;
background-color: var(--_bg-head);
}
th {
padding: 0.5em;
}
tr {
background-color: var(--_bg-row);
border-bottom: 2px solid black;
&:last-child {
border: none;
}
}
td {
padding: 0.75em;
.level-badge {
margin: 0 auto;
}
}
@media screen and (max-width: 550px) {
font-size: 0.9em;
}
}
.text {
&--online {
color: springgreen;
}
&--offline {
color: #ddd;
}
}
</style>
@@ -2,13 +2,17 @@
<div class="journal-stats">
<span v-if="store.driverStatsData">
<h3>
{{ $t('journal.stats-title') }} <span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
{{ $t('journal.stats-title') }}
<span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
</h3>
<div class="info-stats">
<span class="stat-badge">
<span>{{ $t('journal.stats-timetables') }}</span>
<span>{{ store.driverStatsData._count.fulfilled }} / {{ store.driverStatsData._count._all }}</span>
<span
>{{ store.driverStatsData._count.fulfilled }} /
{{ store.driverStatsData._count._all }}</span
>
</span>
<span class="stat-badge">
@@ -39,7 +43,9 @@
</div>
</span>
<b v-else-if="store.driverStatsStatus == DataStatus.Loading">{{ $t('journal.stats-loading') }}</b>
<b v-else-if="store.driverStatsStatus == DataStatus.Loading">{{
$t('journal.stats-loading')
}}</b>
<b v-else-if="store.driverStatsStatus == DataStatus.Error">
{{ $t('journal.stats-error ') }}
</b>
@@ -56,9 +62,9 @@ export default defineComponent({
data() {
return {
store: useStore(),
DataStatus,
DataStatus
};
},
}
});
</script>
+303 -300
View File
@@ -1,300 +1,303 @@
<template>
<div class="filters-options" @keydown.esc="showOptions = false">
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
<div class="actions-bar">
<button class="filter-button btn--filled btn--image" @click="showOptions = !showOptions" ref="button">
<img :src="getIcon('filter2')" alt="Open filters" />
{{ $t('options.filters') }} [F]
<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">
<option v-for="sugg in driverSuggestions" :value="sugg"></option>
</datalist>
<datalist id="search-dispatcher">
<option v-for="sugg in dispatcherSuggestions" :value="sugg"></option>
</datalist>
<transition name="options-anim">
<div class="options_wrapper" v-if="showOptions">
<div class="options_content">
<h1 class="option-title">{{ $t('options.search-title') }}</h1>
<div class="search_content">
<div class="search" v-for="(_, propName) in searchersValues" :key="propName">
<label v-if="propName == 'search-date'" for="date">{{ $t(`options.search-${optionsType}-date`) }}</label>
<div class="search-box">
<input
class="search-input"
v-model="searchersValues[propName]"
@keydown.enter="onSearchConfirm"
@focus="preventKeyDown = true"
@blur="preventKeyDown = false"
:placeholder="$t(`options.${propName}`)"
:type="propName == 'search-date' ? 'date' : 'text'"
:min="propName == 'search-date' ? '2022-02-01' : undefined"
:list="propName.toString()"
/>
<button class="search-exit" v-if="propName != 'search-date'">
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear(propName)" />
</button>
</div>
</div>
</div>
<h1 class="option-title">{{ $t('options.sort-title') }}</h1>
<div class="options_sorters">
<div v-for="opt in translatedSorterOptions">
<button
class="sort-option btn--option"
:data-selected="opt.id == sorterActive.id"
@click="onSorterChange(opt)"
>
{{ opt.value.toUpperCase() }}
</button>
</div>
</div>
<h1 class="option-title" v-if="filters.length != 0">{{ $t('options.filter-title') }}</h1>
<div class="options_filter-sections" v-if="filters.length != 0 && filterList">
<section class="filter-section" v-for="section in JournalFilterSection">
<p>{{ $t(`options.filter-section-${section}`) }}</p>
<div class="options_filters">
<button
v-for="filter in filterList.filter((f) => f.filterSection == section)"
class="filter-option btn--option"
:class="{ checked: filter.isActive }"
:id="filter.id"
@click="onFilterChange(filter)"
>
{{ $t(`options.filter-${filter.id}`) }}
</button>
</div>
</section>
</div>
<div class="options_actions">
<button class="btn--action" @click="onResetButtonClick">
{{ $t('options.reset-button') }}
</button>
<button class="btn--action" @click="onSearchButtonConfirm">
{{ $t('options.search-button') }}
</button>
</div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import axios from 'axios';
import { defineComponent, inject, PropType } from 'vue';
import imageMixin from '../../mixins/imageMixin';
import keyMixin from '../../mixins/keyMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import { DriverStatsAPIData } from '../../scripts/interfaces/api/DriverStatsAPIData';
import { URLs } from '../../scripts/utils/apiURLs';
import { useStore } from '../../store/store';
import ActionButton from '../Global/ActionButton.vue';
import SelectBox from '../Global/SelectBox.vue';
import { JournalFilterSection } from '../../scripts/enums/JournalFilterType';
import { JournalFilter } from '../../scripts/types/JournalTimetablesTypes';
export default defineComponent({
components: { SelectBox, ActionButton },
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
mixins: [imageMixin, keyMixin],
props: {
sorterOptionIds: {
type: Array as PropType<Array<string>>,
required: true,
},
filters: {
type: Array as PropType<JournalFilter[]>,
default: [],
},
dataStatus: {
type: Number as PropType<DataStatus>,
default: DataStatus.Initialized,
},
currentOptionsActive: {
type: Boolean,
default: false,
},
optionsType: {
type: String,
required: true,
},
},
data() {
return {
showOptions: false,
JournalFilterSection,
driverSuggestions: [] as string[],
dispatcherSuggestions: [] as string[],
searchTimeout: 0,
store: useStore(),
DataStatus,
};
},
setup() {
return {
searchersValues: inject('searchersValues') as { [key: string]: string },
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
// journalFilterActive: inject('journalFilterActive') as JournalFilter,
filterList: inject('filterList') as JournalFilter[] | undefined,
};
},
computed: {
driverStatsName() {
return this.store.driverStatsName;
},
translatedSorterOptions() {
return this.$props.sorterOptionIds.map((id) => ({
id,
value: this.$t(`options.sort-${id}`),
}));
},
},
watch: {
async driverStatsName(value: string) {
await this.fetchDriverStats();
// if (value) this.store.currentStatsTab = 'driver';
},
async 'searchersValues.search-driver'(value: string | undefined) {
clearTimeout(this.searchTimeout);
if (!value || value == '') return;
if (value.length < 3) return;
this.startSearchTimeout('driver', value);
},
async 'searchersValues.search-dispatcher'(value: string | undefined) {
if (!value || value == '') return;
if (value.length < 3) return;
this.startSearchTimeout('dispatcher', value);
},
},
methods: {
async fetchDriverStats() {
this.store.driverStatsData = undefined;
if (!this.store.driverStatsName) {
this.store.driverStatsStatus = DataStatus.Initialized;
return;
}
try {
this.store.driverStatsStatus = DataStatus.Loading;
const statsData: DriverStatsAPIData = await (
await axios.get(`${URLs.stacjownikAPI}/api/getDriverInfo?name=${this.store.driverStatsName}`)
).data;
this.store.driverStatsData = statsData;
this.store.driverStatsStatus = DataStatus.Loaded;
} catch (error) {
this.store.driverStatsStatus = DataStatus.Error;
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/');
}
},
refreshData() {
this.$emit('onRefreshData');
},
startSearchTimeout(type: 'driver' | 'dispatcher', value: string) {
if (this[`${type}Suggestions`].includes(value)) return;
window.clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(async () => {
try {
const suggestions: string[] = await (
await axios.get(`${URLs.stacjownikAPI}/api/get${type}Suggestions?name=${value}`)
).data;
this[`${type}Suggestions`] = suggestions;
} catch (error) {
this[`${type}Suggestions`] = [];
}
}, 450);
},
// Override keyMixin function
onKeyDownFunction() {
this.showOptions = !this.showOptions;
this.$nextTick(() => {
if (this.showOptions) (this.$refs['button'] as HTMLButtonElement)?.focus();
});
},
onSorterChange(item: { id: string | number; value: string }) {
this.sorterActive.id = item.id;
this.sorterActive.dir = -1;
this.$emit('onSearchConfirm');
},
onFilterChange(filter: JournalFilter) {
// this.journalFilterActive = filter;
this.filterList?.filter((f) => f.filterSection === filter.filterSection).forEach((f) => (f.isActive = false));
filter.isActive = true;
this.$emit('onSearchConfirm');
},
onInputClear(id: any) {
this.searchersValues[id] = '';
this.$emit('onSearchConfirm');
},
onSearchConfirm() {
this.$emit('onSearchConfirm');
},
onSearchButtonConfirm() {
this.showOptions = false;
this.$emit('onSearchConfirm');
},
onResetButtonClick() {
this.$emit('onOptionsReset');
},
},
});
</script>
<style lang="scss" scoped>
@import '../../styles/filters_options.scss';
</style>
<template>
<div class="filters-options" @keydown.esc="showOptions = false">
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
<div class="actions-bar">
<button
class="filter-button btn--filled btn--image"
@click="showOptions = !showOptions"
ref="button"
>
<img :src="getIcon('filter2')" alt="Open filters" />
{{ $t('options.filters') }} [F]
<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">
<option v-for="(sugg, i) in driverSuggestions" :key="i" :value="sugg"></option>
</datalist>
<datalist id="search-dispatcher">
<option v-for="(sugg, i) in dispatcherSuggestions" :key="i" :value="sugg"></option>
</datalist>
<transition name="options-anim">
<div class="options_wrapper" v-if="showOptions">
<div class="options_content">
<h1 class="option-title">{{ $t('options.search-title') }}</h1>
<div class="search_content">
<div class="search" v-for="(_, propName) in searchersValues" :key="propName">
<label v-if="propName == 'search-date'" for="date">{{
$t(`options.search-${optionsType}-date`)
}}</label>
<div class="search-box">
<input
class="search-input"
v-model="searchersValues[propName]"
@keydown.enter="onSearchConfirm"
@focus="preventKeyDown = true"
@blur="preventKeyDown = false"
:placeholder="$t(`options.${propName}`)"
:type="propName == 'search-date' ? 'date' : 'text'"
:min="propName == 'search-date' ? '2022-02-01' : undefined"
:list="propName.toString()"
/>
<button class="search-exit" v-if="propName != 'search-date'">
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear(propName)" />
</button>
</div>
</div>
</div>
<h1 class="option-title">{{ $t('options.sort-title') }}</h1>
<div class="options_sorters">
<div v-for="opt in translatedSorterOptions" :key="opt.id">
<button
class="sort-option btn--option"
:data-selected="opt.id == sorterActive.id"
@click="onSorterChange(opt)"
>
{{ opt.value.toUpperCase() }}
</button>
</div>
</div>
<h1 class="option-title" v-if="filters.length != 0">{{ $t('options.filter-title') }}</h1>
<div class="options_filter-sections" v-if="filters.length != 0 && filterList">
<section class="filter-section" v-for="section in JournalFilterSection" :key="section">
<p>{{ $t(`options.filter-section-${section}`) }}</p>
<div class="options_filters">
<button
v-for="filter in filterList.filter((f) => f.filterSection == section)"
:key="filter.id"
class="filter-option btn--option"
:class="{ checked: filter.isActive }"
:id="filter.id"
@click="onFilterChange(filter)"
>
{{ $t(`options.filter-${filter.id}`) }}
</button>
</div>
</section>
</div>
<div class="options_actions">
<button class="btn--action" @click="onResetButtonClick">
{{ $t('options.reset-button') }}
</button>
<button class="btn--action" @click="onSearchButtonConfirm">
{{ $t('options.search-button') }}
</button>
</div>
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import axios from 'axios';
import { defineComponent, inject, PropType } from 'vue';
import imageMixin from '../../mixins/imageMixin';
import keyMixin from '../../mixins/keyMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import { DriverStatsAPIData } from '../../scripts/interfaces/api/DriverStatsAPIData';
import { URLs } from '../../scripts/utils/apiURLs';
import { useStore } from '../../store/store';
import { JournalFilterSection } from '../../scripts/enums/JournalFilterType';
import { JournalFilter } from '../../scripts/types/JournalTimetablesTypes';
export default defineComponent({
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
mixins: [imageMixin, keyMixin],
props: {
sorterOptionIds: {
type: Array as PropType<Array<string>>,
required: true
},
filters: {
type: Array as PropType<JournalFilter[]>,
default: () => []
},
dataStatus: {
type: Number as PropType<DataStatus>,
default: DataStatus.Initialized
},
currentOptionsActive: {
type: Boolean,
default: false
},
optionsType: {
type: String,
required: true
}
},
data() {
return {
showOptions: false,
JournalFilterSection,
driverSuggestions: [] as string[],
dispatcherSuggestions: [] as string[],
searchTimeout: 0,
store: useStore(),
DataStatus
};
},
setup() {
return {
searchersValues: inject('searchersValues') as { [key: string]: string },
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
filterList: inject('filterList') as JournalFilter[] | undefined
};
},
computed: {
translatedSorterOptions() {
return this.$props.sorterOptionIds.map((id) => ({
id,
value: this.$t(`options.sort-${id}`)
}));
}
},
watch: {
async driverStatsName() {
await this.fetchDriverStats();
// if (value) this.store.currentStatsTab = 'driver';
},
async 'searchersValues.search-driver'(value: string | undefined) {
clearTimeout(this.searchTimeout);
if (!value || value == '') return;
if (value.length < 3) return;
this.startSearchTimeout('driver', value);
},
async 'searchersValues.search-dispatcher'(value: string | undefined) {
if (!value || value == '') return;
if (value.length < 3) return;
this.startSearchTimeout('dispatcher', value);
}
},
methods: {
async fetchDriverStats() {
this.store.driverStatsData = undefined;
if (!this.store.driverStatsName) {
this.store.driverStatsStatus = DataStatus.Initialized;
return;
}
try {
this.store.driverStatsStatus = DataStatus.Loading;
const statsData: DriverStatsAPIData = await (
await axios.get(
`${URLs.stacjownikAPI}/api/getDriverInfo?name=${this.store.driverStatsName}`
)
).data;
this.store.driverStatsData = statsData;
this.store.driverStatsStatus = DataStatus.Loaded;
} catch (error) {
this.store.driverStatsStatus = DataStatus.Error;
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/');
}
},
refreshData() {
this.$emit('onRefreshData');
},
startSearchTimeout(type: 'driver' | 'dispatcher', value: string) {
if (this[`${type}Suggestions`].includes(value)) return;
window.clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(async () => {
try {
const suggestions: string[] = await (
await axios.get(`${URLs.stacjownikAPI}/api/get${type}Suggestions?name=${value}`)
).data;
this[`${type}Suggestions`] = suggestions;
} catch (error) {
this[`${type}Suggestions`] = [];
}
}, 450);
},
// Override keyMixin function
onKeyDownFunction() {
this.showOptions = !this.showOptions;
this.$nextTick(() => {
if (this.showOptions) (this.$refs['button'] as HTMLButtonElement)?.focus();
});
},
onSorterChange(item: { id: string | number; value: string }) {
this.sorterActive.id = item.id;
this.sorterActive.dir = -1;
this.$emit('onSearchConfirm');
},
onFilterChange(filter: JournalFilter) {
// this.journalFilterActive = filter;
this.filterList
?.filter((f) => f.filterSection === filter.filterSection)
.forEach((f) => (f.isActive = false));
filter.isActive = true;
this.$emit('onSearchConfirm');
},
onInputClear(id: any) {
this.searchersValues[id] = '';
this.$emit('onSearchConfirm');
},
onSearchConfirm() {
this.$emit('onSearchConfirm');
},
onSearchButtonConfirm() {
this.showOptions = false;
this.$emit('onSearchConfirm');
},
onResetButtonClick() {
this.$emit('onOptionsReset');
}
}
});
</script>
<style lang="scss" scoped>
@import '../../styles/filters_options.scss';
</style>
+10 -7
View File
@@ -3,6 +3,7 @@
<div class="tabs">
<button
v-for="tab in data.tabs"
:key="tab.name"
class="btn--filled"
:data-selected="tab.name == store.currentStatsTab && areStatsOpen"
:data-inactive="tab.inactive"
@@ -16,7 +17,10 @@
<div class="stats-tab" v-show="areStatsOpen">
<keep-alive>
<JournalDailyStats v-if="store.currentStatsTab == 'daily'" @toggleStatsOpen="toggleStatsOpen" />
<JournalDailyStats
v-if="store.currentStatsTab == 'daily'"
@toggleStatsOpen="toggleStatsOpen"
/>
<JournalDriverStats v-else-if="store.currentStatsTab == 'driver'" />
</keep-alive>
</div>
@@ -24,7 +28,7 @@
</template>
<script setup lang="ts">
import { computed, KeepAlive, onMounted, reactive, Ref, ref, watch } from 'vue';
import { computed, onMounted, reactive, Ref, ref, watch } from 'vue';
import { useStore } from '../../store/store';
import JournalDailyStats from './DailyStats.vue';
import JournalDriverStats from './JournalDriverStats.vue';
@@ -44,14 +48,14 @@ let data = reactive({
tabs: [
{
name: 'daily',
titlePath: 'journal.daily-stats-title',
titlePath: 'journal.daily-stats-title'
},
{
name: 'driver',
titlePath: 'journal.driver-stats-title',
titlePath: 'journal.driver-stats-title'
// inactive: true,
},
] as { name: TStatTab; titlePath: string; inactive?: boolean }[],
}
] as { name: TStatTab; titlePath: string; inactive?: boolean }[]
});
// Methods
@@ -115,4 +119,3 @@ onMounted(() => {
}
}
</style>
@@ -1,82 +1,83 @@
<template>
<div>
<transition name="status-anim" mode="out-in">
<div :key="dataStatus">
<div class="journal_warning" v-if="store.isOffline">
{{ $t('app.offline') }}
</div>
<Loading v-else-if="dataStatus == DataStatus.Loading" />
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
{{ $t('app.error') }}
</div>
<div v-else-if="timetableHistory.length == 0" class="journal_warning">
{{ $t('app.no-result') }}
</div>
<div v-else>
<TimetableHistoryList :timetableHistory="timetableHistory" />
<AddDataButton
:list="timetableHistory"
:scrollDataLoaded="scrollDataLoaded"
:scrollNoMoreData="scrollNoMoreData"
@addHistoryData="addHistoryData"
/>
</div>
</div>
</transition>
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
<div class="journal_warning" v-else-if="!scrollDataLoaded">{{ $t('journal.loading-further-data') }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { DataStatus } from '../../../scripts/enums/DataStatus';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import { useStore } from '../../../store/store';
import Loading from '../../Global/Loading.vue';
import ProgressBar from '../../Global/ProgressBar.vue';
import AddDataButton from '../../Global/AddDataButton.vue';
import TimetableHistoryList from './TimetableHistoryList.vue';
export default defineComponent({
components: { ProgressBar, Loading, AddDataButton, TimetableHistoryList },
props: {
timetableHistory: {
type: Array as PropType<TimetableHistory[]>,
required: true,
},
scrollNoMoreData: {
type: Boolean,
},
scrollDataLoaded: {
type: Boolean,
},
addHistoryData: {
type: Function as PropType<() => void>,
},
dataStatus: {
type: Number as PropType<DataStatus>,
},
},
data() {
return {
DataStatus,
store: useStore(),
};
},
});
</script>
<style lang="scss" scoped>
@import '../../../styles/JournalSection.scss';
@import '../../../styles/animations.scss';
</style>
<template>
<div>
<transition name="status-anim" mode="out-in">
<div :key="dataStatus">
<div class="journal_warning" v-if="store.isOffline">
{{ $t('app.offline') }}
</div>
<Loading v-else-if="dataStatus == DataStatus.Loading" />
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
{{ $t('app.error') }}
</div>
<div v-else-if="timetableHistory.length == 0" class="journal_warning">
{{ $t('app.no-result') }}
</div>
<div v-else>
<TimetableHistoryList :timetableHistory="timetableHistory" />
<AddDataButton
:list="timetableHistory"
:scrollDataLoaded="scrollDataLoaded"
:scrollNoMoreData="scrollNoMoreData"
@addHistoryData="addHistoryData"
/>
</div>
</div>
</transition>
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
<div class="journal_warning" v-else-if="!scrollDataLoaded">
{{ $t('journal.loading-further-data') }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { DataStatus } from '../../../scripts/enums/DataStatus';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import { useStore } from '../../../store/store';
import Loading from '../../Global/Loading.vue';
import AddDataButton from '../../Global/AddDataButton.vue';
import TimetableHistoryList from './TimetableHistoryList.vue';
export default defineComponent({
components: { Loading, AddDataButton, TimetableHistoryList },
props: {
timetableHistory: {
type: Array as PropType<TimetableHistory[]>,
required: true
},
scrollNoMoreData: {
type: Boolean
},
scrollDataLoaded: {
type: Boolean
},
addHistoryData: {
type: Function as PropType<() => void>
},
dataStatus: {
type: Number as PropType<DataStatus>
}
},
data() {
return {
DataStatus,
store: useStore()
};
}
});
</script>
<style lang="scss" scoped>
@import '../../../styles/JournalSection.scss';
@import '../../../styles/animations.scss';
</style>
@@ -18,7 +18,11 @@
<span class="badge">
<span>{{ $t('journal.stock-length') }}</span>
<span>
{{ currentHistoryIndex == 0 ? timetable.stockLength : stockHistory[currentHistoryIndex].stockLength || timetable.stockLength }}m
{{
currentHistoryIndex == 0
? timetable.stockLength
: stockHistory[currentHistoryIndex].stockLength || timetable.stockLength
}}m
</span>
</span>
@@ -26,7 +30,11 @@
<span>{{ $t('journal.stock-mass') }}</span>
<span>
{{
Math.floor((currentHistoryIndex == 0 ? timetable.stockMass! : stockHistory[currentHistoryIndex].stockMass || timetable.stockMass) / 1000)
Math.floor(
(currentHistoryIndex == 0
? timetable.stockMass!
: stockHistory[currentHistoryIndex].stockMass || timetable.stockMass) / 1000
)
}}t
</span>
</span>
@@ -34,13 +42,26 @@
<!-- Historia zmian w składzie -->
<div class="stock-history" v-if="stockHistory.length > 1">
<button class="btn--action" v-for="(sh, i) in stockHistory" :data-checked="i == currentHistoryIndex" @click.stop="currentHistoryIndex = i">
<button
v-for="(sh, i) in stockHistory"
:key="i"
class="btn--action"
:data-checked="i == currentHistoryIndex"
@click.stop="currentHistoryIndex = i"
>
{{ sh.updatedAt }}
</button>
</div>
<!-- <StockList :trainStockList="currentHistoryIndex == 0 ? timetable.stockString : stockHistory[currentHistoryIndex].stockString).split(';')" /> -->
<StockList :trainStockList="(currentHistoryIndex == 0 ? timetable.stockString : stockHistory[currentHistoryIndex].stockString).split(';') " />
<StockList
:trainStockList="
(currentHistoryIndex == 0
? timetable.stockString
: stockHistory[currentHistoryIndex].stockString
).split(';')
"
/>
<!-- <ul class="stock-list">
<li
@@ -58,24 +79,24 @@
import { PropType, defineComponent } from 'vue';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import imageMixin from '../../../mixins/imageMixin';
import TrainThumbnail from '../../Global/TrainThumbnail.vue';
import StockList from '../../Global/StockList.vue';
export default defineComponent({
mixins: [imageMixin],
components: { StockList },
props: {
showExtraInfo: {
type: Boolean,
required: true,
required: true
},
timetable: {
type: Object as PropType<TimetableHistory>,
required: true,
},
required: true
}
},
data() {
return {
currentHistoryIndex: 0,
currentHistoryIndex: 0
};
},
computed: {
@@ -88,22 +109,21 @@ export default defineComponent({
return {
updatedAt: new Date(Number(historyData[0])).toLocaleTimeString(this.$i18n.locale, {
hour: '2-digit',
minute: '2-digit',
minute: '2-digit'
}),
stockString: historyData[1],
stockMass: Number(historyData[2]) || undefined,
stockLength: Number(historyData[3]) || undefined,
stockLength: Number(historyData[3]) || undefined
};
});
},
}
},
methods: {
onImageError(e: Event) {
const imageEl = e.target as HTMLImageElement;
imageEl.src = this.getImage('unknown.png');
},
},
components: { TrainThumbnail, StockList },
}
}
});
</script>
@@ -45,7 +45,7 @@
:class="{
fulfilled: timetable.fulfilled,
terminated: timetable.terminated && !timetable.fulfilled,
active: !timetable.terminated,
active: !timetable.terminated
}"
>
{{
@@ -74,8 +74,8 @@ export default defineComponent({
props: {
timetable: {
type: Object as PropType<TimetableHistory>,
required: true,
},
required: true
}
},
methods: {
@@ -83,8 +83,8 @@ export default defineComponent({
if (timetable?.terminated) return;
this.selectModalTrain(timetable.driverName + timetable.trainNo.toString(), target);
},
},
}
}
});
</script>
@@ -48,19 +48,19 @@ export default defineComponent({
props: {
timetableHistory: {
type: Array as PropType<TimetableHistory[]>,
required: true,
},
required: true
}
},
computed: {
computedTimetableHistory() {
return this.timetableHistory.map((timetable) => ({
timetable,
showExtraInfo: ref(false),
showExtraInfo: ref(false)
}));
},
}
},
methods: {},
components: { TimetableGeneral, TimetableStops, TimetableStatus, TimetableExtra },
components: { TimetableGeneral, TimetableStops, TimetableStatus, TimetableExtra }
});
</script>
@@ -6,13 +6,19 @@
/>
<span>
<span :style="{ color: timetable.fulfilled ? 'lightgreen' : timetable.terminated ? 'salmon' : '' }">
<span
:style="{
color: timetable.fulfilled ? 'lightgreen' : timetable.terminated ? 'salmon' : ''
}"
>
{{ timetable.currentDistance + ' km' }}
</span>
<span> / </span>
<span class="text--primary">{{ timetable.routeDistance }} km</span>
|
<span class="text--grayed">{{ timetable.confirmedStopsCount }}/{{ timetable.allStopsCount }}</span>
<span class="text--grayed"
>{{ timetable.confirmedStopsCount }}/{{ timetable.allStopsCount }}</span
>
</span>
<span class="text--grayed" v-if="timetable.currentSceneryName">
@@ -46,9 +52,9 @@ export default defineComponent({
props: {
timetable: {
type: Object as PropType<TimetableHistory>,
required: true,
},
},
required: true
}
}
});
</script>
@@ -38,8 +38,8 @@ export default defineComponent({
timetable: {
type: Object as PropType<TimetableHistory>,
required: true,
},
required: true
}
},
computed: {
@@ -65,12 +65,18 @@ export default defineComponent({
if (i == 0) return { stopName, html: beginDateHTML, confirmed };
if (i == stopNames.length - 1) return { stopName, html: endDateHTML, confirmed };
const departureDateScheduled = this.stringToDate(timetable.checkpointDeparturesScheduled?.at(i));
const departureDateScheduled = this.stringToDate(
timetable.checkpointDeparturesScheduled?.at(i)
);
const departureDateReal = this.stringToDate(timetable.checkpointDepartures?.at(i));
const arrivalDateScheduled = this.stringToDate(timetable.checkpointArrivalsScheduled?.at(i));
const arrivalDateScheduled = this.stringToDate(
timetable.checkpointArrivalsScheduled?.at(i)
);
const arrivalDateReal = this.stringToDate(timetable.checkpointArrivals?.at(i));
const arrivalHTML =
(arrivalDateReal && arrivalDateScheduled && arrivalDateReal?.getTime() != arrivalDateScheduled?.getTime()
(arrivalDateReal &&
arrivalDateScheduled &&
arrivalDateReal?.getTime() != arrivalDateScheduled?.getTime()
? `<s class="text--grayed">${this.parseDateToTimeString(arrivalDateScheduled)}</s> `
: '') + this.parseDateToTimeString(arrivalDateReal || arrivalDateScheduled);
const departureHTML =
@@ -83,8 +89,8 @@ export default defineComponent({
if (html) html = ` (${html})`;
return { stopName, html, confirmed };
});
},
},
}
}
});
</script>