mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 21:38:13 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f28600a7fa | |||
| d59ead87e6 | |||
| 34d91bc800 | |||
| cf9991d8a0 | |||
| 4ffb79d62b | |||
| d9f5edb4fe | |||
| 1b2112430a | |||
| 0a972a23ef | |||
| 6d52724d06 | |||
| 99415c35d3 | |||
| c3f687d439 | |||
| 266edfd6e6 | |||
| d32d5ad91b | |||
| c3481470cb | |||
| 57e88b9abc | |||
| 44ebf53798 | |||
| 145dc72b6b | |||
| b7f3761940 | |||
| ea7c49dfb3 | |||
| 5d6785813a | |||
| a0054aed14 | |||
| 471e6f5216 | |||
| a617eef00e | |||
| 38e700ecd6 | |||
| da1be0e10a | |||
| f49bb12948 | |||
| 02673a3d70 | |||
| 4ddc7345df | |||
| 5d822684c0 | |||
| 69fa15c70a | |||
| 9192067388 | |||
| 2b41e5b857 | |||
| 674680ff14 | |||
| 475bd2ff10 | |||
| 074d1eb155 |
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stacjownik",
|
||||
"version": "1.14.0",
|
||||
"version": "1.16.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#F2E147"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#66FF6C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.75 3.75H22.5V1.25H20V3.75H10V1.25H7.5V3.75H6.25C4.875 3.75 3.75 4.875 3.75 6.25V23.75C3.75 25.125 4.875 26.25 6.25 26.25H23.75C25.125 26.25 26.25 25.125 26.25 23.75V6.25C26.25 4.875 25.125 3.75 23.75 3.75ZM23.75 23.75H6.25V11.25H23.75V23.75ZM6.25 8.75V6.25H23.75V8.75H6.25ZM8.75 13.75H21.25V16.25H8.75V13.75ZM8.75 18.75H17.5V21.25H8.75V18.75Z" fill="#898989"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
@@ -95,13 +95,18 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import axios from 'axios';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { computed, onActivated, onDeactivated, onMounted, reactive, ref } from 'vue';
|
||||
import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||
import { ITimetablesDailyStats, ITimetablesDailyStatsResponse } from '../../scripts/interfaces/api/StatsAPIData';
|
||||
import { URLs } from '../../scripts/utils/apiURLs';
|
||||
import StorageManager from '../../scripts/managers/storageManager';
|
||||
|
||||
const intervalId = ref(-1);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggleStatsOpen', value: boolean): void;
|
||||
}>();
|
||||
|
||||
const data = reactive({
|
||||
statsStatus: DataStatus.Loading,
|
||||
|
||||
@@ -158,17 +163,26 @@ async function fetchDailyTimetableStats() {
|
||||
|
||||
function startFetchingDailyStats() {
|
||||
fetchDailyTimetableStats();
|
||||
|
||||
if (intervalId.value != -1) return;
|
||||
|
||||
intervalId.value = setInterval(fetchDailyTimetableStats, 60000);
|
||||
}
|
||||
|
||||
function stopFetchingDailyStats() {
|
||||
clearInterval(intervalId.value);
|
||||
intervalId.value = -1;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
startFetchingDailyStats,
|
||||
stopFetchingDailyStats,
|
||||
onActivated(() => {
|
||||
startFetchingDailyStats();
|
||||
emit('toggleStatsOpen', true);
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
stopFetchingDailyStats();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -49,15 +49,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search_actions">
|
||||
<button class="btn--action" @click="onResetButtonClick">
|
||||
{{ $t('options.reset-button') }}
|
||||
</button>
|
||||
<button class="btn--action" @click="onSearchButtonConfirm">
|
||||
{{ $t('options.search-button') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="option-title">{{ $t('options.sort-title') }}</h1>
|
||||
@@ -74,15 +65,31 @@
|
||||
</div>
|
||||
|
||||
<h1 class="option-title" v-if="filters.length != 0">{{ $t('options.filter-title') }}</h1>
|
||||
<div class="options_filters">
|
||||
<button
|
||||
v-for="filter in filters"
|
||||
class="filter-option btn--option"
|
||||
:class="{ checked: journalFilterActive.id === filter.id }"
|
||||
:id="filter.id"
|
||||
@click="onFilterChange(filter)"
|
||||
>
|
||||
{{ $t(`options.filter-${filter.id}`) }}
|
||||
|
||||
<div class="options_filter-sections" v-if="filters.length != 0 && filterList">
|
||||
<section class="filter-section" v-for="section in JournalFilterSection">
|
||||
<p>{{ $t(`options.filter-section-${section}`) }}</p>
|
||||
|
||||
<div class="options_filters">
|
||||
<button
|
||||
v-for="filter in filterList.filter((f) => f.filterSection == section)"
|
||||
class="filter-option btn--option"
|
||||
:class="{ checked: filter.isActive }"
|
||||
:id="filter.id"
|
||||
@click="onFilterChange(filter)"
|
||||
>
|
||||
{{ $t(`options.filter-${filter.id}`) }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="options_actions">
|
||||
<button class="btn--action" @click="onResetButtonClick">
|
||||
{{ $t('options.reset-button') }}
|
||||
</button>
|
||||
<button class="btn--action" @click="onSearchButtonConfirm">
|
||||
{{ $t('options.search-button') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,9 +107,10 @@ import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||
import { DriverStatsAPIData } from '../../scripts/interfaces/api/DriverStatsAPIData';
|
||||
import { URLs } from '../../scripts/utils/apiURLs';
|
||||
import { useStore } from '../../store/store';
|
||||
import { JournalTimetableFilter } from '../../types/Journal/JournalTimetablesTypes';
|
||||
import ActionButton from '../Global/ActionButton.vue';
|
||||
import SelectBox from '../Global/SelectBox.vue';
|
||||
import { JournalFilterSection } from '../../scripts/enums/JournalFilterType';
|
||||
import { JournalFilter } from '../../scripts/types/JournalTimetablesTypes';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SelectBox, ActionButton },
|
||||
@@ -116,7 +124,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
filters: {
|
||||
type: Array as PropType<JournalTimetableFilter[]>,
|
||||
type: Array as PropType<JournalFilter[]>,
|
||||
default: [],
|
||||
},
|
||||
|
||||
@@ -132,13 +140,14 @@ export default defineComponent({
|
||||
|
||||
optionsType: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showOptions: false,
|
||||
JournalFilterSection,
|
||||
|
||||
driverSuggestions: [] as string[],
|
||||
dispatcherSuggestions: [] as string[],
|
||||
@@ -154,7 +163,8 @@ export default defineComponent({
|
||||
return {
|
||||
searchersValues: inject('searchersValues') as { [key: string]: string },
|
||||
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
||||
journalFilterActive: inject('journalFilterActive') as JournalTimetableFilter,
|
||||
// journalFilterActive: inject('journalFilterActive') as JournalFilter,
|
||||
filterList: inject('filterList') as JournalFilter[] | undefined,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -174,7 +184,8 @@ export default defineComponent({
|
||||
watch: {
|
||||
async driverStatsName(value: string) {
|
||||
await this.fetchDriverStats();
|
||||
this.store.currentStatsTab = value ? 'driver' : 'daily';
|
||||
|
||||
// if (value) this.store.currentStatsTab = 'driver';
|
||||
},
|
||||
|
||||
async 'searchersValues.search-driver'(value: string | undefined) {
|
||||
@@ -249,18 +260,17 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
focusEnd() {
|
||||
console.log('focus end');
|
||||
},
|
||||
|
||||
onSorterChange(item: { id: string | number; value: string }) {
|
||||
this.sorterActive.id = item.id;
|
||||
this.sorterActive.dir = -1;
|
||||
this.$emit('onSearchConfirm');
|
||||
},
|
||||
|
||||
onFilterChange(filter: JournalTimetableFilter) {
|
||||
this.journalFilterActive = filter;
|
||||
onFilterChange(filter: JournalFilter) {
|
||||
// this.journalFilterActive = filter;
|
||||
this.filterList?.filter((f) => f.filterSection === filter.filterSection).forEach((f) => (f.isActive = false));
|
||||
filter.isActive = true;
|
||||
|
||||
this.$emit('onSearchConfirm');
|
||||
},
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div class="journal-stats" v-show="!store.isOffline">
|
||||
<div class="journal-stats" v-if="!store.isOffline">
|
||||
<div class="tabs">
|
||||
<button
|
||||
v-for="tab in data.tabs"
|
||||
class="btn--filled"
|
||||
:data-selected="tab.name == store.currentStatsTab && areStatsOpen"
|
||||
:data-inactive="tab.inactive"
|
||||
:data-disabled="tab.inactive"
|
||||
:disabled="tab.inactive"
|
||||
@click="onTabButtonClick(tab.name)"
|
||||
>
|
||||
{{ $t(tab.titlePath) }}
|
||||
@@ -14,7 +16,7 @@
|
||||
|
||||
<div class="stats-tab" v-show="areStatsOpen">
|
||||
<keep-alive>
|
||||
<JournalDailyStats v-if="store.currentStatsTab == 'daily'" ref="dailyStatsComp" />
|
||||
<JournalDailyStats v-if="store.currentStatsTab == 'daily'" @toggleStatsOpen="toggleStatsOpen" />
|
||||
<JournalDriverStats v-else-if="store.currentStatsTab == 'driver'" />
|
||||
</keep-alive>
|
||||
</div>
|
||||
@@ -22,22 +24,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, KeepAlive, onActivated, onDeactivated, reactive, Ref, ref, watch } from 'vue';
|
||||
import { computed, KeepAlive, onMounted, reactive, Ref, ref, watch } from 'vue';
|
||||
import { useStore } from '../../store/store';
|
||||
import JournalDailyStats from './DailyStats.vue';
|
||||
import JournalDriverStats from './JournalDriverStats.vue';
|
||||
import StorageManager from '../../scripts/managers/storageManager';
|
||||
|
||||
// Types
|
||||
type TStatTab = 'daily' | 'driver';
|
||||
|
||||
// Variables
|
||||
|
||||
const store = useStore();
|
||||
const dailyStatsComp: Ref<InstanceType<typeof JournalDailyStats> | null> = ref(null);
|
||||
|
||||
const lastDailyStatsOpen = ref(false);
|
||||
const areStatsOpen = ref(false);
|
||||
const lastClickedTab = ref('daily');
|
||||
const lastClickedTab: Ref<'daily' | 'driver' | null> = ref(null);
|
||||
|
||||
let data = reactive({
|
||||
tabs: [
|
||||
@@ -57,30 +58,35 @@ let data = reactive({
|
||||
function onTabButtonClick(tab: TStatTab) {
|
||||
if (lastClickedTab.value == tab || !areStatsOpen.value) areStatsOpen.value = !areStatsOpen.value;
|
||||
|
||||
if (tab == 'daily') lastDailyStatsOpen.value = areStatsOpen.value;
|
||||
if (tab == 'daily') {
|
||||
StorageManager.setBooleanValue('dailyStatsOpen', areStatsOpen.value);
|
||||
lastDailyStatsOpen.value = areStatsOpen.value;
|
||||
}
|
||||
|
||||
store.currentStatsTab = tab;
|
||||
lastClickedTab.value = tab;
|
||||
|
||||
if (areStatsOpen.value == false) store.currentStatsTab = null;
|
||||
}
|
||||
|
||||
onActivated(() => {
|
||||
dailyStatsComp.value?.startFetchingDailyStats();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
dailyStatsComp.value?.stopFetchingDailyStats();
|
||||
});
|
||||
function toggleStatsOpen(open: boolean) {
|
||||
areStatsOpen.value = open;
|
||||
}
|
||||
|
||||
watch(
|
||||
computed(() => store.driverStatsData),
|
||||
(statsData) => {
|
||||
data.tabs[1].inactive = statsData ? false : true;
|
||||
|
||||
lastClickedTab.value = statsData ? 'driver' : 'daily';
|
||||
if (statsData) areStatsOpen.value = true;
|
||||
if (!statsData) areStatsOpen.value = lastDailyStatsOpen.value;
|
||||
store.currentStatsTab = statsData ? 'driver' : lastClickedTab.value;
|
||||
areStatsOpen.value = statsData ? true : lastClickedTab.value !== null;
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (StorageManager.getBooleanValue('dailyStatsOpen')) {
|
||||
areStatsOpen.value = true;
|
||||
store.currentStatsTab = 'daily';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<span class="text--grayed">#{{ timetable.id }}</span>
|
||||
|
||||
<span class="badges" v-if="timetable.skr || timetable.twr">
|
||||
<span class="train-badge twr" v-if="timetable.twr" :title="$t('general.TWR')">TWR</span>
|
||||
<span class="train-badge skr" v-if="timetable.skr" :title="$t('general.SKR')">SKR</span>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<strong class="text--primary">
|
||||
{{ timetable.trainCategoryCode }}
|
||||
@@ -342,6 +348,7 @@ hr {
|
||||
|
||||
.general-train {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
}
|
||||
@@ -381,6 +388,13 @@ ul.stock-list {
|
||||
}
|
||||
}
|
||||
|
||||
.badges {
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
|
||||
// badge.scss
|
||||
}
|
||||
|
||||
.stock-history {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -418,6 +432,10 @@ ul.stock-list {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.general-train {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.info-route {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
{{ station.name }}
|
||||
</a>
|
||||
|
||||
<div class="scenery-abbrev">{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b></div>
|
||||
|
||||
<div class="scenery-hash" v-if="station.onlineInfo?.hash">#{{ station.onlineInfo.hash }}</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -28,16 +30,20 @@ export default defineComponent({
|
||||
|
||||
.scenery-name {
|
||||
font-weight: bold;
|
||||
|
||||
position: relative;
|
||||
|
||||
font-size: 3em;
|
||||
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.scenery-abbrev {
|
||||
font-size: 1.3em;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.scenery-hash {
|
||||
margin-top: 0.5em;
|
||||
color: #aaa;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
style="color: salmon; text-decoration: underline; font-weight: bold"
|
||||
:href="station.generalInfo.projectUrl"
|
||||
target="_blank"
|
||||
>{{ station.generalInfo.project }}</a
|
||||
>
|
||||
{{ station.generalInfo.project }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
<b>{{ $t('scenery.one-way-routes') }}</b>
|
||||
|
||||
<ul class="routes-list">
|
||||
<li v-for="route in station.generalInfo.routes.oneWay">
|
||||
<li v-for="route in station.generalInfo.routes.oneWay" @click="setActiveShowLength(route.name)">
|
||||
<span :class="{ 'no-catenary': !route.catenary, internal: route.isInternal }"> {{ route.name }}</span>
|
||||
<span v-if="route.speed" class="speed">{{ route.speed }}</span>
|
||||
<span v-if="route.speed" class="speed">
|
||||
{{ activeShowLength.includes(route.name) ? route.length + 'm' : route.speed }}
|
||||
</span>
|
||||
<span v-if="route.SBL" class="sbl">SBL</span>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -16,9 +18,11 @@
|
||||
<b>{{ $t('scenery.two-way-routes') }}</b>
|
||||
|
||||
<ul class="routes-list">
|
||||
<li v-for="route in station.generalInfo.routes.twoWay">
|
||||
<li v-for="(route, i) in station.generalInfo.routes.twoWay" @click="setActiveShowLength(route.name)">
|
||||
<span :class="{ 'no-catenary': !route.catenary, internal: route.isInternal }">{{ route.name }}</span>
|
||||
<span v-if="route.speed" class="speed">{{ route.speed }}</span>
|
||||
<span v-if="route.speed" class="speed">
|
||||
{{ activeShowLength.includes(route.name) ? route.length + 'm' : route.speed }}
|
||||
</span>
|
||||
<span v-if="route.SBL" class="sbl">SBL</span>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -37,6 +41,19 @@ export default defineComponent({
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
setActiveShowLength(name: string) {
|
||||
if (this.activeShowLength.includes(name)) this.activeShowLength.splice(this.activeShowLength.indexOf(name), 1);
|
||||
else this.activeShowLength.push(name);
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
activeShowLength: [] as string[],
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -66,6 +83,11 @@ ul.routes-list {
|
||||
|
||||
li {
|
||||
margin: 0.5em 0.25em;
|
||||
cursor: pointer;
|
||||
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
span {
|
||||
padding: 0.2em 0.25em;
|
||||
@@ -100,7 +122,6 @@ ul.routes-list {
|
||||
|
||||
&:only-child {
|
||||
border-radius: 0.5em;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
<table v-else-if="sceneryHistoryList.length">
|
||||
<thead>
|
||||
<th>{{ $t('scenery.timetables-history-id') }}</th>
|
||||
<th>{{ $t('scenery.timetables-history-number')}}</th>
|
||||
<th>{{ $t('scenery.timetables-history-route')}}</th>
|
||||
<th>{{ $t('scenery.timetables-history-driver')}}</th>
|
||||
<th>{{ $t('scenery.timetables-history-author')}}</th>
|
||||
<th>{{ $t('scenery.timetables-history-date')}}</th>
|
||||
<th>{{ $t('scenery.timetables-history-id') }}</th>
|
||||
<th>{{ $t('scenery.timetables-history-number') }}</th>
|
||||
<th>{{ $t('scenery.timetables-history-route') }}</th>
|
||||
<th>{{ $t('scenery.timetables-history-driver') }}</th>
|
||||
<th>{{ $t('scenery.timetables-history-author') }}</th>
|
||||
<th>{{ $t('scenery.timetables-history-date') }}</th>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr v-for="historyItem in sceneryHistoryList" @click="test">
|
||||
<tr v-for="historyItem in sceneryHistoryList">
|
||||
<td>
|
||||
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">#{{ historyItem.id }}</router-link>
|
||||
</td>
|
||||
@@ -40,31 +40,6 @@
|
||||
</table>
|
||||
|
||||
<div class="list-warning" v-else>{{ $t('scenery.history-list-empty') }}</div>
|
||||
|
||||
<!-- <ul class="history-list" v-else>
|
||||
<li class="list-item" v-for="historyItem in sceneryHistoryList">
|
||||
<div>
|
||||
<b>{{ localeDay(historyItem.beginDate, $i18n.locale) }}</b>
|
||||
{{ localeTime(historyItem.beginDate, $i18n.locale) }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">
|
||||
<span class="text--grayed"> #{{ historyItem.id }} </span>
|
||||
<b class="text--primary"> {{ historyItem.trainCategoryCode }} {{ historyItem.trainNo }}</b>
|
||||
<div>{{ historyItem.driverName }}</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div>{{ historyItem.route.replace('|', ' -> ') }}</div>
|
||||
<div>
|
||||
{{ $t('scenery.timetable-author-title') }}:
|
||||
<b v-if="historyItem.authorName">{{ historyItem.authorName }}</b>
|
||||
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</ul> -->
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -99,19 +74,15 @@ export default defineComponent({
|
||||
methods: {
|
||||
async fetchAPIData(countFrom = 0, countLimit = 15) {
|
||||
try {
|
||||
const requestString = `${URLs.stacjownikAPI}/api/getSceneryTimetables?name=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
|
||||
const requestString = `${URLs.stacjownikAPI}/api/getIssuedTimetables?name=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
|
||||
const historyAPIData: SceneryTimetableHistory = await (await axios.get(requestString)).data;
|
||||
|
||||
this.sceneryHistoryList = historyAPIData.sceneryTimetables;
|
||||
this.sceneryHistoryList = historyAPIData.timetables;
|
||||
this.dataStatus = DataStatus.Loaded;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
test() {
|
||||
console.log('test');
|
||||
},
|
||||
},
|
||||
components: { Loading },
|
||||
});
|
||||
|
||||
@@ -8,7 +8,12 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="(headerName, i) in headIds" :key="headerName" @click="changeSorter(headerName)">
|
||||
<th
|
||||
v-for="(headerName, i) in headIds"
|
||||
:key="headerName"
|
||||
@click="changeSorter(headerName)"
|
||||
class="header-text"
|
||||
>
|
||||
<span class="header_wrapper">
|
||||
<div v-html="$t(`sceneries.${headerName}`)"></div>
|
||||
|
||||
@@ -21,9 +26,14 @@
|
||||
</span>
|
||||
</th>
|
||||
|
||||
<th v-for="(headerName, i) in headIconsIds" :key="headerName" @click="changeSorter(headerName)">
|
||||
<th
|
||||
v-for="(headerName, i) in headIconsIds"
|
||||
:key="headerName"
|
||||
@click="changeSorter(headerName)"
|
||||
class="header-image"
|
||||
>
|
||||
<span class="header_wrapper">
|
||||
<img :src="getIcon(headerName)" :alt="headerName" :title="$t(`sceneries.${headerName}s`)" />
|
||||
<img :src="getIcon(headerName)" :alt="headerName" :title="$t(`sceneries.${headerName}`)" />
|
||||
|
||||
<img
|
||||
class="sort-icon"
|
||||
@@ -53,10 +63,6 @@
|
||||
{{ station.name }}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ station.generalInfo?.abbr }}
|
||||
</td>
|
||||
|
||||
<td class="station_level">
|
||||
<span v-if="station.generalInfo">
|
||||
<span
|
||||
@@ -194,25 +200,31 @@
|
||||
|
||||
<td class="station_users" :class="{ inactive: !station.onlineInfo }">
|
||||
<span>
|
||||
<span class="highlight">{{ station.onlineInfo?.currentUsers || '0' }}</span>
|
||||
<span class="highlight">{{ station.onlineInfo?.currentUsers || 0 }}</span>
|
||||
/
|
||||
<span>{{ station.onlineInfo?.maxUsers || '0' }}</span>
|
||||
<span class="highlight">{{ station.onlineInfo?.maxUsers || 0 }}</span>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="station_spawns" :class="{ inactive: !station.onlineInfo }">
|
||||
<span class="highlight">{{ station.onlineInfo?.spawns.length || '0' }}</span>
|
||||
<span>{{ station.onlineInfo?.spawns.length || 0 }}</span>
|
||||
</td>
|
||||
|
||||
<td class="station_schedules" :class="{ inactive: !station.onlineInfo }">
|
||||
<span>
|
||||
<span class="highlight">
|
||||
{{ station.onlineInfo?.scheduledTrains?.length || '0' }}
|
||||
</span>
|
||||
/
|
||||
<span style="color: #bbb">
|
||||
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || '0' }}
|
||||
</span>
|
||||
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
|
||||
<span class="highlight">
|
||||
{{ station.onlineInfo?.scheduledTrains?.length || 0 }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
|
||||
<span style="color: #ccc">
|
||||
{{ station.onlineInfo?.scheduledTrains?.filter((train) => !train.stopInfo.confirmed).length || 0 }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="station_schedules" style="width: 30px" :class="{ inactive: !station.onlineInfo }">
|
||||
<span style="color: #66ff6c">
|
||||
{{ station.onlineInfo?.scheduledTrains?.filter((train) => train.stopInfo.confirmed).length || 0 }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -356,9 +368,15 @@ table {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
min-width: 80px;
|
||||
&.header-text {
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
padding: 0.5em;
|
||||
&.header-image {
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
padding: 0.5em 0.25em;
|
||||
background-color: $bgCol;
|
||||
white-space: pre-wrap;
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<span class="timetable-id" v-if="train.timetableData">#{{ train.timetableData.timetableId }}</span>
|
||||
|
||||
<span class="timetable_warnings" v-if="train.timetableData?.TWR || train.timetableData?.SKR">
|
||||
<span class="train-badge twr" v-if="train.timetableData?.TWR">TWR</span>
|
||||
<span class="train-badge skr" v-if="train.timetableData?.SKR">SKR</span>
|
||||
<span class="train-badge twr" v-if="train.timetableData?.TWR" :title="$t('general.TWR')">TWR</span>
|
||||
<span class="train-badge skr" v-if="train.timetableData?.SKR" :title="$t('general.SKR')">SKR</span>
|
||||
</span>
|
||||
|
||||
<strong>
|
||||
@@ -118,7 +118,6 @@ export default defineComponent({
|
||||
@import '../../styles/responsive.scss';
|
||||
@import '../../styles/badge.scss';
|
||||
|
||||
|
||||
.image-warning {
|
||||
height: 1em;
|
||||
|
||||
@@ -182,26 +181,6 @@ export default defineComponent({
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
.train-badge {
|
||||
padding: 0.1em 0.2em;
|
||||
border-radius: 0.2em;
|
||||
font-weight: bold;
|
||||
|
||||
font-size: 0.9em;
|
||||
|
||||
&.twr {
|
||||
background-color: var(--clr-twr);
|
||||
}
|
||||
|
||||
&.skr {
|
||||
background-color: var(--clr-skr);
|
||||
}
|
||||
|
||||
&.offline {
|
||||
background-color: #9c362b;
|
||||
}
|
||||
}
|
||||
|
||||
.train-driver {
|
||||
&.supporter {
|
||||
color: orange;
|
||||
@@ -218,9 +197,7 @@ export default defineComponent({
|
||||
|
||||
.timetable_warnings {
|
||||
display: flex;
|
||||
gap: 0.2em;
|
||||
|
||||
color: black;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
.timetable_progress {
|
||||
|
||||
@@ -82,10 +82,10 @@
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import imageMixin from '../../mixins/imageMixin';
|
||||
import keyMixin from '../../mixins/keyMixin';
|
||||
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
|
||||
import ActionButton from '../Global/ActionButton.vue';
|
||||
import SelectBox from '../Global/SelectBox.vue';
|
||||
import { TrainFilterSection } from '../../scripts/enums/TrainFilterType';
|
||||
import { TrainFilter } from '../../scripts/interfaces/Trains/TrainFilter';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SelectBox, ActionButton },
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
import { JournalFilterType } from "../../scripts/enums/JournalFilterType";
|
||||
import { JournalTimetableFilter } from "../../types/Journal/JournalTimetablesTypes";
|
||||
import { JournalFilterSection, JournalFilterType } from '../../scripts/enums/JournalFilterType';
|
||||
import { JournalFilter } from '../../scripts/types/JournalTimetablesTypes';
|
||||
|
||||
export const journalTimetableFilters: JournalTimetableFilter[] = [
|
||||
export const journalTimetableFilters: JournalFilter[] = [
|
||||
{
|
||||
id: JournalFilterType.all,
|
||||
filterSection: 'timetable-status',
|
||||
id: JournalFilterType.ALL,
|
||||
filterSection: JournalFilterSection.TIMETABLE_STATUS,
|
||||
isActive: true,
|
||||
},
|
||||
|
||||
{
|
||||
id: JournalFilterType.active,
|
||||
filterSection: 'timetable-status',
|
||||
id: JournalFilterType.ACTIVE,
|
||||
filterSection: JournalFilterSection.TIMETABLE_STATUS,
|
||||
isActive: false,
|
||||
},
|
||||
|
||||
{
|
||||
id: JournalFilterType.fulfilled,
|
||||
filterSection: 'timetable-status',
|
||||
id: JournalFilterType.FULFILLED,
|
||||
filterSection: JournalFilterSection.TIMETABLE_STATUS,
|
||||
isActive: false,
|
||||
},
|
||||
|
||||
{
|
||||
id: JournalFilterType.abandoned,
|
||||
filterSection: 'timetable-status',
|
||||
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,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TrainFilterSection, TrainFilterType } from '../../scripts/enums/TrainFilterType';
|
||||
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
|
||||
import { TrainFilter } from '../../scripts/interfaces/Trains/TrainFilter';
|
||||
|
||||
export const trainFilters: TrainFilter[] = [
|
||||
{
|
||||
|
||||
+21
-10
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"general": {
|
||||
"and": " and ",
|
||||
"refresh": "REFRESH"
|
||||
"refresh": "REFRESH",
|
||||
"TWR": "High risk freight train",
|
||||
"SKR": "Train with exceeded gauge"
|
||||
},
|
||||
"app": {
|
||||
"sceneries": "SCENERIES",
|
||||
@@ -97,19 +99,21 @@
|
||||
"search-dispatcher": "Dispatcher name",
|
||||
"search-station": "Scenery name",
|
||||
"search-author": "Timetable author name",
|
||||
"search-timetables-date": "Timetable date (CEST / GMT+2)",
|
||||
"search-dispatchers-date": "Service date (CEST / GMT+2)",
|
||||
"search-issuedFrom": "Origin scenery name",
|
||||
"search-timetables-date": "Timetable date (UTC+2 / CEST)",
|
||||
"search-dispatchers-date": "Service date (UTC+2 / CEST)",
|
||||
"search-date": "Date (UTC+2 / CEST)",
|
||||
|
||||
"sort-mass": "mass",
|
||||
"sort-speed": "speed",
|
||||
"sort-length": "length",
|
||||
"sort-distance": "distance",
|
||||
"sort-routeDistance": "route distance",
|
||||
"sort-timetable": "train no.",
|
||||
"sort-progress": "route progress",
|
||||
"sort-delay": "current delay",
|
||||
"sort-id": "timetable id",
|
||||
|
||||
"sort-total-stops": "total stops",
|
||||
"sort-allStopsCount": "total stops",
|
||||
"sort-beginDate": "date",
|
||||
"sort-timetableId": "timetable ID",
|
||||
"sort-timestampFrom": "date",
|
||||
@@ -119,6 +123,7 @@
|
||||
"filter-withComments": "COMMENTS",
|
||||
"filter-twr": "HIGH RISK CARGO",
|
||||
"filter-skr": "EXCEEDED GAUGE",
|
||||
"filter-twr-skr": "ALL TYPES",
|
||||
"filter-common": "NO WARNINGS",
|
||||
"filter-passenger": "PASSENGER",
|
||||
"filter-freight": "FREIGHT",
|
||||
@@ -129,6 +134,9 @@
|
||||
"filter-reset": "RESET FILTERS",
|
||||
"filter-clear": "CLEAR FILTERS",
|
||||
|
||||
"filter-section-timetable-status": "TIMETABLE STATUS",
|
||||
"filter-section-twrskr": "WARNINGS",
|
||||
|
||||
"filter-all": "ALL ENTRIES",
|
||||
"filter-abandoned": "ABANDONED",
|
||||
"filter-fulfilled": "FULFILLED",
|
||||
@@ -205,16 +213,17 @@
|
||||
},
|
||||
"sceneries": {
|
||||
"station": "Station",
|
||||
"abbr": "Station\nabbrev.",
|
||||
"min-lvl": "Min. dispatcher\nlevel",
|
||||
"status": "Status",
|
||||
"dispatcher": "Dispatcher",
|
||||
"dispatcher-lvl": "Dispatcher\nlevel",
|
||||
"routes": "Routes\ndouble / single",
|
||||
"general": "General info",
|
||||
"users": "Drivers online",
|
||||
"spawns": "Spawns online",
|
||||
"timetables": "Active timetables",
|
||||
"user": "Drivers online",
|
||||
"spawn": "Spawns online",
|
||||
"timetableAll": "Active timetables",
|
||||
"timetableConfirmed": "Confirmed timetables",
|
||||
"timetableUnconfirmed": "Unconfirmed timetables",
|
||||
"no-stations": "No stations to show here!",
|
||||
"scenery-search": "Search for scenery..."
|
||||
},
|
||||
@@ -332,6 +341,7 @@
|
||||
"history-btn": "View the dispatcher history",
|
||||
"info-btn": "Return to the scenery view",
|
||||
"authors-title": "Scenery author | Scenery authors",
|
||||
"abbrev": "Station symbol:",
|
||||
"lines-title": "Real lines",
|
||||
"project-title": "Project name",
|
||||
"one-way-routes": "One way routes",
|
||||
@@ -383,7 +393,8 @@
|
||||
"desc-next-arrival": "Leaves towards: {nextStationName} (szlak {nextArrivalLine})",
|
||||
"desc-departed": "The train is at the station and it's been departed. Leaves towards: {nextStationName} (szlak {nextArrivalLine})",
|
||||
"desc-departed-away": "The train has been departed to: {nextStationName} (szlak {nextArrivalLine})",
|
||||
"desc-end": "The train terminates here"
|
||||
"desc-end": "The train terminates here",
|
||||
"desc-terminated": "The train has been terminated"
|
||||
},
|
||||
"history": {
|
||||
"title": "TIMETABLE JOURNAL",
|
||||
|
||||
+21
-9
@@ -1,7 +1,9 @@
|
||||
{
|
||||
"general": {
|
||||
"and": " oraz ",
|
||||
"refresh": "ODŚWIEŻ"
|
||||
"refresh": "ODŚWIEŻ",
|
||||
"TWR": "Towar niebezpieczny wysokiego ryzyka",
|
||||
"SKR": "Przekroczona skrajnia"
|
||||
},
|
||||
"app": {
|
||||
"sceneries": "SCENERIE",
|
||||
@@ -97,11 +99,13 @@
|
||||
"search-dispatcher": "Nick dyżurnego",
|
||||
"search-station": "Nazwa scenerii",
|
||||
"search-author": "Nick autora rozkładu jazdy",
|
||||
"search-timetables-date": "Data rozkładu jazdy (czas polski)",
|
||||
"search-dispatchers-date": "Data służby (czas polski)",
|
||||
"search-issuedFrom": "Sceneria początkowa",
|
||||
"search-timetables-date": "Data rozkładu jazdy (UTC+2 / CEST)",
|
||||
"search-dispatchers-date": "Data służby (UTC+2 / CEST)",
|
||||
"search-date": "Data (UTC+2 / CEST)",
|
||||
|
||||
"sort-distance": "kilometraż",
|
||||
"sort-total-stops": "stacje",
|
||||
"sort-routeDistance": "kilometraż",
|
||||
"sort-allStopsCount": "stacje",
|
||||
"sort-beginDate": "data",
|
||||
"sort-timetableId": "ID rozkładu",
|
||||
"sort-timestampFrom": "data",
|
||||
@@ -120,6 +124,7 @@
|
||||
"filter-noComments": "BEZ UWAG",
|
||||
"filter-twr": "WYS. RYZYKA",
|
||||
"filter-skr": "SKRAJNIA",
|
||||
"filter-twr-skr": "WSZYSTKIE",
|
||||
"filter-common": "ZWYKŁE",
|
||||
"filter-passenger": "PASAŻERSKIE",
|
||||
"filter-freight": "TOWAROWE",
|
||||
@@ -130,6 +135,9 @@
|
||||
"filter-reset": "ZRESETUJ FILTRY",
|
||||
"filter-clear": "WYŁĄCZ FILTRY",
|
||||
|
||||
"filter-section-timetable-status": "STATUS ROZKŁADU JAZDY",
|
||||
"filter-section-twrskr": "UWAGI",
|
||||
|
||||
"filter-all": "WSZYSTKIE",
|
||||
"filter-abandoned": "PORZUCONE",
|
||||
"filter-fulfilled": "WYPEŁNIONE",
|
||||
@@ -215,9 +223,11 @@
|
||||
"dispatcher-lvl": "Poziom\ndyżurnego",
|
||||
"routes": "Szlaki\n2tor / 1tor",
|
||||
"general": "Informacje\nogólne",
|
||||
"users": "Maszyniści online",
|
||||
"spawns": "Otwarte spawny",
|
||||
"timetables": "Aktywne rozkłady jazdy",
|
||||
"user": "Maszyniści online",
|
||||
"spawn": "Otwarte spawny",
|
||||
"timetableAll": "Aktywne rozkłady jazdy",
|
||||
"timetableConfirmed": "Zatwierdzone rozkłady jazdy",
|
||||
"timetableUnconfirmed": "Niezatwierdzone rozkłady jazdy",
|
||||
"no-stations": "Brak stacji do wyświetlenia!",
|
||||
"scenery-search": "Wyszukaj scenerię..."
|
||||
},
|
||||
@@ -336,6 +346,7 @@
|
||||
"history-btn": "Przejdź do widoku historii dyżurnych ruchu",
|
||||
"info-btn": "Wróć do widoku scenerii",
|
||||
"authors-title": "Autor scenerii | Autorzy scenerii",
|
||||
"abbrev": "Skrót posterunku:",
|
||||
"lines-title": "Rzeczywiste linie",
|
||||
"project-title": "Projekt",
|
||||
"one-way-routes": "Szlaki jednotorowe",
|
||||
@@ -387,7 +398,8 @@
|
||||
"desc-next-arrival": "Odjeżdża do: {nextStationName} (szlak {nextArrivalLine})",
|
||||
"desc-departed": "Pociąg jest na tej scenerii i został odprawiony. Odjeżdża do: {nextStationName} (szlak {nextArrivalLine})",
|
||||
"desc-departed-away": "Pociąg został odprawiony i odjechał do: {nextStationName} (szlak {nextArrivalLine})",
|
||||
"desc-end": "Pociąg kończy bieg"
|
||||
"desc-end": "Pociąg kończy bieg",
|
||||
"desc-terminated": "Pociąg skończył bieg"
|
||||
},
|
||||
"history": {
|
||||
"title": "DZIENNIK ROZKŁADÓW JAZDY"
|
||||
|
||||
-10
@@ -7,7 +7,6 @@ import plLang from './locales/pl.json';
|
||||
|
||||
import { createI18n } from 'vue-i18n';
|
||||
import { createPinia } from 'pinia';
|
||||
import { registerSW } from 'virtual:pwa-register';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'pl',
|
||||
@@ -21,15 +20,6 @@ const i18n = createI18n({
|
||||
enableLegacy: false,
|
||||
});
|
||||
|
||||
registerSW({
|
||||
onRegistered(r) {
|
||||
r &&
|
||||
setInterval(() => {
|
||||
r.update();
|
||||
}, 60 * 60 * 1000);
|
||||
},
|
||||
});
|
||||
|
||||
const clickOutsideDirective: Directive = {
|
||||
mounted(el, binding) {
|
||||
el.clickOutsideEvent = (event: Event) => {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import Filter from "../../scripts/interfaces/Filter";
|
||||
import Filter from "../../interfaces/Filter";
|
||||
|
||||
export const filterInitStates: Filter = {
|
||||
default: false,
|
||||
@@ -1,14 +1,5 @@
|
||||
export const headIds = [
|
||||
'station',
|
||||
'abbr',
|
||||
'min-lvl',
|
||||
'status',
|
||||
'dispatcher',
|
||||
'dispatcher-lvl',
|
||||
'routes',
|
||||
'general',
|
||||
] as const;
|
||||
export const headIds = ['station', 'min-lvl', 'status', 'dispatcher', 'dispatcher-lvl', 'routes', 'general'] as const;
|
||||
|
||||
export const headIconsIds = ['user', 'spawn', 'timetable'] as const;
|
||||
export const headIconsIds = ['user', 'spawn', 'timetableAll', 'timetableUnconfirmed', 'timetableConfirmed'] as const;
|
||||
|
||||
export type HeadIdsTypes = typeof headIds[number] | typeof headIconsIds[number];
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
export const enum JournalFilterType {
|
||||
active = "active",
|
||||
fulfilled = "fulfilled",
|
||||
abandoned = "abandoned",
|
||||
all = "all"
|
||||
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',
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { TrainFilterSection, TrainFilterType } from '../../scripts/enums/TrainFilterType';
|
||||
import { TrainFilterSection, TrainFilterType } from '../../enums/TrainFilterType'
|
||||
|
||||
export interface TrainFilter {
|
||||
id: TrainFilterType;
|
||||
@@ -47,10 +47,12 @@ export interface TimetableHistory {
|
||||
hashesString?: string;
|
||||
currentSceneryName?: string;
|
||||
currentSceneryHash?: string;
|
||||
|
||||
routeSceneries?: string;
|
||||
}
|
||||
|
||||
export interface SceneryTimetableHistory {
|
||||
sceneryTimetables: TimetableHistory[];
|
||||
totalCount: number;
|
||||
sceneryName: string;
|
||||
timetables: TimetableHistory[];
|
||||
// totalCount: number;
|
||||
// sceneryName: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { JournalTimetableSorter } from '../../types/JournalTimetablesTypes';
|
||||
|
||||
export interface TimetablesQueryParams {
|
||||
driverName?: string;
|
||||
trainNo?: string;
|
||||
authorName?: string;
|
||||
timestampFrom?: number;
|
||||
timestampTo?: number;
|
||||
issuedFrom?: string;
|
||||
|
||||
countFrom?: number;
|
||||
countLimit?: number;
|
||||
|
||||
fulfilled?: number;
|
||||
terminated?: number;
|
||||
|
||||
twr?: number;
|
||||
skr?: number;
|
||||
|
||||
sortBy?: JournalTimetableSorter['id'];
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export interface StoreState {
|
||||
|
||||
chosenModalTrainId?: string;
|
||||
|
||||
currentStatsTab: 'daily' | 'driver';
|
||||
currentStatsTab: 'daily' | 'driver' | null;
|
||||
|
||||
dataStatuses: {
|
||||
connection: DataStatus;
|
||||
@@ -55,6 +55,16 @@ export interface APIData {
|
||||
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;
|
||||
@@ -70,7 +80,8 @@ export interface StationJSONData {
|
||||
|
||||
SUP: boolean;
|
||||
|
||||
routes: string;
|
||||
// routes: string;
|
||||
routesInfo: StationRoutesInfo[];
|
||||
|
||||
checkpoints: string | null;
|
||||
authors?: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
|
||||
import { TrainFilter } from '../interfaces/Trains/TrainFilter';
|
||||
import { TrainFilterType } from '../enums/TrainFilterType';
|
||||
import Train from '../interfaces/Train';
|
||||
import TrainStop from '../interfaces/TrainStop';
|
||||
@@ -44,7 +44,7 @@ function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriv
|
||||
return !train.timetableData?.SKR;
|
||||
|
||||
case TrainFilterType.common:
|
||||
return train.timetableData?.SKR || train.timetableData?.TWR;
|
||||
return train.timetableData?.SKR || train.timetableData?.TWR;
|
||||
|
||||
case TrainFilterType.passenger:
|
||||
return !/^[AMRE]\D{2}$/.test(train.timetableData?.category || '');
|
||||
@@ -81,7 +81,7 @@ function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: numb
|
||||
if (a.mass > b.mass) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'distance':
|
||||
case 'routeDistance':
|
||||
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
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';
|
||||
}
|
||||
@@ -1,25 +1,20 @@
|
||||
import { HeadIdsTypes } from '../../scripts/data/stationHeaderNames';
|
||||
import Filter from '../../scripts/interfaces/Filter';
|
||||
import Station from '../../scripts/interfaces/Station';
|
||||
import { HeadIdsTypes } from '../data/stationHeaderNames';
|
||||
import Filter from '../interfaces/Filter';
|
||||
import Station from '../interfaces/Station';
|
||||
|
||||
export const sortStations = (a: Station, b: Station, sorter: { headerName: HeadIdsTypes; dir: number }) => {
|
||||
let diff = 0;
|
||||
|
||||
switch (sorter.headerName) {
|
||||
case 'station':
|
||||
return sorter.dir == 1 ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
|
||||
|
||||
case 'abbr':
|
||||
if ((a.generalInfo?.abbr || '') > (b.generalInfo?.abbr || '')) return sorter.dir;
|
||||
if ((a.generalInfo?.abbr || '') < (b.generalInfo?.abbr || '')) return -sorter.dir;
|
||||
break;
|
||||
|
||||
case 'min-lvl':
|
||||
if ((a.generalInfo?.reqLevel || 0) > (b.generalInfo?.reqLevel || 0)) return sorter.dir;
|
||||
if ((a.generalInfo?.reqLevel || 0) < (b.generalInfo?.reqLevel || 0)) return -sorter.dir;
|
||||
diff = (a.generalInfo?.reqLevel || 0) - (b.generalInfo?.reqLevel || 0);
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
if ((a.onlineInfo?.statusTimestamp || 0) > (b.onlineInfo?.statusTimestamp || 0)) return sorter.dir;
|
||||
if ((a.onlineInfo?.statusTimestamp || 0) < (b.onlineInfo?.statusTimestamp || 0)) return -sorter.dir;
|
||||
diff = (a.onlineInfo?.statusTimestamp || 0) - (b.onlineInfo?.statusTimestamp || 0);
|
||||
break;
|
||||
|
||||
case 'dispatcher':
|
||||
@@ -30,34 +25,48 @@ export const sortStations = (a: Station, b: Station, sorter: { headerName: HeadI
|
||||
break;
|
||||
|
||||
case 'dispatcher-lvl':
|
||||
if ((a.onlineInfo?.dispatcherExp || 0) > (b.onlineInfo?.dispatcherExp || 0)) return sorter.dir;
|
||||
if ((a.onlineInfo?.dispatcherExp || 0) < (b.onlineInfo?.dispatcherExp || 0)) return -sorter.dir;
|
||||
diff = (a.onlineInfo?.dispatcherExp || 0) - (b.onlineInfo?.dispatcherExp || 0);
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
if ((a.onlineInfo?.currentUsers || 0) > (b.onlineInfo?.currentUsers || 0)) return sorter.dir;
|
||||
if ((a.onlineInfo?.currentUsers || 0) < (b.onlineInfo?.currentUsers || 0)) return -sorter.dir;
|
||||
|
||||
if ((a.onlineInfo?.maxUsers || 0) > (b.onlineInfo?.maxUsers || 0)) return sorter.dir;
|
||||
if ((a.onlineInfo?.maxUsers || 0) < (b.onlineInfo?.maxUsers || 0)) return -sorter.dir;
|
||||
diff = (b.onlineInfo ? b.onlineInfo.currentUsers : -1) - (a.onlineInfo ? a.onlineInfo.currentUsers : -1);
|
||||
break;
|
||||
|
||||
case 'spawn':
|
||||
if ((a.onlineInfo?.spawns.length || 0) > (b.onlineInfo?.spawns.length || 0)) return sorter.dir;
|
||||
if ((a.onlineInfo?.spawns.length || 0) < (b.onlineInfo?.spawns.length || 0)) return -sorter.dir;
|
||||
|
||||
diff = (a.onlineInfo ? a.onlineInfo.spawns.length : -1) - (b.onlineInfo ? b.onlineInfo.spawns.length : -1);
|
||||
break;
|
||||
|
||||
case 'timetable':
|
||||
if ((a.onlineInfo?.scheduledTrains?.length || 0) > (b.onlineInfo?.scheduledTrains?.length || 0))
|
||||
return sorter.dir;
|
||||
if ((a.onlineInfo?.scheduledTrains?.length || 0) < (b.onlineInfo?.scheduledTrains?.length || 0))
|
||||
return -sorter.dir;
|
||||
case 'timetableConfirmed':
|
||||
diff =
|
||||
(a.onlineInfo?.scheduledTrains
|
||||
? a.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
|
||||
: -1) -
|
||||
(b.onlineInfo?.scheduledTrains
|
||||
? b.onlineInfo.scheduledTrains.filter((train) => train.stopInfo.confirmed).length
|
||||
: -1);
|
||||
break;
|
||||
|
||||
case 'timetableUnconfirmed':
|
||||
diff =
|
||||
(a.onlineInfo?.scheduledTrains
|
||||
? a.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
|
||||
: -1) -
|
||||
(b.onlineInfo?.scheduledTrains
|
||||
? b.onlineInfo.scheduledTrains.filter((train) => !train.stopInfo.confirmed).length
|
||||
: -1);
|
||||
break;
|
||||
|
||||
case 'timetableAll':
|
||||
diff =
|
||||
(a.onlineInfo?.scheduledTrains ? a.onlineInfo.scheduledTrains.length : -1) -
|
||||
(b.onlineInfo?.scheduledTrains ? b.onlineInfo.scheduledTrains.length : -1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (diff != 0) return Math.sign(diff) * sorter.dir;
|
||||
return a.name.localeCompare(b.name);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useJournalFiltersStore = defineStore('journalFiltersStore', {
|
||||
state: () => ({
|
||||
timetableFilters: {
|
||||
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -3,8 +3,8 @@ import inputData from '../data/options.json';
|
||||
import Station from '../scripts/interfaces/Station';
|
||||
import StorageManager from '../scripts/managers/storageManager';
|
||||
import { useStore } from './store';
|
||||
import { filterInitStates } from './constants/initFilterStates';
|
||||
import { filterStations, sortStations } from './utils/filterUtils';
|
||||
import { filterInitStates } from '../scripts/constants/stores/initFilterStates';
|
||||
import { filterStations, sortStations } from '../scripts/utils/filterUtils';
|
||||
import { HeadIdsTypes } from '../scripts/data/stationHeaderNames';
|
||||
|
||||
export const useStationFiltersStore = defineStore('stationFiltersStore', {
|
||||
|
||||
+31
-49
@@ -3,7 +3,7 @@ import { defineStore } from 'pinia';
|
||||
import { io } from 'socket.io-client';
|
||||
import { DataStatus } from '../scripts/enums/DataStatus';
|
||||
import StationAPIData from '../scripts/interfaces/api/StationAPIData';
|
||||
import {ScheduledTrain} from '../scripts/interfaces/ScheduledTrain';
|
||||
import { ScheduledTrain } from '../scripts/interfaces/ScheduledTrain';
|
||||
import Station from '../scripts/interfaces/Station';
|
||||
import StationRoutes from '../scripts/interfaces/StationRoutes';
|
||||
import Train from '../scripts/interfaces/Train';
|
||||
@@ -54,7 +54,7 @@ export const useStore = defineStore('store', {
|
||||
trains: DataStatus.Loading,
|
||||
},
|
||||
|
||||
currentStatsTab: 'daily',
|
||||
currentStatsTab: null,
|
||||
|
||||
blockScroll: false,
|
||||
listenerLaunched: false,
|
||||
@@ -303,57 +303,39 @@ export const useStore = defineStore('store', {
|
||||
...scenery,
|
||||
authors: scenery.authors?.split(',').map((a) => a.trim()),
|
||||
routes:
|
||||
scenery.routes
|
||||
?.split(';')
|
||||
.filter((routeString) => routeString)
|
||||
.reduce(
|
||||
(acc, routeString) => {
|
||||
const specs1 = routeString.split('_')[0];
|
||||
const isInternal = specs1.startsWith('!');
|
||||
const name = isInternal ? specs1.replace('!', '') : specs1;
|
||||
scenery.routesInfo.reduce(
|
||||
(acc, route) => {
|
||||
const propName: keyof StationRoutes = `${route.routeTracks == 2 ? 'twoWay' : 'oneWay'}${
|
||||
route.isElectric ? '' : 'No'
|
||||
}CatenaryRouteNames`;
|
||||
|
||||
const specs2 = routeString.split('_')[1].split('');
|
||||
const twoWay = specs2[0] == '2';
|
||||
const catenary = specs2[1] == 'E';
|
||||
const SBL = specs2[2] == 'S';
|
||||
const TWB = specs2[3] ? true : false;
|
||||
const speed = Number(routeString.split(':')[1]) || 0;
|
||||
const length = Number(routeString.split(':')[2]) || 0;
|
||||
acc[route.routeTracks == 2 ? 'twoWay' : 'oneWay'].push({
|
||||
name: route.routeName,
|
||||
SBL: route.isRouteSBL,
|
||||
TWB: false,
|
||||
catenary: route.isElectric,
|
||||
isInternal: route.isInternal,
|
||||
tracks: route.routeTracks,
|
||||
length: route.routeLength,
|
||||
speed: route.routeSpeed,
|
||||
});
|
||||
|
||||
const propName = twoWay
|
||||
? catenary
|
||||
? 'twoWayCatenaryRouteNames'
|
||||
: 'twoWayNoCatenaryRouteNames'
|
||||
: catenary
|
||||
? 'oneWayCatenaryRouteNames'
|
||||
: 'oneWayNoCatenaryRouteNames';
|
||||
if (!route.isInternal) acc[propName].push(route.routeName);
|
||||
|
||||
acc[twoWay ? 'twoWay' : 'oneWay'].push({
|
||||
name,
|
||||
SBL,
|
||||
TWB,
|
||||
catenary,
|
||||
isInternal,
|
||||
tracks: twoWay ? 2 : 1,
|
||||
length,
|
||||
speed,
|
||||
});
|
||||
if (!isInternal) acc[propName].push(name);
|
||||
if (route.isRouteSBL) acc['sblRouteNames'].push(route.routeName);
|
||||
|
||||
if (SBL) acc['sblRouteNames'].push(name);
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
oneWay: [],
|
||||
twoWay: [],
|
||||
sblRouteNames: [],
|
||||
oneWayCatenaryRouteNames: [],
|
||||
oneWayNoCatenaryRouteNames: [],
|
||||
twoWayCatenaryRouteNames: [],
|
||||
twoWayNoCatenaryRouteNames: [],
|
||||
} as StationRoutes
|
||||
) || {},
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
oneWay: [],
|
||||
twoWay: [],
|
||||
sblRouteNames: [],
|
||||
oneWayCatenaryRouteNames: [],
|
||||
oneWayNoCatenaryRouteNames: [],
|
||||
twoWayCatenaryRouteNames: [],
|
||||
twoWayNoCatenaryRouteNames: [],
|
||||
} as StationRoutes
|
||||
) || {},
|
||||
checkpoints: scenery.checkpoints
|
||||
? scenery.checkpoints.split(';').map((sub) => ({ checkpointName: sub, scheduledTrains: [] }))
|
||||
: [],
|
||||
|
||||
@@ -55,3 +55,26 @@
|
||||
background-color: forestgreen;
|
||||
}
|
||||
}
|
||||
|
||||
.train-badge {
|
||||
padding: 0.1em 0.2em;
|
||||
border-radius: 0.2em;
|
||||
font-weight: bold;
|
||||
|
||||
font-size: 0.9em;
|
||||
|
||||
&.twr {
|
||||
background-color: var(--clr-twr);
|
||||
box-shadow: 0 0 5px 1px var(--clr-twr);
|
||||
color: black;
|
||||
}
|
||||
|
||||
&.skr {
|
||||
background-color: var(--clr-skr);
|
||||
box-shadow: 0 0 5px 1px var(--clr-skr);
|
||||
}
|
||||
|
||||
&.offline {
|
||||
background-color: #be3728;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +82,16 @@ h1.option-title {
|
||||
padding: 0.25em 0.25em 0 0;
|
||||
}
|
||||
|
||||
.options_filter-sections section {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.options_filters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
gap: 0.5em;
|
||||
margin: 0.5em 0 0 0;
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
|
||||
.sort-option,
|
||||
@@ -118,17 +122,6 @@ h1.option-title {
|
||||
margin: 0.5em auto;
|
||||
}
|
||||
|
||||
.search_actions {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
margin: 1em 0;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
.search-exit {
|
||||
position: absolute;
|
||||
@@ -139,6 +132,17 @@ h1.option-title {
|
||||
}
|
||||
}
|
||||
|
||||
.options_actions {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
width: 100%;
|
||||
margin-top: 1em;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallScreen() {
|
||||
h1 {
|
||||
text-align: center;
|
||||
@@ -155,13 +159,12 @@ h1.option-title {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.filter-option,
|
||||
.sort-option {
|
||||
margin: 0.25em 0.25em;
|
||||
}
|
||||
|
||||
.options_filters,
|
||||
.options_sorters {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { JournalFilterType } from '../../scripts/enums/JournalFilterType';
|
||||
|
||||
export type JournalTimetableSearchKey = 'search-driver' | 'search-train' | 'search-date' | 'search-dispatcher';
|
||||
|
||||
export type JournalTimetableSearchType = {
|
||||
[key in JournalTimetableSearchKey]: string;
|
||||
};
|
||||
|
||||
export interface JournalTimetableFilter {
|
||||
id: JournalFilterType;
|
||||
filterSection: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface JournalTimetableSorter {
|
||||
id: 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
|
||||
dir: -1 | 1;
|
||||
}
|
||||
@@ -70,7 +70,7 @@ import { URLs } from '../scripts/utils/apiURLs';
|
||||
import { DataStatus } from '../scripts/enums/DataStatus';
|
||||
import { useStore } from '../store/store';
|
||||
import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue';
|
||||
import { JournalDispatcherSearcher, JournalDispatcherSorter } from '../types/Journal/JournalDispatcherTypes';
|
||||
import { JournalDispatcherSearcher, JournalDispatcherSorter } from '../scripts/types/JournalDispatcherTypes';
|
||||
import { DispatcherHistory } from '../scripts/interfaces/api/DispatchersAPIData';
|
||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||
import { LocationQuery } from 'vue-router';
|
||||
@@ -134,6 +134,7 @@ export default defineComponent({
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('journalFilterActive', journalFilterActive);
|
||||
provide('searchersValues', searchersValues);
|
||||
provide('filterList', reactive([]));
|
||||
|
||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||
|
||||
@@ -287,4 +288,3 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/JournalSection.scss';
|
||||
</style>
|
||||
|
||||
|
||||
+103
-64
@@ -7,7 +7,7 @@
|
||||
@on-search-confirm="fetchHistoryData"
|
||||
@on-options-reset="resetOptions"
|
||||
@on-refresh-data="fetchHistoryData"
|
||||
:sorter-option-ids="['timetableId', 'beginDate', 'distance', 'total-stops']"
|
||||
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
||||
:filters="journalTimetableFilters"
|
||||
:currentOptionsActive="currentOptionsActive"
|
||||
:data-status="dataStatus"
|
||||
@@ -58,25 +58,32 @@
|
||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
import DriverStats from '../components/JournalView/JournalDriverStats.vue';
|
||||
import Loading from '../components/Global/Loading.vue';
|
||||
import { JournalTimetableSorter } from '../types/Journal/JournalTimetablesTypes';
|
||||
import imageMixin from '../mixins/imageMixin';
|
||||
import dateMixin from '../mixins/dateMixin';
|
||||
import routerMixin from '../mixins/routerMixin';
|
||||
import modalTrainMixin from '../mixins/modalTrainMixin';
|
||||
|
||||
import DriverStats from '../components/JournalView/JournalDriverStats.vue';
|
||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||
import JournalStats from '../components/JournalView/JournalStats.vue';
|
||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||
import JournalTimetablesList from '../components/JournalView/JournalTimetablesList.vue';
|
||||
import Loading from '../components/Global/Loading.vue';
|
||||
|
||||
import { DataStatus } from '../scripts/enums/DataStatus';
|
||||
import { JournalFilterType } from '../scripts/enums/JournalFilterType';
|
||||
import { TimetableHistory } from '../scripts/interfaces/api/TimetablesAPIData';
|
||||
import { URLs } from '../scripts/utils/apiURLs';
|
||||
import { useStore } from '../store/store';
|
||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||
import { JournalTimetableSearchType } from '../types/Journal/JournalTimetablesTypes';
|
||||
import modalTrainMixin from '../mixins/modalTrainMixin';
|
||||
import imageMixin from '../mixins/imageMixin';
|
||||
import JournalTimetablesList from '../components/JournalView/JournalTimetablesList.vue';
|
||||
import { journalTimetableFilters } from '../constants/Journal/JournalTimetablesConsts';
|
||||
import JournalStats from '../components/JournalView/JournalStats.vue';
|
||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||
|
||||
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';
|
||||
|
||||
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
|
||||
|
||||
@@ -93,8 +100,7 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
currentQuery: '',
|
||||
currentQueryArray: [] as string[],
|
||||
currentQueryParams: {} as TimetablesQueryParams,
|
||||
|
||||
scrollDataLoaded: true,
|
||||
scrollNoMoreData: false,
|
||||
@@ -113,13 +119,16 @@ export default defineComponent({
|
||||
}),
|
||||
|
||||
setup() {
|
||||
const sorterActive: JournalTimetableSorter = reactive({ id: 'timetableId', dir: 1 });
|
||||
const journalFilterActive = ref(journalTimetableFilters[0]);
|
||||
const sorterActive: JournalTimetableSorter = reactive({ id: 'timetableId', dir: 'desc' });
|
||||
// const journalFilterActive = ref(journalTimetableFilters[0]);
|
||||
const initFilters: readonly JournalFilter[] = JSON.parse(JSON.stringify(journalTimetableFilters));
|
||||
const filterList: JournalFilter[] = reactive(JSON.parse(JSON.stringify(initFilters)));
|
||||
|
||||
const searchersValues = reactive({
|
||||
'search-train': '',
|
||||
'search-driver': '',
|
||||
'search-dispatcher': '',
|
||||
'search-issuedFrom': '',
|
||||
'search-date': '',
|
||||
} as JournalTimetableSearchType);
|
||||
|
||||
@@ -128,14 +137,15 @@ export default defineComponent({
|
||||
|
||||
provide('searchersValues', searchersValues);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('journalFilterActive', journalFilterActive);
|
||||
provide('filterList', filterList);
|
||||
|
||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||
|
||||
return {
|
||||
sorterActive,
|
||||
journalFilterActive,
|
||||
searchersValues,
|
||||
filterList,
|
||||
initFilters,
|
||||
|
||||
countFromIndex,
|
||||
countLimit,
|
||||
@@ -147,8 +157,8 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentQueryArray(q: string[]) {
|
||||
this.currentOptionsActive = q.length >= 2 || q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1]);
|
||||
currentQueryParams(q: TimetablesQueryParams) {
|
||||
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -163,7 +173,6 @@ export default defineComponent({
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
handleScroll(e: Event) {
|
||||
const listElement = e.target as HTMLElement;
|
||||
@@ -179,29 +188,35 @@ export default defineComponent({
|
||||
if ('timetableId' in query) this.searchersValues['search-train'] = `#${query.timetableId}`;
|
||||
},
|
||||
|
||||
setSearchers(date: string, driver: string, train: string, dispatcher: string) {
|
||||
setSearchers(date: string, driver: string, train: string, dispatcher: string, issuedFrom: string) {
|
||||
this.searchersValues['search-date'] = date;
|
||||
this.searchersValues['search-driver'] = driver;
|
||||
this.searchersValues['search-train'] = train;
|
||||
this.searchersValues['search-dispatcher'] = dispatcher;
|
||||
this.searchersValues['search-issuedFrom'] = issuedFrom;
|
||||
},
|
||||
|
||||
resetOptions() {
|
||||
this.setSearchers('', '', '', '');
|
||||
this.setSearchers('', '', '', '', '');
|
||||
|
||||
this.journalFilterActive = this.journalTimetableFilters[0];
|
||||
this.sorterActive.id = 'timetableId';
|
||||
|
||||
this.filterList.forEach(
|
||||
(f) => (f.isActive = this.initFilters.find((initFilter) => initFilter.id == f.id)?.isActive || false)
|
||||
);
|
||||
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
async addHistoryData() {
|
||||
this.scrollDataLoaded = false;
|
||||
|
||||
const countFrom = this.timetableHistory.length;
|
||||
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
||||
|
||||
const responseData: TimetableHistory[] = await (
|
||||
await axios.get(`${TIMETABLES_API_URL}?${this.currentQuery}&countFrom=${countFrom}`)
|
||||
await axios.get(`${TIMETABLES_API_URL}`, {
|
||||
params: { ...this.currentQueryParams },
|
||||
})
|
||||
).data;
|
||||
|
||||
if (!responseData) return;
|
||||
@@ -216,57 +231,82 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
async fetchHistoryData() {
|
||||
// if(this.dataStatus == DataStatus.Loading) return;
|
||||
|
||||
const queries: string[] = [];
|
||||
|
||||
const driverName = this.searchersValues['search-driver'].trim();
|
||||
const trainNo = this.searchersValues['search-train'].trim();
|
||||
const authorName = this.searchersValues['search-dispatcher'].trim();
|
||||
const dateString = this.searchersValues['search-date'].trim();
|
||||
const driverName = this.searchersValues['search-driver'].trim() || undefined;
|
||||
const trainNo = this.searchersValues['search-train'].trim() || undefined;
|
||||
const authorName = this.searchersValues['search-dispatcher'].trim() || undefined;
|
||||
const dateString = this.searchersValues['search-date'].trim() || undefined;
|
||||
const issuedFrom = this.searchersValues['search-issuedFrom'].trim() || undefined;
|
||||
|
||||
const timestampFrom = dateString ? Date.parse(new Date(dateString).toISOString()) - 120 * 60 * 1000 : undefined;
|
||||
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
||||
|
||||
if (driverName) queries.push(`driverName=${driverName}`);
|
||||
if (trainNo)
|
||||
queries.push(trainNo.startsWith('#') ? `timetableId=${trainNo.replace('#', '')}` : `trainNo=${trainNo}`);
|
||||
if (authorName) queries.push(`authorName=${authorName}`);
|
||||
if (timestampFrom && timestampTo) queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
||||
const queryParams: TimetablesQueryParams = {};
|
||||
|
||||
// Z API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
||||
if (this.sorterActive.id == 'distance') queries.push('sortBy=routeDistance');
|
||||
else if (this.sorterActive.id == 'total-stops') queries.push('sortBy=allStopsCount');
|
||||
else if (this.sorterActive.id == 'beginDate') queries.push('sortBy=beginDate');
|
||||
// else queries.push('sortBy=timetableId');
|
||||
this.filterList
|
||||
.filter((f) => f.isActive)
|
||||
.forEach((f) => {
|
||||
switch (f.id) {
|
||||
case JournalFilterType.ABANDONED:
|
||||
queryParams['fulfilled'] = 0;
|
||||
queryParams['terminated'] = 1;
|
||||
break;
|
||||
|
||||
queries.push('countLimit=15');
|
||||
case JournalFilterType.ACTIVE:
|
||||
queryParams['fulfilled'] = undefined;
|
||||
queryParams['terminated'] = 0;
|
||||
break;
|
||||
|
||||
switch (this.journalFilterActive.id) {
|
||||
case JournalFilterType.abandoned:
|
||||
queries.push('fulfilled=0', 'terminated=1');
|
||||
break;
|
||||
case JournalFilterType.FULFILLED:
|
||||
queryParams['terminated'] = undefined;
|
||||
queryParams['fulfilled'] = 1;
|
||||
break;
|
||||
|
||||
case JournalFilterType.active:
|
||||
queries.push('terminated=0');
|
||||
break;
|
||||
case JournalFilterType.ALL:
|
||||
queryParams['terminated'] = undefined;
|
||||
queryParams['fulfilled'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.fulfilled:
|
||||
queries.push('fulfilled=1');
|
||||
break;
|
||||
case JournalFilterType.TWR_SKR:
|
||||
queryParams['twr'] = undefined;
|
||||
queryParams['skr'] = undefined;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case JournalFilterType.TWR:
|
||||
queryParams['twr'] = 1;
|
||||
queryParams['skr'] = undefined;
|
||||
break;
|
||||
|
||||
if (this.currentQuery != queries.join('&')) this.dataStatus = DataStatus.Loading;
|
||||
case JournalFilterType.SKR:
|
||||
queryParams['twr'] = undefined;
|
||||
queryParams['skr'] = 1;
|
||||
break;
|
||||
|
||||
this.currentQuery = queries.join('&');
|
||||
this.currentQueryArray = queries;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
queryParams['driverName'] = driverName;
|
||||
queryParams['trainNo'] = trainNo;
|
||||
|
||||
queryParams['countFrom'] = undefined;
|
||||
queryParams['countLimit'] = undefined;
|
||||
|
||||
queryParams['authorName'] = authorName;
|
||||
queryParams['timestampFrom'] = timestampFrom;
|
||||
queryParams['timestampTo'] = timestampTo;
|
||||
queryParams['issuedFrom'] = issuedFrom;
|
||||
queryParams['sortBy'] = this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
|
||||
|
||||
if (JSON.stringify(this.currentQueryParams) != JSON.stringify(queryParams)) this.dataStatus = DataStatus.Loading;
|
||||
|
||||
this.currentQueryParams = queryParams;
|
||||
|
||||
try {
|
||||
const responseData: TimetableHistory[] = await (
|
||||
await axios.get(`${TIMETABLES_API_URL}?${this.currentQuery}`)
|
||||
await axios.get(`${TIMETABLES_API_URL}`, {
|
||||
params: this.currentQueryParams,
|
||||
})
|
||||
).data;
|
||||
|
||||
if (!responseData) {
|
||||
@@ -302,4 +342,3 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/JournalSection.scss';
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="scenery-view">
|
||||
<div class="scenery-offline" v-if="!stationInfo && isComponentVisible && store.dataStatuses.sceneries == 2">
|
||||
<div>{{ $t('scenery.no-scenery') }}</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<section class="trains-view">
|
||||
<div class="trains_wrapper">
|
||||
<TrainOptions
|
||||
:sorter-option-ids="['distance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']"
|
||||
:sorter-option-ids="['routeDistance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']"
|
||||
:current-options-active="currentOptionsActive"
|
||||
/>
|
||||
|
||||
@@ -21,7 +21,7 @@ import modalTrainMixin from '../mixins/modalTrainMixin';
|
||||
import Train from '../scripts/interfaces/Train';
|
||||
import { filteredTrainList } from '../scripts/managers/trainFilterManager';
|
||||
import { useStore } from '../store/store';
|
||||
import { TrainFilter } from '../types/Trains/TrainOptionsTypes';
|
||||
import { TrainFilter } from '../scripts/interfaces/Trains/TrainFilter';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -57,7 +57,7 @@ export default defineComponent({
|
||||
const store = useStore();
|
||||
const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))];
|
||||
|
||||
const sorterActive = reactive({ id: 'distance', dir: -1 });
|
||||
const sorterActive = reactive({ id: 'routeDistance', dir: -1 });
|
||||
const filterList = reactive([...trainFilters]) as TrainFilter[];
|
||||
|
||||
const currentOptionsActive = ref(false);
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
VitePWA({
|
||||
registerType: 'prompt',
|
||||
registerType: 'autoUpdate',
|
||||
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,png,svg,jpg}'],
|
||||
|
||||
Reference in New Issue
Block a user