Compare commits

..

20 Commits

Author SHA1 Message Date
Spythere 96714550d0 Merge pull request #62 - Wersja 1.18.3
Wersja 1.18.3
2023-11-15 02:13:54 +01:00
Spythere 2b6c751f55 hotifx toString 2023-11-13 15:57:01 +01:00
Spythere 08d3a2a03a feature: nawigacja URL w widoku scenerii 2023-11-13 15:32:02 +01:00
Spythere a79ca78781 poprawki animacji statusów danych & tryb offline 2023-11-13 14:59:17 +01:00
Spythere e08333dba1 fix: tłumaczenie statusów dr 2023-11-12 16:40:25 +01:00
Spythere 8705dd1df5 fix filtrowania RJ na posterunkach; favicons index 2023-11-12 15:47:55 +01:00
Spythere 7b4da9d422 poprawki layoutu aplikacji 2023-11-10 16:18:06 +01:00
Spythere e51b2fe2f3 poprawki filtrów poc. 2023-11-10 15:39:49 +01:00
Spythere f8b4ce103f refactor typów danych 2023-11-10 15:04:49 +01:00
Spythere e82b4b8817 bump 1.18.3 2023-11-08 22:04:11 +01:00
Spythere 36e9df82b0 hotfixy 2023-11-08 22:03:45 +01:00
Spythere cbce9af00b nowe pobieranie i przetwarzanie statusów dyżurnych 2023-11-07 20:16:58 +01:00
Spythere 4a4304d65f Merge pull request #61 - Wersja 1.18.2
Wersja 1.18.2
2023-11-04 17:03:51 +01:00
Spythere edad5306f2 bump: 1.18.2 2023-11-04 17:01:01 +01:00
Spythere 5b775dfec9 fix: filtry RJ 2023-11-04 17:00:50 +01:00
Spythere a485652ca5 Merge pull request #60 (hotfix)
hotfix: maksymalny timeout dyżurnych (1.18.1)
2023-11-02 22:44:48 +01:00
Spythere ed308246d7 hotfix: maksymalny timeout dyżurnych 2023-11-02 22:42:28 +01:00
Spythere 621bb1ad55 Merge pull request #59 - Wersja 1.18.1
Wersja 1.18.1
2023-11-02 17:41:59 +01:00
Spythere 154ae2ddac bump 1.18.1 2023-11-02 17:40:55 +01:00
Spythere d9da49a867 rozszerzony wybór regionów przez URL; poprawki headerów 2023-11-02 17:40:31 +01:00
94 changed files with 2968 additions and 15245 deletions
+25 -11
View File
@@ -1,11 +1,14 @@
<!DOCTYPE html> <!doctype html>
<html lang="pl"> <html lang="pl">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="keywords" content="Stacjownik, TD2, Train Driver 2, stacjownik-td2, stacjownik, td2.info.pl" /> <meta
name="keywords"
content="Stacjownik, TD2, Train Driver 2, stacjownik-td2, stacjownik, td2.info.pl"
/>
<meta name="description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" /> <meta name="description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
<title>Stacjownik</title> <title>Stacjownik</title>
@@ -18,10 +21,6 @@
<meta name="msapplication-TileColor" content="#da532c" /> <meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#222222" /> <meta name="theme-color" content="#222222" />
<link rel="icon" href="favicon-64.png" sizes="64x64" type="image/png" />
<link rel="icon" href="favicon-62.png" sizes="62x62" type="image/png" />
<link rel="icon" href="favicon-32.png" sizes="32x32" type="image/png" />
<link rel="icon" href="favicon-16.png" sizes="16x16" type="image/png" />
<link rel="icon" href="favicon.ico" /> <link rel="icon" href="favicon.ico" />
<!-- Static OpenGraph meta --> <!-- Static OpenGraph meta -->
@@ -29,18 +28,33 @@
<meta property="og:url" content="https://stacjownik-td2.web.app/" /> <meta property="og:url" content="https://stacjownik-td2.web.app/" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:title" content="Stacjownik" /> <meta property="og:title" content="Stacjownik" />
<meta property="og:description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" /> <meta
<meta property="og:image" content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg" /> property="og:description"
content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2"
/>
<meta
property="og:image"
content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg"
/>
<meta property="og:image:width" content="1200" /> <meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" /> <meta property="og:image:height" content="630" />
<meta property="og:site_name" content="Stacjownik" /> <meta property="og:site_name" content="Stacjownik" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Stacjownik" /> <meta name="twitter:title" content="Stacjownik" />
<meta name="twitter:description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" /> <meta
<meta name="twitter:image" content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg" /> name="twitter:description"
content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2"
/>
<meta
name="twitter:image"
content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg"
/>
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500;700&display=swap" rel="stylesheet" /> <link
href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500;700&display=swap"
rel="stylesheet"
/>
</head> </head>
<body> <body>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.18.0", "version": "1.18.3",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+8 -15
View File
@@ -56,24 +56,17 @@
// CONTAINER // CONTAINER
.app_container { .app_container {
display: flex; // display: flex;
flex-flow: column; // flex-flow: column;
display: grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: 100%;
min-height: 100vh; min-height: 100vh;
}
header { .app_main {
flex: 0 0 auto; padding: 0 0.5em;
}
main {
flex: 1 1 auto;
padding: 0 0.5em;
}
footer {
flex: 0 1 0.2em;
}
} }
.warning { .warning {
+13 -12
View File
@@ -10,7 +10,7 @@
<main class="app_main"> <main class="app_main">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<keep-alive exclude="JournalView"> <keep-alive exclude="JournalView,SceneryView">
<component :is="Component" :key="$route.name" /> <component :is="Component" :key="$route.name" />
</keep-alive> </keep-alive>
</router-view> </router-view>
@@ -37,20 +37,19 @@ import { defineComponent, watch } from 'vue';
import Clock from './components/App/Clock.vue'; import Clock from './components/App/Clock.vue';
import packageInfo from '.././package.json'; import packageInfo from '.././package.json';
import { regions } from './data/options.json';
import { useStore } from './store/store'; import { useStore } from './store/mainStore';
import StatusIndicator from './components/App/StatusIndicator.vue'; import StatusIndicator from './components/App/StatusIndicator.vue';
import SelectBox from './components/Global/SelectBox.vue';
import TrainModal from './components/Global/TrainModal.vue'; import TrainModal from './components/Global/TrainModal.vue';
import StorageManager from './scripts/managers/storageManager';
import AppHeader from './components/App/AppHeader.vue'; import AppHeader from './components/App/AppHeader.vue';
import axios from 'axios'; import axios from 'axios';
import StorageManager from './managers/storageManager';
export default defineComponent({ export default defineComponent({
components: { components: {
Clock, Clock,
StatusIndicator, StatusIndicator,
SelectBox,
TrainModal, TrainModal,
AppHeader AppHeader
}, },
@@ -73,12 +72,9 @@ export default defineComponent({
window.addEventListener('offline', () => { window.addEventListener('offline', () => {
this.store.isOffline = true; this.store.isOffline = true;
this.store.apiData = { this.store.activeData.activeSceneries = [];
stations: [], this.store.activeData.trains = [];
dispatchers: [], this.store.activeData.connectedSocketCount = 0;
trains: [],
connectedSocketCount: 0
};
this.store.setStatuses(); this.store.setStatuses();
}); });
@@ -105,7 +101,12 @@ export default defineComponent({
immediate: true, immediate: true,
handler(regionQuery: string) { handler(regionQuery: string) {
if (regionQuery) { if (regionQuery) {
this.store.region.id = regionQuery; this.store.region.id =
regions.find(
(reg) =>
reg.id == regionQuery.toLocaleLowerCase() ||
reg.value.toLocaleLowerCase() == regionQuery.toLocaleLowerCase()
)?.id || 'eu';
} }
} }
} }
+10 -45
View File
@@ -39,9 +39,9 @@
<img src="/images/icon-train.svg" alt="icon train" /> <img src="/images/icon-train.svg" alt="icon train" />
</div> </div>
<span class="info_region"> <div class="info_region">
<SelectBox :itemList="computedRegions" :defaultItemIndex="0" @selected="changeRegion" /> <RegionDropdown />
</span> </div>
</span> </span>
<span class="header_links"> <span class="header_links">
@@ -68,11 +68,10 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import options from '../../data/options.json';
import SelectBox from '../Global/SelectBox.vue';
import StatusIndicator from './StatusIndicator.vue'; import StatusIndicator from './StatusIndicator.vue';
import Clock from './Clock.vue'; import Clock from './Clock.vue';
import RegionDropdown from '../Global/RegionDropdown.vue';
export default defineComponent({ export default defineComponent({
emits: ['changeLang'], emits: ['changeLang'],
@@ -90,10 +89,6 @@ export default defineComponent({
}, },
methods: { methods: {
changeRegion(region: { id: string; value: string }) {
this.store.changeRegion(region);
},
changeLang(lang: string) { changeLang(lang: string) {
this.$emit('changeLang', lang); this.$emit('changeLang', lang);
} }
@@ -101,37 +96,22 @@ export default defineComponent({
computed: { computed: {
onlineTrainsCount() { onlineTrainsCount() {
return this.store.trainList.filter((train) => train.online).length; return this.store.trainList.filter((train) => train.region == this.store.region.id).length;
}, },
onlineDispatchersCount() { onlineDispatchersCount() {
return this.store.onlineSceneryList.length; return this.store.onlineSceneryList.filter(
(scenery) => scenery.region == this.store.region.id
).length;
}, },
factorU() { factorU() {
return this.onlineDispatchersCount == 0 return this.onlineDispatchersCount == 0
? '-' ? '-'
: (this.onlineTrainsCount / this.onlineDispatchersCount).toFixed(2); : (this.onlineTrainsCount / this.onlineDispatchersCount).toFixed(2);
},
computedRegions() {
return options.regions.map((region) => {
const regionStationCount =
this.store.apiData.stations?.filter(
(station) => station.region == region.id && station.isOnline
).length || 0;
const regionTrainCount =
this.store.apiData.trains?.filter((train) => train.region == region.id && train.online)
.length || 0;
return {
id: region.id,
value: `${region.value} <div class='text--grayed'>${regionStationCount} / ${regionTrainCount}</div>`,
selectedValue: region.value
};
});
} }
}, },
components: { SelectBox, StatusIndicator, Clock } components: { StatusIndicator, Clock, RegionDropdown }
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -227,23 +207,8 @@ export default defineComponent({
} }
} }
// REGION SELECTION
.info_region { .info_region {
color: white;
font-weight: bold;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
.select-box_content button {
background-color: transparent;
font-weight: bold;
padding: 0.1em 0.5em;
color: paleturquoise;
}
.options {
font-size: 0.9em;
}
} }
</style> </style>
+20 -20
View File
@@ -194,9 +194,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { DataStatus } from '../../scripts/enums/DataStatus'; import { StoreState } from '../../store/typings';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import { StoreState } from '../../scripts/interfaces/store/storeTypes'; import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
data() { data() {
@@ -204,7 +204,7 @@ export default defineComponent({
tooltipActive: false, tooltipActive: false,
indicator: { indicator: {
offline: false, offline: false,
status: DataStatus.Loading, status: Status.Data.Loading,
message: 'data-status.S3' message: 'data-status.S3'
}, },
@@ -217,7 +217,7 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.setSignalStatus(DataStatus.Loading); this.setSignalStatus(Status.Data.Loading);
}, },
setup() { setup() {
@@ -240,44 +240,44 @@ export default defineComponent({
const dispatcherDataStatus = statuses.dispatchers; const dispatcherDataStatus = statuses.dispatchers;
if (this.store.isOffline) { if (this.store.isOffline) {
this.setSignalStatus(DataStatus.Initialized); this.setSignalStatus(Status.Data.Initialized);
this.indicator.status = DataStatus.Initialized; this.indicator.status = Status.Data.Initialized;
this.indicator.message = 'data-status.S1-offline'; this.indicator.message = 'data-status.S1-offline';
return; return;
} }
if (connectionStatus == DataStatus.Error) { if (connectionStatus == Status.Data.Error) {
this.setSignalStatus(connectionStatus); this.setSignalStatus(connectionStatus);
this.indicator.status = connectionStatus; this.indicator.status = connectionStatus;
this.indicator.message = 'data-status.S1a-connection'; this.indicator.message = 'data-status.S1a-connection';
return; return;
} }
if (sceneryDataStatus == DataStatus.Error) { if (sceneryDataStatus == Status.Data.Error) {
this.setSignalStatus(sceneryDataStatus); this.setSignalStatus(sceneryDataStatus);
this.indicator.status = sceneryDataStatus; this.indicator.status = sceneryDataStatus;
this.indicator.message = 'data-status.S1a-sceneries'; this.indicator.message = 'data-status.S1a-sceneries';
return; return;
} }
if (trainsDataStatus == DataStatus.Warning) { if (trainsDataStatus == Status.Data.Warning) {
this.setSignalStatus(trainsDataStatus); this.setSignalStatus(trainsDataStatus);
this.indicator.status = trainsDataStatus; this.indicator.status = trainsDataStatus;
this.indicator.message = 'data-status.S5-trains'; this.indicator.message = 'data-status.S5-trains';
return; return;
} }
if (dispatcherDataStatus == DataStatus.Warning) { if (dispatcherDataStatus == Status.Data.Warning) {
this.setSignalStatus(dispatcherDataStatus); this.setSignalStatus(dispatcherDataStatus);
this.indicator.status = dispatcherDataStatus; this.indicator.status = dispatcherDataStatus;
this.indicator.message = 'data-status.S5-dispatchers'; this.indicator.message = 'data-status.S5-dispatchers';
return; return;
} }
if (sceneryDataStatus == DataStatus.Loaded) { if (sceneryDataStatus == Status.Data.Loaded) {
this.setSignalStatus(DataStatus.Loaded); this.setSignalStatus(Status.Data.Loaded);
this.indicator.status = DataStatus.Loaded; this.indicator.status = Status.Data.Loaded;
this.indicator.message = 'data-status.S2'; this.indicator.message = 'data-status.S2';
} }
} }
@@ -285,31 +285,31 @@ export default defineComponent({
}, },
methods: { methods: {
setSignalStatus(status: DataStatus) { setSignalStatus(status: Status.Data) {
this.greenLight = false; this.greenLight = false;
this.greenBlinkLight = false; this.greenBlinkLight = false;
this.redTopLight = false; this.redTopLight = false;
this.orangeLight = false; this.orangeLight = false;
this.redBottomLight = false; this.redBottomLight = false;
if (status == DataStatus.Initialized) { if (status == Status.Data.Initialized) {
this.redTopLight = true; this.redTopLight = true;
} }
if (status == DataStatus.Loaded) { if (status == Status.Data.Loaded) {
this.greenLight = true; this.greenLight = true;
} }
if (status == DataStatus.Warning) { if (status == Status.Data.Warning) {
this.orangeLight = true; this.orangeLight = true;
} }
if (status == DataStatus.Error) { if (status == Status.Data.Error) {
this.redTopLight = true; this.redTopLight = true;
this.redBottomLight = true; this.redBottomLight = true;
} }
if (status == DataStatus.Loading) { if (status == Status.Data.Loading) {
this.greenBlinkLight = true; this.greenBlinkLight = true;
} }
} }
@@ -1,28 +1,23 @@
<template> <template>
<div class="select-box"> <div class="region-dropdown" v-click-outside="clickedOutside">
<div class="select-box_content"> <div class="content">
<button class="selected" @click="toggleBox"> <button class="selected-region" @click="toggleBox">
<span>{{ computedSelectedItem.selectedValue || computedSelectedItem.value }}</span> <span>{{ selectedItem.name }}</span>
<div class="arrow"> <img :src="`/images/icon-arrow-${listOpen ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
<img :src="`/images/icon-arrow-${listOpen ? 'asc' : 'desc'}.svg`" alt="Arrow icon" />
</div>
</button> </button>
<ul class="options" :ref="(el) => (listRef = el as Element)"> <ul class="options">
<li class="option" v-for="(item, i) in itemList" :key="item.id"> <li class="option" v-for="(item, i) in regionList" :key="item.id">
<transition <transition
name="unfold" name="unfold"
:style="` :style="`
--delay-in: ${i * 55}ms; --delay-in: ${i * 55}ms;
--delay-out: ${(itemList.length - 1 - i) * 55}ms`" --delay-out: ${(regionList.length - 1 - i) * 55}ms`"
> >
<label :for="item.id" v-if="listOpen"> <label :for="item.id" v-if="listOpen">
<input type="button" :id="item.id" name="select-box" @click="selectOption(item)" /> <input type="button" :id="item.id" name="select-box" @click="selectOption(item)" />
<span <span :style="selectedItem.id == item.id ? 'color: gold;' : ''" v-html="item.value">
:style="computedSelectedItem.id == item.id ? 'color: gold;' : ''"
v-html="item.value"
>
</span> </span>
</label> </label>
</transition> </transition>
@@ -33,79 +28,68 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, Ref, ref, computed } from 'vue'; import { defineComponent, Ref, ref } from 'vue';
import { regions as regionsJSON } from '../../data/options.json';
import { useStore } from '../../store/mainStore';
interface Item { interface Item {
id: string; id: string;
value: string; value: string;
selectedValue?: string; name: string;
} }
export default defineComponent({ export default defineComponent({
emits: ['selected'], data() {
return {
props: { store: useStore(),
itemList: { selectedItemIndex: 0,
type: Array as () => Item[], listOpen: false
required: true };
},
defaultItemIndex: {
type: Number,
default: 0
},
prefix: {
type: String,
default: ''
}
}, },
setup(props) { setup() {
let listRef: Ref<Element | null> = ref(null);
let buttonRef: Ref<HTMLButtonElement | null> = ref(null); let buttonRef: Ref<HTMLButtonElement | null> = ref(null);
let activeEl: Ref<Element | null> = ref(document.activeElement);
let listOpen = ref(false);
let selectedItem: Ref<Item> = ref(props.itemList[props.defaultItemIndex]);
const computedSelectedItem = computed(() => {
return (
props.itemList.find((item) => item.id === selectedItem.value.id) ||
props.itemList[props.defaultItemIndex]
);
});
return { return {
computedSelectedItem, buttonRef
listOpen,
selectedItem,
listRef,
buttonRef,
activeEl
}; };
}, },
watch: { watch: {
'$route.query': { 'store.region.id': {
immediate: true, handler(regionId) {
handler(newVal) { this.selectedItemIndex = this.regionList.findIndex((reg) => reg.id == regionId);
if (newVal.region) {
const item = this.itemList.find((it) => it.id == newVal.region);
if (item) this.selectedItem = item;
}
} }
} }
}, },
methods: { computed: {
selectOption(item: Item) { selectedItem() {
this.selectedItem = item; return this.regionList[this.selectedItemIndex] || null;
this.listOpen = false; },
this.$emit('selected', item); regionList() {
return regionsJSON.map((region) => {
const regionStationCount = this.store.onlineSceneryList.filter(
(scenery) => scenery.region == region.id
).length;
const regionTrainCount =
this.store.trainList.filter((train) => train.region == region.id).length || 0;
return {
id: region.id,
value: `${region.value} <div class='text--grayed'>${regionStationCount} / ${regionTrainCount}</div>`,
name: region.name
};
});
}
},
methods: {
selectOption(selectedRegion: Item) {
this.store.region = selectedRegion;
this.listOpen = false;
}, },
toggleBox(e: Event) { toggleBox(e: Event) {
@@ -125,40 +109,20 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/variables.scss'; @import '../../styles/variables.scss';
.unfold { .region-dropdown {
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(-10px) scale(0.85);
}
&-enter-active,
&-leave-active {
transition: all 110ms ease-out;
}
&-enter-active {
transition-delay: var(--delay-in);
}
&-leave-active {
transition-delay: var(--delay-out);
}
}
.select-box {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
} }
.arrow { button img {
img { vertical-align: middle;
vertical-align: middle; width: 1.35em;
width: 1.35em;
}
} }
button.selected { button.selected-region {
display: flex;
justify-content: space-between;
color: paleturquoise; color: paleturquoise;
font-weight: bold; font-weight: bold;
@@ -167,11 +131,16 @@ button.selected {
&:focus { &:focus {
background-color: #262626; background-color: #262626;
} }
span {
margin-right: 10px;
}
} }
.select-box_content { .content {
position: relative; position: relative;
margin: 0 auto; margin: 0 auto;
font-weight: bold;
height: 100%; height: 100%;
@@ -232,4 +201,25 @@ li.option {
cursor: pointer; cursor: pointer;
} }
} }
.unfold {
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(-10px) scale(0.85);
}
&-enter-active,
&-leave-active {
transition: all 110ms ease-out;
}
&-enter-active {
transition-delay: var(--delay-in);
}
&-leave-active {
transition-delay: var(--delay-out);
}
}
</style> </style>
+51 -15
View File
@@ -1,7 +1,11 @@
<template> <template>
<span class="status-badge" :class="statusID" v-if="isOnline"> <span class="status-badge" :class="statusName" v-if="isOnline">
{{ $t(`status.${statusID}`) }} {{ $t(`status.${statusName}`) }}
{{ statusID == 'online' ? timestampToString(statusTimestamp!) : '' }} {{
statusName == 'online' && dispatcherStatus && dispatcherStatus > 5
? timestampToString(dispatcherStatus)
: ''
}}
</span> </span>
<span class="status-badge free" v-else> <span class="status-badge free" v-else>
@@ -10,22 +14,53 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
props: { props: {
statusID: { dispatcherStatus: {
type: String type: Number as PropType<Status.ActiveDispatcher | number>
},
statusTimestamp: {
type: Number
}, },
isOnline: { isOnline: {
type: Boolean type: Boolean
} }
}, },
mixins: [dateMixin] mixins: [dateMixin],
computed: {
statusName() {
if (!this.dispatcherStatus) return 'free';
switch (this.dispatcherStatus) {
case Status.ActiveDispatcher.AFK:
return 'afk';
case Status.ActiveDispatcher.ENDING:
return 'ending';
case Status.ActiveDispatcher.INVALID:
return 'invalid';
case Status.ActiveDispatcher.NOT_LOGGED_IN:
return 'not-signed';
case Status.ActiveDispatcher.NO_SPACE:
return 'no-space';
case Status.ActiveDispatcher.UNAVAILABLE:
return 'unavailable';
case Status.ActiveDispatcher.UNKNOWN:
return 'unknown';
default:
if (this.dispatcherStatus >= Date.now() + 25500000) return 'no-limit';
return 'online';
}
}
}
}); });
</script> </script>
@@ -34,10 +69,10 @@ $free: #8a8a8a;
$ending: #e6c300; $ending: #e6c300;
$no-limit: #117fc9; $no-limit: #117fc9;
$unav: #ff3d5d; $unav: #ff3d5d;
$brb: #e6a100; $afk: #e6a100;
$no-space: #222; $no-space: #222;
$online: #09a116; $online: #09a116;
$unknown: rgb(185, 60, 60); $unknown: #b93c3c;
.status-badge { .status-badge {
border-radius: 1rem; border-radius: 1rem;
@@ -69,8 +104,8 @@ $unknown: rgb(185, 60, 60);
font-size: 0.85em; font-size: 0.85em;
} }
&.brb { &.afk {
background-color: $brb; background-color: $afk;
color: black; color: black;
font-size: 0.95em; font-size: 0.95em;
} }
@@ -82,7 +117,8 @@ $unknown: rgb(185, 60, 60);
font-size: 0.85em; font-size: 0.85em;
} }
&.unknown { &.unknown,
&.invalid {
background-color: $unknown; background-color: $unknown;
font-size: 0.95em; font-size: 0.95em;
} }
+3 -3
View File
@@ -50,8 +50,8 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import { RollingStockInfo } from '../../scripts/interfaces/github_api/StockInfoGithubData'; import { API } from '../../typings/api';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -71,7 +71,7 @@ export default defineComponent({
onImageError(event: Event, stockName: string) { onImageError(event: Event, stockName: string) {
const fallbackName = const fallbackName =
Object.keys(this.store.rollingStockData!.info).find((type) => { Object.keys(this.store.rollingStockData!.info).find((type) => {
return this.store.rollingStockData!.info[type as keyof RollingStockInfo].find( return this.store.rollingStockData!.info[type as keyof API.RollingStock.Info].find(
(v) => v[0] === stockName.split(':')[0] (v) => v[0] === stockName.split(':')[0]
); );
}) || 'vehicle-unknown'; }) || 'vehicle-unknown';
+3 -3
View File
@@ -51,16 +51,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import TrainStop from '../../scripts/interfaces/TrainStop'; import { TrainStop } from '../../store/typings';
export default defineComponent({ export default defineComponent({
mixins: [dateMixin], mixins: [dateMixin],
props: { props: {
stop: { stop: {
type: Object as () => TrainStop, type: Object as PropType<TrainStop>,
required: true required: true
} }
}, },
-9
View File
@@ -16,7 +16,6 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import trainInfoMixin from '../../mixins/trainInfoMixin'; import trainInfoMixin from '../../mixins/trainInfoMixin';
import { useStore } from '../../store/store';
import TrainInfo from '../TrainsView/TrainInfo.vue'; import TrainInfo from '../TrainsView/TrainInfo.vue';
import TrainSchedule from '../TrainsView/TrainSchedule.vue'; import TrainSchedule from '../TrainsView/TrainSchedule.vue';
@@ -30,14 +29,6 @@ export default defineComponent({
}; };
}, },
setup() {
const store = useStore();
return {
store
};
},
activated() { activated() {
const contentEl = this.$refs['content'] as HTMLElement; const contentEl = this.$refs['content'] as HTMLElement;
+3 -3
View File
@@ -16,8 +16,8 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import { RollingStockInfo } from '../../scripts/interfaces/github_api/StockInfoGithubData'; import { API } from '../../typings/api';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -54,7 +54,7 @@ export default defineComponent({
return ( return (
Object.keys(this.store.rollingStockData.info).find((type) => { Object.keys(this.store.rollingStockData.info).find((type) => {
return this.store.rollingStockData?.info[type as keyof RollingStockInfo].find( return this.store.rollingStockData?.info[type as keyof API.RollingStock.Info].find(
(v) => v[0] === this.name.split(':')[0] (v) => v[0] === this.name.split(':')[0]
); );
}) || 'vehicle-unknown' }) || 'vehicle-unknown'
+33 -42
View File
@@ -1,7 +1,7 @@
<template> <template>
<section class="daily-stats"> <section class="daily-stats">
<span :data-active="statsStatus"> <span :data-active="statsStatus">
<b v-if="statsStatus == DataStatus.Loading"> <b v-if="statsStatus == Status.Data.Loading">
{{ $t('app.loading') }} {{ $t('app.loading') }}
</b> </b>
@@ -32,24 +32,26 @@
</i18n-t> </i18n-t>
</div> </div>
<div v-if="stats.timetableId"> <div v-if="stats.maxTimetable">
&bull; &bull;
<i18n-t keypath="journal.timetable-stats-longest"> <i18n-t keypath="journal.timetable-stats-longest">
<template #id> <template #id>
<router-link :to="`/journal/timetables?timetableId=${stats.timetableId}`"> <router-link :to="`/journal/timetables?timetableId=${stats.maxTimetable.id}`">
<b>{{ stats.timetableId }}</b> <b>{{ stats.maxTimetable.id }}</b>
</router-link> </router-link>
</template> </template>
<template #author> <template #author>
<router-link :to="`/journal/dispatchers?dispatcherName=${stats.timetableAuthor}`"> <router-link
<b>{{ stats.timetableAuthor }}</b> :to="`/journal/dispatchers?dispatcherName=${stats.maxTimetable.authorName}`"
>
<b>{{ stats.maxTimetable.authorName }}</b>
</router-link> </router-link>
</template> </template>
<template #driver> <template #driver>
<b class="text--primary">{{ stats.timetableDriver }}</b> <b class="text--primary">{{ stats.maxTimetable.driverName }}</b>
</template> </template>
<template #distance> <template #distance>
<b class="text--primary">{{ stats.timetableRouteDistance }} km</b> <b class="text--primary">{{ stats.maxTimetable.routeDistance }} km</b>
</template> </template>
</i18n-t> </i18n-t>
</div> </div>
@@ -134,12 +136,10 @@
import axios from 'axios'; import axios from 'axios';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import {
ITimetablesDailyStats,
ITimetablesDailyStatsResponse
} from '../../scripts/interfaces/api/StatsAPIData';
import { URLs } from '../../scripts/utils/apiURLs'; import { URLs } from '../../scripts/utils/apiURLs';
import { API } from '../../typings/api';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
mixins: [dateMixin], mixins: [dateMixin],
@@ -147,22 +147,11 @@ export default defineComponent({
data() { data() {
return { return {
DataStatus, Status,
statsStatus: DataStatus.Loading, statsStatus: Status.Data.Loading,
intervalId: -1, intervalId: -1,
stats: { stats: {} as API.DailyStats.Response
totalTimetables: 0,
distanceSum: 0,
distanceAvg: 0,
timetableAuthor: '',
timetableDriver: '',
timetableId: 0,
timetableRouteDistance: 0,
longestDuties: [],
mostActiveDrivers: [],
mostActiveDispatchers: []
} as ITimetablesDailyStats
}; };
}, },
@@ -187,28 +176,30 @@ export default defineComponent({
methods: { methods: {
async fetchDailyTimetableStats() { async fetchDailyTimetableStats() {
try { try {
const res: ITimetablesDailyStatsResponse = await ( const res: API.DailyStats.Response = await (
await axios.get(`${URLs.stacjownikAPI}/api/getDailyTimetableStats`) await axios.get(`${URLs.stacjownikAPI}/api/getDailyTimetableStats`)
).data; ).data;
this.stats = { // this.stats = {
totalTimetables: res.totalTimetables, // totalTimetables: res.totalTimetables,
distanceSum: res.distanceSum, // distanceSum: res.distanceSum,
distanceAvg: res.distanceAvg, // distanceAvg: res.distanceAvg,
timetableAuthor: res.maxTimetable?.authorName || '', // // timetableAuthor: res.maxTimetable?.authorName || '',
timetableDriver: res.maxTimetable?.driverName || '', // // timetableDriver: res.maxTimetable?.driverName || '',
timetableId: res.maxTimetable?.id || 0, // // timetableId: res.maxTimetable?.id || 0,
timetableRouteDistance: res.maxTimetable?.routeDistance || 0, // // timetableRouteDistance: res.maxTimetable?.routeDistance || 0,
mostActiveDispatchers: res.mostActiveDispatchers, // mostActiveDispatchers: res.mostActiveDispatchers,
mostActiveDrivers: res.mostActiveDrivers, // mostActiveDrivers: res.mostActiveDrivers,
longestDuties: res.longestDuties // longestDuties: res.longestDuties
}; // };
this.statsStatus = DataStatus.Loaded; this.stats = res;
this.statsStatus = Status.Data.Loaded;
} catch (error) { } catch (error) {
console.error('Ups! Wystąpił błąd podczas pobierania statystyk rozkładów jazdy...'); console.error('Ups! Wystąpił błąd podczas pobierania statystyk rozkładów jazdy...');
this.statsStatus = DataStatus.Error; this.statsStatus = Status.Data.Error;
} }
}, },
@@ -52,11 +52,10 @@
<script lang="ts"> <script lang="ts">
import axios from 'axios'; import axios from 'axios';
import { 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'; import { URLs } from '../../scripts/utils/apiURLs';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { API } from '../../typings/api';
export default defineComponent({ export default defineComponent({
components: { Loading }, components: { Loading },
@@ -73,7 +72,7 @@ export default defineComponent({
return { return {
cardVisible: false, cardVisible: false,
lastDispatcherName: '', lastDispatcherName: '',
timetables: [] as TimetableHistory[] timetables: [] as API.TimetableHistory.Response
}; };
}, },
@@ -90,13 +89,13 @@ export default defineComponent({
this.store.dispatcherStatsData = undefined; this.store.dispatcherStatsData = undefined;
} }
const statsData: DispatcherStatsAPIData = await ( const statsData: API.DispatcherStats.Response = await (
await axios.get( await axios.get(
`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}` `${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`
) )
).data; ).data;
const timetables: TimetableHistory[] = await ( const timetables: API.TimetableHistory.Response = await (
await axios.get( await axios.get(
`${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}` `${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}`
) )
@@ -6,9 +6,9 @@
{{ $t('app.offline') }} {{ $t('app.offline') }}
</div> </div>
<Loading v-else-if="dataStatus == DataStatus.Loading" /> <Loading v-else-if="dataStatus == Status.Data.Loading" />
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error"> <div v-else-if="dataStatus == Status.Data.Error" class="journal_warning error">
{{ $t('app.error') }} {{ $t('app.error') }}
</div> </div>
@@ -114,13 +114,13 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import { DataStatus } from '../../scripts/enums/DataStatus'; import { useStore } from '../../store/mainStore';
import { useStore } from '../../store/store';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { regions } from '../../data/options.json'; import { regions } from '../../data/options.json';
import AddDataButton from '../Global/AddDataButton.vue'; import AddDataButton from '../Global/AddDataButton.vue';
import { API } from '../../typings/api';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
components: { Loading, AddDataButton }, components: { Loading, AddDataButton },
@@ -129,7 +129,7 @@ export default defineComponent({
props: { props: {
dispatcherHistory: { dispatcherHistory: {
type: Array as PropType<DispatcherHistory[]>, type: Array as PropType<API.DispatcherHistory.Response>,
required: true required: true
}, },
scrollNoMoreData: { scrollNoMoreData: {
@@ -142,13 +142,13 @@ export default defineComponent({
type: Function as PropType<() => void> type: Function as PropType<() => void>
}, },
dataStatus: { dataStatus: {
type: Number as PropType<DataStatus> type: Number as PropType<Status.Data>
} }
}, },
data() { data() {
return { return {
DataStatus, Status,
store: useStore(), store: useStore(),
regions regions
}; };
@@ -166,7 +166,7 @@ export default defineComponent({
return acc; return acc;
}, },
[] as (DispatcherHistory | string)[] [] as (API.DispatcherHistory.Data | string)[]
); );
} }
}, },
@@ -43,10 +43,10 @@
</div> </div>
</span> </span>
<b v-else-if="store.driverStatsStatus == DataStatus.Loading">{{ <b v-else-if="store.driverStatsStatus == Status.Data.Loading">{{
$t('journal.stats-loading') $t('journal.stats-loading')
}}</b> }}</b>
<b v-else-if="store.driverStatsStatus == DataStatus.Error"> <b v-else-if="store.driverStatsStatus == Status.Data.Error">
{{ $t('journal.stats-error ') }} {{ $t('journal.stats-error ') }}
</b> </b>
<b v-else>{{ $t('journal.driver-stats-info') }}</b> <b v-else>{{ $t('journal.driver-stats-info') }}</b>
@@ -55,14 +55,14 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { DataStatus } from '../../scripts/enums/DataStatus'; import { useStore } from '../../store/mainStore';
import { useStore } from '../../store/store'; import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
data() { data() {
return { return {
store: useStore(), store: useStore(),
DataStatus Status: Status
}; };
} }
}); });
+15 -17
View File
@@ -113,12 +113,11 @@
import axios from 'axios'; import axios from 'axios';
import { defineComponent, inject, PropType } from 'vue'; import { defineComponent, inject, PropType } from 'vue';
import keyMixin from '../../mixins/keyMixin'; 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 { URLs } from '../../scripts/utils/apiURLs';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import { JournalFilterSection } from '../../scripts/enums/JournalFilterType'; import { Journal } from './typings';
import { JournalFilter } from '../../scripts/types/JournalTimetablesTypes'; import { API } from '../../typings/api';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'], emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
@@ -131,13 +130,13 @@ export default defineComponent({
}, },
filters: { filters: {
type: Array as PropType<JournalFilter[]>, type: Array as PropType<Journal.TimetableFilter[]>,
default: () => [] default: () => []
}, },
dataStatus: { dataStatus: {
type: Number as PropType<DataStatus>, type: Number as PropType<Status.Data>,
default: DataStatus.Initialized default: -1
}, },
currentOptionsActive: { currentOptionsActive: {
@@ -154,7 +153,6 @@ export default defineComponent({
data() { data() {
return { return {
showOptions: false, showOptions: false,
JournalFilterSection,
driverSuggestions: [] as string[], driverSuggestions: [] as string[],
dispatcherSuggestions: [] as string[], dispatcherSuggestions: [] as string[],
@@ -162,7 +160,7 @@ export default defineComponent({
searchTimeout: 0, searchTimeout: 0,
store: useStore(), store: useStore(),
DataStatus JournalFilterSection: Journal.FilterSection
}; };
}, },
@@ -170,7 +168,7 @@ export default defineComponent({
return { return {
searchersValues: inject('searchersValues') as { [key: string]: string }, searchersValues: inject('searchersValues') as { [key: string]: string },
sorterActive: inject('sorterActive') as { id: string | number; dir: number }, sorterActive: inject('sorterActive') as { id: string | number; dir: number },
filterList: inject('filterList') as JournalFilter[] | undefined filterList: inject('filterList') as Journal.TimetableFilter[] | undefined
}; };
}, },
@@ -212,23 +210,23 @@ export default defineComponent({
this.store.driverStatsData = undefined; this.store.driverStatsData = undefined;
if (!this.store.driverStatsName) { if (!this.store.driverStatsName) {
this.store.driverStatsStatus = DataStatus.Initialized; this.store.driverStatsStatus = Status.Data.Initialized;
return; return;
} }
try { try {
this.store.driverStatsStatus = DataStatus.Loading; this.store.driverStatsStatus = Status.Data.Loading;
const statsData: DriverStatsAPIData = await ( const statsData: API.DriverStats.Response = await (
await axios.get( await axios.get(
`${URLs.stacjownikAPI}/api/getDriverInfo?name=${this.store.driverStatsName}` `${URLs.stacjownikAPI}/api/getDriverInfo?name=${this.store.driverStatsName}`
) )
).data; ).data;
this.store.driverStatsData = statsData; this.store.driverStatsData = statsData;
this.store.driverStatsStatus = DataStatus.Loaded; this.store.driverStatsStatus = Status.Data.Loaded;
} catch (error) { } catch (error) {
this.store.driverStatsStatus = DataStatus.Error; this.store.driverStatsStatus = Status.Data.Error;
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/'); console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/');
} }
}, },
@@ -270,7 +268,7 @@ export default defineComponent({
this.$emit('onSearchConfirm'); this.$emit('onSearchConfirm');
}, },
onFilterChange(filter: JournalFilter) { onFilterChange(filter: Journal.TimetableFilter) {
// this.journalFilterActive = filter; // this.journalFilterActive = filter;
this.filterList this.filterList
?.filter((f) => f.filterSection === filter.filterSection) ?.filter((f) => f.filterSection === filter.filterSection)
+4 -3
View File
@@ -29,10 +29,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, reactive, Ref, ref, watch } from 'vue'; import { computed, onMounted, reactive, Ref, ref, watch } from 'vue';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import JournalDailyStats from './DailyStats.vue'; import JournalDailyStats from './DailyStats.vue';
import JournalDriverStats from './JournalDriverStats.vue'; import JournalDriverStats from './JournalDriverStats.vue';
import StorageManager from '../../scripts/managers/storageManager'; import StorageManager from '../../managers/storageManager';
// Types // Types
type TStatTab = 'daily' | 'driver'; type TStatTab = 'daily' | 'driver';
@@ -60,7 +60,8 @@ let data = reactive({
// Methods // Methods
function onTabButtonClick(tab: TStatTab) { function onTabButtonClick(tab: TStatTab) {
if (lastClickedTab.value == tab || !lastClickedTab.value || !areStatsOpen.value) areStatsOpen.value = !areStatsOpen.value; if (lastClickedTab.value == tab || !lastClickedTab.value || !areStatsOpen.value)
areStatsOpen.value = !areStatsOpen.value;
if (tab == 'daily') { if (tab == 'daily') {
StorageManager.setBooleanValue('dailyStatsOpen', areStatsOpen.value); StorageManager.setBooleanValue('dailyStatsOpen', areStatsOpen.value);
@@ -6,9 +6,9 @@
{{ $t('app.offline') }} {{ $t('app.offline') }}
</div> </div>
<Loading v-else-if="dataStatus == DataStatus.Loading" /> <Loading v-else-if="dataStatus == Status.Data.Loading" />
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error"> <div v-else-if="dataStatus == Status.Data.Error" class="journal_warning error">
{{ $t('app.error') }} {{ $t('app.error') }}
</div> </div>
@@ -38,20 +38,20 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType } from 'vue'; 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 Loading from '../../Global/Loading.vue';
import AddDataButton from '../../Global/AddDataButton.vue'; import AddDataButton from '../../Global/AddDataButton.vue';
import TimetableHistoryList from './TimetableHistoryList.vue'; import TimetableHistoryList from './TimetableHistoryList.vue';
import { useStore } from '../../../store/mainStore';
import { Status } from '../../../typings/common';
import { API } from '../../../typings/api';
export default defineComponent({ export default defineComponent({
components: { Loading, AddDataButton, TimetableHistoryList }, components: { Loading, AddDataButton, TimetableHistoryList },
props: { props: {
timetableHistory: { timetableHistory: {
type: Array as PropType<TimetableHistory[]>, type: Array as PropType<API.TimetableHistory.Response>,
required: true required: true
}, },
scrollNoMoreData: { scrollNoMoreData: {
@@ -64,13 +64,13 @@ export default defineComponent({
type: Function as PropType<() => void> type: Function as PropType<() => void>
}, },
dataStatus: { dataStatus: {
type: Number as PropType<DataStatus> type: Number as PropType<Status.Data>
} }
}, },
data() { data() {
return { return {
DataStatus, Status,
store: useStore() store: useStore()
}; };
} }
@@ -77,8 +77,8 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import StockList from '../../Global/StockList.vue'; import StockList from '../../Global/StockList.vue';
import { API } from '../../../typings/api';
export default defineComponent({ export default defineComponent({
components: { StockList }, components: { StockList },
@@ -88,7 +88,7 @@ export default defineComponent({
required: true required: true
}, },
timetable: { timetable: {
type: Object as PropType<TimetableHistory>, type: Object as PropType<API.TimetableHistory.Data>,
required: true required: true
} }
}, },
@@ -62,24 +62,24 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import dateMixin from '../../../mixins/dateMixin'; import dateMixin from '../../../mixins/dateMixin';
import modalTrainMixin from '../../../mixins/modalTrainMixin'; import modalTrainMixin from '../../../mixins/modalTrainMixin';
import styleMixin from '../../../mixins/styleMixin'; import styleMixin from '../../../mixins/styleMixin';
import { API } from '../../../typings/api';
export default defineComponent({ export default defineComponent({
mixins: [dateMixin, modalTrainMixin, styleMixin], mixins: [dateMixin, modalTrainMixin, styleMixin],
props: { props: {
timetable: { timetable: {
type: Object as PropType<TimetableHistory>, type: Object as PropType<API.TimetableHistory.Data>,
required: true required: true
} }
}, },
methods: { methods: {
showTimetable(timetable: TimetableHistory, target: EventTarget | null) { showTimetable(timetable: API.TimetableHistory.Data, target: EventTarget | null) {
if (timetable?.terminated) return; if (timetable?.terminated) return;
this.selectModalTrain(timetable.driverName + timetable.trainNo.toString(), target); this.selectModalTrain(timetable.driverName + timetable.trainNo.toString(), target);
@@ -38,17 +38,17 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent, ref } from 'vue'; import { PropType, defineComponent, ref } from 'vue';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import TimetableGeneral from './TimetableGeneral.vue'; import TimetableGeneral from './TimetableGeneral.vue';
import TimetableStops from './TimetableStops.vue'; import TimetableStops from './TimetableStops.vue';
import TimetableStatus from './TimetableStatus.vue'; import TimetableStatus from './TimetableStatus.vue';
import TimetableExtra from './TimetableExtra.vue'; import TimetableExtra from './TimetableExtra.vue';
import { API } from '../../../typings/api';
export default defineComponent({ export default defineComponent({
props: { props: {
timetableHistory: { timetableHistory: {
type: Array as PropType<TimetableHistory[]>, type: Array as PropType<API.TimetableHistory.Response>,
required: true required: true
} }
}, },
@@ -44,14 +44,14 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
import ProgressBar from '../../Global/ProgressBar.vue'; import ProgressBar from '../../Global/ProgressBar.vue';
import { API } from '../../../typings/api';
export default defineComponent({ export default defineComponent({
components: { ProgressBar }, components: { ProgressBar },
props: { props: {
timetable: { timetable: {
type: Object as PropType<TimetableHistory>, type: Object as PropType<API.TimetableHistory.Data>,
required: true required: true
} }
} }
@@ -24,8 +24,7 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import dateMixin from '../../../mixins/dateMixin'; import dateMixin from '../../../mixins/dateMixin';
import { API } from '../../../typings/api';
import { TimetableHistory } from '../../../scripts/interfaces/api/TimetablesAPIData';
export default defineComponent({ export default defineComponent({
mixins: [dateMixin], mixins: [dateMixin],
@@ -37,7 +36,7 @@ export default defineComponent({
}, },
timetable: { timetable: {
type: Object as PropType<TimetableHistory>, type: Object as PropType<API.TimetableHistory.Data>,
required: true required: true
} }
}, },
+49
View File
@@ -0,0 +1,49 @@
export namespace Journal {
export type DispatcherSearcher = {
[key in 'search-dispatcher' | 'search-station' | 'search-date']: string;
};
export interface DispatcherSorter {
id: 'timestampFrom' | 'duration';
dir: -1 | 1;
}
export type TimetableSearchKey =
| 'search-driver'
| 'search-train'
| 'search-date'
| 'search-dispatcher'
| 'search-issuedFrom';
export type TimetableSearchType = {
[key in TimetableSearchKey]: string;
};
export const enum TimetableFilterId {
ACTIVE = 'active',
FULFILLED = 'fulfilled',
ABANDONED = 'abandoned',
ALL = 'all',
TWR = 'twr',
SKR = 'skr',
TWR_SKR = 'twr-skr'
}
export enum FilterSection {
TIMETABLE_STATUS = 'timetable-status',
TWRSKR = 'twrskr'
}
export interface TimetableFilter {
id: TimetableFilterId;
filterSection: string;
isActive: boolean;
}
export type TimetableSorterKey = 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
export interface TimetableSorter {
id: TimetableSorterKey;
dir: 'asc' | 'desc';
}
}
@@ -69,14 +69,14 @@
import axios from 'axios'; import axios from 'axios';
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs'; import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import listObserverMixin from '../../mixins/listObserverMixin'; import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../store/typings';
import { API } from '../../typings/api';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
name: 'SceneryDispatchersHistory', name: 'SceneryDispatchersHistory',
@@ -95,9 +95,9 @@ export default defineComponent({
data() { data() {
return { return {
historyList: [] as DispatcherHistory[], historyList: [] as API.DispatcherHistory.Response,
dataStatus: DataStatus.Loading, dataStatus: Status.Data.Loading,
DataStatus DataStatus: Status.Data
}; };
}, },
@@ -109,17 +109,22 @@ export default defineComponent({
}, },
methods: { methods: {
async fetchAPIData(countFrom = 0, countLimit = 30): Promise<DispatcherHistory[] | null> { async fetchAPIData(
countFrom = 0,
countLimit = 30
): Promise<API.DispatcherHistory.Response | null> {
try { try {
this.dataStatus = DataStatus.Loading; this.dataStatus = Status.Data.Loading;
const requestString = `${URLs.stacjownikAPI}/api/getDispatchers?stationName=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`; const requestString = `${URLs.stacjownikAPI}/api/getDispatchers?stationName=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
const historyAPIData: DispatcherHistory[] = await (await axios.get(requestString)).data; const historyAPIData: API.DispatcherHistory.Response = await (
await axios.get(requestString)
).data;
this.dataStatus = DataStatus.Loaded; this.dataStatus = Status.Data.Loaded;
return historyAPIData; return historyAPIData;
} catch (error) { } catch (error) {
this.dataStatus = DataStatus.Error; this.dataStatus = Status.Data.Error;
console.error(error); console.error(error);
return null; return null;
} }
@@ -153,3 +158,4 @@ export default defineComponent({
} }
} }
</style> </style>
../../store/storeTypes
+2 -1
View File
@@ -15,7 +15,7 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../store/typings';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -58,3 +58,4 @@ export default defineComponent({
font-size: 1.2em; font-size: 1.2em;
} }
</style> </style>
../../store/storeTypes
+2 -2
View File
@@ -90,7 +90,7 @@ import SceneryInfoUserList from './SceneryInfo/SceneryInfoUserList.vue';
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue'; import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue'; import SceneryInfoRoutes from './SceneryInfo/SceneryInfoRoutes.vue';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../store/typings';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -110,7 +110,7 @@ export default defineComponent({
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>,
required: false required: false
} }
}, }
}); });
</script> </script>
@@ -22,9 +22,8 @@
</div> </div>
<StationStatusBadge <StationStatusBadge
:statusID="onlineScenery?.statusID"
:isOnline="onlineScenery ? true : false" :isOnline="onlineScenery ? true : false"
:statusTimestamp="onlineScenery?.statusTimestamp" :dispatcherStatus="onlineScenery?.dispatcherStatus"
/> />
</section> </section>
</template> </template>
@@ -35,7 +34,7 @@ import dateMixin from '../../../mixins/dateMixin';
import routerMixin from '../../../mixins/routerMixin'; import routerMixin from '../../../mixins/routerMixin';
import styleMixin from '../../../mixins/styleMixin'; import styleMixin from '../../../mixins/styleMixin';
import StationStatusBadge from '../../Global/StationStatusBadge.vue'; import StationStatusBadge from '../../Global/StationStatusBadge.vue';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../../store/typings';
export default defineComponent({ export default defineComponent({
mixins: [styleMixin, dateMixin, routerMixin], mixins: [styleMixin, dateMixin, routerMixin],
@@ -7,7 +7,11 @@
</h3> </h3>
<transition-group name="spawns-anim" tag="ul"> <transition-group name="spawns-anim" tag="ul">
<li class="badge spawn badge-none" v-if="!onlineScenery || onlineScenery.spawns.length == 0" key="no-spawns"> <li
class="badge spawn badge-none"
v-if="!onlineScenery || onlineScenery.spawns.length == 0"
key="no-spawns"
>
{{ $t('scenery.no-spawns') }} {{ $t('scenery.no-spawns') }}
</li> </li>
@@ -26,7 +30,7 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../../store/typings';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -32,7 +32,7 @@
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import modalTrainMixin from '../../../mixins/modalTrainMixin'; import modalTrainMixin from '../../../mixins/modalTrainMixin';
import routerMixin from '../../../mixins/routerMixin'; import routerMixin from '../../../mixins/routerMixin';
import { OnlineScenery } from '../../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../../store/typings';
export default defineComponent({ export default defineComponent({
mixins: [routerMixin, modalTrainMixin], mixins: [routerMixin, modalTrainMixin],
@@ -185,10 +185,10 @@ import Loading from '../Global/Loading.vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import routerMixin from '../../mixins/routerMixin'; import routerMixin from '../../mixins/routerMixin';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import ScheduledTrainStatus from './ScheduledTrainStatus.vue'; import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../store/typings';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetable', name: 'SceneryTimetable',
@@ -1,6 +1,16 @@
<template> <template>
<!-- WIP -->
<!-- <div class="top-filters">
<button class="btn btn--option">ROZPOCZYNA BIEG</button>
<button class="btn btn--option">PRZEZ</button>
<button class="btn btn--option">KOŃCZY BIEG</button>
</div> -->
<section class="scenery-table-section"> <section class="scenery-table-section">
<Loading v-if="dataStatus != DataStatus.Loaded" /> <Loading v-if="dataStatus != DataStatus.Loaded" />
<div class="no-history" v-else-if="historyList.length == 0"> <div class="no-history" v-else-if="historyList.length == 0">
{{ $t('scenery.history-list-empty') }} {{ $t('scenery.history-list-empty') }}
</div> </div>
@@ -18,9 +28,9 @@
<tbody> <tbody>
<tr v-for="historyItem in historyList" :key="historyItem.id"> <tr v-for="historyItem in historyList" :key="historyItem.id">
<td> <td>
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`" <router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">
>#{{ historyItem.id }}</router-link #{{ historyItem.id }}
> </router-link>
</td> </td>
<td> <td>
<b class="text--primary">{{ historyItem.trainCategoryCode }}</b> <br /> <b class="text--primary">{{ historyItem.trainCategoryCode }}</b> <br />
@@ -56,16 +66,14 @@
import axios from 'axios'; import axios from 'axios';
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import {
TimetableHistory,
SceneryTimetableHistory
} from '../../scripts/interfaces/api/TimetablesAPIData';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs'; import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import listObserverMixin from '../../mixins/listObserverMixin'; import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../scripts/interfaces/store/storeTypes'; import { OnlineScenery } from '../../store/typings';
import { API } from '../../typings/api';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetablesHistory', name: 'SceneryTimetablesHistory',
@@ -83,28 +91,28 @@ export default defineComponent({
data() { data() {
return { return {
historyList: [] as TimetableHistory[], historyList: [] as API.TimetableHistory.Response,
dataStatus: DataStatus.Loading, dataStatus: Status.Data.Loading,
DataStatus DataStatus: Status.Data
}; };
}, },
async activated() { async activated() {
const fetchedHistory = await this.fetchAPIData(); this.fetchAPIData();
if (fetchedHistory) this.historyList = fetchedHistory.timetables;
}, },
methods: { methods: {
async fetchAPIData(countFrom = 0, countLimit = 15): Promise<SceneryTimetableHistory | null> { async fetchAPIData(countFrom = 0, countLimit = 15) {
try { try {
const requestString = `${URLs.stacjownikAPI}/api/getIssuedTimetables?name=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`; const requestString = `${URLs.stacjownikAPI}/api/getTimetables?issuedFrom=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
const historyAPIData: SceneryTimetableHistory = await (await axios.get(requestString)).data;
this.dataStatus = DataStatus.Loaded; const response: API.TimetableHistory.Response = await (await axios.get(requestString)).data;
return historyAPIData;
this.historyList = response;
this.dataStatus = Status.Data.Loaded;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
}, },
@@ -119,4 +127,14 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/sceneryViewTables.scss'; @import '../../styles/sceneryViewTables.scss';
.top-filters {
display: flex;
justify-content: center;
gap: 0.5em;
button {
padding: 0.5em;
}
}
</style> </style>
@@ -11,7 +11,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import { ScheduledTrain, StopStatus } from '../../scripts/interfaces/ScheduledTrain'; import { ScheduledTrain, StopStatus } from '../../store/typings';
interface ScheduledTrainComp extends ScheduledTrain { interface ScheduledTrainComp extends ScheduledTrain {
stopStatusIndicator: string; stopStatusIndicator: string;
@@ -42,7 +42,7 @@ export default defineComponent({
stopStatusIndicator = ''; stopStatusIndicator = '';
switch (stopStatus) { switch (stopStatus) {
case StopStatus.arriving: case StopStatus.ARRIVING:
stopStatusIndicator = `${this.$t('timetables.from')}: ${prevDepartureIndicator}`; stopStatusIndicator = `${this.$t('timetables.from')}: ${prevDepartureIndicator}`;
stopStatusDescription = this.$t('timetables.desc-arriving', { stopStatusDescription = this.$t('timetables.desc-arriving', {
prevStationName, prevStationName,
@@ -50,8 +50,8 @@ export default defineComponent({
}); });
break; break;
case StopStatus.online: case StopStatus.ONLINE:
case StopStatus.stopped: case StopStatus.STOPPED:
stopStatusIndicator = nextArrivalLine stopStatusIndicator = nextArrivalLine
? `${this.$t('timetables.to')}: ${nextArrivalIndicator}` ? `${this.$t('timetables.to')}: ${nextArrivalIndicator}`
: `${this.$t('timetables.desc-end')}`; : `${this.$t('timetables.desc-end')}`;
@@ -60,7 +60,7 @@ export default defineComponent({
: ''; : '';
break; break;
case StopStatus.departed: case StopStatus.DEPARTED:
stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`; stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`;
stopStatusDescription = this.$t('timetables.desc-departed', { stopStatusDescription = this.$t('timetables.desc-departed', {
nextStationName, nextStationName,
@@ -68,7 +68,7 @@ export default defineComponent({
}); });
break; break;
case StopStatus['departed-away']: case StopStatus.DEPARTED_AWAY:
stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`; stopStatusIndicator = `${this.$t('timetables.to')}: ${nextArrivalIndicator}`;
stopStatusDescription = this.$t('timetables.desc-departed-away', { stopStatusDescription = this.$t('timetables.desc-departed-away', {
nextStationName, nextStationName,
@@ -76,7 +76,7 @@ export default defineComponent({
}); });
break; break;
case StopStatus.terminated: case StopStatus.TERMINATED:
stopStatusIndicator = `X ${this.$t('timetables.desc-terminated')}`; stopStatusIndicator = `X ${this.$t('timetables.desc-terminated')}`;
stopStatusDescription = this.$t('timetables.desc-terminated'); stopStatusDescription = this.$t('timetables.desc-terminated');
break; break;
@@ -138,11 +138,11 @@
import { defineComponent, inject } from 'vue'; import { defineComponent, inject } from 'vue';
import keyMixin from '../../mixins/keyMixin'; import keyMixin from '../../mixins/keyMixin';
import routerMixin from '../../mixins/routerMixin'; import routerMixin from '../../mixins/routerMixin';
import StorageManager from '../../scripts/managers/storageManager';
import { useStationFiltersStore } from '../../store/stationFiltersStore'; import { useStationFiltersStore } from '../../store/stationFiltersStore';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import FilterOption from './FilterOption.vue'; import FilterOption from './FilterOption.vue';
import StorageManager from '../../managers/storageManager';
export default defineComponent({ export default defineComponent({
components: { FilterOption }, components: { FilterOption },
+7 -6
View File
@@ -108,9 +108,8 @@
<td class="station_status"> <td class="station_status">
<StationStatusBadge <StationStatusBadge
:statusID="station.onlineInfo?.statusID"
:isOnline="station.onlineInfo ? true : false" :isOnline="station.onlineInfo ? true : false"
:statusTimestamp="station.onlineInfo?.statusTimestamp" :dispatcherStatus="station.onlineInfo?.dispatcherStatus"
/> />
</td> </td>
@@ -280,13 +279,13 @@ import { defineComponent, computed, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import stationInfoMixin from '../../mixins/stationInfoMixin'; import stationInfoMixin from '../../mixins/stationInfoMixin';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import { DataStatus } from '../../scripts/enums/DataStatus';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { useStationFiltersStore } from '../../store/stationFiltersStore'; import { useStationFiltersStore } from '../../store/stationFiltersStore';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames'; import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames';
import StationStatusBadge from '../Global/StationStatusBadge.vue'; import StationStatusBadge from '../Global/StationStatusBadge.vue';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -316,8 +315,9 @@ export default defineComponent({
const stationFiltersStore = useStationFiltersStore(); const stationFiltersStore = useStationFiltersStore();
const isDataLoaded = computed(() => { const isDataLoaded = computed(() => {
return store.dataStatuses.sceneries != DataStatus.Loading; return store.dataStatuses.sceneries != Status.Data.Loading;
}); });
return { return {
isDataLoaded, isDataLoaded,
stationFiltersStore stationFiltersStore
@@ -388,7 +388,8 @@ section.station_table {
table { table {
white-space: nowrap; white-space: nowrap;
border-collapse: collapse; border-collapse: collapse;
min-width: 1350px; // min-width: 1350px;
width: 100%;
@include smallScreen() { @include smallScreen() {
min-width: auto; min-width: auto;
@@ -1,4 +1,11 @@
export default interface Filter { export interface FilterOption {
id: string;
name: string;
value: boolean;
defaultValue: boolean;
}
export interface Filter {
[key: string]: boolean | number | string; [key: string]: boolean | number | string;
default: boolean; default: boolean;
notDefault: boolean; notDefault: boolean;
+52 -67
View File
@@ -1,22 +1,22 @@
<template> <template>
<div class="train-info"> <div class="train-info">
<section class="train-route"> <section class="train-general">
<div class="train_general"> <div class="general-info">
<b class="warning-timeout" v-if="train.isTimeout" :title="$t('trains.timeout')">?</b> <b class="warning-timeout" v-if="train.isTimeout" :title="$t('trains.timeout')">?</b>
<span class="timetable-id" v-if="train.timetableData" <span class="timetable-id" v-if="train.timetableData">
>#{{ train.timetableData.timetableId }}</span #{{ train.timetableData.timetableId }}
> </span>
<span <span
class="timetable_warnings" class="timetable-warnings"
v-if="train.timetableData?.TWR || train.timetableData?.SKR" v-if="train.timetableData?.TWR || train.timetableData?.SKR"
> >
<span class="train-badge twr" v-if="train.timetableData?.TWR" :title="$t('general.TWR')" <span class="train-badge twr" v-if="train.timetableData?.TWR" :title="$t('general.TWR')">
>TWR</span TWR
> </span>
<span class="train-badge skr" v-if="train.timetableData?.SKR" :title="$t('general.SKR')" <span class="train-badge skr" v-if="train.timetableData?.SKR" :title="$t('general.SKR')">
>SKR</span SKR
> </span>
</span> </span>
<strong> <strong>
@@ -35,7 +35,7 @@
<span>{{ train.driverName }}</span> <span>{{ train.driverName }}</span>
</div> </div>
<div class="timetable_route" v-if="train.timetableData"> <div class="general-timetable" v-if="train.timetableData">
<strong>{{ train.timetableData.route.replace('|', ' - ') }}</strong> <strong>{{ train.timetableData.route.replace('|', ' - ') }}</strong>
<img <img
v-if="getSceneriesWithComments(train.timetableData).length > 0" v-if="getSceneriesWithComments(train.timetableData).length > 0"
@@ -49,27 +49,30 @@
<hr style="margin: 0.25em 0" /> <hr style="margin: 0.25em 0" />
<div class="timetable_stops" v-if="train.timetableData"> <div class="general-stops" v-if="train.timetableData">
<span v-if="train.timetableData.followingStops.length > 2"> <span v-if="train.timetableData.followingStops.length > 2">
{{ $t('trains.via-title') }} {{ $t('trains.via-title') }}
<span v-html="displayStopList(train.timetableData.followingStops)"></span> <span v-html="displayStopList(train.timetableData.followingStops)"></span>
</span> </span>
</div> </div>
<div class="timetable_progress" style="margin-top: 0.5em" v-if="train.timetableData"> <div class="general-status">
<ProgressBar :progressPercent="confirmedPercentage(train.timetableData.followingStops)" /> <div class="timetable-progress" v-if="train.timetableData">
<ProgressBar :progressPercent="confirmedPercentage(train.timetableData.followingStops)" />
<span class="timetable_progress-distance"> <span class="progress-distance">
&nbsp; {{ currentDistance(train.timetableData.followingStops) }} km / &nbsp; {{ currentDistance(train.timetableData.followingStops) }} km /
<span class="text--primary"> {{ train.timetableData.routeDistance }} km </span> <span class="text--primary"> {{ train.timetableData.routeDistance }} km </span>
| |
<span v-html="currentDelay(train.timetableData.followingStops)"></span> <span v-html="currentDelay(train.timetableData.followingStops)"></span>
</span> </span>
</div>
<div class="train-status-badges"> <div class="status-badges">
<div v-if="!train.currentStationHash" class="train-badge offline"> <div v-if="!train.currentStationHash" class="train-badge offline">
{{ $t('trains.scenery-offline') }} {{ $t('trains.scenery-offline') }}
</div> </div>
<div v-if="!train.online" class="train-badge offline"> <div v-if="!train.online" class="train-badge offline">
Offline {{ lastSeenMessage(train.lastSeen) }} Offline {{ lastSeenMessage(train.lastSeen) }}
</div> </div>
@@ -181,11 +184,17 @@ export default defineComponent({
padding: 0 0.25em; padding: 0 0.25em;
} }
.timetable_stops { .train-general {
font-size: 0.75em; display: flex;
flex-direction: column;
gap: 0.25em;
} }
.train_general { .general-stops {
font-size: 0.8em;
}
.general-info {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
@@ -193,55 +202,41 @@ export default defineComponent({
gap: 0.25em; gap: 0.25em;
margin-right: 1.5em; margin-right: 1.5em;
} }
.train-status-badges { .general-status {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.25em;
}
.status-badges {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.25em; gap: 0.25em;
} }
.train-driver { .general-timetable {
&.supporter {
color: orange;
text-shadow: orange 0 0 5px;
}
}
.timetable_route {
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 0.5em;
} }
.timetable_warnings { .timetable-warnings {
display: flex; display: flex;
gap: 0.25em; gap: 0.25em;
} }
.timetable_progress { .timetable-progress {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
.timetable_progress-distance { .progress-distance {
margin-right: 0.25em; margin-right: 0.25em;
} }
.comments {
display: flex;
align-items: center;
font-size: 0.9em;
margin-top: 1em;
img {
margin-right: 0.5em;
}
}
@include smallScreen() { @include smallScreen() {
.train-info { .train-info {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@@ -251,23 +246,13 @@ export default defineComponent({
font-size: 1.15em; font-size: 1.15em;
} }
.train-stats { .general-info,
font-size: 1.1em; .general-status,
} .general-timetable {
.train_general {
justify-content: center; justify-content: center;
} }
.train-status-badges { .timetable-progress {
justify-content: center;
}
.timetable_route {
justify-content: center;
}
.timetable_progress {
justify-content: center; justify-content: center;
} }
+11 -7
View File
@@ -23,7 +23,11 @@
v-model="searchedTrain" v-model="searchedTrain"
/> />
<button class="search-exit"> <button class="search-exit">
<img src="/images/icon-exit.svg" alt="Trains search clear icon" @click="onInputClear('train')" /> <img
src="/images/icon-exit.svg"
alt="Trains search clear icon"
@click="onInputClear('train')"
/>
</button> </button>
</div> </div>
@@ -36,7 +40,11 @@
v-model="searchedDriver" v-model="searchedDriver"
/> />
<button class="search-exit"> <button class="search-exit">
<img src="/images/icon-exit.svg" alt="Driver search clear icon" @click="onInputClear('driver')" /> <img
src="/images/icon-exit.svg"
alt="Driver search clear icon"
@click="onInputClear('driver')"
/>
</button> </button>
</div> </div>
</div> </div>
@@ -87,8 +95,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, inject, PropType } from 'vue'; import { defineComponent, inject, PropType } from 'vue';
import keyMixin from '../../mixins/keyMixin'; import keyMixin from '../../mixins/keyMixin';
import { TrainFilterSection } from '../../scripts/enums/TrainFilterType'; import { TrainFilter, TrainFilterSection } from './typings';
import { TrainFilter } from '../../scripts/interfaces/Trains/TrainFilter';
export default defineComponent({ export default defineComponent({
mixins: [keyMixin], mixins: [keyMixin],
@@ -152,9 +159,6 @@ export default defineComponent({
}, },
onFilterChange(filter: TrainFilter) { onFilterChange(filter: TrainFilter) {
// if (this.lastSelectedFilter?.id === filter.id)
// this.trainFilterList.forEach((tf) => (tf.isActive = filter.id === tf.id));
filter.isActive = !filter.isActive; filter.isActive = !filter.isActive;
this.lastSelectedFilter = filter; this.lastSelectedFilter = filter;
}, },
+2 -2
View File
@@ -72,10 +72,10 @@
import { computed, defineComponent, PropType } from 'vue'; import { computed, defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import Train from '../../scripts/interfaces/Train'; import Train from '../../scripts/interfaces/Train';
import TrainStop from '../../scripts/interfaces/TrainStop'; import { useStore } from '../../store/mainStore';
import { useStore } from '../../store/store';
import StopDate from '../Global/StopDate.vue'; import StopDate from '../Global/StopDate.vue';
import StockList from '../Global/StockList.vue'; import StockList from '../Global/StockList.vue';
import { TrainStop } from '../../store/typings';
export default defineComponent({ export default defineComponent({
components: { StopDate, StockList }, components: { StopDate, StockList },
+54 -138
View File
@@ -1,44 +1,44 @@
<template> <template>
<div class="train-table"> <transition name="status-anim" mode="out-in" tag="div" class="train-table">
<transition name="anim" mode="out-in"> <div :key="store.dataStatuses.trains">
<div :key="store.dataStatuses.trains"> <div class="table-info" key="offline" v-if="store.isOffline">
<div class="table-info" v-if="store.isOffline"> {{ $t('app.offline') }}
{{ $t('app.offline') }}
</div>
<Loading v-else-if="trains.length == 0 && store.dataStatuses.trains == 0" />
<div
class="table-info no-trains"
v-else-if="trains.length == 0 && store.dataStatuses.trains != 0"
>
{{ $t('trains.no-trains') }}
</div>
<transition-group name="list-anim" tag="ul" class="train-list" v-else>
<li
class="train-row"
v-for="train in currentTrains"
:key="train.trainId"
tabindex="0"
@click.stop="selectModalTrain(train.trainId, $event.currentTarget)"
@keydown.enter="selectModalTrain(train.trainId, $event.currentTarget)"
>
<TrainInfo :train="train" />
</li>
</transition-group>
</div> </div>
</transition>
</div> <Loading v-else-if="trains.length == 0 && store.dataStatuses.trains == 0" key="loading" />
<div
class="table-info"
key="no-trains"
v-else-if="trains.length == 0 && store.dataStatuses.trains != 0"
>
{{ $t('trains.no-trains') }}
</div>
<transition-group name="list-anim" tag="ul">
<li
class="train-row"
v-for="train in trains"
:key="train.trainId"
tabindex="0"
@click.stop="selectModalTrain(train.trainId, $event.currentTarget)"
@keydown.enter="selectModalTrain(train.trainId, $event.currentTarget)"
>
<TrainInfo :train="train" />
</li>
</transition-group>
</div>
</transition>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, inject, PropType, Ref } from 'vue'; import { defineComponent, inject, PropType, Ref } from 'vue';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import Train from '../../scripts/interfaces/Train'; import Train from '../../scripts/interfaces/Train';
import { useStore } from '../../store/store'; import { useStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import TrainInfo from './TrainInfo.vue'; import TrainInfo from './TrainInfo.vue';
import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
components: { Loading, TrainInfo }, components: { Loading, TrainInfo },
@@ -56,14 +56,10 @@ export default defineComponent({
const store = useStore(); const store = useStore();
const searchedTrain = inject('searchedTrain') as Ref<string>; const searchedTrain = inject('searchedTrain') as Ref<string>;
const searchedDriver = inject('searchedDriver') as Ref<string>; const searchedDriver = inject('searchedDriver') as Ref<string>;
const currentTrains = computed(() => {
return props.trains;
});
return { return {
searchedTrain, searchedTrain,
searchedDriver, searchedDriver,
currentTrains,
store, store,
sorterActive: inject('sorterActive') as { sorterActive: inject('sorterActive') as {
id: string | number; id: string | number;
@@ -72,6 +68,17 @@ export default defineComponent({
}; };
}, },
computed: {
dataStatus() {
if (this.store.isOffline) return Status.Data.Offline;
if (this.trains.length == 0 && this.store.dataStatuses.trains == Status.Data.Loading)
return Status.Data.Loading;
return Status.Data.Loaded;
}
},
activated() { activated() {
const query = this.$route.query; const query = this.$route.query;
if (query.trainNo && query.driverName) { if (query.trainNo && query.driverName) {
@@ -89,19 +96,13 @@ export default defineComponent({
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
@import '../../styles/animations.scss'; @import '../../styles/animations.scss';
.anim { .train-table {
&-enter-from, position: relative;
&-leave-to {
opacity: 0;
}
&-enter-active { height: 90vh;
transition: all 100ms ease-out; min-height: 550px;
} overflow-y: auto;
overflow-x: hidden;
&-leave-active {
transition: all 100ms ease-out;
}
} }
.table-info { .table-info {
@@ -114,96 +115,11 @@ export default defineComponent({
background: #1a1a1a; background: #1a1a1a;
} }
img.train-image { li.train-row {
width: 12em; background-color: var(--clr-secondary);
} margin-bottom: 1em;
width: 100%;
.traffic-warning { cursor: pointer;
padding: 1em 0;
margin-bottom: 0.5em;
background: var(--clr-warning);
}
.timeouts-warning {
background-color: #333;
font-weight: bold;
font-size: 1.05em;
margin-bottom: 0.5em;
padding: 0.5em;
}
.warning-timeout {
background-color: #be3728;
color: white;
display: inline-block;
text-align: center;
width: 1.25em;
height: 1.25em;
border-radius: 50%;
}
.train {
&-list {
position: relative;
@include smallScreen() {
width: 100%;
}
}
&-row {
background-color: var(--clr-secondary);
margin-bottom: 1em;
cursor: pointer;
}
&_cars {
display: flex;
align-items: center;
overflow: auto;
}
}
.paginator {
display: flex;
justify-content: center;
&_item {
padding: 0.25em 0.5em;
margin: 0 0.5em;
outline: 2px solid salmon;
min-width: 30px;
text-align: center;
cursor: pointer;
&.page-number {
font-weight: bold;
color: gold;
}
&.disabled {
outline: 2px solid lightgray;
color: lightgray;
}
&:focus {
outline: 2px solid white;
}
}
}
@include smallScreen() {
.info-bottom {
text-align: center;
}
} }
</style> </style>
@@ -1,63 +1,93 @@
import { TrainFilterSection, TrainFilterType } from '../../scripts/enums/TrainFilterType'; export enum TrainFilterSection {
import { TrainFilter } from '../../scripts/interfaces/Trains/TrainFilter'; TRAIN_TYPE = 'TRAIN_TYPE',
TIMETABLE_TYPE = 'TIMETABLE_TYPE',
COMMENTS = 'COMMENTS',
TIMETABLE = 'TIMETABLE'
}
export const enum TrainFilterId {
noComments = 'noComments',
withComments = 'withComments',
twr = 'twr',
skr = 'skr',
common = 'common',
passenger = 'passenger',
freight = 'freight',
other = 'other',
noTimetable = 'noTimetable',
withTimetable = 'withTimetable'
}
export interface TrainFilter {
id: TrainFilterId;
section: TrainFilterSection;
isActive: boolean;
}
export interface TrainSorter {
id: string;
value: string;
}
export const trainFilters: TrainFilter[] = [ export const trainFilters: TrainFilter[] = [
{ {
id: TrainFilterType.twr, id: TrainFilterId.twr,
section: TrainFilterSection.TRAIN_TYPE, section: TrainFilterSection.TRAIN_TYPE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.skr, id: TrainFilterId.skr,
section: TrainFilterSection.TRAIN_TYPE, section: TrainFilterSection.TRAIN_TYPE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.common, id: TrainFilterId.common,
section: TrainFilterSection.TRAIN_TYPE, section: TrainFilterSection.TRAIN_TYPE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.passenger, id: TrainFilterId.passenger,
section: TrainFilterSection.TIMETABLE_TYPE, section: TrainFilterSection.TIMETABLE_TYPE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.freight, id: TrainFilterId.freight,
section: TrainFilterSection.TIMETABLE_TYPE, section: TrainFilterSection.TIMETABLE_TYPE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.other, id: TrainFilterId.other,
section: TrainFilterSection.TIMETABLE_TYPE, section: TrainFilterSection.TIMETABLE_TYPE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.withComments, id: TrainFilterId.withComments,
section: TrainFilterSection.COMMENTS, section: TrainFilterSection.COMMENTS,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.noComments, id: TrainFilterId.noComments,
section: TrainFilterSection.COMMENTS, section: TrainFilterSection.COMMENTS,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.withTimetable, id: TrainFilterId.withTimetable,
section: TrainFilterSection.TIMETABLE, section: TrainFilterSection.TIMETABLE,
isActive: true isActive: true
}, },
{ {
id: TrainFilterType.noTimetable, id: TrainFilterId.noTimetable,
section: TrainFilterSection.TIMETABLE, section: TrainFilterSection.TIMETABLE,
isActive: true isActive: true
} }
]; ];
export const sorterOptions = [ export const sorterOptions: TrainSorter[] = [
{ {
id: 'distance', id: 'distance',
value: 'kilometraż' value: 'kilometraż'
@@ -1,46 +0,0 @@
import { JournalFilterSection, JournalFilterType } from '../../scripts/enums/JournalFilterType';
import { JournalFilter } from '../../scripts/types/JournalTimetablesTypes';
export const journalTimetableFilters: JournalFilter[] = [
{
id: JournalFilterType.ALL,
filterSection: JournalFilterSection.TIMETABLE_STATUS,
isActive: true
},
{
id: JournalFilterType.ACTIVE,
filterSection: JournalFilterSection.TIMETABLE_STATUS,
isActive: false
},
{
id: JournalFilterType.FULFILLED,
filterSection: JournalFilterSection.TIMETABLE_STATUS,
isActive: false
},
{
id: JournalFilterType.ABANDONED,
filterSection: JournalFilterSection.TIMETABLE_STATUS,
isActive: false
},
{
id: JournalFilterType.TWR_SKR,
filterSection: JournalFilterSection.TWRSKR,
isActive: true
},
{
id: JournalFilterType.TWR,
filterSection: JournalFilterSection.TWRSKR,
isActive: false
},
{
id: JournalFilterType.SKR,
filterSection: JournalFilterSection.TWRSKR,
isActive: false
}
];
-23
View File
@@ -1,23 +0,0 @@
[
"EP07-356",
"EP07-356",
"EP07-356",
"ET41-074",
"2EN57-694+716rb",
"EU07E-083",
"EN57-716rb",
"EN57-716rb",
"EN57-716rb",
"EN57-038rb",
"EN57-038rb",
"SM42-329_PLREG",
"2EN57-038+1715rb",
"EN57-1953rb",
"EN57-1953rb",
"SM42-1121",
"SM42-091",
"SM42-404",
"SM42-404",
"EN57-1914rb",
"EN57-961rb"
]
+1438 -13428
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -299,22 +299,27 @@
"regions": [ "regions": [
{ {
"id": "eu", "id": "eu",
"name": "PL1",
"value": "PL1" "value": "PL1"
}, },
{ {
"id": "cae", "id": "cae",
"name": "PL2",
"value": "PL2" "value": "PL2"
}, },
{ {
"id": "usw", "id": "usw",
"name": "DE",
"value": "DE" "value": "DE"
}, },
{ {
"id": "us", "id": "us",
"name": "CZE",
"value": "CZE" "value": "CZE"
}, },
{ {
"id": "ru", "id": "ru",
"name": "ENG",
"value": "ENG" "value": "ENG"
} }
] ]
+2 -1
View File
@@ -78,8 +78,9 @@
"not-signed": "NOT SIGNED IN", "not-signed": "NOT SIGNED IN",
"no-limit": "NO LIMIT", "no-limit": "NO LIMIT",
"unavailable": "UNAVAILABLE", "unavailable": "UNAVAILABLE",
"brb": "AFK", "afk": "AFK",
"no-space": "NO SPACE", "no-space": "NO SPACE",
"invalid": "INVALID HASH",
"unknown": "UNKNOWN" "unknown": "UNKNOWN"
}, },
"options": { "options": {
+2 -1
View File
@@ -66,8 +66,9 @@
"not-signed": "NIEZALOGOWANY", "not-signed": "NIEZALOGOWANY",
"no-limit": "BEZ LIMITU", "no-limit": "BEZ LIMITU",
"unavailable": "NIEDOSTĘPNY", "unavailable": "NIEDOSTĘPNY",
"brb": "Z/W", "afk": "Z/W",
"no-space": "BRAK MIEJSCA", "no-space": "BRAK MIEJSCA",
"invalid": "NIEWPISANY",
"unknown": "NIEZNANY" "unknown": "NIEZNANY"
}, },
"options": { "options": {
@@ -1,7 +1,6 @@
import { TrainFilter } from '../interfaces/Trains/TrainFilter'; import { TrainFilter, TrainFilterId } from '../components/TrainsView/typings';
import { TrainFilterType } from '../enums/TrainFilterType'; import Train from '../scripts/interfaces/Train';
import Train from '../interfaces/Train'; import { TrainStop } from '../store/typings';
import TrainStop from '../interfaces/TrainStop';
function confirmedPercentage(stops: TrainStop[] | undefined) { function confirmedPercentage(stops: TrainStop[] | undefined) {
if (!stops) return -1; if (!stops) return -1;
@@ -32,34 +31,34 @@ function filterTrainList(
if (f.isActive) return true; if (f.isActive) return true;
switch (f.id) { switch (f.id) {
case TrainFilterType.noTimetable: case TrainFilterId.noTimetable:
return train.timetableData; return train.timetableData;
case TrainFilterType.withTimetable: case TrainFilterId.withTimetable:
return !train.timetableData; return !train.timetableData;
case TrainFilterType.withComments: case TrainFilterId.withComments:
return !train.timetableData?.followingStops.some((stop) => stop.comments); return !train.timetableData?.followingStops.some((stop) => stop.comments);
case TrainFilterType.noComments: case TrainFilterId.noComments:
return train.timetableData?.followingStops.some((stop) => stop.comments); return train.timetableData?.followingStops.some((stop) => stop.comments);
case TrainFilterType.twr: case TrainFilterId.twr:
return !train.timetableData?.TWR; return !train.timetableData?.TWR;
case TrainFilterType.skr: case TrainFilterId.skr:
return !train.timetableData?.SKR; return !train.timetableData?.SKR;
case TrainFilterType.common: case TrainFilterId.common:
return train.timetableData?.SKR || train.timetableData?.TWR; return train.timetableData?.SKR || train.timetableData?.TWR;
case TrainFilterType.passenger: case TrainFilterId.passenger:
return !/^[AMRE]\D{2}$/.test(train.timetableData?.category || ''); return !/^[AMRE]\D{2}$/.test(train.timetableData?.category || '');
case TrainFilterType.freight: case TrainFilterId.freight:
return !train.timetableData?.category.startsWith('T'); return !train.timetableData?.category.startsWith('T');
case TrainFilterType.other: case TrainFilterId.other:
return !/^[PXZL]\D{2}$/.test(train.timetableData?.category || ''); return !/^[PXZL]\D{2}$/.test(train.timetableData?.category || '');
default: default:
@@ -72,7 +71,6 @@ function filterTrainList(
(searchedDriver.length > 0 (searchedDriver.length > 0
? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase())
: true) && : true) &&
(!train.timetableData ? train.online : train.timetableData) &&
isFiltered isFiltered
); );
}); });
+1 -1
View File
@@ -1,5 +1,5 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../store/store'; import { useStore } from '../store/mainStore';
export default defineComponent({ export default defineComponent({
data() { data() {
+1 -1
View File
@@ -1,6 +1,6 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
import TrainStop from '../scripts/interfaces/TrainStop'; import { TrainStop } from '../store/typings';
export default defineComponent({ export default defineComponent({
data: () => ({ data: () => ({
+2 -1
View File
@@ -61,7 +61,8 @@ const routes: Array<RouteRecordRaw> = [
const router = createRouter({ const router = createRouter({
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
if (to.name == 'SceneryView' && from.name) return { el: `.app_main` }; if (to.name == 'SceneryView' && from.name && from.query['view'] === undefined)
return { el: `.app_main` };
if (savedPosition) return savedPosition; if (savedPosition) return savedPosition;
@@ -1,49 +0,0 @@
import Filter from '../../interfaces/Filter';
export const filterInitStates: Filter = {
default: false,
notDefault: false,
real: false,
fictional: false,
SPK: false,
SCS: false,
SPE: false,
SUP: false,
noSUP: false,
ręczne: false,
'ręczne+SPK': false,
'ręczne+SCS': false,
mechaniczne: false,
'mechaniczne+SPK': false,
'mechaniczne+SCS': false,
współczesna: false,
kształtowa: false,
historyczna: false,
mieszana: false,
SBL: false,
PBL: false,
minLevel: 0,
maxLevel: 20,
minOneWayCatenary: 0,
minOneWay: 0,
minTwoWayCatenary: 0,
minTwoWay: 0,
'include-selected': false,
'no-1track': false,
'no-2track': false,
free: true,
occupied: false,
ending: false,
nonPublic: false,
unavailable: true,
abandoned: true,
afkStatus: false,
endingStatus: false,
noSpaceStatus: false,
unavailableStatus: false,
unsignedStatus: false,
authors: '',
onlineFromHours: 0
};
-7
View File
@@ -1,7 +0,0 @@
export enum DataStatus {
Initialized = -1,
Loading = 0,
Error = 1,
Loaded = 2,
Warning = 3
}
-11
View File
@@ -1,11 +0,0 @@
export enum DispatcherStatusID {
Unknown = 'unknown',
Outdated = 'outdated',
Unauthorized = 'not-signed',
OnlineNoLimit = 'no-limit',
Afk = 'brb',
Ending = 'ending',
NoSpace = 'no-space',
Unavailable = 'unavailable',
OnlineWithHours = 'online'
}
-14
View File
@@ -1,14 +0,0 @@
export const enum JournalFilterType {
ACTIVE = 'active',
FULFILLED = 'fulfilled',
ABANDONED = 'abandoned',
ALL = 'all',
TWR = 'twr',
SKR = 'skr',
TWR_SKR = 'twr-skr'
}
export enum JournalFilterSection {
TIMETABLE_STATUS = 'timetable-status',
TWRSKR = 'twrskr'
}
-21
View File
@@ -1,21 +0,0 @@
export enum TrainFilterSection {
TRAIN_TYPE = 'TRAIN_TYPE',
TIMETABLE_TYPE = 'TIMETABLE_TYPE',
COMMENTS = 'COMMENTS',
TIMETABLE = 'TIMETABLE'
}
export const enum TrainFilterType {
noComments = 'noComments',
withComments = 'withComments',
twr = 'twr',
skr = 'skr',
common = 'common',
passenger = 'passenger',
freight = 'freight',
other = 'other',
noTimetable = 'noTimetable',
withTimetable = 'withTimetable'
}
-6
View File
@@ -1,6 +0,0 @@
export default interface FilterOption {
id: string;
name: string;
value: boolean;
defaultValue: boolean;
}
-38
View File
@@ -1,38 +0,0 @@
interface Scenery {
stationName: string;
stationURL: string;
stationLines: string;
stationProject: string;
reqLevel: string;
supportersOnly: string;
signalType: string;
controlType: string;
SBL: string;
twoWayBlock: string;
routesOneWayCatenary: number;
routesOneWayOther: number;
routesTwoWayCatenary: number;
routesToWayOther: number;
default: boolean;
nonPublic: boolean;
unavailable: boolean;
hasData: boolean;
stops: string[];
checkpoints: string[];
currentDispatcher: string;
currentDispatcherId: number;
currentDispatcherFrom: number;
dispatcherHistory: {
dispatcherName: string;
dispatcherId: number;
dispatcherFrom: number;
dispatcherTo: number;
}[];
}
export default Scenery;
-43
View File
@@ -1,43 +0,0 @@
import TrainStop from './TrainStop';
export enum StopStatus {
'arriving' = 'arriving',
'departed' = 'departed',
'departed-away' = 'departed-away',
'online' = 'online',
'stopped' = 'stopped',
'terminated' = 'terminated'
}
export interface ScheduledTrain {
checkpointName: string;
trainId: string;
trainNo: number;
driverName: string;
driverId: number;
currentStationName: string;
currentStationHash: string;
category: string;
stopInfo: TrainStop;
terminatesAt: string;
beginsAt: string;
prevStationName: string;
nextStationName: string;
arrivingLine: string | null;
departureLine: string | null;
prevDepartureLine: string | null;
nextArrivalLine: string | null;
signal: string;
connectedTrack: string;
stopLabel: string;
stopStatus: StopStatus;
stopStatusID: number;
}
+1 -2
View File
@@ -1,5 +1,4 @@
import { Availability, OnlineScenery } from './store/storeTypes'; import { Availability, OnlineScenery, ScheduledTrain } from '../../store/typings';
import { ScheduledTrain } from './ScheduledTrain';
import StationRoutes from './StationRoutes'; import StationRoutes from './StationRoutes';
export default interface Station { export default interface Station {
-13
View File
@@ -1,13 +0,0 @@
import { DataStatus } from '../enums/DataStatus';
import Station from './Station';
import Train from './Train';
export interface StoreData {
stationList: Station[];
trainList: Train[];
dispatcherCount: number;
sceneryDataStatus: DataStatus;
dispatcherDataStatus: DataStatus;
trainsDataStatus: DataStatus;
}
-23
View File
@@ -1,23 +0,0 @@
import TrainStop from './TrainStop';
export default interface Timetable {
trainNo: number;
success: boolean;
data?: {
trainNo: number;
driverName: string;
driverId: number;
currentStationName: string;
currentStationHash: string;
timetableId: number;
category: string;
route: string;
TWR: boolean;
SKR: boolean;
routeDistance: number;
followingStops: TrainStop[];
followingSceneries: string[];
};
}
+1 -1
View File
@@ -1,4 +1,4 @@
import TrainStop from './TrainStop'; import { TrainStop } from '../../store/typings';
export default interface Train { export default interface Train {
trainId: string; trainId: string;
-30
View File
@@ -1,30 +0,0 @@
export default interface TrainStop {
stopName: string;
stopNameRAW: string;
stopType: string;
stopDistance: number;
mainStop: boolean;
arrivalLine: string | null;
// arrivalTimeString: string | null;
arrivalTimestamp: number;
// arrivalRealTimeString: string | null;
arrivalRealTimestamp: number;
arrivalDelay: number;
departureLine: string | null;
// departureTimeString: string | null;
departureTimestamp: number;
// departureRealTimeString: string | null;
departureRealTimestamp: number;
departureDelay: number;
pointId: number;
comments?: any;
beginsHere: boolean;
terminatesHere: boolean;
confirmed: boolean;
stopped: boolean;
stopTime: number | null;
}
@@ -1,7 +0,0 @@
import { TrainFilterSection, TrainFilterType } from '../../enums/TrainFilterType';
export interface TrainFilter {
id: TrainFilterType;
section: TrainFilterSection;
isActive: boolean;
}
@@ -1,27 +0,0 @@
export interface Sum {
routeDistance: number;
}
export interface Max {
routeDistance: number;
}
export interface Min {
routeDistance: number;
}
export interface Avg {
routeDistance: number;
}
export interface Count {
_all: number;
}
export interface DispatcherStatsAPIData {
_sum: Sum;
_max: Max;
_min: Min;
_avg: Avg;
_count: Count;
}
@@ -1,18 +0,0 @@
export interface DispatcherHistory {
id: string;
currentDuration: number;
dispatcherId: number;
dispatcherName: string;
dispatcherLevel: number | null;
dispatcherRate: number;
dispatcherIsSupporter: boolean;
dispatcherStatus?: number;
isOnline: boolean;
lastOnlineTimestamp: number;
region: string;
stationHash: string;
stationName: string;
timestampFrom: number;
timestampTo?: number;
}
@@ -1,27 +0,0 @@
export interface Sum {
routeDistance: number;
confirmedStopsCount: number;
allStopsCount: number;
currentDistance: number;
}
export interface Count {
fulfilled: number;
terminated: number;
_all: number;
}
export interface Max {
routeDistance: number;
}
export interface Avg {
routeDistance: number;
}
export interface DriverStatsAPIData {
_sum: Sum;
_count: Count;
_max: Max;
_avg: Avg;
}
@@ -1,18 +0,0 @@
export default interface StationAPIData {
dispatcherId: number;
dispatcherName: string;
dispatcherIsSupporter: boolean;
stationName: string;
stationHash: string;
region: string;
maxUsers: number;
currentUsers: number;
spawn: number;
lastSeen: number;
dispatcherExp: number;
nameFromHeader: string;
spawnString: string | null;
networkConnectionString: string;
isOnline: number;
dispatcherRate: number;
}
@@ -1,51 +0,0 @@
import { TimetableHistory } from './TimetablesAPIData';
export interface ITimetablesDailyStats {
totalTimetables: number;
distanceSum: number;
distanceAvg: number;
timetableId: number;
timetableAuthor: string;
timetableDriver: string;
timetableRouteDistance: number;
mostActiveDispatchers: {
name: string;
count: number;
}[];
mostActiveDrivers: {
name: string;
distance: number;
}[];
longestDuties: {
name: string;
duration: number;
station: string;
}[];
}
export interface ITimetablesDailyStatsResponse {
totalTimetables: number;
distanceSum: number;
distanceAvg: number;
maxTimetable: TimetableHistory | null;
mostActiveDispatchers: {
name: string;
count: number;
}[];
mostActiveDrivers: {
name: string;
distance: number;
}[];
longestDuties: {
name: string;
duration: number;
station: string;
}[];
}
@@ -1,67 +0,0 @@
export interface TimetableHistory {
id: number;
createdAt: string;
updatedAt: string;
timetableId: number;
trainNo: number;
trainCategoryCode: string;
driverId: number;
driverName: string;
driverLevel: number | null;
driverIsSupporter: boolean;
route: string;
twr: number;
skr: number;
sceneriesString: string;
currentLocation: string[];
routeDistance: number;
currentDistance: number;
confirmedStopsCount: number;
allStopsCount: number;
beginDate: string;
endDate: string;
scheduledBeginDate: string;
scheduledEndDate: string;
terminated: boolean;
fulfilled: boolean;
authorName?: string;
authorId?: number;
stopsString?: string;
stockString?: string;
stockHistory: string[];
stockMass?: number;
stockLength?: number;
maxSpeed?: number;
hashesString?: string;
currentSceneryName?: string;
currentSceneryHash?: string;
routeSceneries?: string;
checkpointArrivals?: string[];
checkpointDepartures?: string[];
checkpointArrivalsScheduled?: string[];
checkpointDeparturesScheduled?: string[];
checkpointStopTypes?: string[];
}
export interface SceneryTimetableHistory {
timetables: TimetableHistory[];
// totalCount: number;
// sceneryName: string;
}
@@ -1,23 +0,0 @@
import { JournalTimetableSorter } from '../../types/JournalTimetablesTypes';
export interface TimetablesQueryParams {
driverName?: string;
trainNo?: string;
timetableId?: string;
authorName?: string;
timestampFrom?: number;
timestampTo?: number;
issuedFrom?: string;
countFrom?: number;
countLimit?: number;
fulfilled?: number;
terminated?: number;
twr?: number;
skr?: number;
sortBy?: JournalTimetableSorter['id'];
}
@@ -1,68 +0,0 @@
export interface TimetableStop {
stopName: string;
stopNameRAW: string;
stopType: string;
stopDistance: number;
pointId: number;
mainStop: boolean;
arrivalLine: string;
arrivalTimestamp: number;
arrivalRealTimestamp: number;
arrivalDelay: number;
departureLine: string;
departureTimestamp: number;
departureRealTimestamp: number;
departureDelay: number;
comments?: any;
beginsHere: boolean;
terminatesHere: boolean;
confirmed: boolean;
stopped: boolean;
stopTime: number;
}
export interface TrainTimetable {
timetableId: number;
category: string;
route: string;
stopList: TimetableStop[];
TWR: boolean;
SKR: boolean;
sceneries: string[];
}
export interface TrainAPIData {
trainNo: number;
mass: number;
length: number;
speed: number;
stockString: string;
signal: string;
distance: number;
connectedTrack: string;
driverName: string;
driverId: number;
driverIsSupporter: boolean;
driverLevel?: number;
currentStationName: string;
currentStationHash: string;
online: boolean;
lastSeen: number;
region: string;
isTimeout: boolean;
timetable?: TrainTimetable;
}
@@ -1,41 +0,0 @@
export interface Author {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}
export interface ReleaseAPIData {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
author: Author;
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: Date;
published_at: Date;
assets: any[];
tarball_url: string;
zipball_url: string;
body: string;
}
@@ -1,13 +0,0 @@
export interface RollingStockGithubData {
usage: Record<string, string>;
info: RollingStockInfo;
}
export interface RollingStockInfo {
'loco-e': [string, string, string, string, boolean][];
'loco-s': [string, string, string, string, boolean][];
'loco-szt': [string, string, string, string, boolean][];
'loco-ezt': [string, string, string, string, boolean][];
'car-passenger': [string, string, boolean, boolean, string][];
'car-cargo': [string, string, boolean, boolean, string][];
}
-129
View File
@@ -1,129 +0,0 @@
import { Socket } from 'socket.io-client';
import { DataStatus } from '../../enums/DataStatus';
import StationAPIData from '../api/StationAPIData';
import { TrainAPIData } from '../api/TrainAPIData';
import { DispatcherStatsAPIData } from '../api/DispatcherStatsAPIData';
import { DriverStatsAPIData } from '../api/DriverStatsAPIData';
import { RollingStockGithubData } from '../github_api/StockInfoGithubData';
import Station from '../Station';
import { ScheduledTrain } from '../ScheduledTrain';
import { DispatcherStatusID } from '../../enums/DispatcherStatus';
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
export interface StoreState {
stationList: Station[];
apiData: APIData;
rollingStockData?: RollingStockGithubData;
lastDispatcherStatuses: { hash: string; statusTimestamp: number; statusID: DispatcherStatusID }[];
sceneryData: any[][];
region: { id: string; value: string };
trainCount: number;
stationCount: number;
webSocket?: Socket;
isOffline: boolean;
dispatcherStatsName: string;
dispatcherStatsData?: DispatcherStatsAPIData;
driverStatsName: string;
driverStatsData?: DriverStatsAPIData;
driverStatsStatus: DataStatus;
chosenModalTrainId?: string;
currentStatsTab: 'daily' | 'driver' | null;
dataStatuses: {
connection: DataStatus;
sceneries: DataStatus;
timetables: DataStatus;
dispatchers: DataStatus;
trains: DataStatus;
};
listenerLaunched: boolean;
blockScroll: boolean;
modalLastClickedTarget: EventTarget | null;
}
export interface APIData {
stations?: StationAPIData[];
dispatchers?: string[][];
trains?: TrainAPIData[];
connectedSocketCount: number;
}
export interface StationRoutesInfo {
routeName: string;
isElectric: boolean;
isInternal: boolean;
isRouteSBL: boolean;
routeLength: number;
routeSpeed: number;
routeTracks: number;
}
export interface StationJSONData {
name: string;
abbr: string;
url: string;
lines: string;
project: string;
projectUrl: string;
reqLevel: number;
signalType: string;
controlType: string;
SUP: boolean;
// routes: string;
routesInfo: StationRoutesInfo[];
checkpoints: string | null;
authors?: string;
availability: Availability;
}
export interface StationTrain {
driverName: string;
driverId: number;
trainNo: number;
trainId: string;
stopStatus: string;
}
export interface OnlineScenery {
name: string;
hash: string;
region: string;
maxUsers: number;
currentUsers: number;
spawns: { spawnName: string; spawnLength: number; isElectrified: boolean }[];
dispatcherName: string;
dispatcherRate: number;
dispatcherId: number;
dispatcherExp: number;
dispatcherIsSupporter: boolean;
statusTimestamp: number;
statusID: DispatcherStatusID;
isOnline: boolean;
stationTrains?: StationTrain[];
scheduledTrains?: ScheduledTrain[];
scheduledTrainCount: {
all: number;
confirmed: number;
unconfirmed: number;
}
}
@@ -1,8 +0,0 @@
export type JournalDispatcherSearcher = {
[key in 'search-dispatcher' | 'search-station' | 'search-date']: string;
};
export interface JournalDispatcherSorter {
id: 'timestampFrom' | 'duration';
dir: -1 | 1;
}
@@ -1,25 +0,0 @@
import { JournalFilterType } from '../../scripts/enums/JournalFilterType';
export type JournalTimetableSearchKey =
| 'search-driver'
| 'search-train'
| 'search-date'
| 'search-dispatcher'
| 'search-issuedFrom';
export type JournalTimetableSorterKey = 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
export type JournalTimetableSearchType = {
[key in JournalTimetableSearchKey]: string;
};
export interface JournalFilter {
id: JournalFilterType;
filterSection: string;
isActive: boolean;
}
export interface JournalTimetableSorter {
id: JournalTimetableSorterKey;
dir: 'asc' | 'desc';
}
+22 -29
View File
@@ -1,6 +1,6 @@
import { Filter } from '../../components/StationsView/typings';
import { Status } from '../../typings/common';
import { HeadIdsTypes } from '../data/stationHeaderNames'; import { HeadIdsTypes } from '../data/stationHeaderNames';
import { DispatcherStatusID } from '../enums/DispatcherStatus';
import Filter from '../interfaces/Filter';
import Station from '../interfaces/Station'; import Station from '../interfaces/Station';
export const sortStations = ( export const sortStations = (
@@ -19,7 +19,7 @@ export const sortStations = (
break; break;
case 'status': case 'status':
diff = (a.onlineInfo?.statusTimestamp || 0) - (b.onlineInfo?.statusTimestamp || 0); diff = (a.onlineInfo?.dispatcherStatus || 0) - (b.onlineInfo?.dispatcherStatus || 0);
break; break;
case 'dispatcher': case 'dispatcher':
@@ -53,28 +53,20 @@ export const sortStations = (
case 'timetableConfirmed': case 'timetableConfirmed':
diff = diff =
(a.onlineInfo?.scheduledTrains (a.onlineInfo?.scheduledTrainCount.confirmed ?? -1) -
? a.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length (b.onlineInfo?.scheduledTrainCount.confirmed ?? -1);
: -1) -
(b.onlineInfo?.scheduledTrains
? b.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
: -1);
break; break;
case 'timetableUnconfirmed': case 'timetableUnconfirmed':
diff = diff =
(a.onlineInfo?.scheduledTrains (a.onlineInfo?.scheduledTrainCount.unconfirmed ?? -1) -
? a.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length (b.onlineInfo?.scheduledTrainCount.unconfirmed ?? -1);
: -1) -
(b.onlineInfo?.scheduledTrains
? b.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
: -1);
break; break;
case 'timetableAll': case 'timetableAll':
diff = diff =
(a.onlineInfo?.scheduledTrains ? a.onlineInfo.scheduledTrains.length : -1) - (a.onlineInfo?.scheduledTrainCount.all ?? -1) -
(b.onlineInfo?.scheduledTrains ? b.onlineInfo.scheduledTrains.length : -1); (b.onlineInfo?.scheduledTrainCount.all ?? -1);
break; break;
default: default:
@@ -89,27 +81,28 @@ export const filterStations = (station: Station, filters: Filter) => {
if (!station.onlineInfo && filters['free']) return false; if (!station.onlineInfo && filters['free']) return false;
if (station.onlineInfo) { if (station.onlineInfo) {
const { statusID, statusTimestamp } = station.onlineInfo; const { dispatcherStatus } = station.onlineInfo;
const isEnding = statusID == DispatcherStatusID.Ending && filters['endingStatus']; const isEnding = dispatcherStatus == Status.ActiveDispatcher.ENDING && filters['endingStatus'];
const isNotSigned = const isNotSigned =
(statusID == 'not-signed' || statusID == 'unavailable') && filters['unavailableStatus']; (dispatcherStatus == Status.ActiveDispatcher.NOT_LOGGED_IN ||
dispatcherStatus == Status.ActiveDispatcher.UNAVAILABLE) &&
filters['unavailableStatus'];
const isAFK = statusID == 'brb' && filters['afkStatus']; const isAFK = dispatcherStatus == Status.ActiveDispatcher.AFK && filters['afkStatus'];
const isNoSpace = statusID == 'no-space' && filters['noSpaceStatus']; const isNoSpace =
dispatcherStatus == Status.ActiveDispatcher.NO_SPACE && filters['noSpaceStatus'];
const isOccupied = station.onlineInfo && filters['occupied']; const isOccupied = station.onlineInfo && filters['occupied'];
const isOnlineInBounds = if (isEnding || isNotSigned || isAFK || isNoSpace || isOccupied) return false;
(filters['onlineFromHours'] < 8 &&
statusTimestamp > 0 &&
statusTimestamp <= Date.now() + filters['onlineFromHours'] * 3600000) ||
(filters['onlineFromHours'] > 0 && statusTimestamp <= 0) ||
(filters['onlineFromHours'] == 8 && statusID != 'no-limit');
if (isEnding || isOnlineInBounds || isNotSigned || isAFK || isNoSpace || isOccupied) if (
filters['onlineFromHours'] > 0 &&
dispatcherStatus <= Date.now() + filters['onlineFromHours'] * 3600000
)
return false; return false;
} }
+80 -109
View File
@@ -1,36 +1,25 @@
import axios from 'axios'; import axios from 'axios';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { io } from 'socket.io-client'; import { io } from 'socket.io-client';
import { DataStatus } from '../scripts/enums/DataStatus';
import StationRoutes from '../scripts/interfaces/StationRoutes'; import StationRoutes from '../scripts/interfaces/StationRoutes';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
import { URLs } from '../scripts/utils/apiURLs'; import { URLs } from '../scripts/utils/apiURLs';
import { import { parseSpawns, getScheduledTrains, getStationTrains } from './utils';
getDispatcherStatus,
parseSpawns,
getScheduledTrains,
getStationTrains
} from '../scripts/utils/storeUtils';
import { import { OnlineScenery, ScheduledTrain, StationJSONData, StoreState } from './typings';
APIData,
OnlineScenery,
StationJSONData,
StoreState
} from '../scripts/interfaces/store/storeTypes';
import packageInfo from '../../package.json'; import packageInfo from '../../package.json';
import { RollingStockGithubData } from '../scripts/interfaces/github_api/StockInfoGithubData'; import { Websocket, API } from '../typings/api';
import { ScheduledTrain } from '../scripts/interfaces/ScheduledTrain'; import { Status } from '../typings/common';
import { DispatcherStatusID } from '../scripts/enums/DispatcherStatus';
export const useStore = defineStore('store', { export const useStore = defineStore('store', {
state: () => state: () =>
({ ({
apiData: {} as unknown, activeData: {} as unknown,
rollingStockData: undefined, rollingStockData: undefined,
stationList: [], stationList: [],
regionOnlineCounters: [],
routesList: [], routesList: [],
@@ -50,16 +39,16 @@ export const useStore = defineStore('store', {
driverStatsName: '', driverStatsName: '',
driverStatsData: undefined, driverStatsData: undefined,
driverStatsStatus: DataStatus.Initialized, driverStatsStatus: Status.Data.Initialized,
chosenModalTrainId: undefined, chosenModalTrainId: undefined,
dataStatuses: { dataStatuses: {
connection: DataStatus.Loading, connection: Status.Data.Loading,
sceneries: DataStatus.Loading, sceneries: Status.Data.Loading,
timetables: DataStatus.Loading, timetables: Status.Data.Loading,
dispatchers: DataStatus.Loading, dispatchers: Status.Data.Loading,
trains: DataStatus.Loading trains: Status.Data.Loading
}, },
currentStatsTab: null, currentStatsTab: null,
@@ -71,12 +60,8 @@ export const useStore = defineStore('store', {
getters: { getters: {
trainList(): Train[] { trainList(): Train[] {
return (this.apiData?.trains ?? []) return (this.activeData?.trains ?? [])
.filter( .filter((train) => train.timetable || train.online)
(train) =>
train.region === this.region.id &&
(train.online || train.timetable || train.lastSeen > Date.now() - 180000)
)
.map((train) => { .map((train) => {
const stock = train.stockString.split(';'); const stock = train.stockString.split(';');
const locoType = stock ? stock[0] : train.stockString; const locoType = stock ? stock[0] : train.stockString;
@@ -127,71 +112,59 @@ export const useStore = defineStore('store', {
onlineSceneryList(state): OnlineScenery[] { onlineSceneryList(state): OnlineScenery[] {
if (state.isOffline) return []; if (state.isOffline) return [];
if (!state.apiData?.stations) return []; if (!state.activeData?.activeSceneries) return [];
return ( return state.activeData?.activeSceneries.reduce((list, scenery) => {
state.apiData?.stations if (scenery.isOnline !== 1 && Date.now() - scenery.lastSeen > 1000 * 60 * 2) return list;
// ?.filter((apiStation) => apiStation.region == state.region.id) if (scenery.dispatcherStatus == Status.ActiveDispatcher.UNKNOWN) return list;
.reduce((list, apiStation) => {
if (apiStation.region != state.region.id) return list;
const dispatcherStatus = getDispatcherStatus(state as StoreState, apiStation); const station = this.stationList.find((s) => s.name === scenery.stationName);
if (dispatcherStatus.statusID == DispatcherStatusID.Unknown) return list; const scheduledTrains = getScheduledTrains(this.trainList, scenery, station?.generalInfo);
const station = this.stationList.find((s) => s.name === apiStation.stationName); const stationTrains = getStationTrains(
this.trainList,
scheduledTrains,
this.region.id,
scenery
);
const scheduledTrains = getScheduledTrains( // Remove checkpoint duplicates
this.trainList, const uniqueScheduledTrains = scheduledTrains.reduce(
apiStation, (uniqueList, sTrain) =>
station?.generalInfo uniqueList.find((v) => v.trainId === sTrain.trainId)
); ? uniqueList
: [...uniqueList, sTrain],
[] as ScheduledTrain[]
);
const stationTrains = getStationTrains( list.push({
this.trainList, name: scenery.stationName,
scheduledTrains, hash: scenery.stationHash,
this.region.id, region: scenery.region,
apiStation maxUsers: scenery.maxUsers,
); currentUsers: scenery.currentUsers,
spawns: parseSpawns(scenery.spawnString),
dispatcherName: scenery.dispatcherName,
dispatcherRate: scenery.dispatcherRate,
dispatcherId: scenery.dispatcherId,
dispatcherExp: scenery.dispatcherExp,
dispatcherIsSupporter: scenery.dispatcherIsSupporter,
scheduledTrains: scheduledTrains,
stationTrains: stationTrains,
dispatcherStatus: scenery.dispatcherStatus,
// Remove checkpoint duplicates isOnline: scenery.isOnline == 1,
const uniqueScheduledTrains = scheduledTrains.reduce(
(uniqueList, sTrain) =>
uniqueList.find((v) => v.trainId === sTrain.trainId)
? uniqueList
: [...uniqueList, sTrain],
[] as ScheduledTrain[]
);
list.push({ scheduledTrainCount: {
name: apiStation.stationName, all: uniqueScheduledTrains.length,
hash: apiStation.stationHash, confirmed: uniqueScheduledTrains.filter((train) => train.stopInfo.confirmed).length,
region: apiStation.region, unconfirmed: uniqueScheduledTrains.filter((train) => !train.stopInfo.confirmed).length
maxUsers: apiStation.maxUsers, }
currentUsers: apiStation.currentUsers, });
spawns: parseSpawns(apiStation.spawnString),
dispatcherName: apiStation.dispatcherName,
dispatcherRate: apiStation.dispatcherRate,
dispatcherId: apiStation.dispatcherId,
dispatcherExp: apiStation.dispatcherExp,
dispatcherIsSupporter: apiStation.dispatcherIsSupporter,
scheduledTrains: scheduledTrains,
stationTrains: stationTrains,
statusTimestamp: dispatcherStatus.statusTimestamp,
statusID: dispatcherStatus.statusID,
isOnline: apiStation.isOnline == 1, return list;
}, [] as OnlineScenery[]);
scheduledTrainCount: {
all: uniqueScheduledTrains.length,
confirmed: uniqueScheduledTrains.filter((train) => train.stopInfo.confirmed).length,
unconfirmed: uniqueScheduledTrains.filter((train) => !train.stopInfo.confirmed).length
}
});
return list;
}, [] as OnlineScenery[])
);
} }
}, },
actions: { actions: {
@@ -201,7 +174,7 @@ export const useStore = defineStore('store', {
).data; ).data;
if (!sceneryData) { if (!sceneryData) {
this.dataStatuses.sceneries = DataStatus.Error; this.dataStatuses.sceneries = Status.Data.Error;
return; return;
} }
@@ -259,8 +232,8 @@ export const useStore = defineStore('store', {
async connectToWebsocket() { async connectToWebsocket() {
if (import.meta.env.VITE_APP_WS_DEV === '1') { if (import.meta.env.VITE_APP_WS_DEV === '1') {
const mockWebsocketData = await import('../data/mockWebsocketData.json'); const mockWebsocketData = await import('../data/mockWebsocketData.json');
this.dataStatuses.connection = DataStatus.Loaded; this.dataStatuses.connection = Status.Data.Loaded;
this.apiData = mockWebsocketData as any; this.activeData = mockWebsocketData as any;
this.setStatuses(); this.setStatuses();
console.warn('Stacjownik działa w trybie mockowania danych z WS'); console.warn('Stacjownik działa w trybie mockowania danych z WS');
@@ -271,25 +244,25 @@ export const useStore = defineStore('store', {
const socket = io(URLs.stacjownikAPI, { const socket = io(URLs.stacjownikAPI, {
transports: ['websocket', 'polling'], transports: ['websocket', 'polling'],
rememberUpgrade: true, rememberUpgrade: true,
reconnection: true, reconnection: true
extraHeaders: {
version: packageInfo.version
}
}); });
socket.emit('CONNECTION', { version: packageInfo.version });
socket.on('connect_error', () => { socket.on('connect_error', () => {
this.dataStatuses.connection = DataStatus.Error; this.dataStatuses.connection = Status.Data.Error;
}); });
socket.on('UPDATE', (data: APIData) => { socket.on('UPDATE', (data: Websocket.ActiveData) => {
this.apiData = data; this.activeData = data;
this.dataStatuses.connection = DataStatus.Loaded; this.dataStatuses.connection = Status.Data.Loaded;
this.setStatuses(); this.setStatuses();
}); });
socket.emit('FETCH_DATA', { version: packageInfo.version }, (data: APIData) => { socket.emit('FETCH_DATA', { version: packageInfo.version }, (data: Websocket.ActiveData) => {
this.dataStatuses.connection = DataStatus.Loaded; this.dataStatuses.connection = Status.Data.Loaded;
this.apiData = data; this.activeData = data;
this.setStatuses(); this.setStatuses();
}); });
@@ -309,7 +282,7 @@ export const useStore = defineStore('store', {
async fetchStockInfoData() { async fetchStockInfoData() {
try { try {
this.rollingStockData = ( this.rollingStockData = (
await axios.get<RollingStockGithubData>( await axios.get<API.RollingStock.Response>(
'https://raw.githubusercontent.com/Spythere/api/main/td2/data/stockInfo.json' 'https://raw.githubusercontent.com/Spythere/api/main/td2/data/stockInfo.json'
) )
).data; ).data;
@@ -319,19 +292,17 @@ export const useStore = defineStore('store', {
}, },
async setStatuses() { async setStatuses() {
if (!this.apiData.stations) { if (!this.activeData.activeSceneries) {
this.dataStatuses.sceneries = DataStatus.Error; this.dataStatuses.sceneries = Status.Data.Error;
this.dataStatuses.trains = DataStatus.Error; this.dataStatuses.trains = Status.Data.Error;
this.dataStatuses.dispatchers = DataStatus.Error; this.dataStatuses.dispatchers = Status.Data.Error;
return; return;
} }
this.dataStatuses.sceneries = DataStatus.Loaded; this.dataStatuses.sceneries = Status.Data.Loaded;
this.dataStatuses.trains = !this.apiData.trains ? DataStatus.Warning : DataStatus.Loaded; this.dataStatuses.trains = !this.activeData.trains ? Status.Data.Warning : Status.Data.Loaded;
this.dataStatuses.dispatchers = !this.apiData.dispatchers this.dataStatuses.dispatchers = Status.Data.Loaded;
? DataStatus.Warning
: DataStatus.Loaded;
// if (this.apiData.dispatchers != null) this.lastDispatcherStatuses = prevDispatcherStatuses; // if (this.apiData.dispatchers != null) this.lastDispatcherStatuses = prevDispatcherStatuses;
} }
+54 -4
View File
@@ -1,10 +1,58 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import inputData from '../data/options.json'; import inputData from '../data/options.json';
import StorageManager from '../scripts/managers/storageManager'; import { useStore } from './mainStore';
import { useStore } from './store';
import { filterInitStates } from '../scripts/constants/stores/initFilterStates';
import { filterStations, sortStations } from '../scripts/utils/filterUtils'; import { filterStations, sortStations } from '../scripts/utils/filterUtils';
import { HeadIdsTypes } from '../scripts/data/stationHeaderNames'; import { HeadIdsTypes } from '../scripts/data/stationHeaderNames';
import StorageManager from '../managers/storageManager';
import { Filter } from '../components/StationsView/typings';
const filterInitStates: Filter = {
default: false,
notDefault: false,
real: false,
fictional: false,
SPK: false,
SCS: false,
SPE: false,
SUP: false,
noSUP: false,
ręczne: false,
'ręczne+SPK': false,
'ręczne+SCS': false,
mechaniczne: false,
'mechaniczne+SPK': false,
'mechaniczne+SCS': false,
współczesna: false,
kształtowa: false,
historyczna: false,
mieszana: false,
SBL: false,
PBL: false,
minLevel: 0,
maxLevel: 20,
minOneWayCatenary: 0,
minOneWay: 0,
minTwoWayCatenary: 0,
minTwoWay: 0,
'include-selected': false,
'no-1track': false,
'no-2track': false,
free: true,
occupied: false,
ending: false,
nonPublic: false,
unavailable: true,
abandoned: true,
afkStatus: false,
endingStatus: false,
noSpaceStatus: false,
unavailableStatus: false,
unsignedStatus: false,
authors: '',
onlineFromHours: 0
};
export const useStationFiltersStore = defineStore('stationFiltersStore', { export const useStationFiltersStore = defineStore('stationFiltersStore', {
state() { state() {
@@ -26,7 +74,9 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
return store.stationList return store.stationList
.map((station) => ({ .map((station) => ({
...station, ...station,
onlineInfo: store.onlineSceneryList.find((os) => os.name == station.name) onlineInfo: store.onlineSceneryList.find(
(os) => os.name == station.name && os.region == store.region.id
)
})) }))
.filter((station) => filterStations(station, state.filters)) .filter((station) => filterStations(station, state.filters))
.sort((a, b) => sortStations(a, b, state.sorterActive)); .sort((a, b) => sortStations(a, b, state.sorterActive));
+196
View File
@@ -0,0 +1,196 @@
import { Socket } from 'socket.io-client';
import Station from '../scripts/interfaces/Station';
import { API, Websocket } from '../typings/api';
import { Status } from '../typings/common';
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
export interface RegionCounters {
stationCount: number;
trainsCount: number;
timetablesCount: number;
}
export interface StoreState {
stationList: Station[];
activeData: Websocket.ActiveData;
rollingStockData?: API.RollingStock.Response;
regionOnlineCounters: RegionCounters[];
lastDispatcherStatuses: {
hash: string;
statusTimestamp: number;
statusID: Status.ActiveDispatcher;
}[];
sceneryData: any[][];
region: { id: string; value: string };
trainCount: number;
stationCount: number;
webSocket?: Socket;
isOffline: boolean;
dispatcherStatsName: string;
dispatcherStatsData?: API.DispatcherStats.Response;
driverStatsName: string;
driverStatsData?: API.DriverStats.Response;
driverStatsStatus: Status.Data;
chosenModalTrainId?: string;
currentStatsTab: 'daily' | 'driver' | null;
dataStatuses: {
connection: Status.Data;
sceneries: Status.Data;
timetables: Status.Data;
dispatchers: Status.Data;
trains: Status.Data;
};
listenerLaunched: boolean;
blockScroll: boolean;
modalLastClickedTarget: EventTarget | null;
}
export interface StationRoutesInfo {
routeName: string;
isElectric: boolean;
isInternal: boolean;
isRouteSBL: boolean;
routeLength: number;
routeSpeed: number;
routeTracks: number;
}
export interface StationJSONData {
name: string;
abbr: string;
url: string;
lines: string;
project: string;
projectUrl: string;
reqLevel: number;
signalType: string;
controlType: string;
SUP: boolean;
// routes: string;
routesInfo: StationRoutesInfo[];
checkpoints: string | null;
authors?: string;
availability: Availability;
}
export interface OnlineScenery {
name: string;
hash: string;
region: string;
maxUsers: number;
currentUsers: number;
spawns: { spawnName: string; spawnLength: number; isElectrified: boolean }[];
dispatcherName: string;
dispatcherRate: number;
dispatcherId: number;
dispatcherExp: number;
dispatcherIsSupporter: boolean;
dispatcherStatus: Status.ActiveDispatcher | number;
isOnline: boolean;
stationTrains?: StationTrain[];
scheduledTrains?: ScheduledTrain[];
scheduledTrainCount: {
all: number;
confirmed: number;
unconfirmed: number;
};
}
export interface StationTrain {
driverName: string;
driverId: number;
trainNo: number;
trainId: string;
stopStatus: string;
}
export interface ScheduledTrain {
checkpointName: string;
trainId: string;
trainNo: number;
driverName: string;
driverId: number;
currentStationName: string;
currentStationHash: string;
category: string;
stopInfo: TrainStop;
terminatesAt: string;
beginsAt: string;
prevStationName: string;
nextStationName: string;
arrivingLine: string | null;
departureLine: string | null;
prevDepartureLine: string | null;
nextArrivalLine: string | null;
signal: string;
connectedTrack: string;
stopLabel: string;
stopStatus: StopStatus;
stopStatusID: number;
}
export enum StopStatus {
ARRIVING = 'arriving',
DEPARTED = 'departed',
DEPARTED_AWAY = 'departed-away',
ONLINE = 'online',
STOPPED = 'stopped',
TERMINATED = 'terminated'
}
export interface TrainStop {
stopName: string;
stopNameRAW: string;
stopType: string;
stopDistance: number;
mainStop: boolean;
arrivalLine: string | null;
arrivalTimestamp: number;
arrivalRealTimestamp: number;
arrivalDelay: number;
departureLine: string | null;
departureTimestamp: number;
departureRealTimestamp: number;
departureDelay: number;
pointId: number;
comments?: any;
beginsHere: boolean;
terminatesHere: boolean;
confirmed: boolean;
stopped: boolean;
stopTime: number | null;
}
@@ -1,52 +1,15 @@
import { DispatcherStatusID } from '../enums/DispatcherStatus'; import Station from '../scripts/interfaces/Station';
import { ScheduledTrain, StopStatus } from '../interfaces/ScheduledTrain'; import Train from '../scripts/interfaces/Train';
import Station from '../interfaces/Station'; import { API } from '../typings/api';
import Train from '../interfaces/Train'; import { ScheduledTrain, StationTrain, StopStatus, TrainStop } from './typings';
import TrainStop from '../interfaces/TrainStop';
import StationAPIData from '../interfaces/api/StationAPIData';
import { StationTrain, StoreState } from '../interfaces/store/storeTypes';
export const getLocoURL = (locoType: string): string => export function getLocoURL(locoType: string): string {
`https://rj.td2.info.pl/dist/img/thumbnails/${ return `https://rj.td2.info.pl/dist/img/thumbnails/${
locoType.includes('EN') ? locoType + 'rb' : locoType locoType.includes('EN') ? locoType + 'rb' : locoType
}.png`; }.png`;
}
export const getStatusID = ( export function getStatusTimestamp(stationStatus: any): number {
stationStatus: any[] | undefined,
isSWDROnline: boolean
): DispatcherStatusID => {
if (isSWDROnline && !stationStatus) return DispatcherStatusID.Unauthorized;
if (!stationStatus) return DispatcherStatusID.Unknown;
// if (stationStatus == -1) return DispatcherStatusID.Unauthorized;
const statusCode = stationStatus[2];
const statusTimestamp = stationStatus[3];
switch (statusCode) {
case 0:
if (statusTimestamp - Date.now() > 21000000) return DispatcherStatusID.OnlineNoLimit;
return DispatcherStatusID.OnlineWithHours;
case 1:
return DispatcherStatusID.Afk;
case 2:
if (statusTimestamp == 0) return DispatcherStatusID.Ending;
break;
case 3:
return DispatcherStatusID.NoSpace;
default:
break;
}
return DispatcherStatusID.Unavailable;
};
export const getStatusTimestamp = (stationStatus: any): number => {
if (!stationStatus) return -2; if (!stationStatus) return -2;
const statusCode = stationStatus[2]; const statusCode = stationStatus[2];
@@ -67,9 +30,9 @@ export const getStatusTimestamp = (stationStatus: any): number => {
} }
return -1; return -1;
}; }
export const parseSpawns = (spawnString: string | null) => { export function parseSpawns(spawnString: string | null) {
if (!spawnString) return []; if (!spawnString) return [];
if (spawnString === 'NO_SPAWN') return []; if (spawnString === 'NO_SPAWN') return [];
@@ -81,47 +44,49 @@ export const parseSpawns = (spawnString: string | null) => {
return { spawnName, spawnLength, isElectrified }; return { spawnName, spawnLength, isElectrified };
}); });
}; }
export const getTimestamp = (date: string | null): number => (date ? new Date(date).getTime() : 0); export function getTimestamp(date: string | null): number {
return date ? new Date(date).getTime() : 0;
}
export const getTrainStopStatus = ( export function getTrainStopStatus(
stopInfo: TrainStop, stopInfo: TrainStop,
currentStationName: string, currentStationName: string,
sceneryName: string sceneryName: string
) => { ) {
let stopStatus = StopStatus['arriving'], let stopStatus = StopStatus.ARRIVING,
stopLabel = '', stopLabel = '',
stopStatusID = -1; stopStatusID = -1;
if (stopInfo.terminatesHere && stopInfo.confirmed) { if (stopInfo.terminatesHere && stopInfo.confirmed) {
stopStatus = StopStatus['terminated']; stopStatus = StopStatus.TERMINATED;
stopLabel = 'Skończył bieg'; stopLabel = 'Skończył bieg';
stopStatusID = 5; stopStatusID = 5;
} else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == sceneryName) { } else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == sceneryName) {
stopStatus = StopStatus['departed']; stopStatus = StopStatus.DEPARTED;
stopLabel = 'Odprawiony'; stopLabel = 'Odprawiony';
stopStatusID = 2; stopStatusID = 2;
} else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != sceneryName) { } else if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != sceneryName) {
stopStatus = StopStatus['departed-away']; stopStatus = StopStatus.DEPARTED_AWAY;
stopLabel = 'Odjechał'; stopLabel = 'Odjechał';
stopStatusID = 4; stopStatusID = 4;
} else if (currentStationName == sceneryName && !stopInfo.stopped) { } else if (currentStationName == sceneryName && !stopInfo.stopped) {
stopStatus = StopStatus['online']; stopStatus = StopStatus.ONLINE;
stopLabel = 'Na stacji'; stopLabel = 'Na stacji';
stopStatusID = 0; stopStatusID = 0;
} else if (currentStationName == sceneryName && stopInfo.stopped) { } else if (currentStationName == sceneryName && stopInfo.stopped) {
stopStatus = StopStatus['stopped']; stopStatus = StopStatus.STOPPED;
stopLabel = 'Postój'; stopLabel = 'Postój';
stopStatusID = 1; stopStatusID = 1;
} else if (currentStationName != sceneryName) { } else if (currentStationName != sceneryName) {
stopStatus = StopStatus['arriving']; stopStatus = StopStatus.ARRIVING;
stopLabel = 'W drodze'; stopLabel = 'W drodze';
stopStatusID = 3; stopStatusID = 3;
} }
return { stopStatus, stopLabel, stopStatusID }; return { stopStatus, stopLabel, stopStatusID };
}; }
export function getCheckpointTrain( export function getCheckpointTrain(
train: Train, train: Train,
@@ -218,40 +183,12 @@ export function getCheckpointTrain(
}; };
} }
export function getDispatcherStatus(state: StoreState, onlineStationData: StationAPIData) {
const { dispatchers } = state.apiData;
const prevDispatcherStatus = state.lastDispatcherStatuses.find(
(dispatcher) => dispatcher.hash === onlineStationData.stationHash
);
const stationStatus = dispatchers?.find(
(status: string[]) => status[0] == onlineStationData.stationHash && status[1] == state.region.id
);
const statusTimestamp =
prevDispatcherStatus && !dispatchers
? prevDispatcherStatus.statusTimestamp
: getStatusTimestamp(stationStatus);
const statusID =
prevDispatcherStatus && !dispatchers
? prevDispatcherStatus.statusID
: getStatusID(stationStatus, onlineStationData.isOnline === 1);
return {
hash: onlineStationData.stationHash,
statusID,
statusTimestamp
};
}
export function getScheduledTrains( export function getScheduledTrains(
trainList: Train[], trainList: Train[],
stationAPIData: StationAPIData, sceneryData: API.ActiveSceneries.Data,
stationGeneralInfo: Station['generalInfo'] stationGeneralInfo: Station['generalInfo']
): ScheduledTrain[] { ): ScheduledTrain[] {
const stationName = stationAPIData.stationName.toLocaleLowerCase(); const stationNameLower = sceneryData.stationName.toLocaleLowerCase();
stationGeneralInfo?.checkpoints.forEach((cp) => (cp.scheduledTrains.length = 0)); stationGeneralInfo?.checkpoints.forEach((cp) => (cp.scheduledTrains.length = 0));
@@ -259,17 +196,17 @@ export function getScheduledTrains(
if (!train.timetableData) return acc; if (!train.timetableData) return acc;
const timetable = train.timetableData; const timetable = train.timetableData;
if (!timetable.sceneries.includes(stationAPIData.stationHash)) return acc; if (!timetable.sceneries.includes(sceneryData.stationHash)) return acc;
const stopInfoIndex = timetable.followingStops.findIndex((stop) => { const stopInfoIndex = timetable.followingStops.findIndex((stop) => {
const stopName = stop.stopNameRAW.toLowerCase(); const stopNameLower = stop.stopNameRAW.toLocaleLowerCase();
return ( return (
stationName == stopName || stationNameLower == stopNameLower ||
(!/(po\.|podg\.)/.test(stopName) && stopName.includes(stationName)) || (!/(po\.|podg\.)/.test(stopNameLower) && stopNameLower.includes(stationNameLower)) ||
(!/(po\.|podg\.)/.test(stationName) && stationName.includes(stopName)) || (!/(po\.|podg\.)/.test(stationNameLower) && stationNameLower.includes(stopNameLower)) ||
(stopName.split(', podg.')[0] !== undefined && (stopNameLower.split(', podg.')[0] !== undefined &&
stationName.startsWith(stopName.split(', podg.')[0])) stationNameLower.startsWith(stopNameLower.split(', podg.')[0]))
); );
}); });
@@ -277,12 +214,12 @@ export function getScheduledTrains(
if (stopInfoIndex != -1) { if (stopInfoIndex != -1) {
checkpointScheduledTrains.push( checkpointScheduledTrains.push(
getCheckpointTrain(train, stopInfoIndex, stationAPIData.stationName) getCheckpointTrain(train, stopInfoIndex, sceneryData.stationName)
); );
} }
stationGeneralInfo?.checkpoints?.forEach((checkpoint) => { stationGeneralInfo?.checkpoints?.forEach((checkpoint) => {
if (checkpoint.checkpointName.toLocaleLowerCase() == stationName) return; // if (checkpoint.checkpointName.toLocaleLowerCase() == stationNameLower) return;
if ( if (
checkpointScheduledTrains.findIndex( checkpointScheduledTrains.findIndex(
@@ -298,9 +235,7 @@ export function getScheduledTrains(
); );
if (index > -1) if (index > -1)
checkpointScheduledTrains.push( checkpointScheduledTrains.push(getCheckpointTrain(train, index, sceneryData.stationName));
getCheckpointTrain(train, index, stationAPIData.stationName)
);
}); });
acc.push(...checkpointScheduledTrains); acc.push(...checkpointScheduledTrains);
@@ -312,14 +247,14 @@ export function getStationTrains(
trainList: Train[], trainList: Train[],
scheduledTrainList: ScheduledTrain[], scheduledTrainList: ScheduledTrain[],
region: string, region: string,
apiStation: StationAPIData sceneryData: API.ActiveSceneries.Data
): StationTrain[] { ): StationTrain[] {
return trainList return trainList
.filter( .filter(
(train) => (train) =>
train?.region === region && train?.region === region &&
train.online && train.online &&
train.currentStationName === apiStation.stationName train.currentStationName === sceneryData.stationName
) )
.map((train) => ({ .map((train) => ({
driverName: train.driverName, driverName: train.driverName,
+2 -1
View File
@@ -4,6 +4,7 @@
.filters-options { .filters-options {
margin-bottom: 0.5em; margin-bottom: 0.5em;
position: relative;
} }
.actions-bar { .actions-bar {
@@ -57,7 +58,7 @@ h1.option-title {
background-color: $bgCol; background-color: $bgCol;
box-shadow: 0 5px 10px 2px #0f0f0f; box-shadow: 0 5px 10px 2px #0f0f0f;
width: 97%; width: 100%;
max-width: 550px; max-width: 550px;
padding: 1em; padding: 1em;
+331
View File
@@ -0,0 +1,331 @@
import { Status } from './common';
export namespace API {
export namespace DispatcherHistory {
export type Response = Data[];
export interface Data {
id: string;
currentDuration: number;
dispatcherId: number;
dispatcherName: string;
dispatcherLevel: number | null;
dispatcherRate: number;
dispatcherIsSupporter: boolean;
dispatcherStatus?: number;
isOnline: boolean;
lastOnlineTimestamp: number;
region: string;
stationHash: string;
stationName: string;
timestampFrom: number;
timestampTo?: number;
}
}
export namespace DispatcherStats {
export interface DistanceStat {
routeDistance: number;
}
export interface Count {
_all: number;
}
export interface Response {
_sum: DistanceStat;
_max: DistanceStat;
_min: DistanceStat;
_avg: DistanceStat;
_count: Count;
}
}
export namespace DriverStats {
export interface SumStats {
routeDistance: number;
confirmedStopsCount: number;
allStopsCount: number;
currentDistance: number;
}
export interface CountStats {
fulfilled: number;
terminated: number;
_all: number;
}
export interface MaxStats {
routeDistance: number;
}
export interface AvdStats {
routeDistance: number;
}
export interface Response {
_sum: SumStats;
_count: CountStats;
_max: MaxStats;
_avg: AvdStats;
}
}
export namespace ActiveSceneries {
export interface Data {
dispatcherId: number;
dispatcherName: string;
dispatcherIsSupporter: boolean;
stationName: string;
stationHash: string;
region: string;
maxUsers: number;
currentUsers: number;
spawn: number;
lastSeen: number;
dispatcherExp: number;
nameFromHeader: string;
spawnString: string | null;
networkConnectionString: string;
isOnline: number;
dispatcherRate: number;
dispatcherStatus: Status.ActiveDispatcher | number;
}
export type Response = Data[];
}
export namespace ActiveTrains {
export type Response = Data[];
export interface Data {
trainNo: number;
mass: number;
length: number;
speed: number;
stockString: string;
signal: string;
distance: number;
connectedTrack: string;
driverName: string;
driverId: number;
driverIsSupporter: boolean;
driverLevel?: number;
currentStationName: string;
currentStationHash: string;
online: boolean;
lastSeen: number;
region: string;
isTimeout: boolean;
timetable?: Timetable;
}
export interface TimetableStop {
stopName: string;
stopNameRAW: string;
stopType: string;
stopDistance: number;
pointId: number;
mainStop: boolean;
arrivalLine: string;
arrivalTimestamp: number;
arrivalRealTimestamp: number;
arrivalDelay: number;
departureLine: string;
departureTimestamp: number;
departureRealTimestamp: number;
departureDelay: number;
comments?: any;
beginsHere: boolean;
terminatesHere: boolean;
confirmed: boolean;
stopped: boolean;
stopTime: number;
}
export interface Timetable {
timetableId: number;
category: string;
route: string;
stopList: TimetableStop[];
TWR: boolean;
SKR: boolean;
sceneries: string[];
}
}
export namespace TimetableHistory {
export interface Data {
id: number;
createdAt: string;
updatedAt: string;
timetableId: number;
trainNo: number;
trainCategoryCode: string;
driverId: number;
driverName: string;
driverLevel: number | null;
driverIsSupporter: boolean;
route: string;
twr: number;
skr: number;
sceneriesString: string;
currentLocation: string[];
routeDistance: number;
currentDistance: number;
confirmedStopsCount: number;
allStopsCount: number;
beginDate: string;
endDate: string;
scheduledBeginDate: string;
scheduledEndDate: string;
terminated: boolean;
fulfilled: boolean;
authorName?: string;
authorId?: number;
stopsString?: string;
stockString?: string;
stockHistory: string[];
stockMass?: number;
stockLength?: number;
maxSpeed?: number;
hashesString?: string;
currentSceneryName?: string;
currentSceneryHash?: string;
routeSceneries?: string;
checkpointArrivals?: string[];
checkpointDepartures?: string[];
checkpointArrivalsScheduled?: string[];
checkpointDeparturesScheduled?: string[];
checkpointStopTypes?: string[];
}
export type Response = Data[];
}
export namespace RollingStock {
export interface Response {
usage: Record<string, string>;
info: Info;
}
export interface Info {
'loco-e': [string, string, string, string, boolean][];
'loco-s': [string, string, string, string, boolean][];
'loco-szt': [string, string, string, string, boolean][];
'loco-ezt': [string, string, string, string, boolean][];
'car-passenger': [string, string, boolean, boolean, string][];
'car-cargo': [string, string, boolean, boolean, string][];
}
}
export namespace DailyStats {
export interface Response {
totalTimetables: number;
distanceSum: number;
distanceAvg: number;
maxTimetable: API.TimetableHistory.Data | null;
mostActiveDispatchers: {
name: string;
count: number;
}[];
mostActiveDrivers: {
name: string;
distance: number;
}[];
longestDuties: {
name: string;
duration: number;
station: string;
}[];
}
}
}
export namespace Websocket {
export interface ActiveData {
activeSceneries?: API.ActiveSceneries.Response;
trains?: API.ActiveTrains.Response;
connectedSocketCount: number;
}
}
export namespace GithubAPI {
export namespace Release {
export interface Author {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
}
export interface Response {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
author: Author;
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: Date;
published_at: Date;
assets: any[];
tarball_url: string;
zipball_url: string;
body: string;
}
}
}
+20
View File
@@ -0,0 +1,20 @@
export namespace Status {
export enum ActiveDispatcher {
INVALID = -2,
UNKNOWN = -1,
AFK = 1,
ENDING = 2,
NO_SPACE = 3,
UNAVAILABLE = 4,
NOT_LOGGED_IN = 5
}
export enum Data {
Offline = 2,
Initialized = -1,
Loading = 0,
Error = 1,
Loaded = 2,
Warning = 3
}
}
+17 -20
View File
@@ -36,16 +36,14 @@ import axios from 'axios';
import JournalOptions from '../components/JournalView/JournalOptions.vue'; import JournalOptions from '../components/JournalView/JournalOptions.vue';
import { URLs } from '../scripts/utils/apiURLs'; import { URLs } from '../scripts/utils/apiURLs';
import { DataStatus } from '../scripts/enums/DataStatus'; import { useStore } from '../store/mainStore';
import { useStore } from '../store/store';
import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue'; import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue';
import {
JournalDispatcherSearcher,
JournalDispatcherSorter
} from '../scripts/types/JournalDispatcherTypes';
import { DispatcherHistory } from '../scripts/interfaces/api/DispatchersAPIData';
import JournalHeader from '../components/JournalView/JournalHeader.vue'; import JournalHeader from '../components/JournalView/JournalHeader.vue';
import { LocationQuery } from 'vue-router'; import { LocationQuery } from 'vue-router';
import { Journal } from '../components/JournalView/typings';
import { API } from '../typings/api';
import { Status } from '../typings/common';
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`; const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
@@ -81,21 +79,20 @@ export default defineComponent({
statsCardOpen: false, statsCardOpen: false,
currentOptionsActive: false, currentOptionsActive: false,
dataStatus: DataStatus.Loading, dataStatus: Status.Data.Loading,
DataStatus,
historyList: [] as DispatcherHistory[] historyList: [] as API.DispatcherHistory.Response
}), }),
setup() { setup() {
const sorterActive: JournalDispatcherSorter = reactive({ id: 'timestampFrom', dir: -1 }); const sorterActive: Journal.DispatcherSorter = reactive({ id: 'timestampFrom', dir: -1 });
const journalFilterActive = ref({}); const journalFilterActive = ref({});
const searchersValues = reactive({ const searchersValues = reactive({
'search-dispatcher': '', 'search-dispatcher': '',
'search-station': '', 'search-station': '',
'search-date': '' 'search-date': ''
} as JournalDispatcherSearcher); } as Journal.DispatcherSearcher);
const countFromIndex = ref(0); const countFromIndex = ref(0);
const countLimit = 15; const countLimit = 15;
@@ -153,7 +150,7 @@ export default defineComponent({
const scrollTop = listElement.scrollTop; const scrollTop = listElement.scrollTop;
const elementHeight = listElement.scrollHeight - listElement.offsetHeight; const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded) if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != Status.Data.Loaded)
return; return;
if (scrollTop > elementHeight * 0.85) this.addHistoryData(); if (scrollTop > elementHeight * 0.85) this.addHistoryData();
@@ -185,7 +182,7 @@ export default defineComponent({
this.countFromIndex = this.historyList.length; this.countFromIndex = this.historyList.length;
const responseData: DispatcherHistory[] = await ( const responseData: API.DispatcherHistory.Response = await (
await axios.get( await axios.get(
`${DISPATCHERS_API_URL}?${this.currentQuery}&countFrom=${this.countFromIndex}` `${DISPATCHERS_API_URL}?${this.currentQuery}&countFrom=${this.countFromIndex}`
) )
@@ -226,20 +223,20 @@ export default defineComponent({
queries.push('countLimit=30'); queries.push('countLimit=30');
if (this.currentQuery != queries.join('&')) this.dataStatus = DataStatus.Loading; if (this.currentQuery != queries.join('&')) this.dataStatus = Status.Data.Loading;
this.currentQuery = queries.join('&'); this.currentQuery = queries.join('&');
this.currentQueryArray = queries; this.currentQueryArray = queries;
try { try {
if (reset) this.dataStatus = DataStatus.Loading; if (reset) this.dataStatus = Status.Data.Loading;
const responseData: DispatcherHistory[] = await ( const responseData: API.DispatcherHistory.Response = await (
await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}`) await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}`)
).data; ).data;
if (!responseData) { if (!responseData) {
this.dataStatus = DataStatus.Error; this.dataStatus = Status.Data.Error;
return; return;
} }
@@ -255,9 +252,9 @@ export default defineComponent({
: ''; : '';
this.dataRefreshedAt = new Date(); this.dataRefreshedAt = new Date();
this.dataStatus = DataStatus.Loaded; this.dataStatus = Status.Data.Loaded;
} catch (error) { } catch (error) {
this.dataStatus = DataStatus.Error; this.dataStatus = Status.Data.Error;
} }
this.scrollNoMoreData = false; this.scrollNoMoreData = false;
+92 -34
View File
@@ -45,24 +45,84 @@ import JournalOptions from '../components/JournalView/JournalOptions.vue';
import JournalStats from '../components/JournalView/JournalStats.vue'; import JournalStats from '../components/JournalView/JournalStats.vue';
import JournalHeader from '../components/JournalView/JournalHeader.vue'; import JournalHeader from '../components/JournalView/JournalHeader.vue';
import { DataStatus } from '../scripts/enums/DataStatus';
import { TimetableHistory } from '../scripts/interfaces/api/TimetablesAPIData';
import { URLs } from '../scripts/utils/apiURLs'; import { URLs } from '../scripts/utils/apiURLs';
import { useStore } from '../store/store'; import { useStore } from '../store/mainStore';
import { LocationQuery } from 'vue-router'; import { LocationQuery } from 'vue-router';
import { TimetablesQueryParams } from '../scripts/interfaces/api/TimetablesQueryParams';
import { JournalFilterType } from '../scripts/enums/JournalFilterType';
import {
JournalFilter,
JournalTimetableSearchType,
JournalTimetableSorter
} from '../scripts/types/JournalTimetablesTypes';
import { journalTimetableFilters } from '../constants/Journal/JournalTimetablesConsts';
import JournalTimetablesList from '../components/JournalView/JournalTimetables/JournalTimetablesList.vue'; import JournalTimetablesList from '../components/JournalView/JournalTimetables/JournalTimetablesList.vue';
import { Journal } from '../components/JournalView/typings';
import { Status } from '../typings/common';
import { API } from '../typings/api';
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`; const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
export const journalTimetableFilters: Journal.TimetableFilter[] = [
{
id: Journal.TimetableFilterId.ALL,
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
isActive: true
},
{
id: Journal.TimetableFilterId.ACTIVE,
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
isActive: false
},
{
id: Journal.TimetableFilterId.FULFILLED,
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
isActive: false
},
{
id: Journal.TimetableFilterId.ABANDONED,
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
isActive: false
},
{
id: Journal.TimetableFilterId.TWR_SKR,
filterSection: Journal.FilterSection.TWRSKR,
isActive: true
},
{
id: Journal.TimetableFilterId.TWR,
filterSection: Journal.FilterSection.TWRSKR,
isActive: false
},
{
id: Journal.TimetableFilterId.SKR,
filterSection: Journal.FilterSection.TWRSKR,
isActive: false
}
];
interface TimetablesQueryParams {
driverName?: string;
trainNo?: string;
timetableId?: string;
authorName?: string;
timestampFrom?: number;
timestampTo?: number;
issuedFrom?: string;
countFrom?: number;
countLimit?: number;
fulfilled?: number;
terminated?: number;
twr?: number;
skr?: number;
sortBy?: Journal.TimetableSorter['id'];
}
export default defineComponent({ export default defineComponent({
components: { components: {
JournalOptions, JournalOptions,
@@ -91,22 +151,20 @@ export default defineComponent({
statsCardOpen: false, statsCardOpen: false,
currentOptionsActive: false, currentOptionsActive: false,
timetableHistory: [] as TimetableHistory[], timetableHistory: [] as API.TimetableHistory.Response,
journalTimetableFilters, journalTimetableFilters,
dataStatus: DataStatus.Loading, dataStatus: Status.Data.Loading,
dataErrorMessage: '', dataErrorMessage: ''
DataStatus
}), }),
setup() { setup() {
const sorterActive: JournalTimetableSorter = reactive({ id: 'timetableId', dir: 'desc' }); const sorterActive: Journal.TimetableSorter = reactive({ id: 'timetableId', dir: 'desc' });
// const journalFilterActive = ref(journalTimetableFilters[0]); // const journalFilterActive = ref(journalTimetableFilters[0]);
const initFilters: readonly JournalFilter[] = JSON.parse( const initFilters: readonly Journal.TimetableFilter[] = JSON.parse(
JSON.stringify(journalTimetableFilters) JSON.stringify(journalTimetableFilters)
); );
const filterList: JournalFilter[] = reactive(JSON.parse(JSON.stringify(initFilters))); const filterList: Journal.TimetableFilter[] = reactive(JSON.parse(JSON.stringify(initFilters)));
const searchersValues = reactive({ const searchersValues = reactive({
'search-train': '', 'search-train': '',
@@ -114,7 +172,7 @@ export default defineComponent({
'search-dispatcher': '', 'search-dispatcher': '',
'search-issuedFrom': '', 'search-issuedFrom': '',
'search-date': '' 'search-date': ''
} as JournalTimetableSearchType); } as Journal.TimetableSearchType);
const countFromIndex = ref(0); const countFromIndex = ref(0);
const countLimit = 15; const countLimit = 15;
@@ -163,7 +221,7 @@ export default defineComponent({
const scrollTop = listElement.scrollTop; const scrollTop = listElement.scrollTop;
const elementHeight = listElement.scrollHeight - listElement.offsetHeight; const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded) if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != Status.Data.Loaded)
return; return;
if (scrollTop > elementHeight * 0.85) this.addHistoryData(); if (scrollTop > elementHeight * 0.85) this.addHistoryData();
@@ -213,7 +271,7 @@ export default defineComponent({
this.currentQueryParams['countFrom'] = this.timetableHistory.length; this.currentQueryParams['countFrom'] = this.timetableHistory.length;
const responseData: TimetableHistory[] = await ( const responseData: API.TimetableHistory.Response = await (
await axios.get(`${TIMETABLES_API_URL}`, { await axios.get(`${TIMETABLES_API_URL}`, {
params: { ...this.currentQueryParams } params: { ...this.currentQueryParams }
}) })
@@ -248,37 +306,37 @@ export default defineComponent({
.filter((f) => f.isActive) .filter((f) => f.isActive)
.forEach((f) => { .forEach((f) => {
switch (f.id) { switch (f.id) {
case JournalFilterType.ABANDONED: case Journal.TimetableFilterId.ABANDONED:
queryParams['fulfilled'] = 0; queryParams['fulfilled'] = 0;
queryParams['terminated'] = 1; queryParams['terminated'] = 1;
break; break;
case JournalFilterType.ACTIVE: case Journal.TimetableFilterId.ACTIVE:
queryParams['fulfilled'] = undefined; queryParams['fulfilled'] = undefined;
queryParams['terminated'] = 0; queryParams['terminated'] = 0;
break; break;
case JournalFilterType.FULFILLED: case Journal.TimetableFilterId.FULFILLED:
queryParams['terminated'] = undefined; queryParams['terminated'] = undefined;
queryParams['fulfilled'] = 1; queryParams['fulfilled'] = 1;
break; break;
case JournalFilterType.ALL: case Journal.TimetableFilterId.ALL:
queryParams['terminated'] = undefined; queryParams['terminated'] = undefined;
queryParams['fulfilled'] = undefined; queryParams['fulfilled'] = undefined;
break; break;
case JournalFilterType.TWR_SKR: case Journal.TimetableFilterId.TWR_SKR:
queryParams['twr'] = undefined; queryParams['twr'] = undefined;
queryParams['skr'] = undefined; queryParams['skr'] = undefined;
break; break;
case JournalFilterType.TWR: case Journal.TimetableFilterId.TWR:
queryParams['twr'] = 1; queryParams['twr'] = 1;
queryParams['skr'] = undefined; queryParams['skr'] = undefined;
break; break;
case JournalFilterType.SKR: case Journal.TimetableFilterId.SKR:
queryParams['twr'] = undefined; queryParams['twr'] = undefined;
queryParams['skr'] = 1; queryParams['skr'] = 1;
break; break;
@@ -301,19 +359,19 @@ export default defineComponent({
this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined; this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
if (JSON.stringify(this.currentQueryParams) != JSON.stringify(queryParams)) if (JSON.stringify(this.currentQueryParams) != JSON.stringify(queryParams))
this.dataStatus = DataStatus.Loading; this.dataStatus = Status.Data.Loading;
this.currentQueryParams = queryParams; this.currentQueryParams = queryParams;
try { try {
const responseData: TimetableHistory[] = await ( const responseData: API.TimetableHistory.Response = await (
await axios.get(`${TIMETABLES_API_URL}`, { await axios.get(`${TIMETABLES_API_URL}`, {
params: this.currentQueryParams params: this.currentQueryParams
}) })
).data; ).data;
if (!responseData) { if (!responseData) {
this.dataStatus = DataStatus.Error; this.dataStatus = Status.Data.Error;
this.dataErrorMessage = 'Brak danych!'; this.dataErrorMessage = 'Brak danych!';
return; return;
} }
@@ -329,10 +387,10 @@ export default defineComponent({
? this.timetableHistory[0].driverName ? this.timetableHistory[0].driverName
: ''; : '';
this.dataStatus = DataStatus.Loaded; this.dataStatus = Status.Data.Loaded;
this.dataRefreshedAt = new Date(); this.dataRefreshedAt = new Date();
} catch (error) { } catch (error) {
this.dataStatus = DataStatus.Error; this.dataStatus = Status.Data.Error;
this.dataErrorMessage = 'Ups! Coś poszło nie tak!'; this.dataErrorMessage = 'Ups! Coś poszło nie tak!';
} }
+23 -9
View File
@@ -27,7 +27,7 @@
:key="i" :key="i"
class="btn btn--option" class="btn btn--option"
@click="setViewMode(viewMode.component)" @click="setViewMode(viewMode.component)"
:data-checked="currentViewCompontent == viewMode.component" :data-checked="currentMode == viewMode.component"
> >
{{ $t(viewMode.id) }} {{ $t(viewMode.id) }}
</button> </button>
@@ -35,10 +35,10 @@
<keep-alive> <keep-alive>
<component <component
:is="currentViewCompontent" :is="currentMode"
:onlineScenery="onlineSceneryInfo" :onlineScenery="onlineSceneryInfo"
:station="stationInfo" :station="stationInfo"
:key="currentViewCompontent" :key="currentMode"
></component> ></component>
</keep-alive> </keep-alive>
</div> </div>
@@ -50,7 +50,7 @@
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import routerMixin from '../mixins/routerMixin'; import routerMixin from '../mixins/routerMixin';
import { useStore } from '../store/store'; import { useStore } from '../store/mainStore';
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue'; import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue'; import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
@@ -66,6 +66,8 @@ enum SceneryViewMode {
} }
export default defineComponent({ export default defineComponent({
name: 'SceneryView',
components: { components: {
SceneryInfo, SceneryInfo,
SceneryTimetable, SceneryTimetable,
@@ -111,9 +113,9 @@ export default defineComponent({
onlineFrom: -1 onlineFrom: -1
}), }),
activated() { // activated() {
this.loadSelectedCheckpoint(); // this.loadSelectedCheckpoint();
}, // },
setup() { setup() {
const route = useRoute(); const route = useRoute();
@@ -126,6 +128,10 @@ export default defineComponent({
}, },
computed: { computed: {
currentMode() {
return this.$route.query.view?.toString() ?? 'SceneryTimetable';
},
stationInfo() { stationInfo() {
return this.store.stationList.find( return this.store.stationList.find(
(station) => station.name === this.station?.toString().replace(/_/g, ' ') (station) => station.name === this.station?.toString().replace(/_/g, ' ')
@@ -134,14 +140,22 @@ export default defineComponent({
onlineSceneryInfo() { onlineSceneryInfo() {
return this.store.onlineSceneryList.find( return this.store.onlineSceneryList.find(
(scenery) => scenery.name === this.station?.toString().replace(/_/g, ' ') (scenery) =>
scenery.name === this.station?.toString().replace(/_/g, ' ') &&
scenery.region == this.store.region.id
); );
} }
}, },
methods: { methods: {
setViewMode(componentName: string) { setViewMode(componentName: string) {
this.currentViewCompontent = componentName; this.$router.push({
path: this.$route.path,
query: {
...this.$route.query,
view: componentName
}
});
}, },
loadSelectedCheckpoint() { loadSelectedCheckpoint() {
+1 -2
View File
@@ -21,7 +21,7 @@ import { defineComponent } from 'vue';
import StationTable from '../components/StationsView/StationTable.vue'; import StationTable from '../components/StationsView/StationTable.vue';
import StationFilterCard from '../components/StationsView/StationFilterCard.vue'; import StationFilterCard from '../components/StationsView/StationFilterCard.vue';
import { useStationFiltersStore } from '../store/stationFiltersStore'; import { useStationFiltersStore } from '../store/stationFiltersStore';
import { useStore } from '../store/store'; import { useStore } from '../store/mainStore';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -38,7 +38,6 @@ export default defineComponent({
store: useStore() store: useStore()
}), }),
computed: { computed: {
computedStationList() { computedStationList() {
return this.filterStore.filteredStationList; return this.filterStore.filteredStationList;
+5 -7
View File
@@ -15,12 +15,11 @@
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue'; import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
import TrainOptions from '../components/TrainsView/TrainOptions.vue'; import TrainOptions from '../components/TrainsView/TrainOptions.vue';
import TrainTable from '../components/TrainsView/TrainTable.vue'; import TrainTable from '../components/TrainsView/TrainTable.vue';
import { trainFilters } from '../constants/Trains/TrainOptionsConsts';
import modalTrainMixin from '../mixins/modalTrainMixin'; import modalTrainMixin from '../mixins/modalTrainMixin';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
import { filteredTrainList } from '../scripts/managers/trainFilterManager'; import { useStore } from '../store/mainStore';
import { useStore } from '../store/store'; import { TrainFilter, trainFilters } from '../components/TrainsView/typings';
import { TrainFilter } from '../scripts/interfaces/Trains/TrainFilter'; import { filteredTrainList } from '../managers/trainFilterManager';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -70,7 +69,7 @@ export default defineComponent({
const computedTrains: ComputedRef<Train[]> = computed(() => { const computedTrains: ComputedRef<Train[]> = computed(() => {
return filteredTrainList( return filteredTrainList(
store.trainList, store.trainList.filter((train) => train.region == store.region.id),
searchedTrain.value, searchedTrain.value,
searchedDriver.value, searchedDriver.value,
sorterActive, sorterActive,
@@ -83,7 +82,6 @@ export default defineComponent({
currentOptionsActive.value = currentOptionsActive.value =
sT.length > 0 || sD.length > 0 || sA.id != 'routeDistance' || areFiltersActive; sT.length > 0 || sD.length > 0 || sA.id != 'routeDistance' || areFiltersActive;
console.log(sA.id);
}); });
return { return {
@@ -115,7 +113,7 @@ export default defineComponent({
@import '../styles/responsive.scss'; @import '../styles/responsive.scss';
.trains-view { .trains-view {
min-height: 100%; min-height: 600px;
position: relative; position: relative;
} }
+4 -11
View File
@@ -10,22 +10,15 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"esModuleInterop": true, "esModuleInterop": true,
"lib": [
"ESNext", "lib": ["ESNext", "DOM"],
"DOM"
],
"types": ["vite/client", "vite-plugin-pwa/client"], "types": ["vite/client", "vite-plugin-pwa/client"],
"skipLibCheck": true "skipLibCheck": true
}, },
"include": [ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"references": [ "references": [
{ {
"path": "./tsconfig.node.json" "path": "./tsconfig.node.json"
} }
] ]
} }