Compare commits

...

23 Commits

Author SHA1 Message Date
Spythere a459fdf178 Merge pull request #93 from Spythere/development
v1.25.0
2024-06-09 23:40:54 +02:00
Spythere 4e7fba89ee chore: improved stop label information 2024-06-09 00:58:45 +02:00
Spythere 6084e5876d chore: changed default history mode 2024-06-08 21:38:05 +02:00
Spythere 44f548c7b7 chore: scenery history locales 2024-06-08 21:37:28 +02:00
Spythere 59a5fbe5ac chore: adjusted to new version of API vehicles data 2024-06-08 20:53:22 +02:00
Spythere c252213ed9 hotfix 2024-06-07 18:31:20 +02:00
Spythere fb56378f18 chore: redesigned scenery history tables 2024-06-07 16:44:09 +02:00
Spythere e9635eae06 chore: redesigned train schedule list 2024-06-06 17:11:52 +02:00
Spythere 1fc98a8f99 chore: added test data mocks 2024-06-06 14:41:54 +02:00
Spythere c9de1a48ce chore: scenery timetables history translation; layout fixes 2024-06-06 14:19:17 +02:00
Spythere fee9774f88 chore: layout fixes 2024-06-06 14:12:21 +02:00
Spythere 7c974e8d0e bump: 1.25.0 2024-06-06 14:04:07 +02:00
Spythere c84fbbcf42 chore: added scenery timetables history modes 2024-06-05 20:03:05 +02:00
Spythere 45af649505 chore: changes in scenery view layout 2024-06-05 16:01:17 +02:00
Spythere 6c1e00d002 chore: layout & design fixes 2024-06-04 15:57:17 +02:00
Spythere 69ff85cfb1 chore: added route electrification indicators in train schedule 2024-06-03 22:26:58 +02:00
Spythere bdc2ca784c chore: missing translations 2024-06-03 21:37:33 +02:00
Spythere dbd73d448d chore: added active train's rolling stock vmax 2024-06-03 20:09:15 +02:00
Spythere 26b1ec246d chore: added extra data to vehicles tooltip 2024-06-03 18:10:45 +02:00
Spythere 8190dfa2cb chore: fetching & caching vehicles data information 2024-06-03 01:31:31 +02:00
Spythere 44df685606 Merge pull request #92 from Spythere/development
v1.24.4
2024-05-30 14:38:04 +02:00
Spythere 785a42b849 hotfix: detecting user timetable status at checkpoints 2024-05-30 14:29:09 +02:00
Spythere ccfcca8728 hotfix: scenery timetable duplicating 2024-05-30 14:24:18 +02:00
35 changed files with 7595 additions and 11421 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.24.3", "version": "1.25.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

+2 -1
View File
@@ -180,7 +180,7 @@ export default defineComponent({
const naviLanguage = window.navigator.language.toString(); const naviLanguage = window.navigator.language.toString();
if (naviLanguage.includes('en')) { if (naviLanguage.startsWith('en')) {
this.changeLang('en'); this.changeLang('en');
return; return;
} }
@@ -226,6 +226,7 @@ export default defineComponent({
min-height: 100vh; min-height: 100vh;
overflow: hidden; overflow: hidden;
position: relative;
} }
.app_main { .app_main {
+3 -3
View File
@@ -23,15 +23,15 @@
<ul v-else> <ul v-else>
<li v-for="(stockName, i) in computedStockList" :key="i"> <li v-for="(stockName, i) in computedStockList" :key="i">
<p> <p>
{{ stockName.split(':')[0].split('_').splice(0, 2).join(' ') }} {{ stockName.split(':')[0].split('_').splice(0, 3).join(' ') }}
{{ stockName.split(':')[1] }} <div v-if="stockName.split(':')[1]">({{ stockName.split(':')[1] }})</div>
</p> </p>
<span> <span>
<img <img
:data-mouseover="stockName" :data-mouseover="stockName"
data-tooltip-type="VehiclePreviewTooltip" data-tooltip-type="VehiclePreviewTooltip"
:data-tooltip-content="stockName.split(':')[0]" :data-tooltip-content="stockName"
:src=" :src="
getVehicleThumbnailURL(stockName.split(':')[0], /^EN/.test(stockName) ? 'rb' : '') getVehicleThumbnailURL(stockName.split(':')[0], /^EN/.test(stockName) ? 'rb' : '')
" "
+3 -1
View File
@@ -6,7 +6,9 @@ export namespace Journal {
| 'search-train' | 'search-train'
| 'search-date' | 'search-date'
| 'search-dispatcher' | 'search-dispatcher'
| 'search-issuedFrom'; | 'search-issuedFrom'
| 'search-terminatingAt'
| 'search-via';
export type TimetableSearchType = { export type TimetableSearchType = {
[key in TimetableSearchKey]: string; [key in TimetableSearchKey]: string;
@@ -1,31 +1,18 @@
<template> <template>
<section class="scenery-table-section"> <div class="scenery-dispatchers-history">
<Loading v-if="dataStatus != DataStatus.Loaded && historyList.length == 0" /> <div class="history-wrapper">
<Loading v-if="dataStatus != DataStatus.Loaded && historyList.length == 0" />
<div class="no-history" v-else-if="historyList.length == 0"> <div v-else-if="historyList.length == 0" class="no-history">
{{ $t('scenery.history-list-empty') }} {{ $t('scenery.history-list-empty') }}
</div> </div>
<table class="scenery-history-table" v-else> <div v-else class="history-list">
<thead> <div v-for="historyItem in historyList" :key="historyItem.id">
<th>{{ $t('scenery.dispatchers-history-hash') }}</th> <span>
<th>{{ $t('scenery.dispatchers-history-dispatcher') }}</th> <span class="text--grayed" style="margin-right: 10px">
<th>{{ $t('scenery.dispatchers-history-level') }}</th> #{{ historyItem.stationHash }}
<th>{{ $t('scenery.dispatchers-history-rate') }}</th> </span>
<th>{{ $t('scenery.dispatchers-history-date') }}</th>
</thead>
<tbody>
<tr v-for="historyItem in historyList" :key="historyItem.id">
<td>#{{ historyItem.stationHash }}</td>
<td>
<router-link
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
>
<b>{{ historyItem.dispatcherName }}</b>
</router-link>
</td>
<td>
<b <b
v-if="historyItem.dispatcherLevel !== null" v-if="historyItem.dispatcherLevel !== null"
class="level-badge dispatcher" class="level-badge dispatcher"
@@ -35,37 +22,52 @@
> >
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }} {{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
</b> </b>
<b style="margin-left: 5px">
<router-link
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
>
{{ historyItem.dispatcherName }}
</router-link>
</b>
<b v-else>?</b> <div>
</td> <span>
<td class="text--primary"> {{ $t('scenery.dispatcher-rate') }}
<b>{{ historyItem.dispatcherRate }}</b> <b class="text--primary"> {{ historyItem.dispatcherRate }}</b>
</td> </span>
<td style="min-width: 300px"> |
<div v-if="historyItem.timestampTo"> <span>
{{ $t('scenery.dispatcher-status-changes') }}
<b class="text--primary">{{ historyItem.statusHistory.length }}</b>
</span>
</div>
</span>
<span>
<span v-if="historyItem.timestampTo">
<b>{{ $d(historyItem.timestampFrom) }}</b> <b>{{ $d(historyItem.timestampFrom) }}</b>
{{ timestampToString(historyItem.timestampFrom) }} {{ timestampToString(historyItem.timestampFrom) }}
- {{ timestampToString(historyItem.timestampTo) }} ({{ - {{ timestampToString(historyItem.timestampTo) }} ({{
calculateDuration(historyItem.currentDuration) calculateDuration(historyItem.currentDuration)
}}) }})
</div> </span>
<div class="dispatcher-online" v-else> <span class="dispatcher-online" v-else>
{{ $t('journal.online-since') }} {{ $t('journal.online-since') }}
<b>{{ timestampToString(historyItem.timestampFrom) }}</b> <b>{{ timestampToString(historyItem.timestampFrom) }}</b>
({{ calculateDuration(historyItem.currentDuration) }}) ({{ calculateDuration(historyItem.currentDuration) }})
</div> </span>
</td> </span>
</tr> </div>
</tbody> </div>
</table> </div>
</section>
<div class="bottom-info"> <div class="bottom-info">
<button class="btn btn--option" v-if="historyList.length > 0" @click="navigateToHistory"> <button class="btn btn--option" v-if="historyList.length > 0" @click="navigateToHistory">
{{ $t('scenery.bottom-info') }} {{ $t('scenery.bottom-info') }}
</button> </button>
</div>
</div> </div>
</template> </template>
@@ -149,8 +151,43 @@ export default defineComponent({
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/sceneryViewTables.scss'; @import '../../styles/sceneryViewTables.scss';
.scenery-dispatchers-history {
height: 100%;
overflow: auto;
display: grid;
gap: 0.5em;
grid-template-rows: auto 40px;
}
.history-wrapper {
position: relative;
overflow: auto;
}
.history-list {
display: flex;
flex-direction: column;
gap: 0.5em;
text-align: left;
}
.history-list > div {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 0.5em;
padding: 0.5em;
background-color: #2b2b2b;
line-height: 1.75em;
}
.level-badge { .level-badge {
margin: 0 auto; text-align: center;
display: inline-block;
line-height: 1.6em;
} }
.dispatcher-online { .dispatcher-online {
@@ -158,13 +195,10 @@ export default defineComponent({
} }
@include smallScreen { @include smallScreen {
.history-list { .history-list > div {
font-size: 1.1em;
}
.list-item {
align-items: center;
flex-direction: column; flex-direction: column;
justify-content: center;
text-align: center;
} }
} }
</style> </style>
../../store/storeTypes
@@ -124,11 +124,6 @@ h3.section-header {
align-items: center; align-items: center;
font-size: 1.2em; font-size: 1.2em;
img {
width: 1.1em;
margin-left: 0.5em;
}
} }
.info-lists { .info-lists {
@@ -63,7 +63,9 @@ export default defineComponent({
return this.onlineScenery.stationTrains.map((train) => { return this.onlineScenery.stationTrains.map((train) => {
const stop = train.timetableData?.followingStops.find( const stop = train.timetableData?.followingStops.find(
(stop) => stop.stopNameRAW.toLowerCase() == name.toLowerCase() (stop) =>
stop.stopNameRAW.toLowerCase() == name.toLowerCase() ||
this.station?.generalInfo?.checkpoints.includes(stop.stopNameRAW)
); );
const status = stop const status = stop
@@ -66,7 +66,7 @@
class="timetable-item" class="timetable-item"
v-else v-else
v-for="(row, i) in sceneryTimetables" v-for="(row, i) in sceneryTimetables"
:key="row.train.id" :key="row.train.id + i"
tabindex="0" tabindex="0"
@click.prevent.stop="selectModalTrain(row.train, $event.currentTarget)" @click.prevent.stop="selectModalTrain(row.train, $event.currentTarget)"
@keydown.enter.prevent="selectModalTrain(row.train, $event.currentTarget)" @keydown.enter.prevent="selectModalTrain(row.train, $event.currentTarget)"
@@ -1,69 +1,97 @@
<template> <template>
<!-- WIP --> <div class="scenery-timetables-history">
<!-- <div class="top-filters"> <div class="history-modes">
<button class="btn btn--option">ROZPOCZYNA BIEG</button> <button
class="btn btn--option"
<button class="btn btn--option">PRZEZ</button> v-for="mode in historyModeList"
:key="mode"
<button class="btn btn--option">KOŃCZY BIEG</button> :class="{ checked: checkedHistoryMode == mode }"
</div> --> @click="checkHistoryMode(mode)"
>
<section class="scenery-table-section"> {{ $t(`scenery.timetable-${mode}`) }}
<Loading v-if="dataStatus != DataStatus.Loaded" /> </button>
<div class="no-history" v-else-if="historyList.length == 0">
{{ $t('scenery.history-list-empty') }}
</div> </div>
<table class="scenery-history-table" v-else> <div class="history-wrapper">
<thead> <Loading v-if="dataStatus != DataStatus.Loaded" />
<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> <div v-else-if="historyList.length == 0" class="no-history">
<tr v-for="historyItem in historyList" :key="historyItem.id"> {{ $t('scenery.history-list-empty') }}
<td> </div>
<router-link :to="`/journal/timetables?search-train=%23${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>
<router-link :to="`/journal/timetables?search-driver=${historyItem.driverName}`">
{{ historyItem.driverName }}
</router-link>
</td>
<td> <div v-else class="history-list">
<router-link <div v-for="timetableHistory in historyList" :key="timetableHistory.id">
v-if="historyItem.authorName" <span>
:to="`/journal/timetables?search-dispatcher=${historyItem.authorName}`" <div>
>{{ historyItem.authorName }} <span
</router-link> class="timetable-status-indicator"
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i> :data-terminated="timetableHistory.terminated"
</td> :data-fulfilled="timetableHistory.fulfilled"
<td> >
<b>{{ localeDay(historyItem.beginDate, $i18n.locale) }}</b> &ofcir;
{{ localeTime(historyItem.beginDate, $i18n.locale) }} </span>
</td> #{{ timetableHistory.id }} |
</tr> <b class="text--primary">{{ timetableHistory.trainCategoryCode }}</b>
</tbody> {{ timetableHistory.trainNo }}
</table> {{ timetableHistory.route.replace('|', ' &Rightarrow; ') }}
</section> </div>
<div class="bottom-info"> <div class="text--grayed">
<button class="btn btn--option" v-if="historyList.length > 0" @click="navigateToHistory()"> <span>
{{ $t('scenery.bottom-info') }} {{ $t('scenery.timetable-issued-date') }}
</button> <b>
{{
localeDateTime(
timetableHistory.createdAt > timetableHistory.beginDate
? timetableHistory.beginDate
: timetableHistory.createdAt,
$i18n.locale
)
}}
</b></span
>
<span v-if="timetableHistory.authorName">
{{ $t('scenery.timetable-issued-by') }}
<b>
<router-link
:to="`/journal/timetables?search-dispatcher=${timetableHistory.authorName}`"
>
{{ timetableHistory.authorName }}
</router-link>
</b>
</span>
<span>
{{ $t('scenery.timetable-issued-for') }}
<b>
<router-link
:to="`/journal/timetables?search-driver=${timetableHistory.driverName}`"
>
{{ timetableHistory.driverName }}
</router-link>
</b>
</span>
</div>
</span>
<button
@click="
navigateTo(`/journal/timetables`, {
'search-train': `#${timetableHistory.id}`
})
"
>
<img src="/images/icon-back.svg" alt="icon navigate to timetable" />
</button>
</div>
</div>
</div>
<div class="bottom-info">
<button class="btn btn--option" v-if="historyList.length > 0" @click="navigateToHistory()">
{{ $t('scenery.bottom-info') }}
</button>
</div>
</div> </div>
</template> </template>
@@ -75,10 +103,15 @@ import Loading from '../Global/Loading.vue';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { ActiveScenery, Station, Status } from '../../typings/common'; import { ActiveScenery, Station, Status } from '../../typings/common';
import { useApiStore } from '../../store/apiStore'; import { useApiStore } from '../../store/apiStore';
import routerMixin from '../../mixins/routerMixin';
import { useMainStore } from '../../store/mainStore';
const historyModeList = ['via', 'issuedFrom', 'terminatingAt'] as const;
type HistoryMode = (typeof historyModeList)[number];
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetablesHistory', name: 'SceneryTimetablesHistory',
mixins: [dateMixin], mixins: [dateMixin, routerMixin],
props: { props: {
station: { station: {
type: Object as PropType<Station> type: Object as PropType<Station>
@@ -91,9 +124,14 @@ export default defineComponent({
data() { data() {
return { return {
historyList: [] as API.TimetableHistory.Response, historyList: [] as API.TimetableHistory.Response,
historyModeList,
apiStore: useApiStore(), apiStore: useApiStore(),
mainStore: useMainStore(),
dataStatus: Status.Data.Loading, dataStatus: Status.Data.Loading,
DataStatus: Status.Data DataStatus: Status.Data,
checkedHistoryMode: 'via' as HistoryMode
}; };
}, },
@@ -103,17 +141,22 @@ export default defineComponent({
methods: { methods: {
async fetchAPIData() { async fetchAPIData() {
if (!this.station && !this.onlineScenery) { const stationName = this.$route.query['station'];
if (!stationName) {
this.historyList = [];
this.dataStatus = Status.Data.Loaded; this.dataStatus = Status.Data.Loaded;
return; return;
} }
const requestFilters: Record<string, any> = {};
requestFilters[this.checkedHistoryMode] = stationName.toString();
requestFilters.countLimit = 30;
try { try {
const response: API.TimetableHistory.Response = await ( const response: API.TimetableHistory.Response = await (
await this.apiStore.client!.get('api/getTimetables', { await this.apiStore.client!.get('api/getTimetables', {
params: { params: requestFilters
issuedFrom: this.station?.name || this.onlineScenery?.name
}
}) })
).data; ).data;
@@ -125,11 +168,17 @@ export default defineComponent({
} }
}, },
checkHistoryMode(mode: HistoryMode) {
this.checkedHistoryMode = mode;
this.dataStatus = Status.Data.Loading;
this.fetchAPIData();
},
navigateToHistory() { navigateToHistory() {
this.$router.push({ this.$router.push({
path: '/journal/timetables', path: '/journal/timetables',
query: { query: {
'search-issuedFrom': this.station?.name || this.onlineScenery?.name [`search-${this.checkedHistoryMode}`]: this.station?.name || this.onlineScenery?.name
} }
}); });
} }
@@ -142,13 +191,66 @@ export default defineComponent({
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/sceneryViewTables.scss'; @import '../../styles/sceneryViewTables.scss';
.top-filters { .scenery-timetables-history {
height: 100%;
overflow: auto;
display: grid;
gap: 1em;
grid-template-rows: auto 1fr 40px;
}
.history-wrapper {
position: relative;
overflow: auto;
}
.history-modes {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-wrap: wrap;
gap: 0.5em; gap: 0.5em;
padding: 0.25em;
button { button {
padding: 0.5em; padding: 0.35em;
min-width: 120px;
}
}
.history-list {
display: flex;
flex-direction: column;
gap: 0.5em;
text-align: left;
}
.history-list > div {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5em;
background-color: #2b2b2b;
line-height: 1.5em;
}
.history-list > div > button > img {
width: 2em;
transform: rotate(180deg);
}
.timetable-status-indicator {
&[data-fulfilled='true'] {
color: lightgreen;
}
&[data-terminated='false'] {
color: lightblue;
}
&[data-terminated='true'][data-fulfilled='false'] {
color: lightcoral;
} }
} }
</style> </style>
+266 -268
View File
@@ -4,302 +4,299 @@
v-if="apiStore.dataStatuses.connection == Status.Loading && filteredStationList.length == 0" v-if="apiStore.dataStatuses.connection == Status.Loading && filteredStationList.length == 0"
/> />
<div class="table_wrapper" v-else-if="filteredStationList.length > 0"> <table v-else-if="filteredStationList.length > 0">
<table> <thead>
<thead> <tr>
<tr> <th
<th v-for="headerName in headIds"
v-for="headerName in headIds" :key="headerName"
:key="headerName" @click="changeSorter(headerName)"
@click="changeSorter(headerName)" class="header-text"
class="header-text" :class="headerName"
:class="headerName"
>
<span class="header_wrapper">
<div v-html="$t(`sceneries.headers.${headerName}`)"></div>
<img
class="sort-icon"
v-if="activeSorter.headerName == headerName"
:src="`/images/icon-arrow-${activeSorter.dir == 1 ? 'asc' : 'desc'}.svg`"
alt="sort icon"
/>
</span>
</th>
<th
v-for="headerName in headIconsIds"
:key="headerName"
@click="changeSorter(headerName)"
class="header-image"
:class="headerName"
>
<span class="header_wrapper">
<img
:src="`/images/icon-${headerName}.svg`"
:alt="headerName"
:title="$t(`sceneries.headers.${headerName}`)"
/>
<img
class="sort-icon"
v-if="activeSorter.headerName == headerName"
:src="`/images/icon-arrow-${activeSorter.dir == 1 ? 'asc' : 'desc'}.svg`"
alt="sort icon"
/>
</span>
</th>
</tr>
</thead>
<tbody>
<tr
v-for="station in filteredStationList"
:class="{ 'last-selected': lastSelectedStationName == station.name }"
:key="station.name"
@click.left="setScenery(station.name)"
@click.right="openForumSite($event, station.generalInfo?.url)"
@keydown.enter="setScenery(station.name)"
@keydown.space="openForumSite($event, station.generalInfo?.url)"
tabindex="0"
> >
<td class="station-name" :class="station.generalInfo?.availability"> <span class="header_wrapper">
<b v-if="station.generalInfo?.project" style="color: salmon">{{ <div v-html="$t(`sceneries.headers.${headerName}`)"></div>
station.generalInfo.project
}}</b>
{{ station.name }}
</td>
<td class="station-level"> <img
<span v-if="station.generalInfo"> class="sort-icon"
<span v-if="activeSorter.headerName == headerName"
v-if=" :src="`/images/icon-arrow-${activeSorter.dir == 1 ? 'asc' : 'desc'}.svg`"
station.generalInfo.reqLevel > -1 && alt="sort icon"
station.generalInfo.availability != 'nonPublic' &&
station.generalInfo.availability != 'unavailable'
"
:style="calculateExpStyle(station.generalInfo.reqLevel)"
>
{{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }}
</span>
<span v-else-if="station.generalInfo.availability == 'abandoned'">
<img
src="/images/icon-abandoned.svg"
alt="non-public"
:title="$t('sceneries.info.abandoned')"
/>
</span>
<span v-else-if="station.generalInfo.availability == 'nonPublic'">
<img
src="/images/icon-lock.svg"
alt="non-public"
:title="$t('sceneries.info.non-public')"
/>
</span>
<span v-else>
<img
src="/images/icon-unavailable.svg"
alt="unavailable"
:title="$t('sceneries.info.unavailable')"
/>
</span>
</span>
<span v-else> ? </span>
</td>
<td class="station-status">
<StationStatusBadge
:isOnline="station.onlineInfo ? true : false"
:dispatcherStatus="station.onlineInfo?.dispatcherStatus"
/> />
</td> </span>
</th>
<td class="station-dispatcher-name"> <th
<span v-if="station.onlineInfo?.dispatcherName"> v-for="headerName in headIconsIds"
<b :key="headerName"
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)" @click="changeSorter(headerName)"
@click.stop="openDonationCard" class="header-image"
data-tooltip-type="DonatorTooltip" :class="headerName"
:data-tooltip-content="$t('donations.dispatcher-message')" >
> <span class="header_wrapper">
<img src="/images/icon-diamond.svg" alt="" /> <img
{{ station.onlineInfo.dispatcherName }} :src="`/images/icon-${headerName}.svg`"
</b> :alt="headerName"
:title="$t(`sceneries.headers.${headerName}`)"
/>
<div v-else> <img
{{ station.onlineInfo.dispatcherName }} class="sort-icon"
</div> v-if="activeSorter.headerName == headerName"
</span> :src="`/images/icon-arrow-${activeSorter.dir == 1 ? 'asc' : 'desc'}.svg`"
</td> alt="sort icon"
/>
</span>
</th>
</tr>
</thead>
<td class="station-dispatcher-exp"> <tbody>
<tr
v-for="station in filteredStationList"
:class="{ 'last-selected': lastSelectedStationName == station.name }"
:key="station.name"
@click.left="setScenery(station.name)"
@click.right="openForumSite($event, station.generalInfo?.url)"
@keydown.enter="setScenery(station.name)"
@keydown.space="openForumSite($event, station.generalInfo?.url)"
tabindex="0"
>
<td class="station-name" :class="station.generalInfo?.availability">
<b v-if="station.generalInfo?.project" style="color: salmon">{{
station.generalInfo.project
}}</b>
{{ station.name }}
</td>
<td class="station-level">
<span v-if="station.generalInfo">
<span <span
v-if="station.onlineInfo && station.onlineInfo?.dispatcherExp != -1" v-if="
:style=" station.generalInfo.reqLevel > -1 &&
calculateExpStyle( station.generalInfo.availability != 'nonPublic' &&
station.onlineInfo.dispatcherExp, station.generalInfo.availability != 'unavailable'
station.onlineInfo.dispatcherIsSupporter
)
" "
:style="calculateExpStyle(station.generalInfo.reqLevel)"
> >
{{ station.onlineInfo.dispatcherExp < 2 ? 'L' : station.onlineInfo.dispatcherExp }} {{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }}
</span> </span>
</td>
<td class="station-tracks"> <span v-else-if="station.generalInfo.availability == 'abandoned'">
<div v-if="station.generalInfo"> <img
<span src="/images/icon-abandoned.svg"
v-if="station.generalInfo.routes.singleElectrifiedNames.length != 0" alt="non-public"
class="track catenary" :title="$t('sceneries.info.abandoned')"
:title="`${$t('sceneries.info.single-track-routes-catenary')}${ />
station.generalInfo.routes.singleElectrifiedNames.length </span>
}`"
>
{{ station.generalInfo.routes.singleElectrifiedNames.length }}
</span>
<span <span v-else-if="station.generalInfo.availability == 'nonPublic'">
v-if="station.generalInfo.routes.singleOtherNames.length != 0" <img
class="track no-catenary" src="/images/icon-lock.svg"
:title="`${$t('sceneries.info.single-track-routes-other')}${ alt="non-public"
station.generalInfo.routes.singleOtherNames.length :title="$t('sceneries.info.non-public')"
}`" />
> </span>
{{ station.generalInfo.routes.singleOtherNames.length }}
</span> <span v-else>
<img
src="/images/icon-unavailable.svg"
alt="unavailable"
:title="$t('sceneries.info.unavailable')"
/>
</span>
</span>
<span v-else> ? </span>
</td>
<td class="station-status">
<StationStatusBadge
:isOnline="station.onlineInfo ? true : false"
:dispatcherStatus="station.onlineInfo?.dispatcherStatus"
/>
</td>
<td class="station-dispatcher-name">
<span v-if="station.onlineInfo?.dispatcherName">
<b
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
@click.stop="openDonationCard"
data-tooltip-type="DonatorTooltip"
:data-tooltip-content="$t('donations.dispatcher-message')"
>
<img src="/images/icon-diamond.svg" alt="" />
{{ station.onlineInfo.dispatcherName }}
</b>
<div v-else>
{{ station.onlineInfo.dispatcherName }}
</div> </div>
</td> </span>
</td>
<td class="station-tracks"> <td class="station-dispatcher-exp">
<div v-if="station.generalInfo"> <span
<span v-if="station.onlineInfo && station.onlineInfo?.dispatcherExp != -1"
v-if="station.generalInfo.routes.doubleElectrifiedNames.length != 0" :style="
class="track catenary" calculateExpStyle(
:title="`${$t('sceneries.info.double-track-routes-catenary')}${ station.onlineInfo.dispatcherExp,
station.generalInfo.routes.doubleElectrifiedNames.length station.onlineInfo.dispatcherIsSupporter
}`" )
> "
{{ station.generalInfo.routes.doubleElectrifiedNames.length }} >
</span> {{ station.onlineInfo.dispatcherExp < 2 ? 'L' : station.onlineInfo.dispatcherExp }}
</span>
</td>
<span <td class="station-tracks">
v-if="station.generalInfo.routes.doubleOtherNames.length != 0" <div v-if="station.generalInfo">
class="track no-catenary"
:title="`${$t('sceneries.info.double-track-routes-other')}${
station.generalInfo.routes.doubleOtherNames.length
}`"
>
{{ station.generalInfo.routes.doubleOtherNames.length }}
</span>
</div>
</td>
<td class="station-info">
<span <span
v-if="station.generalInfo?.signalType" v-if="station.generalInfo.routes.singleElectrifiedNames.length != 0"
class="scenery-icon icon-info" class="track catenary"
:class="station.generalInfo?.controlType.replace('+', '-')" :title="`${$t('sceneries.info.single-track-routes-catenary')}${
:title=" station.generalInfo.routes.singleElectrifiedNames.length
$t('sceneries.info.control-type') + }`"
$t(`controls.${station.generalInfo?.controlType}`)
"
> >
{{ $t(`controls.abbrevs.${station.generalInfo.controlType}`) }} {{ station.generalInfo.routes.singleElectrifiedNames.length }}
</span> </span>
<img <span
v-if="station.generalInfo?.signalType" v-if="station.generalInfo.routes.singleOtherNames.length != 0"
class="icon-info" class="track no-catenary"
:src="`/images/icon-${station.generalInfo.signalType}.svg`" :title="`${$t('sceneries.info.single-track-routes-other')}${
:alt="station.generalInfo.signalType" station.generalInfo.routes.singleOtherNames.length
:title=" }`"
$t('sceneries.info.signals-type') + >
$t(`signals.${station.generalInfo.signalType}`) {{ station.generalInfo.routes.singleOtherNames.length }}
" </span>
/> </div>
</td>
<img <td class="station-tracks">
v-if="station.generalInfo?.SUP" <div v-if="station.generalInfo">
class="icon-info" <span
src="/images/icon-SUP.svg" v-if="station.generalInfo.routes.doubleElectrifiedNames.length != 0"
alt="SUP (RASP-UZK)" class="track catenary"
:title="$t('sceneries.info.SUP')" :title="`${$t('sceneries.info.double-track-routes-catenary')}${
/> station.generalInfo.routes.doubleElectrifiedNames.length
}`"
>
{{ station.generalInfo.routes.doubleElectrifiedNames.length }}
</span>
<img <span
v-if="station.generalInfo?.ASDEK" v-if="station.generalInfo.routes.doubleOtherNames.length != 0"
class="icon-info" class="track no-catenary"
src="/images/icon-ASDEK.svg" :title="`${$t('sceneries.info.double-track-routes-other')}${
alt="dSAT ASDEK" station.generalInfo.routes.doubleOtherNames.length
:title="$t('sceneries.info.ASDEK')" }`"
/> >
{{ station.generalInfo.routes.doubleOtherNames.length }}
</span>
</div>
</td>
<img <td class="station-info">
v-if="!station.generalInfo" <span
class="icon-info" v-if="station.generalInfo?.signalType"
src="/images/icon-unknown.svg" class="scenery-icon icon-info"
alt="icon-unknown" :class="station.generalInfo?.controlType.replace('+', '-')"
:title="$t('sceneries.info.unknown')" :title="
/> $t('sceneries.info.control-type') +
</td> $t(`controls.${station.generalInfo?.controlType}`)
"
<td
class="station-users"
:class="{ inactive: !station.onlineInfo }"
data-tooltip-type="UsersTooltip"
:data-tooltip-content="JSON.stringify(station.onlineInfo?.stationTrains ?? [])"
> >
<span class="text--primary">{{ {{ $t(`controls.abbrevs.${station.generalInfo.controlType}`) }}
station.onlineInfo?.stationTrains?.length ?? '-' </span>
}}</span>
/
<span class="text--primary">{{ station.onlineInfo?.maxUsers ?? '-' }}</span>
</td>
<td class="station-likes" :class="{ inactive: !station.onlineInfo }"> <img
<span>{{ station.onlineInfo?.dispatcherRate ?? '-' }}</span> v-if="station.generalInfo?.signalType"
</td> class="icon-info"
:src="`/images/icon-${station.generalInfo.signalType}.svg`"
:alt="station.generalInfo.signalType"
:title="
$t('sceneries.info.signals-type') + $t(`signals.${station.generalInfo.signalType}`)
"
/>
<td <img
class="station-spawns" v-if="station.generalInfo?.SUP"
:class="{ inactive: !station.onlineInfo }" class="icon-info"
data-tooltip-type="SpawnsTooltip" src="/images/icon-SUP.svg"
:data-tooltip-content="JSON.stringify(station.onlineInfo?.spawns ?? [])" alt="SUP (RASP-UZK)"
> :title="$t('sceneries.info.SUP')"
<span>{{ station.onlineInfo?.spawns.length ?? '-' }}</span> />
</td>
<td <img
class="station-schedules all" v-if="station.generalInfo?.ASDEK"
style="width: 30px" class="icon-info"
:class="{ inactive: !station.onlineInfo }" src="/images/icon-ASDEK.svg"
> alt="dSAT ASDEK"
{{ station.onlineInfo?.scheduledTrainCount.all ?? '-' }} :title="$t('sceneries.info.ASDEK')"
</td> />
<td <img
class="station-schedules unconfirmed" v-if="!station.generalInfo"
style="width: 30px" class="icon-info"
:class="{ inactive: !station.onlineInfo }" src="/images/icon-unknown.svg"
> alt="icon-unknown"
{{ station.onlineInfo?.scheduledTrainCount.unconfirmed ?? '-' }} :title="$t('sceneries.info.unknown')"
</td> />
</td>
<td <td
class="station-schedules confirmed" class="station-users"
style="width: 30px" :class="{ inactive: !station.onlineInfo }"
:class="{ inactive: !station.onlineInfo }" data-tooltip-type="UsersTooltip"
> :data-tooltip-content="JSON.stringify(station.onlineInfo?.stationTrains ?? [])"
{{ station.onlineInfo?.scheduledTrainCount.confirmed ?? '-' }} >
</td> <span class="text--primary">{{
</tr> station.onlineInfo?.stationTrains?.length ?? '-'
</tbody> }}</span>
</table> /
</div> <span class="text--primary">{{ station.onlineInfo?.maxUsers ?? '-' }}</span>
</td>
<td class="station-likes" :class="{ inactive: !station.onlineInfo }">
<span>{{ station.onlineInfo?.dispatcherRate ?? '-' }}</span>
</td>
<td
class="station-spawns"
:class="{ inactive: !station.onlineInfo }"
data-tooltip-type="SpawnsTooltip"
:data-tooltip-content="JSON.stringify(station.onlineInfo?.spawns ?? [])"
>
<span>{{ station.onlineInfo?.spawns.length ?? '-' }}</span>
</td>
<td
class="station-schedules all"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
{{ station.onlineInfo?.scheduledTrainCount.all ?? '-' }}
</td>
<td
class="station-schedules unconfirmed"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
{{ station.onlineInfo?.scheduledTrainCount.unconfirmed ?? '-' }}
</td>
<td
class="station-schedules confirmed"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
{{ station.onlineInfo?.scheduledTrainCount.confirmed ?? '-' }}
</td>
</tr>
</tbody>
</table>
<div class="no-stations" v-else> <div class="no-stations" v-else>
<div> <div>
@@ -418,6 +415,7 @@ $rowCol: #424242;
.station_table { .station_table {
height: 80vh; height: 80vh;
max-height: 2000px;
min-height: 700px; min-height: 700px;
overflow: auto; overflow: auto;
font-weight: 500; font-weight: 500;
+2 -5
View File
@@ -1,5 +1,5 @@
<template> <template>
<div class="tooltip" v-show="tooltipStore.type" ref="preview"> <div class="tooltip" ref="preview">
<component v-if="tooltipStore.type" :is="tooltipStore.type" /> <component v-if="tooltipStore.type" :is="tooltipStore.type" />
</div> </div>
</template> </template>
@@ -35,10 +35,7 @@ export default defineComponent({
let translateX = '0', let translateX = '0',
translateY = '30px'; translateY = '30px';
if (clientWidth < 500) { if (val[0] <= boxWidth / 2) {
previewEl.style.left = '50%';
translateX = '-50%';
} else if (val[0] <= boxWidth / 2) {
previewEl.style.left = '0'; previewEl.style.left = '0';
translateX = '0px'; translateX = '0px';
} else if (val[0] >= clientWidth - boxWidth / 2) { } else if (val[0] >= clientWidth - boxWidth / 2) {
@@ -13,13 +13,20 @@
width="300" width="300"
height="176" height="176"
class="rounded-md w-full h-auto" class="rounded-md w-full h-auto"
:src="`https://static.spythere.eu/images/${tooltipStore.content}--300px.jpg`" :src="`https://static.spythere.eu/images/${vehicleName}--300px.jpg`"
/> />
<div v-if="imageState == 'error'" class="error-placeholder"></div> <div v-if="imageState == 'error'" class="error-placeholder"></div>
<div class="vehicle-name"> <div class="vehicle-name">
{{ tooltipStore.content.replace(/_/g, ' ') }} {{ vehicleName.replace(/_/g, ' ') }}
<span v-if="vehicleCargo">({{ vehicleCargo.id }})</span>
</div>
<div class="vehicle-props" v-if="vehicleData">
{{ vehicleData.group.speed }}km/h &bull; {{ vehicleData.group.length }}m &bull;
{{ (vehicleData.group.weight / 1000).toFixed(1) }}t
<span v-if="vehicleCargo">(+{{ (vehicleCargo.weight / 1000).toFixed(1) }}t)</span>
</div> </div>
</div> </div>
</template> </template>
@@ -27,11 +34,13 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useTooltipStore } from '../../store/tooltipStore'; import { useTooltipStore } from '../../store/tooltipStore';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
data() { data() {
return { return {
tooltipStore: useTooltipStore(), tooltipStore: useTooltipStore(),
apiStore: useApiStore(),
imageState: 'loading' imageState: 'loading'
}; };
}, },
@@ -56,6 +65,34 @@ export default defineComponent({
(e.target as HTMLElement).style.display = 'none'; (e.target as HTMLElement).style.display = 'none';
} }
},
computed: {
vehicleName() {
return this.tooltipStore.content.split(':')[0];
},
vehicleData() {
return this.apiStore.vehiclesData?.find((v) => v.name == this.vehicleName);
},
vehicleCargo() {
return this.vehicleData?.group.cargoTypes?.find(
(c) => c.id == this.tooltipStore.content.split(':')[1]
);
}
// vehicleProps() {
// const vehicleDataArray = this.apiStore.vehiclesData?.vehicleList.find(
// ([name]) => name === this.vehicleName
// );
// if (!vehicleDataArray) return null;
// return (
// this.apiStore.vehiclesData!.vehicleProps.find((v) => v.type == vehicleDataArray[1]) ?? null
// );
// }
} }
}); });
</script> </script>
@@ -85,10 +122,13 @@ img {
.vehicle-name { .vehicle-name {
text-align: center; text-align: center;
margin-top: 0.5em; margin-top: 0.5em;
color: #ccc;
text-wrap: wrap; text-wrap: wrap;
} }
.vehicle-props {
color: #ccc;
}
.error-placeholder { .error-placeholder {
height: 176px; height: 176px;
} }
+33 -21
View File
@@ -1,5 +1,8 @@
<template> <template>
<span class="stop-label" :data-sbl="stop.isSBL"> <span
class="stop-label"
:data-minor="stop.isSBL || (stop.nameRaw.endsWith(', po.') && !stop.duration)"
>
<span class="name" v-html="stop.nameHtml"></span> <span class="name" v-html="stop.nameHtml"></span>
<span <span
@@ -9,12 +12,13 @@
stop.arrivalDelay > 0 && stop.status != 'unconfirmed' stop.arrivalDelay > 0 && stop.status != 'unconfirmed'
? 'delayed' ? 'delayed'
: stop.arrivalDelay < 0 && stop.status != 'unconfirmed' : stop.arrivalDelay < 0 && stop.status != 'unconfirmed'
? 'preponed' ? 'preponed'
: stop.arrivalDelay == 0 && stop.status == 'confirmed' : stop.arrivalDelay == 0 && stop.status == 'confirmed'
? 'on-time' ? 'on-time'
: '' : ''
" "
> >
p.
<span v-if="stop.arrivalDelay != 0 && stop.status != 'unconfirmed'"> <span v-if="stop.arrivalDelay != 0 && stop.status != 'unconfirmed'">
<s>{{ timestampToString(stop.arrivalScheduled) }}</s> <s>{{ timestampToString(stop.arrivalScheduled) }}</s>
{{ timestampToString(stop.arrivalReal) }} {{ timestampToString(stop.arrivalReal) }}
@@ -29,17 +33,17 @@
<span <span
v-if=" v-if="
stop.duration || stop.duration ||
(stop.status == 'stopped' && (stop.status == 'stopped' && stop.position != 'begin' && stop.departureDelay > 0)
stop.position != 'begin' &&
stop.departureDelay != stop.arrivalDelay)
" "
class="date stop" class="date stop"
:data-stop-types="stop.type.replace(', ', '-')" :data-stop-types="stop.type.replace(', ', '-')"
:data-stop-status=" :data-stop-status="stop.departureDelay > 0 && !stop.duration ? 'delayed' : ''"
stop.departureDelay - stop.arrivalDelay > 0 && !stop.duration ? 'delayed' : ''
"
> >
{{ stop.duration || stop.departureDelay - stop.arrivalDelay }} {{
stop.duration == 0 && stop.departureDelay > 0
? stop.departureDelay - stop.arrivalDelay
: stop.duration
}}
{{ stop.type == '' ? 'pt' : stop.type }} {{ stop.type == '' ? 'pt' : stop.type }}
</span> </span>
@@ -53,13 +57,16 @@
stop.departureDelay > 0 && stop.status == 'confirmed' stop.departureDelay > 0 && stop.status == 'confirmed'
? 'delayed' ? 'delayed'
: stop.departureDelay < 0 && stop.status == 'confirmed' : stop.departureDelay < 0 && stop.status == 'confirmed'
? 'preponed' ? 'preponed'
: stop.departureDelay == 0 && stop.status == 'confirmed' : stop.departureDelay == 0 && stop.status == 'confirmed'
? 'on-time' ? 'on-time'
: '' : ''
" "
> >
<span v-if="stop.departureDelay != 0 && stop.status == 'confirmed'"> o.
<span
v-if="stop.departureDelay != 0 && (stop.status == 'confirmed' || stop.status == 'stopped')"
>
<s>{{ timestampToString(stop.departureScheduled) }}</s> <s>{{ timestampToString(stop.departureScheduled) }}</s>
{{ timestampToString(stop.departureReal) }} {{ timestampToString(stop.departureReal) }}
@@ -96,14 +103,14 @@ $delayedClr: salmon;
$dateClr: #525151; $dateClr: #525151;
$stopExchangeClr: #db8e29; $stopExchangeClr: #db8e29;
$stopDefaultClr: #252525; $stopDefaultClr: #252525;
$stopNameClr: #22a8d1; $stopNameClr: #303030;
.stop-label { .stop-label {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
&[data-sbl='true'] { &[data-minor='true'] {
.date { .date {
display: none; display: none;
} }
@@ -117,6 +124,7 @@ $stopNameClr: #22a8d1;
.name { .name {
background: $stopNameClr; background: $stopNameClr;
border-radius: 0.5em 0 0 0.5em;
padding: 0.3em 0.5em; padding: 0.3em 0.5em;
display: flex; display: flex;
@@ -130,6 +138,10 @@ $stopNameClr: #22a8d1;
.date { .date {
background: $dateClr; background: $dateClr;
padding: 0.3em 0.5em; padding: 0.3em 0.5em;
&:last-child {
border-radius: 0 0.5em 0.5em 0;
}
} }
.stop { .stop {
@@ -150,7 +162,7 @@ $stopNameClr: #22a8d1;
.departure { .departure {
&[data-status='delayed'] { &[data-status='delayed'] {
s { s {
color: #999; color: #ccc;
} }
span { span {
@@ -160,7 +172,7 @@ $stopNameClr: #22a8d1;
&[data-status='preponed'] { &[data-status='preponed'] {
s { s {
color: #999; color: #ccc;
} }
span { span {
+24
View File
@@ -131,6 +131,18 @@
<div> <div>
<img src="/images/icon-speed.svg" alt="speed icon" /> <img src="/images/icon-speed.svg" alt="speed icon" />
{{ train.speed }} km/h {{ train.speed }} km/h
<span v-if="stockSpeedLimit != Infinity">
&bull;
<em
class="text--grayed"
style="text-decoration: underline dotted"
tabindex="0"
:data-tooltip="$t('trains.vmax-tooltip')"
>
{{ stockSpeedLimit }} km/h
</em>
</span>
</div> </div>
</div> </div>
@@ -191,6 +203,18 @@ export default defineComponent({
}; };
}, },
computed: {
stockSpeedLimit() {
return this.train.stockList.reduce((acc, stockName) => {
const vehicleSpeed =
this.apiStore.vehiclesData?.find((v) => v.name == stockName.split(':')[0])?.group.speed ??
300;
return Math.min(vehicleSpeed, acc);
}, 300);
}
},
methods: { methods: {
navigateToJournal() { navigateToJournal() {
this.$router.push({ this.$router.push({
+85 -42
View File
@@ -14,7 +14,6 @@
:data-stop-type="stop.type" :data-stop-type="stop.type"
:data-minor-stop-active="stop.isActive" :data-minor-stop-active="stop.isActive"
:data-last-confirmed="stop.isLastConfirmed" :data-last-confirmed="stop.isLastConfirmed"
x
> >
<span class="stop_info"> <span class="stop_info">
<span class="distance"> <span class="distance">
@@ -48,26 +47,46 @@
<span <span
v-if=" v-if="
stop.departureLine && stop.departureLine &&
stop.departureLine == scheduleStops[i + 1]?.arrivalLine && scheduleStops[i + 1] != undefined &&
!/sbl/gi.test(stop.departureLine) !/-|_|(^it\d+)|(^sbl)/gi.test(stop.departureLine)
" "
> >
{{ stop.departureLine }} <div class="scenery-route">
</span> <span>{{ stop.departureLine }}</span>
<span v-if="stop.departureLineInfo">
<span v-else-if="stop.departureLine && !/sbl/gi.test(stop.departureLine)"> | {{ stop.departureLineInfo.routeSpeed }}
<div>{{ stop.departureLine }}</div> <span v-if="stop.departureLineInfo.isElectric">⚡</span>
<div <img
class="scenery-change-name" v-else
v-if=" src="/images/icon-we4a.png"
i < scheduleStops.length - 1 && :title="$t('trains.we4a-tooltip')"
stop.sceneryName != scheduleStops[i + 1].sceneryName width="12"
" />
> </span>
{{ scheduleStops[i + 1].sceneryName }}
</div> </div>
<div>
{{ scheduleStops[i + 1].arrivalLine }} <div
v-if="stop.sceneryName != scheduleStops[i + 1]?.sceneryName"
class="scenery-change-name"
>
<span>{{ scheduleStops[i + 1].sceneryName }}</span>
<span v-if="stop.departureLineInfo?.routeTracks == 1"> &UpDownArrow;</span>
<span v-else> &UpArrowDownArrow;</span>
</div>
<div class="scenery-route">
<span> {{ scheduleStops[i + 1].arrivalLine }}</span>
<span v-if="scheduleStops[i + 1].arrivalLineInfo">
| {{ scheduleStops[i + 1].arrivalLineInfo!.routeSpeed }}
<span v-if="scheduleStops[i + 1].arrivalLineInfo!.isElectric">⚡</span>
<img
v-else
src="/images/icon-we4a.png"
:title="$t('trains.we4a-tooltip')"
width="12"
/>
</span>
</div> </div>
</span> </span>
</div> </div>
@@ -85,7 +104,7 @@ import StopLabel from './StopLabel.vue';
import StockList from '../Global/StockList.vue'; import StockList from '../Global/StockList.vue';
import { useMainStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import { useApiStore } from '../../store/apiStore'; import { useApiStore } from '../../store/apiStore';
import { Train } from '../../typings/common'; import { StationRoutesInfo, Train } from '../../typings/common';
export interface TrainScheduleStop { export interface TrainScheduleStop {
nameHtml: string; nameHtml: string;
@@ -111,12 +130,16 @@ export interface TrainScheduleStop {
isSBL: boolean; isSBL: boolean;
sceneryName: string | null; sceneryName: string | null;
sceneryHash: string;
distance: number; distance: number;
arrivalLine: string | null; arrivalLine: string | null;
departureLine: string | null; departureLine: string | null;
arrivalLineInfo?: StationRoutesInfo;
departureLineInfo?: StationRoutesInfo;
isExternal: boolean;
comments: string | null; comments: string | null;
} }
@@ -146,13 +169,25 @@ export default defineComponent({
return ( return (
this.train.timetableData?.followingStops.map((stop, i, arr) => { this.train.timetableData?.followingStops.map((stop, i, arr) => {
if ( const isExternal =
i > 0 && i > 0 &&
stop.arrivalLine && stop.arrivalLine != null &&
stop.arrivalLine != arr[i - 1].departureLine && (stop.arrivalLine != arr[i - 1].departureLine ||
!/sbl/gi.test(stop.arrivalLine) (stop.arrivalLine == arr[i - 1].departureLine &&
) !/-|_|(^it\d+)|(^sbl)/gi.test(stop.arrivalLine)));
currentSceneryIndex++;
if (isExternal) currentSceneryIndex++;
const sceneryName = this.train.timetableData!.sceneryNames[currentSceneryIndex];
const sceneryInfo = this.apiStore.sceneryData.find((st) => st.name == sceneryName);
const arrivalLineInfo = sceneryInfo?.routesInfo.find(
(r) => r.routeName == stop.arrivalLine
);
const departureLineInfo = sceneryInfo?.routesInfo.find(
(r) => r.routeName == stop.departureLine
);
return { return {
nameHtml: stop.stopName, nameHtml: stop.stopName,
@@ -174,14 +209,18 @@ export default defineComponent({
arrivalLine: stop.arrivalLine, arrivalLine: stop.arrivalLine,
departureLine: stop.departureLine, departureLine: stop.departureLine,
arrivalLineInfo: arrivalLineInfo,
departureLineInfo: departureLineInfo,
isExternal,
type: stop.stopType, type: stop.stopType,
distance: stop.stopDistance, distance: stop.stopDistance,
isActive: this.activeMinorStops.includes(i), isActive: this.activeMinorStops.includes(i),
isLastConfirmed: this.lastConfirmed === i && !stop.terminatesHere, isLastConfirmed: this.lastConfirmed === i && !stop.terminatesHere,
isSBL: /sbl/gi.test(stop.stopName), isSBL: /sbl/gi.test(stop.stopName),
position: stop.beginsHere ? 'begin' : stop.terminatesHere ? 'end' : 'en-route', position: stop.beginsHere ? 'begin' : stop.terminatesHere ? 'end' : 'en-route',
sceneryHash: '', sceneryName,
sceneryName: this.train.timetableData!.sceneryNames[currentSceneryIndex],
status: stop.confirmed ? 'confirmed' : stop.stopped ? 'stopped' : 'unconfirmed' status: stop.confirmed ? 'confirmed' : stop.stopped ? 'stopped' : 'unconfirmed'
}; };
}) ?? [] }) ?? []
@@ -483,22 +522,26 @@ $blinkAnim: 0.5s ease-in-out alternate infinite blink;
} }
} }
.bottom-line-info { .scenery-route {
.scenery-change-name { img {
position: relative; vertical-align: middle;
margin: 0.25em 0; }
}
&::before { .scenery-change-name {
content: ''; position: relative;
position: absolute; margin: 0.25em 0;
height: 2px;
width: 30px;
background-color: #aaa;
top: 50%; &::before {
right: calc(100% + 5px); content: '';
transform: translate(0, -50%); position: absolute;
} height: 2px;
width: 30px;
background-color: #aaa;
top: 50%;
right: calc(100% + 5px);
transform: translate(0, -50%);
} }
} }
</style> </style>
File diff suppressed because it is too large Load Diff
-365
View File
@@ -1,369 +1,4 @@
{ {
"optionSections": [
"status",
"timetables",
"reality",
"package-access",
"station-type",
"access",
"control",
"blockades",
"signals",
"addons"
],
"options": [
{
"id": "real",
"name": "real",
"section": "reality",
"value": true,
"defaultValue": true
},
{
"id": "fictional",
"name": "fictional",
"section": "reality",
"value": true,
"defaultValue": true
},
{
"id": "default",
"name": "default",
"section": "package-access",
"value": true,
"defaultValue": true
},
{
"id": "not-default",
"name": "notDefault",
"section": "package-access",
"value": true,
"defaultValue": true
},
{
"id": "non-public",
"name": "nonPublic",
"section": "access",
"value": true,
"defaultValue": true
},
{
"id": "unavailable",
"name": "unavailable",
"section": "access",
"value": false,
"defaultValue": false
},
{
"id": "abandoned",
"name": "abandoned",
"section": "access",
"value": false,
"defaultValue": false
},
{
"id": "junction",
"name": "junction",
"section": "station-type",
"value": true,
"defaultValue": true
},
{
"id": "nonJunction",
"name": "nonJunction",
"section": "station-type",
"value": true,
"defaultValue": true
},
{
"id": "SPK",
"name": "SPK",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SCS",
"name": "SCS",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SPE",
"name": "SPE",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SPK-M",
"name": "mechaniczne+SPK",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SCS-M",
"name": "mechaniczne+SCS",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "mechanical",
"name": "mechaniczne",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SPK-R",
"name": "ręczne+SPK",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SCS-R",
"name": "ręczne+SCS",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "manual",
"name": "ręczne",
"section": "control",
"value": true,
"defaultValue": true
},
{
"id": "SUP",
"name": "SUP",
"section": "addons",
"value": true,
"defaultValue": true
},
{
"id": "noSUP",
"name": "noSUP",
"section": "addons",
"value": true,
"defaultValue": true
},
{
"id": "ASDEK",
"name": "ASDEK",
"section": "addons",
"value": true,
"defaultValue": true
},
{
"id": "noASDEK",
"name": "noASDEK",
"section": "addons",
"value": true,
"defaultValue": true
},
{
"id": "SBL",
"name": "SBL",
"section": "blockades",
"value": true,
"defaultValue": true
},
{
"id": "PBL",
"name": "PBL",
"section": "blockades",
"value": true,
"defaultValue": true
},
{
"id": "modern",
"name": "współczesna",
"section": "signals",
"value": true,
"defaultValue": true
},
{
"id": "semaphores",
"name": "kształtowa",
"section": "signals",
"value": true,
"defaultValue": true
},
{
"id": "mixed",
"name": "mieszana",
"section": "signals",
"value": true,
"defaultValue": true
},
{
"id": "historical",
"name": "historyczna",
"section": "signals",
"value": true,
"defaultValue": true
},
{
"id": "free",
"name": "free",
"section": "status",
"value": false,
"defaultValue": false
},
{
"id": "occupied",
"name": "occupied",
"section": "status",
"value": true,
"defaultValue": true
},
{
"id": "endingStatus",
"name": "endingStatus",
"section": "status",
"value": true,
"defaultValue": true
},
{
"id": "afkStatus",
"name": "afkStatus",
"section": "status",
"value": true,
"defaultValue": true
},
{
"id": "noSpaceStatus",
"name": "noSpaceStatus",
"section": "status",
"value": true,
"defaultValue": true
},
{
"id": "unavailableStatus",
"name": "unavailableStatus",
"section": "status",
"value": true,
"defaultValue": true
},
{
"id": "withActiveTimetables",
"name": "withActiveTimetables",
"section": "timetables",
"value": true,
"defaultValue": true
},
{
"id": "withoutActiveTimetables",
"name": "withoutActiveTimetables",
"section": "timetables",
"value": true,
"defaultValue": true
}
],
"sliders": [
{
"id": "min-lvl",
"name": "minLevel",
"minRange": 0,
"maxRange": 20,
"step": 1,
"value": 0,
"defaultValue": 0
},
{
"id": "max-lvl",
"name": "maxLevel",
"minRange": 0,
"maxRange": 20,
"step": 1,
"value": 20,
"defaultValue": 20
},
{
"id": "min-vmax",
"name": "minVmax",
"minRange": 0,
"maxRange": 200,
"step": 10,
"value": 0,
"defaultValue": 0
},
{
"id": "max-vmax",
"name": "maxVmax",
"minRange": 0,
"maxRange": 200,
"step": 10,
"value": 200,
"defaultValue": 200
},
{
"id": "routes-1t-cat",
"name": "minOneWayCatenary",
"minRange": 0,
"maxRange": 5,
"step": 1,
"value": 0,
"defaultValue": 0
},
{
"id": "routes-1t-other",
"name": "minOneWay",
"minRange": 0,
"maxRange": 5,
"step": 1,
"value": 0,
"defaultValue": 0
},
{
"id": "routes-2t-cat",
"name": "minTwoWayCatenary",
"minRange": 0,
"maxRange": 5,
"step": 1,
"value": 0,
"defaultValue": 0
},
{
"id": "routes-2t-other",
"name": "minTwoWay",
"minRange": 0,
"maxRange": 5,
"step": 1,
"value": 0,
"defaultValue": 0
}
],
"modes": [
{
"id": "include-selected",
"name": "include-selected",
"section": "mode",
"value": true,
"defaultValue": true
},
{
"id": "save",
"name": "save",
"section": "mode",
"value": true,
"defaultValue": true
}
],
"regions": [ "regions": [
{ {
"id": "eu", "id": "eu",
+14 -14
View File
@@ -125,7 +125,9 @@
"search-dispatcher": "Dispatcher name", "search-dispatcher": "Dispatcher name",
"search-station": "Scenery name", "search-station": "Scenery name",
"search-author": "Timetable author name", "search-author": "Timetable author name",
"search-issuedFrom": "Origin scenery name", "search-issuedFrom": "Issuing scenery name",
"search-via": "Via scenery name",
"search-terminatingAt": "Terminating scenery name",
"search-timetables-date": "Timetable date (UTC+2 / CEST)", "search-timetables-date": "Timetable date (UTC+2 / CEST)",
"search-dispatchers-date": "Service date (UTC+2 / CEST)", "search-dispatchers-date": "Service date (UTC+2 / CEST)",
"search-date": "Date (UTC+2 / CEST)", "search-date": "Date (UTC+2 / CEST)",
@@ -329,6 +331,9 @@
"current-signal": "at signal", "current-signal": "at signal",
"current-track": "on track", "current-track": "on track",
"vmax-tooltip": "Maximum train speed based on rolling stock vehicles - braked weight is not included",
"we4a-tooltip": "Non-electrified track",
"delayed": "Delayed: ", "delayed": "Delayed: ",
"preponed": "Ahead of schedule: ", "preponed": "Ahead of schedule: ",
"on-time": "On time", "on-time": "On time",
@@ -491,21 +496,16 @@
"option-timetables-history": "Timetables history PL1", "option-timetables-history": "Timetables history PL1",
"option-dispatchers-history": "Dispatchers history PL1", "option-dispatchers-history": "Dispatchers history PL1",
"timetable-author-title": "Issued by", "timetable-via": "ALL TIMETABLES",
"timetable-author-unknown": "Author unknown", "timetable-issuedFrom": "BEGINS HERE",
"timetable-terminatingAt": "TERMINATES HERE",
"timetables-history-id": "ID", "timetable-issued-date": "Issued",
"timetables-history-number": "Number", "timetable-issued-by": " by:",
"timetables-history-route": "Route", "timetable-issued-for": " for driver:",
"timetables-history-driver": "Driver",
"timetables-history-author": "TT author",
"timetables-history-date": "Date",
"dispatchers-history-hash": "Hash", "dispatcher-rate": "Rate:",
"dispatchers-history-dispatcher": "Dispatcher", "dispatcher-status-changes": "Status changes:",
"dispatchers-history-level": "Level",
"dispatchers-history-rate": "Rate",
"dispatchers-history-date": "Service 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!",
+13 -13
View File
@@ -122,6 +122,8 @@
"search-station": "Nazwa scenerii", "search-station": "Nazwa scenerii",
"search-author": "Nick autora rozkładu jazdy", "search-author": "Nick autora rozkładu jazdy",
"search-issuedFrom": "Sceneria początkowa", "search-issuedFrom": "Sceneria początkowa",
"search-via": "Przez scenerię",
"search-terminatingAt": "Sceneria końcowa",
"search-timetables-date": "Data rozkładu jazdy (UTC+2 / CEST)", "search-timetables-date": "Data rozkładu jazdy (UTC+2 / CEST)",
"search-dispatchers-date": "Data służby (UTC+2 / CEST)", "search-dispatchers-date": "Data służby (UTC+2 / CEST)",
"search-date": "Data (UTC+2 / CEST)", "search-date": "Data (UTC+2 / CEST)",
@@ -315,6 +317,9 @@
"current-signal": "przy semaforze", "current-signal": "przy semaforze",
"current-track": "na szlaku", "current-track": "na szlaku",
"vmax-tooltip": "Maksymalna prędkość na podstawie pojazdów w składzie - nie bierze pod uwagę masy hamowania",
"we4a-tooltip": "Szlak niezelektryfikowany",
"delayed": "Opóźniony: ", "delayed": "Opóźniony: ",
"preponed": "Przed czasem: ", "preponed": "Przed czasem: ",
"on-time": "Planowo", "on-time": "Planowo",
@@ -474,21 +479,16 @@
"option-timetables-history": "Historia rozkładów PL1", "option-timetables-history": "Historia rozkładów PL1",
"option-dispatchers-history": "Historia dyżurów PL1", "option-dispatchers-history": "Historia dyżurów PL1",
"timetable-author-title": "Wydany przez", "timetable-via": "WSZYSTKIE RJ",
"timetable-author-unknown": "Autor nieznany", "timetable-issuedFrom": "ROZPOCZYNA BIEG",
"timetable-terminatingAt": "KOŃCZY BIEG",
"timetables-history-id": "ID", "timetable-issued-date": "Wystawiony",
"timetables-history-number": "Numer", "timetable-issued-by": " przez:",
"timetables-history-route": "Trasa", "timetable-issued-for": " dla maszynisty:",
"timetables-history-driver": "Maszynista",
"timetables-history-author": "Autor RJ",
"timetables-history-date": "Data",
"dispatchers-history-hash": "Hash", "dispatcher-rate": "Ocena:",
"dispatchers-history-dispatcher": "Dyżurny", "dispatcher-status-changes": "Zmiany statusów:",
"dispatchers-history-level": "Poziom",
"dispatchers-history-rate": "Ocena",
"dispatchers-history-date": "Data służby",
"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!",
+1 -1
View File
@@ -58,7 +58,7 @@ const routes: Array<RouteRecordRaw> = [
const router = createRouter({ const router = createRouter({
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
if (to.name == 'SceneryView' && from.name !== to.name && from.query['view'] === undefined) if (to.name == 'SceneryView' && from.name !== to.name && from.query['view'] === undefined)
return { el: `.app_main` }; return { el: `.app_main`, top: -15 };
if (savedPosition) return savedPosition; if (savedPosition) return savedPosition;
}, },
+46 -14
View File
@@ -4,20 +4,17 @@ import { Status } from '../typings/common';
import { StationJSONData } from './typings'; import { StationJSONData } from './typings';
import axios, { AxiosInstance } from 'axios'; import axios, { AxiosInstance } from 'axios';
export enum APIMode {
PRODUCTION = 0,
DEV = 1,
MOCK = 2
}
export const useApiStore = defineStore('apiStore', { export const useApiStore = defineStore('apiStore', {
state: () => ({ state: () => ({
dataStatuses: { dataStatuses: {
connection: Status.Data.Loading, connection: Status.Data.Loading,
sceneries: Status.Data.Loading sceneries: Status.Data.Loading,
vehicles: Status.Data.Loading
}, },
activeData: undefined as API.ActiveData.Response | undefined, activeData: undefined as API.ActiveData.Response | undefined,
vehiclesData: undefined as API.Vehicles.Response | undefined,
donatorsData: [] as API.Donators.Response, donatorsData: [] as API.Donators.Response,
sceneryData: [] as StationJSONData[], sceneryData: [] as StationJSONData[],
@@ -54,9 +51,22 @@ export const useApiStore = defineStore('apiStore', {
// Static data // Static data
this.fetchDonatorsData(); this.fetchDonatorsData();
this.fetchStationsGeneralInfo(); this.fetchStationsGeneralInfo();
this.fetchVehiclesInfo();
}, },
async fetchActiveData() { async fetchActiveData() {
if (import.meta.env.VITE_API_ACTIVE_DATA_MODE == 'mocking') {
import('../../tests/data/getActiveData.json').then((data) => {
console.warn('activeData: mocking mode');
this.activeData = data.default as API.ActiveData.Response;
this.lastFetchData = new Date();
this.dataStatuses.connection = Status.Data.Loaded;
});
return;
}
if (!this.activeData) this.dataStatuses.connection = Status.Data.Loading; if (!this.activeData) this.dataStatuses.connection = Status.Data.Loading;
try { try {
@@ -82,17 +92,39 @@ export const useApiStore = defineStore('apiStore', {
}, },
async fetchStationsGeneralInfo() { async fetchStationsGeneralInfo() {
const sceneryData: StationJSONData[] = ( try {
await this.client!.get<StationJSONData[]>('api/getSceneries') const sceneryData: StationJSONData[] = (
).data; await this.client!.get<StationJSONData[]>('api/getSceneries')
).data;
if (!sceneryData) { this.dataStatuses.sceneries = Status.Data.Loaded;
this.sceneryData = sceneryData;
} catch (error) {
this.dataStatuses.sceneries = Status.Data.Error; this.dataStatuses.sceneries = Status.Data.Error;
return; console.error('Ups! Wystąpił błąd podczas pobierania informacji o sceneriach:', error);
} }
},
this.dataStatuses.sceneries = Status.Data.Loaded; async fetchVehiclesInfo() {
this.sceneryData = sceneryData; // if (import.meta.env.VITE_API_VEHICLES_MODE == 'mocking') {
// import('../../tests/data/vehicles.json').then((data) => {
// console.warn('vehicles.json: mocking mode');
// this.vehiclesData = data.default;
// this.dataStatuses.vehicles = Status.Data.Loaded;
// });
// return;
// }
try {
const response = await this.client!.get<API.Vehicles.Response>('api/getVehicles');
this.vehiclesData = response.data;
this.dataStatuses.vehicles = response.data ? Status.Data.Loaded : Status.Data.Warning;
} catch (error) {
this.dataStatuses.vehicles = Status.Data.Error;
console.error('Ups! Wystąpił błąd podczas pobierania informacji o pojazdach:', error);
}
} }
} }
}); });
+1 -1
View File
@@ -4,7 +4,7 @@
.list_wrapper { .list_wrapper {
overflow-y: auto; overflow-y: auto;
height: 90vh; height: 90vh;
min-height: 550px; min-height: 650px;
margin-top: 0.5em; margin-top: 0.5em;
position: relative; position: relative;
+31 -19
View File
@@ -228,6 +228,10 @@ a.a-button {
background-color: #3c3c3c; background-color: #3c3c3c;
} }
&:hover {
background-color: #555;
}
} }
&.btn--image { &.btn--image {
@@ -283,6 +287,27 @@ a.a-button {
} }
} }
// Basic tooltip
[data-tooltip] {
cursor: help;
}
[data-tooltip]:hover::after,
[data-tooltip]:focus::after {
position: absolute;
transform: translate(0, -50%);
content: attr(data-tooltip);
color: white;
background-color: #333;
box-shadow: 0 0 5px 2px #aaa;
border-radius: 0.5em;
padding: 0.5em;
margin: 0 0.5em;
max-width: 300px;
z-index: 100;
}
@include smallScreen { @include smallScreen {
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0.5em; width: 0.5em;
@@ -296,24 +321,11 @@ a.a-button {
background-color: #777; background-color: #777;
} }
} }
}
// Basic tooltip [data-tooltip]:hover::after,
[data-tooltip]:hover::after, [data-tooltip]:focus::after {
[data-tooltip]:focus::after { transform: translate(-50%, 2em);
position: absolute; left: 50%;
transform: translate(10px, -50%); width: 100%;
}
content: attr(data-tooltip);
color: white;
background-color: #171717;
border-radius: 0.5em;
padding: 0.5em;
margin: 0 0.25em;
max-width: 300px;
z-index: 100;
}
[data-tooltip] {
cursor: help;
} }
+3 -7
View File
@@ -1,11 +1,7 @@
.scenery-table-section {
position: relative;
height: 100%;
overflow-y: scroll;
}
table.scenery-history-table { table.scenery-history-table {
width: 100%; width: 100%;
table-layout: fixed;
min-width: 900px;
border-collapse: collapse; border-collapse: collapse;
thead { thead {
@@ -25,7 +21,7 @@ table.scenery-history-table {
td { td {
padding: 0.75em; padding: 0.75em;
border-bottom: solid 5px #111; border-bottom: solid 5px #181818;
} }
} }
+7 -1
View File
@@ -1,4 +1,4 @@
import { Status } from './common'; import { Status, VehicleData } from './common';
export enum APIDataStatus { export enum APIDataStatus {
OK = 'OK', OK = 'OK',
@@ -19,6 +19,7 @@ export namespace API {
apiStatuses?: APIStatuses; apiStatuses?: APIStatuses;
} }
} }
export namespace DispatcherHistory { export namespace DispatcherHistory {
export type Response = Data[]; export type Response = Data[];
@@ -38,6 +39,7 @@ export namespace API {
stationName: string; stationName: string;
timestampFrom: number; timestampFrom: number;
timestampTo?: number; timestampTo?: number;
statusHistory: string[];
} }
} }
@@ -316,6 +318,10 @@ export namespace API {
export namespace Donators { export namespace Donators {
export type Response = string[]; export type Response = string[];
} }
export namespace Vehicles {
export type Response = VehicleData[];
}
} }
export namespace GithubAPI { export namespace GithubAPI {
+30
View File
@@ -189,3 +189,33 @@ export interface CheckpointTrain {
checkpointStop: TrainStop; checkpointStop: TrainStop;
train: Train; train: Train;
} }
// Vehicles Data
export interface VehicleData {
id: number;
name: string;
type: string;
cabinName: string | null;
restrictions: Record<string, any> | null;
vehicleGroupsId: number;
group: VehiclesGroup;
}
export interface VehiclesGroup {
id: number;
name: string;
speed: number;
length: number;
weight: number;
cargoTypes: VehicleCargo[] | null;
locoProps: {
coldStart: boolean;
doubleManned: boolean;
} | null;
}
export interface VehicleCargo {
id: string;
weight: number;
}
+21 -5
View File
@@ -126,6 +126,8 @@ interface TimetablesQueryParams {
dateTo?: string; dateTo?: string;
issuedFrom?: string; issuedFrom?: string;
terminatingAt?: string;
via?: string;
countFrom?: number; countFrom?: number;
countLimit?: number; countLimit?: number;
@@ -206,6 +208,8 @@ export default defineComponent({
'search-driver': '', 'search-driver': '',
'search-dispatcher': '', 'search-dispatcher': '',
'search-issuedFrom': '', 'search-issuedFrom': '',
'search-via': '',
'search-terminatingAt': '',
'search-date': '' 'search-date': ''
} as Journal.TimetableSearchType); } as Journal.TimetableSearchType);
@@ -299,11 +303,17 @@ export default defineComponent({
}, },
setOptions(options: { [key: string]: string }) { setOptions(options: { [key: string]: string }) {
this.searchersValues['search-date'] = options['search-date'] ?? ''; Object.keys(this.searchersValues).forEach((v) => {
this.searchersValues['search-driver'] = options['search-driver'] ?? ''; this.searchersValues[v as Journal.TimetableSearchKey] = options[v] ?? '';
this.searchersValues['search-train'] = options['search-train'] ?? ''; });
this.searchersValues['search-dispatcher'] = options['search-dispatcher'] ?? '';
this.searchersValues['search-issuedFrom'] = options['search-issuedFrom'] ?? ''; // this.searchersValues['search-date'] = options['search-date'] ?? '';
// this.searchersValues['search-driver'] = options['search-driver'] ?? '';
// this.searchersValues['search-train'] = options['search-train'] ?? '';
// this.searchersValues['search-dispatcher'] = options['search-dispatcher'] ?? '';
// this.searchersValues['search-issuedFrom'] = options['search-issuedFrom'] ?? '';
// this.searchersValues['search-via'] = options['search-via'] ?? '';
// this.searchersValues['search-terminatingAt'] = options['search-terminatingAt'] ?? '';
this.sorterActive.id = this.sorterActive.id =
(options['sorter-active'] as Journal.TimetableSorterKey) ?? 'timetableId'; (options['sorter-active'] as Journal.TimetableSorterKey) ?? 'timetableId';
@@ -347,6 +357,8 @@ export default defineComponent({
const authorName = this.searchersValues['search-dispatcher'].trim() || undefined; const authorName = this.searchersValues['search-dispatcher'].trim() || undefined;
const dateFrom = this.searchersValues['search-date'].trim() || undefined; const dateFrom = this.searchersValues['search-date'].trim() || undefined;
const issuedFrom = this.searchersValues['search-issuedFrom'].trim() || undefined; const issuedFrom = this.searchersValues['search-issuedFrom'].trim() || undefined;
const via = this.searchersValues['search-via'].trim() || undefined;
const terminatingAt = this.searchersValues['search-terminatingAt'].trim() || undefined;
let dateTo: string | undefined = undefined; let dateTo: string | undefined = undefined;
@@ -418,6 +430,10 @@ export default defineComponent({
queryParams['authorName'] = authorName; queryParams['authorName'] = authorName;
queryParams['dateFrom'] = dateFrom; queryParams['dateFrom'] = dateFrom;
queryParams['dateTo'] = dateTo; queryParams['dateTo'] = dateTo;
queryParams['issuedFrom'] = issuedFrom;
queryParams['terminatingAt'] = terminatingAt;
queryParams['via'] = via;
queryParams['issuedFrom'] = issuedFrom; queryParams['issuedFrom'] = issuedFrom;
queryParams['sortBy'] = queryParams['sortBy'] =
this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined; this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
+15 -29
View File
@@ -22,8 +22,8 @@
v-for="(viewMode, i) in viewModes" v-for="(viewMode, i) in viewModes"
:key="i" :key="i"
class="btn btn--option" class="btn btn--option"
:class="{ checked: currentMode == viewMode.component }"
@click="setViewMode(viewMode.component)" @click="setViewMode(viewMode.component)"
:data-checked="currentMode == viewMode.component"
> >
{{ $t(viewMode.id) }} {{ $t(viewMode.id) }}
</button> </button>
@@ -121,10 +121,6 @@ export default defineComponent({
Status: Status.Data Status: Status.Data
}), }),
// activated() {
// this.loadSelectedCheckpoint();
// },
setup() { setup() {
const route = useRoute(); const route = useRoute();
@@ -215,11 +211,10 @@ button.back-btn {
position: relative; position: relative;
width: 100%;
max-width: var(--max-container-width); max-width: var(--max-container-width);
min-height: 100vh; width: 100%;
margin: 1rem 0; padding: 1rem 0;
text-align: center; text-align: center;
&[data-timetable-only='true'] { &[data-timetable-only='true'] {
@@ -228,30 +223,27 @@ button.back-btn {
} }
} }
.scenery-left { .scenery-left,
.scenery-right {
position: relative; position: relative;
overflow: auto;
background-color: #181818; background-color: #181818;
padding: 1em 0.5em; padding: 1em 0.5em;
height: 100vh; height: calc(100vh - 0.5em);
min-height: 750px; min-height: 800px;
max-height: 1000px; max-height: 2000px;
overflow: auto; }
.scenery-left {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.scenery-right { .scenery-right {
background: #181818;
padding: 1em 0.5em;
height: 100vh;
min-height: 750px;
max-height: 1000px;
display: grid; display: grid;
grid-template-rows: auto 1fr auto; grid-template-rows: auto 1fr;
gap: 1em; gap: 1em;
} }
@@ -261,18 +253,12 @@ button.back-btn {
.info-actions { .info-actions {
display: flex; display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center;
gap: 0.75em; gap: 0.75em;
.btn { button {
padding: 0.5em; padding: 0.5em;
box-shadow: 0 0 10px 4px #242424;
&[data-checked='true'] {
color: var(--clr-primary);
}
} }
} }
+4 -2
View File
@@ -6,8 +6,10 @@ declare module '*.vue' {
export default component; export default component;
} }
interface ImportMetaEnv { interface ImportMetaEnv {
readonly VITE_APP_API_DEV: string; readonly VITE_API_MODE: 'production' | 'mocking' | 'development';
readonly VITE_APP_WS_DEV: string; readonly VITE_API_VEHICLES_MODE: 'production' | 'mocking' | 'development';
readonly VITE_API_ACTIVE_DATA_MODE: 'production' | 'mocking' | 'development';
readonly VITE_UPDATE_TEST: 'test' | 'production';
} }
interface ImportMeta { interface ImportMeta {
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
["kowbojYT","matseb","peterminecraft333","MIlanSVK_SimRailCZ","kierownik_z_ulicy","luk31as","pppatryk123","Kryszakos","MilyPan","paweld","Isitkiwi","Krisoy007","zeswaq","robcioRK","Ugulele","Spanky","KapitanKoza","Kuba6396","BravuraLion","trichlor","jasieleczeq","trannelgamer","tommy001","Waffel","krytaqu","NadrazakHonza","zordem","Ludolog"]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -24,10 +24,11 @@ export default defineConfig({
cleanupOutdatedCaches: true, cleanupOutdatedCaches: true,
runtimeCaching: [ runtimeCaching: [
{ {
urlPattern: /^https:\/\/stacjownik.spythere.eu\/api\/getSceneries/i, urlPattern:
handler: 'StaleWhileRevalidate', /^https:\/\/stacjownik.spythere.eu\/api\/(getVehicles|getDonators|getSceneries)/i,
handler: 'NetworkFirst',
options: { options: {
cacheName: 'spythere-sceneries-cache', cacheName: 'stacjownik-api-cache',
cacheableResponse: { cacheableResponse: {
statuses: [0, 200] statuses: [0, 200]
} }
@@ -35,15 +36,14 @@ export default defineConfig({
}, },
{ {
urlPattern: /^https:\/\/static.spythere.eu\/.*/i, urlPattern: /^https:\/\/static.spythere.eu\/.*/i,
handler: 'CacheFirst', handler: 'StaleWhileRevalidate',
options: { options: {
cacheName: 'spythere-static-cache', cacheName: 'spythere-static-cache',
cacheableResponse: { cacheableResponse: {
statuses: [0, 200] statuses: [0, 200]
}, },
expiration: { expiration: {
maxEntries: 100, maxEntries: 100
maxAgeSeconds: 60 * 60 * 8
} }
} }
} }