mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-04 05:48:11 +00:00
format & lint
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<div class="error-view">
|
||||
<div class="container">
|
||||
<img :src="getIcon('error')" alt="error" />
|
||||
<div class="desc">Z powodu błędu w zapisywaniu rozkładów jazdy w tej zakładce można do odwołania nacieszyć się animacją sygnału S1a. Jak naprawię to będzie :)</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import imageMixin from "../mixins/imageMixin";
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [imageMixin]
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.error-view {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
margin-top: 5em;
|
||||
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
flex-direction: column;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
text-align: center;
|
||||
font-size: 1.4em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 15em;
|
||||
}
|
||||
</style>
|
||||
+272
-270
@@ -1,270 +1,272 @@
|
||||
<template>
|
||||
<section class="journal-dispatchers">
|
||||
<JournalHeader />
|
||||
|
||||
<div class="journal_wrapper">
|
||||
<JournalOptions
|
||||
@on-search-confirm="fetchHistoryData"
|
||||
@on-options-reset="resetOptions"
|
||||
@on-refresh-data="fetchHistoryData(true)"
|
||||
:sorter-option-ids="['timestampFrom', 'duration']"
|
||||
:data-status="dataStatus"
|
||||
:current-options-active="currentOptionsActive"
|
||||
optionsType="dispatchers"
|
||||
/>
|
||||
|
||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||
</div>
|
||||
|
||||
<div class="list_wrapper" @scroll="handleScroll">
|
||||
<JournalDispatchersList
|
||||
:dispatcherHistory="computedHistoryList"
|
||||
:addHistoryData="addHistoryData"
|
||||
:dataStatus="dataStatus"
|
||||
:scrollDataLoaded="scrollDataLoaded"
|
||||
:scrollNoMoreData="scrollNoMoreData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
import ActionButton from '../components/Global/ActionButton.vue';
|
||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||
import DispatcherStats from '../components/JournalView/DispatcherStats.vue';
|
||||
import SearchBox from '../components/Global/SearchBox.vue';
|
||||
|
||||
import Loading from '../components/Global/Loading.vue';
|
||||
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 '../scripts/types/JournalDispatcherTypes';
|
||||
import { DispatcherHistory } from '../scripts/interfaces/api/DispatchersAPIData';
|
||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||
import { LocationQuery } from 'vue-router';
|
||||
|
||||
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SearchBox,
|
||||
ActionButton,
|
||||
JournalOptions,
|
||||
DispatcherStats,
|
||||
Loading,
|
||||
JournalDispatchersList,
|
||||
JournalHeader,
|
||||
},
|
||||
name: 'JournalDispatchers',
|
||||
|
||||
props: {
|
||||
sceneryName: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
dispatcherName: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
currentQuery: '',
|
||||
currentQueryArray: [] as string[],
|
||||
dataRefreshedAt: null as Date | null,
|
||||
|
||||
scrollDataLoaded: true,
|
||||
scrollNoMoreData: false,
|
||||
|
||||
showReturnButton: false,
|
||||
statsCardOpen: false,
|
||||
currentOptionsActive: false,
|
||||
|
||||
dataStatus: DataStatus.Loading,
|
||||
DataStatus,
|
||||
|
||||
historyList: [] as DispatcherHistory[],
|
||||
}),
|
||||
|
||||
setup() {
|
||||
const sorterActive: JournalDispatcherSorter = reactive({ id: 'timestampFrom', dir: -1 });
|
||||
const journalFilterActive = ref({});
|
||||
|
||||
const searchersValues = reactive({
|
||||
'search-dispatcher': '',
|
||||
'search-station': '',
|
||||
'search-date': '',
|
||||
} as JournalDispatcherSearcher);
|
||||
|
||||
const countFromIndex = ref(0);
|
||||
const countLimit = 15;
|
||||
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('journalFilterActive', journalFilterActive);
|
||||
provide('searchersValues', searchersValues);
|
||||
provide('filterList', reactive([]));
|
||||
|
||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||
|
||||
return {
|
||||
store: useStore(),
|
||||
|
||||
sorterActive,
|
||||
searchersValues,
|
||||
|
||||
countFromIndex,
|
||||
countLimit,
|
||||
|
||||
scrollElement,
|
||||
maxCount: ref(15),
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentQueryArray(q: string[]) {
|
||||
this.currentOptionsActive =
|
||||
q.length > 2 || q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'timestampFrom');
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
computedHistoryList() {
|
||||
return this.historyList.filter(
|
||||
(doc) => doc.isOnline || (doc.currentDuration && doc.currentDuration > 10 * 60000)
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
beforeRouteUpdate(to, _) {
|
||||
this.handleQueries(to.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
activated() {
|
||||
this.handleQueries(this.$route.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleScroll(e: Event) {
|
||||
const listElement = e.target as HTMLElement;
|
||||
const scrollTop = listElement.scrollTop;
|
||||
const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
|
||||
|
||||
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded) return;
|
||||
|
||||
if (scrollTop > elementHeight * 0.85) this.addHistoryData();
|
||||
},
|
||||
|
||||
handleQueries(query: LocationQuery) {
|
||||
const queryKeys = Object.keys(query);
|
||||
|
||||
if (queryKeys.includes('sceneryName')) this.setSearchers('', `${query.sceneryName}`, '');
|
||||
if (queryKeys.includes('dispatcherName')) this.setSearchers('', '', `${query.dispatcherName}`);
|
||||
},
|
||||
|
||||
setSearchers(date: string, station: string, dispatcher: string) {
|
||||
this.searchersValues['search-date'] = date;
|
||||
this.searchersValues['search-station'] = station;
|
||||
this.searchersValues['search-dispatcher'] = dispatcher;
|
||||
},
|
||||
|
||||
resetOptions() {
|
||||
this.setSearchers('', '', '');
|
||||
this.sorterActive.id = 'timestampFrom';
|
||||
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
async addHistoryData() {
|
||||
this.scrollDataLoaded = false;
|
||||
|
||||
this.countFromIndex = this.historyList.length;
|
||||
|
||||
const responseData: DispatcherHistory[] = await (
|
||||
await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}&countFrom=${this.countFromIndex}`)
|
||||
).data;
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
if (responseData.length == 0) {
|
||||
this.scrollNoMoreData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.historyList.push(...responseData);
|
||||
this.scrollDataLoaded = true;
|
||||
},
|
||||
|
||||
async fetchHistoryData(reset = false) {
|
||||
const queries: string[] = [];
|
||||
|
||||
const dispatcher = this.searchersValues['search-dispatcher'].trim();
|
||||
const station = this.searchersValues['search-station'].trim();
|
||||
const dateString = this.searchersValues['search-date'].trim();
|
||||
|
||||
const timestampFrom = dateString ? Date.parse(new Date(dateString).toISOString()) - 120 * 60 * 1000 : undefined;
|
||||
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
||||
|
||||
if (dispatcher) queries.push(`dispatcherName=${dispatcher}`);
|
||||
if (station) queries.push(`stationName=${station}`);
|
||||
if (timestampFrom && timestampTo) queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
||||
|
||||
// API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
||||
if (this.sorterActive.id == 'timestampFrom') queries.push('sortBy=timestampFrom');
|
||||
else if (this.sorterActive.id == 'duration') queries.push('sortBy=currentDuration');
|
||||
else queries.push('sortBy=timestampFrom');
|
||||
|
||||
queries.push('countLimit=30');
|
||||
|
||||
if (this.currentQuery != queries.join('&')) this.dataStatus = DataStatus.Loading;
|
||||
|
||||
this.currentQuery = queries.join('&');
|
||||
this.currentQueryArray = queries;
|
||||
|
||||
try {
|
||||
if (reset) this.dataStatus = DataStatus.Loading;
|
||||
|
||||
const responseData: DispatcherHistory[] = await (
|
||||
await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}`)
|
||||
).data;
|
||||
|
||||
if (!responseData) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
// Response data exists
|
||||
this.historyList = responseData;
|
||||
|
||||
// Stats display
|
||||
this.store.dispatcherStatsName =
|
||||
this.historyList.length > 0 && this.searchersValues['search-dispatcher'].trim()
|
||||
? this.historyList[0].dispatcherName
|
||||
: '';
|
||||
|
||||
this.dataRefreshedAt = new Date();
|
||||
this.dataStatus = DataStatus.Loaded;
|
||||
} catch (error) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
}
|
||||
|
||||
this.scrollNoMoreData = false;
|
||||
this.scrollDataLoaded = true;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/JournalSection.scss';
|
||||
</style>
|
||||
<template>
|
||||
<section class="journal-dispatchers">
|
||||
<JournalHeader />
|
||||
|
||||
<div class="journal_wrapper">
|
||||
<JournalOptions
|
||||
@on-search-confirm="fetchHistoryData"
|
||||
@on-options-reset="resetOptions"
|
||||
@on-refresh-data="fetchHistoryData(true)"
|
||||
:sorter-option-ids="['timestampFrom', 'duration']"
|
||||
:data-status="dataStatus"
|
||||
:current-options-active="currentOptionsActive"
|
||||
optionsType="dispatchers"
|
||||
/>
|
||||
|
||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||
</div>
|
||||
|
||||
<div class="list_wrapper" @scroll="handleScroll">
|
||||
<JournalDispatchersList
|
||||
:dispatcherHistory="computedHistoryList"
|
||||
:addHistoryData="addHistoryData"
|
||||
:dataStatus="dataStatus"
|
||||
:scrollDataLoaded="scrollDataLoaded"
|
||||
:scrollNoMoreData="scrollNoMoreData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||
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 '../scripts/types/JournalDispatcherTypes';
|
||||
import { DispatcherHistory } from '../scripts/interfaces/api/DispatchersAPIData';
|
||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||
import { LocationQuery } from 'vue-router';
|
||||
|
||||
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
JournalOptions,
|
||||
JournalDispatchersList,
|
||||
JournalHeader
|
||||
},
|
||||
name: 'JournalDispatchers',
|
||||
|
||||
props: {
|
||||
sceneryName: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
|
||||
dispatcherName: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
currentQuery: '',
|
||||
currentQueryArray: [] as string[],
|
||||
dataRefreshedAt: null as Date | null,
|
||||
|
||||
scrollDataLoaded: true,
|
||||
scrollNoMoreData: false,
|
||||
|
||||
showReturnButton: false,
|
||||
statsCardOpen: false,
|
||||
currentOptionsActive: false,
|
||||
|
||||
dataStatus: DataStatus.Loading,
|
||||
DataStatus,
|
||||
|
||||
historyList: [] as DispatcherHistory[]
|
||||
}),
|
||||
|
||||
setup() {
|
||||
const sorterActive: JournalDispatcherSorter = reactive({ id: 'timestampFrom', dir: -1 });
|
||||
const journalFilterActive = ref({});
|
||||
|
||||
const searchersValues = reactive({
|
||||
'search-dispatcher': '',
|
||||
'search-station': '',
|
||||
'search-date': ''
|
||||
} as JournalDispatcherSearcher);
|
||||
|
||||
const countFromIndex = ref(0);
|
||||
const countLimit = 15;
|
||||
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('journalFilterActive', journalFilterActive);
|
||||
provide('searchersValues', searchersValues);
|
||||
provide('filterList', reactive([]));
|
||||
|
||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||
|
||||
return {
|
||||
store: useStore(),
|
||||
|
||||
sorterActive,
|
||||
searchersValues,
|
||||
|
||||
countFromIndex,
|
||||
countLimit,
|
||||
|
||||
scrollElement,
|
||||
maxCount: ref(15)
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentQueryArray(q: string[]) {
|
||||
this.currentOptionsActive =
|
||||
q.length > 2 ||
|
||||
q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'timestampFrom');
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
computedHistoryList() {
|
||||
return this.historyList.filter(
|
||||
(doc) => doc.isOnline || (doc.currentDuration && doc.currentDuration > 10 * 60000)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteUpdate(to) {
|
||||
this.handleQueries(to.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
activated() {
|
||||
this.handleQueries(this.$route.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleScroll(e: Event) {
|
||||
const listElement = e.target as HTMLElement;
|
||||
const scrollTop = listElement.scrollTop;
|
||||
const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
|
||||
|
||||
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded)
|
||||
return;
|
||||
|
||||
if (scrollTop > elementHeight * 0.85) this.addHistoryData();
|
||||
},
|
||||
|
||||
handleQueries(query: LocationQuery) {
|
||||
const queryKeys = Object.keys(query);
|
||||
|
||||
if (queryKeys.includes('sceneryName')) this.setSearchers('', `${query.sceneryName}`, '');
|
||||
if (queryKeys.includes('dispatcherName'))
|
||||
this.setSearchers('', '', `${query.dispatcherName}`);
|
||||
},
|
||||
|
||||
setSearchers(date: string, station: string, dispatcher: string) {
|
||||
this.searchersValues['search-date'] = date;
|
||||
this.searchersValues['search-station'] = station;
|
||||
this.searchersValues['search-dispatcher'] = dispatcher;
|
||||
},
|
||||
|
||||
resetOptions() {
|
||||
this.setSearchers('', '', '');
|
||||
this.sorterActive.id = 'timestampFrom';
|
||||
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
async addHistoryData() {
|
||||
this.scrollDataLoaded = false;
|
||||
|
||||
this.countFromIndex = this.historyList.length;
|
||||
|
||||
const responseData: DispatcherHistory[] = await (
|
||||
await axios.get(
|
||||
`${DISPATCHERS_API_URL}?${this.currentQuery}&countFrom=${this.countFromIndex}`
|
||||
)
|
||||
).data;
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
if (responseData.length == 0) {
|
||||
this.scrollNoMoreData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.historyList.push(...responseData);
|
||||
this.scrollDataLoaded = true;
|
||||
},
|
||||
|
||||
async fetchHistoryData(reset = false) {
|
||||
const queries: string[] = [];
|
||||
|
||||
const dispatcher = this.searchersValues['search-dispatcher'].trim();
|
||||
const station = this.searchersValues['search-station'].trim();
|
||||
const dateString = this.searchersValues['search-date'].trim();
|
||||
|
||||
const timestampFrom = dateString
|
||||
? Date.parse(new Date(dateString).toISOString()) - 120 * 60 * 1000
|
||||
: undefined;
|
||||
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
||||
|
||||
if (dispatcher) queries.push(`dispatcherName=${dispatcher}`);
|
||||
if (station) queries.push(`stationName=${station}`);
|
||||
if (timestampFrom && timestampTo)
|
||||
queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
||||
|
||||
// API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
||||
if (this.sorterActive.id == 'timestampFrom') queries.push('sortBy=timestampFrom');
|
||||
else if (this.sorterActive.id == 'duration') queries.push('sortBy=currentDuration');
|
||||
else queries.push('sortBy=timestampFrom');
|
||||
|
||||
queries.push('countLimit=30');
|
||||
|
||||
if (this.currentQuery != queries.join('&')) this.dataStatus = DataStatus.Loading;
|
||||
|
||||
this.currentQuery = queries.join('&');
|
||||
this.currentQueryArray = queries;
|
||||
|
||||
try {
|
||||
if (reset) this.dataStatus = DataStatus.Loading;
|
||||
|
||||
const responseData: DispatcherHistory[] = await (
|
||||
await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}`)
|
||||
).data;
|
||||
|
||||
if (!responseData) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
// Response data exists
|
||||
this.historyList = responseData;
|
||||
|
||||
// Stats display
|
||||
this.store.dispatcherStatsName =
|
||||
this.historyList.length > 0 && this.searchersValues['search-dispatcher'].trim()
|
||||
? this.historyList[0].dispatcherName
|
||||
: '';
|
||||
|
||||
this.dataRefreshedAt = new Date();
|
||||
this.dataStatus = DataStatus.Loaded;
|
||||
} catch (error) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
}
|
||||
|
||||
this.scrollNoMoreData = false;
|
||||
this.scrollDataLoaded = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/JournalSection.scss';
|
||||
</style>
|
||||
|
||||
+349
-328
@@ -1,328 +1,349 @@
|
||||
<template>
|
||||
<section class="journal-timetables">
|
||||
<JournalHeader />
|
||||
|
||||
<div class="journal_wrapper">
|
||||
<JournalOptions
|
||||
@on-search-confirm="fetchHistoryData"
|
||||
@on-options-reset="resetOptions"
|
||||
@on-refresh-data="fetchHistoryData"
|
||||
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
||||
:filters="journalTimetableFilters"
|
||||
:currentOptionsActive="currentOptionsActive"
|
||||
:data-status="dataStatus"
|
||||
optionsType="timetables"
|
||||
/>
|
||||
|
||||
<JournalStats />
|
||||
|
||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||
</div>
|
||||
|
||||
<div class="list_wrapper" @scroll="handleScroll">
|
||||
<JournalTimetablesList
|
||||
:timetableHistory="timetableHistory"
|
||||
:addHistoryData="addHistoryData"
|
||||
:dataStatus="dataStatus"
|
||||
:scrollDataLoaded="scrollDataLoaded"
|
||||
:scrollNoMoreData="scrollNoMoreData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
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 Loading from '../components/Global/Loading.vue';
|
||||
|
||||
import { DataStatus } from '../scripts/enums/DataStatus';
|
||||
import { TimetableHistory } from '../scripts/interfaces/api/TimetablesAPIData';
|
||||
import { URLs } from '../scripts/utils/apiURLs';
|
||||
import { useStore } from '../store/store';
|
||||
|
||||
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';
|
||||
|
||||
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
|
||||
|
||||
export default defineComponent({
|
||||
components: { DriverStats, Loading, JournalOptions, JournalStats, JournalHeader, JournalTimetablesList },
|
||||
mixins: [dateMixin, routerMixin, modalTrainMixin, imageMixin],
|
||||
|
||||
name: 'JournalTimetables',
|
||||
|
||||
props: {
|
||||
timetableId: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
currentQueryParams: {} as TimetablesQueryParams,
|
||||
dataRefreshedAt: null as Date | null,
|
||||
|
||||
scrollDataLoaded: true,
|
||||
scrollNoMoreData: false,
|
||||
|
||||
showReturnButton: false,
|
||||
statsCardOpen: false,
|
||||
currentOptionsActive: false,
|
||||
|
||||
timetableHistory: [] as TimetableHistory[],
|
||||
journalTimetableFilters,
|
||||
|
||||
dataStatus: DataStatus.Loading,
|
||||
dataErrorMessage: '',
|
||||
|
||||
DataStatus,
|
||||
}),
|
||||
|
||||
setup() {
|
||||
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);
|
||||
|
||||
const countFromIndex = ref(0);
|
||||
const countLimit = 15;
|
||||
|
||||
provide('searchersValues', searchersValues);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('filterList', filterList);
|
||||
|
||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||
|
||||
return {
|
||||
sorterActive,
|
||||
searchersValues,
|
||||
filterList,
|
||||
initFilters,
|
||||
|
||||
countFromIndex,
|
||||
countLimit,
|
||||
|
||||
scrollElement,
|
||||
|
||||
store: useStore(),
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentQueryParams(q: TimetablesQueryParams) {
|
||||
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
||||
},
|
||||
},
|
||||
|
||||
// Handle route updates for route-links
|
||||
beforeRouteUpdate(to, _) {
|
||||
this.handleQueries(to.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
activated() {
|
||||
this.handleQueries(this.$route.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleScroll(e: Event) {
|
||||
const listElement = e.target as HTMLElement;
|
||||
const scrollTop = listElement.scrollTop;
|
||||
const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
|
||||
|
||||
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded) return;
|
||||
|
||||
if (scrollTop > elementHeight * 0.85) this.addHistoryData();
|
||||
},
|
||||
|
||||
handleQueries(query: LocationQuery) {
|
||||
const queryKeys = Object.keys(query);
|
||||
|
||||
if (queryKeys.includes('timetableId')) this.setSearchers('', '', `#${query.timetableId}`, '', '');
|
||||
if (queryKeys.includes('issuedFrom')) this.setSearchers('', '', '', '', `${query.issuedFrom}`);
|
||||
if (queryKeys.includes('authorName')) this.setSearchers('', '', '', `${query.authorName}`, '');
|
||||
},
|
||||
|
||||
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.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;
|
||||
|
||||
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
||||
|
||||
const responseData: TimetableHistory[] = await (
|
||||
await axios.get(`${TIMETABLES_API_URL}`, {
|
||||
params: { ...this.currentQueryParams },
|
||||
})
|
||||
).data;
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
if (responseData.length == 0) {
|
||||
this.scrollNoMoreData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.timetableHistory.push(...responseData);
|
||||
this.scrollDataLoaded = true;
|
||||
},
|
||||
|
||||
async fetchHistoryData() {
|
||||
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;
|
||||
|
||||
const queryParams: TimetablesQueryParams = {};
|
||||
|
||||
this.filterList
|
||||
.filter((f) => f.isActive)
|
||||
.forEach((f) => {
|
||||
switch (f.id) {
|
||||
case JournalFilterType.ABANDONED:
|
||||
queryParams['fulfilled'] = 0;
|
||||
queryParams['terminated'] = 1;
|
||||
break;
|
||||
|
||||
case JournalFilterType.ACTIVE:
|
||||
queryParams['fulfilled'] = undefined;
|
||||
queryParams['terminated'] = 0;
|
||||
break;
|
||||
|
||||
case JournalFilterType.FULFILLED:
|
||||
queryParams['terminated'] = undefined;
|
||||
queryParams['fulfilled'] = 1;
|
||||
break;
|
||||
|
||||
case JournalFilterType.ALL:
|
||||
queryParams['terminated'] = undefined;
|
||||
queryParams['fulfilled'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.TWR_SKR:
|
||||
queryParams['twr'] = undefined;
|
||||
queryParams['skr'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.TWR:
|
||||
queryParams['twr'] = 1;
|
||||
queryParams['skr'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.SKR:
|
||||
queryParams['twr'] = undefined;
|
||||
queryParams['skr'] = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
queryParams['driverName'] = driverName;
|
||||
queryParams[trainNo?.startsWith('#') ? 'timetableId' : 'trainNo'] = trainNo?.replace('#', '');
|
||||
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}`, {
|
||||
params: this.currentQueryParams,
|
||||
})
|
||||
).data;
|
||||
|
||||
if (!responseData) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
this.dataErrorMessage = 'Brak danych!';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
// Response data exists
|
||||
this.timetableHistory = responseData;
|
||||
|
||||
// Stats display
|
||||
this.store.driverStatsName =
|
||||
this.timetableHistory.length > 0 && this.searchersValues['search-driver'].trim()
|
||||
? this.timetableHistory[0].driverName
|
||||
: '';
|
||||
|
||||
this.dataStatus = DataStatus.Loaded;
|
||||
this.dataRefreshedAt = new Date();
|
||||
} catch (error) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
this.dataErrorMessage = 'Ups! Coś poszło nie tak!';
|
||||
}
|
||||
|
||||
this.scrollNoMoreData = false;
|
||||
this.scrollDataLoaded = true;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/JournalSection.scss';
|
||||
</style>
|
||||
<template>
|
||||
<section class="journal-timetables">
|
||||
<JournalHeader />
|
||||
|
||||
<div class="journal_wrapper">
|
||||
<JournalOptions
|
||||
@on-search-confirm="fetchHistoryData"
|
||||
@on-options-reset="resetOptions"
|
||||
@on-refresh-data="fetchHistoryData"
|
||||
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
||||
:filters="journalTimetableFilters"
|
||||
:currentOptionsActive="currentOptionsActive"
|
||||
:data-status="dataStatus"
|
||||
optionsType="timetables"
|
||||
/>
|
||||
|
||||
<JournalStats />
|
||||
|
||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||
</div>
|
||||
|
||||
<div class="list_wrapper" @scroll="handleScroll">
|
||||
<JournalTimetablesList
|
||||
:timetableHistory="timetableHistory"
|
||||
:addHistoryData="addHistoryData"
|
||||
:dataStatus="dataStatus"
|
||||
:scrollDataLoaded="scrollDataLoaded"
|
||||
:scrollNoMoreData="scrollNoMoreData"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
import imageMixin from '../mixins/imageMixin';
|
||||
import dateMixin from '../mixins/dateMixin';
|
||||
import routerMixin from '../mixins/routerMixin';
|
||||
import modalTrainMixin from '../mixins/modalTrainMixin';
|
||||
|
||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||
import JournalStats from '../components/JournalView/JournalStats.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 { useStore } from '../store/store';
|
||||
|
||||
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';
|
||||
|
||||
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
JournalOptions,
|
||||
JournalStats,
|
||||
JournalHeader,
|
||||
JournalTimetablesList
|
||||
},
|
||||
mixins: [dateMixin, routerMixin, modalTrainMixin, imageMixin],
|
||||
|
||||
name: 'JournalTimetables',
|
||||
|
||||
props: {
|
||||
timetableId: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
currentQueryParams: {} as TimetablesQueryParams,
|
||||
dataRefreshedAt: null as Date | null,
|
||||
|
||||
scrollDataLoaded: true,
|
||||
scrollNoMoreData: false,
|
||||
|
||||
showReturnButton: false,
|
||||
statsCardOpen: false,
|
||||
currentOptionsActive: false,
|
||||
|
||||
timetableHistory: [] as TimetableHistory[],
|
||||
journalTimetableFilters,
|
||||
|
||||
dataStatus: DataStatus.Loading,
|
||||
dataErrorMessage: '',
|
||||
|
||||
DataStatus
|
||||
}),
|
||||
|
||||
setup() {
|
||||
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);
|
||||
|
||||
const countFromIndex = ref(0);
|
||||
const countLimit = 15;
|
||||
|
||||
provide('searchersValues', searchersValues);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('filterList', filterList);
|
||||
|
||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||
|
||||
return {
|
||||
sorterActive,
|
||||
searchersValues,
|
||||
filterList,
|
||||
initFilters,
|
||||
|
||||
countFromIndex,
|
||||
countLimit,
|
||||
|
||||
scrollElement,
|
||||
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentQueryParams(q: TimetablesQueryParams) {
|
||||
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
||||
}
|
||||
},
|
||||
|
||||
// Handle route updates for route-links
|
||||
beforeRouteUpdate(to) {
|
||||
this.handleQueries(to.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
activated() {
|
||||
this.handleQueries(this.$route.query);
|
||||
this.fetchHistoryData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleScroll(e: Event) {
|
||||
const listElement = e.target as HTMLElement;
|
||||
const scrollTop = listElement.scrollTop;
|
||||
const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
|
||||
|
||||
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded)
|
||||
return;
|
||||
|
||||
if (scrollTop > elementHeight * 0.85) this.addHistoryData();
|
||||
},
|
||||
|
||||
handleQueries(query: LocationQuery) {
|
||||
const queryKeys = Object.keys(query);
|
||||
|
||||
if (queryKeys.includes('timetableId'))
|
||||
this.setSearchers('', '', `#${query.timetableId}`, '', '');
|
||||
if (queryKeys.includes('issuedFrom'))
|
||||
this.setSearchers('', '', '', '', `${query.issuedFrom}`);
|
||||
if (queryKeys.includes('authorName'))
|
||||
this.setSearchers('', '', '', `${query.authorName}`, '');
|
||||
},
|
||||
|
||||
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.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;
|
||||
|
||||
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
||||
|
||||
const responseData: TimetableHistory[] = await (
|
||||
await axios.get(`${TIMETABLES_API_URL}`, {
|
||||
params: { ...this.currentQueryParams }
|
||||
})
|
||||
).data;
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
if (responseData.length == 0) {
|
||||
this.scrollNoMoreData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.timetableHistory.push(...responseData);
|
||||
this.scrollDataLoaded = true;
|
||||
},
|
||||
|
||||
async fetchHistoryData() {
|
||||
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;
|
||||
|
||||
const queryParams: TimetablesQueryParams = {};
|
||||
|
||||
this.filterList
|
||||
.filter((f) => f.isActive)
|
||||
.forEach((f) => {
|
||||
switch (f.id) {
|
||||
case JournalFilterType.ABANDONED:
|
||||
queryParams['fulfilled'] = 0;
|
||||
queryParams['terminated'] = 1;
|
||||
break;
|
||||
|
||||
case JournalFilterType.ACTIVE:
|
||||
queryParams['fulfilled'] = undefined;
|
||||
queryParams['terminated'] = 0;
|
||||
break;
|
||||
|
||||
case JournalFilterType.FULFILLED:
|
||||
queryParams['terminated'] = undefined;
|
||||
queryParams['fulfilled'] = 1;
|
||||
break;
|
||||
|
||||
case JournalFilterType.ALL:
|
||||
queryParams['terminated'] = undefined;
|
||||
queryParams['fulfilled'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.TWR_SKR:
|
||||
queryParams['twr'] = undefined;
|
||||
queryParams['skr'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.TWR:
|
||||
queryParams['twr'] = 1;
|
||||
queryParams['skr'] = undefined;
|
||||
break;
|
||||
|
||||
case JournalFilterType.SKR:
|
||||
queryParams['twr'] = undefined;
|
||||
queryParams['skr'] = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
queryParams['driverName'] = driverName;
|
||||
queryParams[trainNo?.startsWith('#') ? 'timetableId' : 'trainNo'] = trainNo?.replace('#', '');
|
||||
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}`, {
|
||||
params: this.currentQueryParams
|
||||
})
|
||||
).data;
|
||||
|
||||
if (!responseData) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
this.dataErrorMessage = 'Brak danych!';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
// Response data exists
|
||||
this.timetableHistory = responseData;
|
||||
|
||||
// Stats display
|
||||
this.store.driverStatsName =
|
||||
this.timetableHistory.length > 0 && this.searchersValues['search-driver'].trim()
|
||||
? this.timetableHistory[0].driverName
|
||||
: '';
|
||||
|
||||
this.dataStatus = DataStatus.Loaded;
|
||||
this.dataRefreshedAt = new Date();
|
||||
} catch (error) {
|
||||
this.dataStatus = DataStatus.Error;
|
||||
this.dataErrorMessage = 'Ups! Coś poszło nie tak!';
|
||||
}
|
||||
|
||||
this.scrollNoMoreData = false;
|
||||
this.scrollDataLoaded = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/JournalSection.scss';
|
||||
</style>
|
||||
|
||||
+303
-288
@@ -1,288 +1,303 @@
|
||||
<template>
|
||||
<div class="scenery-view">
|
||||
<div class="scenery-offline" v-if="!stationInfo && isComponentVisible && store.dataStatuses.sceneries == 2">
|
||||
<div>{{ $t('scenery.no-scenery') }}</div>
|
||||
|
||||
<action-button>
|
||||
<router-link to="/">{{ $t('scenery.return-btn') }}</router-link>
|
||||
</action-button>
|
||||
</div>
|
||||
|
||||
<div class="scenery-wrapper" v-if="stationInfo" ref="card-wrapper" :data-timetable-only="timetableOnly">
|
||||
<div class="scenery-left" v-if="!timetableOnly">
|
||||
<div class="scenery-actions">
|
||||
<button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')">
|
||||
<img :src="getIcon('back')" alt="Back to scenery" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<SceneryHeader :station="stationInfo" />
|
||||
<SceneryInfo :station="stationInfo" />
|
||||
</div>
|
||||
|
||||
<div class="scenery-right">
|
||||
<div class="info-actions">
|
||||
<button
|
||||
v-for="viewMode in viewModes"
|
||||
class="btn btn--option"
|
||||
@click="setViewMode(viewMode.component)"
|
||||
:data-checked="currentViewCompontent == viewMode.component"
|
||||
>
|
||||
{{ $t(viewMode.id) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<keep-alive>
|
||||
<component :is="currentViewCompontent" :station="stationInfo" :key="currentViewCompontent"></component>
|
||||
</keep-alive>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import routerMixin from '../mixins/routerMixin';
|
||||
import { useStore } from '../store/store';
|
||||
|
||||
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
|
||||
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
|
||||
import SceneryTimetable from '../components/SceneryView/SceneryTimetable.vue';
|
||||
import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue';
|
||||
import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue';
|
||||
import ActionButton from '../components/Global/ActionButton.vue';
|
||||
import imageMixin from '../mixins/imageMixin';
|
||||
|
||||
enum SceneryViewMode {
|
||||
'TIMETABLES_ACTIVE',
|
||||
'TIMETABLES_HISTORY',
|
||||
'SCENERY_HISTORY',
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SceneryInfo,
|
||||
SceneryTimetable,
|
||||
ActionButton,
|
||||
SceneryHeader,
|
||||
SceneryTimetablesHistory,
|
||||
SceneryDispatchersHistory,
|
||||
},
|
||||
|
||||
mixins: [routerMixin, imageMixin],
|
||||
|
||||
data: () => ({
|
||||
viewModes: [
|
||||
{
|
||||
id: 'scenery.option-active-timetables',
|
||||
component: 'SceneryTimetable',
|
||||
},
|
||||
{
|
||||
id: 'scenery.option-timetables-history',
|
||||
component: 'SceneryTimetablesHistory',
|
||||
},
|
||||
{
|
||||
id: 'scenery.option-dispatchers-history',
|
||||
component: 'SceneryDispatchersHistory',
|
||||
},
|
||||
],
|
||||
sceneryViewMode: SceneryViewMode,
|
||||
selectedCheckpoint: '',
|
||||
currentViewCompontent: 'SceneryTimetable',
|
||||
onlineFrom: -1,
|
||||
}),
|
||||
|
||||
activated() {
|
||||
this.loadSelectedCheckpoint();
|
||||
},
|
||||
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
|
||||
const timetableOnly = computed(() => (route.query['timetableOnly'] == '1' ? true : false));
|
||||
const isComponentVisible = computed(() => route.path === '/scenery');
|
||||
|
||||
const stationInfo = computed(() => {
|
||||
return store.stationList.find((station) => station.name === route.query.station?.toString().replace(/_/g, ' '));
|
||||
});
|
||||
|
||||
return {
|
||||
timetableOnly,
|
||||
isComponentVisible,
|
||||
stationInfo,
|
||||
store,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
setViewMode(componentName: string) {
|
||||
this.currentViewCompontent = componentName;
|
||||
},
|
||||
|
||||
loadSelectedCheckpoint() {
|
||||
if (!this.stationInfo?.generalInfo?.checkpoints) return;
|
||||
if (this.stationInfo.generalInfo.checkpoints.length == 0) return;
|
||||
this.selectedCheckpoint = this.stationInfo.generalInfo.checkpoints[0].checkpointName;
|
||||
},
|
||||
|
||||
selectCheckpoint(cp: { checkpointName: string }) {
|
||||
this.selectedCheckpoint = cp.checkpointName;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/responsive.scss';
|
||||
@import '../styles/variables.scss';
|
||||
|
||||
button.back-btn {
|
||||
img {
|
||||
width: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.scenery {
|
||||
&-view {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
&-offline {
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
padding: 2em 1em;
|
||||
|
||||
color: $warningCol;
|
||||
|
||||
display: inline-block;
|
||||
|
||||
font-size: 1.5em;
|
||||
|
||||
button {
|
||||
margin: 1em auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scenery-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 4fr 5fr;
|
||||
gap: 0 1em;
|
||||
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
max-width: 1700px;
|
||||
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
|
||||
&[data-timetable-only='true'] {
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
.scenery-left {
|
||||
position: relative;
|
||||
background-color: #181818;
|
||||
padding: 1em 0.5em;
|
||||
|
||||
height: 95vh;
|
||||
min-height: 550px;
|
||||
max-height: 1000px;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scenery-right {
|
||||
background: #181818;
|
||||
padding: 1em 0.5em;
|
||||
|
||||
height: 95vh;
|
||||
min-height: 550px;
|
||||
max-height: 1000px;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.scenery-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75em;
|
||||
|
||||
.btn {
|
||||
padding: 0.5em;
|
||||
box-shadow: 0 0 10px 4px #242424;
|
||||
|
||||
&[data-checked='true'] {
|
||||
color: var(--clr-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timetable-checkpoints {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
flex-wrap: wrap;
|
||||
font-size: 1.1em;
|
||||
margin: 0.75em 0;
|
||||
|
||||
.checkpoint_item {
|
||||
&.current {
|
||||
font-weight: bold;
|
||||
color: $accentCol;
|
||||
}
|
||||
|
||||
&:not(:last-child)::after {
|
||||
margin: 0 0.5em;
|
||||
content: '•';
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include midScreen {
|
||||
.scenery-wrapper {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.scenery-left {
|
||||
border-radius: 1em;
|
||||
margin-bottom: 1em;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.scenery-right {
|
||||
border-radius: 1em;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallScreen {
|
||||
.scenery-left {
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.scenery-right {
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="scenery-view">
|
||||
<div
|
||||
class="scenery-offline"
|
||||
v-if="!stationInfo && isComponentVisible && store.dataStatuses.sceneries == 2"
|
||||
>
|
||||
<div>{{ $t('scenery.no-scenery') }}</div>
|
||||
|
||||
<action-button>
|
||||
<router-link to="/">{{ $t('scenery.return-btn') }}</router-link>
|
||||
</action-button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="scenery-wrapper"
|
||||
v-if="stationInfo"
|
||||
ref="card-wrapper"
|
||||
:data-timetable-only="timetableOnly"
|
||||
>
|
||||
<div class="scenery-left" v-if="!timetableOnly">
|
||||
<div class="scenery-actions">
|
||||
<button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')">
|
||||
<img :src="getIcon('back')" alt="Back to scenery" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<SceneryHeader :station="stationInfo" />
|
||||
<SceneryInfo :station="stationInfo" />
|
||||
</div>
|
||||
|
||||
<div class="scenery-right">
|
||||
<div class="info-actions">
|
||||
<button
|
||||
v-for="(viewMode, i) in viewModes"
|
||||
:key="i"
|
||||
class="btn btn--option"
|
||||
@click="setViewMode(viewMode.component)"
|
||||
:data-checked="currentViewCompontent == viewMode.component"
|
||||
>
|
||||
{{ $t(viewMode.id) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<keep-alive>
|
||||
<component
|
||||
:is="currentViewCompontent"
|
||||
:station="stationInfo"
|
||||
:key="currentViewCompontent"
|
||||
></component>
|
||||
</keep-alive>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import routerMixin from '../mixins/routerMixin';
|
||||
import { useStore } from '../store/store';
|
||||
|
||||
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
|
||||
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
|
||||
import SceneryTimetable from '../components/SceneryView/SceneryTimetable.vue';
|
||||
import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue';
|
||||
import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue';
|
||||
import ActionButton from '../components/Global/ActionButton.vue';
|
||||
import imageMixin from '../mixins/imageMixin';
|
||||
|
||||
enum SceneryViewMode {
|
||||
'TIMETABLES_ACTIVE',
|
||||
'TIMETABLES_HISTORY',
|
||||
'SCENERY_HISTORY'
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SceneryInfo,
|
||||
SceneryTimetable,
|
||||
ActionButton,
|
||||
SceneryHeader,
|
||||
SceneryTimetablesHistory,
|
||||
SceneryDispatchersHistory
|
||||
},
|
||||
|
||||
mixins: [routerMixin, imageMixin],
|
||||
|
||||
data: () => ({
|
||||
viewModes: [
|
||||
{
|
||||
id: 'scenery.option-active-timetables',
|
||||
component: 'SceneryTimetable'
|
||||
},
|
||||
{
|
||||
id: 'scenery.option-timetables-history',
|
||||
component: 'SceneryTimetablesHistory'
|
||||
},
|
||||
{
|
||||
id: 'scenery.option-dispatchers-history',
|
||||
component: 'SceneryDispatchersHistory'
|
||||
}
|
||||
],
|
||||
sceneryViewMode: SceneryViewMode,
|
||||
selectedCheckpoint: '',
|
||||
currentViewCompontent: 'SceneryTimetable',
|
||||
onlineFrom: -1
|
||||
}),
|
||||
|
||||
activated() {
|
||||
this.loadSelectedCheckpoint();
|
||||
},
|
||||
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
|
||||
const timetableOnly = computed(() => (route.query['timetableOnly'] == '1' ? true : false));
|
||||
const isComponentVisible = computed(() => route.path === '/scenery');
|
||||
|
||||
const stationInfo = computed(() => {
|
||||
return store.stationList.find(
|
||||
(station) => station.name === route.query.station?.toString().replace(/_/g, ' ')
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
timetableOnly,
|
||||
isComponentVisible,
|
||||
stationInfo,
|
||||
store
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
setViewMode(componentName: string) {
|
||||
this.currentViewCompontent = componentName;
|
||||
},
|
||||
|
||||
loadSelectedCheckpoint() {
|
||||
if (!this.stationInfo?.generalInfo?.checkpoints) return;
|
||||
if (this.stationInfo.generalInfo.checkpoints.length == 0) return;
|
||||
this.selectedCheckpoint = this.stationInfo.generalInfo.checkpoints[0].checkpointName;
|
||||
},
|
||||
|
||||
selectCheckpoint(cp: { checkpointName: string }) {
|
||||
this.selectedCheckpoint = cp.checkpointName;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/responsive.scss';
|
||||
@import '../styles/variables.scss';
|
||||
|
||||
button.back-btn {
|
||||
img {
|
||||
width: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.scenery {
|
||||
&-view {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
&-offline {
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
padding: 2em 1em;
|
||||
|
||||
color: $warningCol;
|
||||
|
||||
display: inline-block;
|
||||
|
||||
font-size: 1.5em;
|
||||
|
||||
button {
|
||||
margin: 1em auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scenery-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 4fr 5fr;
|
||||
gap: 0 1em;
|
||||
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
max-width: 1700px;
|
||||
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
|
||||
&[data-timetable-only='true'] {
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 1000px;
|
||||
}
|
||||
}
|
||||
|
||||
.scenery-left {
|
||||
position: relative;
|
||||
background-color: #181818;
|
||||
padding: 1em 0.5em;
|
||||
|
||||
height: 95vh;
|
||||
min-height: 550px;
|
||||
max-height: 1000px;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scenery-right {
|
||||
background: #181818;
|
||||
padding: 1em 0.5em;
|
||||
|
||||
height: 95vh;
|
||||
min-height: 550px;
|
||||
max-height: 1000px;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.scenery-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75em;
|
||||
|
||||
.btn {
|
||||
padding: 0.5em;
|
||||
box-shadow: 0 0 10px 4px #242424;
|
||||
|
||||
&[data-checked='true'] {
|
||||
color: var(--clr-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timetable-checkpoints {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
flex-wrap: wrap;
|
||||
font-size: 1.1em;
|
||||
margin: 0.75em 0;
|
||||
|
||||
.checkpoint_item {
|
||||
&.current {
|
||||
font-weight: bold;
|
||||
color: $accentCol;
|
||||
}
|
||||
|
||||
&:not(:last-child)::after {
|
||||
margin: 0 0.5em;
|
||||
content: '•';
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include midScreen {
|
||||
.scenery-wrapper {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.scenery-left {
|
||||
border-radius: 1em;
|
||||
margin-bottom: 1em;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.scenery-right {
|
||||
border-radius: 1em;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallScreen {
|
||||
.scenery-left {
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.scenery-right {
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+108
-106
@@ -1,106 +1,108 @@
|
||||
<template>
|
||||
<section class="stations-view">
|
||||
<div class="wrapper">
|
||||
<div class="body">
|
||||
<div class="options-bar">
|
||||
<StationFilterCard :showCard="filterCardOpen" :exit="(filterCardOpen = false)" ref="filterCardRef" />
|
||||
</div>
|
||||
|
||||
<StationTable :stations="computedStationList" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import StationTable from '../components/StationsView/StationTable.vue';
|
||||
import StationFilterCard from '../components/StationsView/StationFilterCard.vue';
|
||||
import SelectBox from '../components/Global/SelectBox.vue';
|
||||
import { useStationFiltersStore } from '../store/stationFiltersStore';
|
||||
import { useStore } from '../store/store';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
StationTable,
|
||||
StationFilterCard,
|
||||
SelectBox,
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
filterCardOpen: false,
|
||||
modalHidden: true,
|
||||
STORAGE_KEY: 'options_saved',
|
||||
focusedStationName: '',
|
||||
}),
|
||||
|
||||
setup() {
|
||||
return {
|
||||
filterStore: useStationFiltersStore(),
|
||||
store: useStore(),
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
computedStationList() {
|
||||
return this.filterStore.getFilteredStationList(this.store.stationList, this.store.region.id);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.filterStore.setupFilters();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/variables.scss';
|
||||
@import '../styles/responsive.scss';
|
||||
|
||||
@keyframes blinkAnim {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.indicator-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
&-enter,
|
||||
&-leave-to {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.stations-view {
|
||||
position: relative;
|
||||
|
||||
padding: 1em 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.options-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<section class="stations-view">
|
||||
<div class="wrapper">
|
||||
<div class="body">
|
||||
<div class="options-bar">
|
||||
<StationFilterCard
|
||||
:showCard="filterCardOpen"
|
||||
:exit="(filterCardOpen = false)"
|
||||
ref="filterCardRef"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<StationTable :stations="computedStationList" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import StationTable from '../components/StationsView/StationTable.vue';
|
||||
import StationFilterCard from '../components/StationsView/StationFilterCard.vue';
|
||||
import { useStationFiltersStore } from '../store/stationFiltersStore';
|
||||
import { useStore } from '../store/store';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
StationTable,
|
||||
StationFilterCard
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
filterCardOpen: false,
|
||||
modalHidden: true,
|
||||
STORAGE_KEY: 'options_saved',
|
||||
focusedStationName: ''
|
||||
}),
|
||||
|
||||
setup() {
|
||||
return {
|
||||
filterStore: useStationFiltersStore(),
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
computedStationList() {
|
||||
return this.filterStore.getFilteredStationList(this.store.stationList, this.store.region.id);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.filterStore.setupFilters();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/variables.scss';
|
||||
@import '../styles/responsive.scss';
|
||||
|
||||
@keyframes blinkAnim {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.indicator-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
&-enter,
|
||||
&-leave-to {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.stations-view {
|
||||
position: relative;
|
||||
|
||||
padding: 1em 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.options-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
+126
-120
@@ -1,120 +1,126 @@
|
||||
<template>
|
||||
<section class="trains-view">
|
||||
<div class="trains_wrapper">
|
||||
<TrainOptions
|
||||
:sorter-option-ids="['routeDistance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']"
|
||||
:current-options-active="currentOptionsActive"
|
||||
/>
|
||||
|
||||
<TrainTable :trains="computedTrains" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
|
||||
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
||||
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
||||
import { trainFilters } from '../constants/Trains/TrainOptionsConsts';
|
||||
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 '../scripts/interfaces/Trains/TrainFilter';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
TrainTable,
|
||||
TrainOptions,
|
||||
},
|
||||
|
||||
mixins: [modalTrainMixin],
|
||||
|
||||
props: {
|
||||
train: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
driver: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
trainId: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
trainStatsOpen: false,
|
||||
}),
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))];
|
||||
|
||||
const sorterActive = reactive({ id: 'routeDistance', dir: -1 });
|
||||
const filterList = reactive([...trainFilters]) as TrainFilter[];
|
||||
|
||||
const currentOptionsActive = ref(false);
|
||||
|
||||
const searchedDriver = ref('');
|
||||
const searchedTrain = ref('');
|
||||
|
||||
provide('searchedTrain', searchedTrain);
|
||||
provide('searchedDriver', searchedDriver);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('filterList', filterList);
|
||||
|
||||
const computedTrains: ComputedRef<Train[]> = computed(() => {
|
||||
return filteredTrainList(store.trainList, searchedTrain.value, searchedDriver.value, sorterActive, filterList);
|
||||
});
|
||||
|
||||
watch([searchedTrain, searchedDriver, sorterActive, filterList], ([sT, sD, sA, fL]) => {
|
||||
const areFiltersActive = fL.some((f, i) => f.isActive !== initTrainFilters[i].isActive);
|
||||
|
||||
|
||||
currentOptionsActive.value = sT.length > 0 || sD.length > 0 || sA.id != 'routeDistance' || areFiltersActive;
|
||||
console.log(sA.id);
|
||||
});
|
||||
|
||||
return {
|
||||
computedTrains,
|
||||
searchedTrain,
|
||||
searchedDriver,
|
||||
sorterActive,
|
||||
store,
|
||||
currentOptionsActive,
|
||||
};
|
||||
},
|
||||
|
||||
activated() {
|
||||
if (this.train) {
|
||||
this.searchedTrain = this.train;
|
||||
this.searchedDriver = this.driver || '';
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.trainId) {
|
||||
this.selectModalTrain(this.trainId);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/responsive.scss';
|
||||
|
||||
.trains-view {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trains_wrapper {
|
||||
margin: 1rem auto;
|
||||
max-width: 1350px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<section class="trains-view">
|
||||
<div class="trains_wrapper">
|
||||
<TrainOptions
|
||||
:sorter-option-ids="['routeDistance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']"
|
||||
:current-options-active="currentOptionsActive"
|
||||
/>
|
||||
|
||||
<TrainTable :trains="computedTrains" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ComputedRef, defineComponent, provide, reactive, ref, watch } from 'vue';
|
||||
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
||||
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
||||
import { trainFilters } from '../constants/Trains/TrainOptionsConsts';
|
||||
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 '../scripts/interfaces/Trains/TrainFilter';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
TrainTable,
|
||||
TrainOptions
|
||||
},
|
||||
|
||||
mixins: [modalTrainMixin],
|
||||
|
||||
props: {
|
||||
train: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
|
||||
driver: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
|
||||
trainId: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
trainStatsOpen: false
|
||||
}),
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))];
|
||||
|
||||
const sorterActive = reactive({ id: 'routeDistance', dir: -1 });
|
||||
const filterList = reactive([...trainFilters]) as TrainFilter[];
|
||||
|
||||
const currentOptionsActive = ref(false);
|
||||
|
||||
const searchedDriver = ref('');
|
||||
const searchedTrain = ref('');
|
||||
|
||||
provide('searchedTrain', searchedTrain);
|
||||
provide('searchedDriver', searchedDriver);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('filterList', filterList);
|
||||
|
||||
const computedTrains: ComputedRef<Train[]> = computed(() => {
|
||||
return filteredTrainList(
|
||||
store.trainList,
|
||||
searchedTrain.value,
|
||||
searchedDriver.value,
|
||||
sorterActive,
|
||||
filterList
|
||||
);
|
||||
});
|
||||
|
||||
watch([searchedTrain, searchedDriver, sorterActive, filterList], ([sT, sD, sA, fL]) => {
|
||||
const areFiltersActive = fL.some((f, i) => f.isActive !== initTrainFilters[i].isActive);
|
||||
|
||||
currentOptionsActive.value =
|
||||
sT.length > 0 || sD.length > 0 || sA.id != 'routeDistance' || areFiltersActive;
|
||||
console.log(sA.id);
|
||||
});
|
||||
|
||||
return {
|
||||
computedTrains,
|
||||
searchedTrain,
|
||||
searchedDriver,
|
||||
sorterActive,
|
||||
store,
|
||||
currentOptionsActive
|
||||
};
|
||||
},
|
||||
|
||||
activated() {
|
||||
if (this.train) {
|
||||
this.searchedTrain = this.train;
|
||||
this.searchedDriver = this.driver || '';
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.trainId) {
|
||||
this.selectModalTrain(this.trainId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/responsive.scss';
|
||||
|
||||
.trains-view {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trains_wrapper {
|
||||
margin: 1rem auto;
|
||||
max-width: 1350px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user