mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
470 lines
13 KiB
Vue
470 lines
13 KiB
Vue
<template>
|
|
<section class="journal-timetables">
|
|
<JournalHeader />
|
|
|
|
<div class="journal_wrapper">
|
|
<div class="journal_top-bar">
|
|
<JournalOptions
|
|
@onOptionsReset="resetOptions"
|
|
@onRefreshData="fetchHistoryData"
|
|
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
|
:filters="journalTimetableFilters"
|
|
:currentOptionsActive="currentOptionsActive"
|
|
:data-status="dataStatus"
|
|
optionsType="timetables"
|
|
/>
|
|
|
|
<JournalStats :statsButtons="statsButtons" />
|
|
</div>
|
|
|
|
<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 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 { useMainStore } from '../store/mainStore';
|
|
|
|
import { LocationQuery } from 'vue-router';
|
|
|
|
import JournalTimetablesList from '../components/JournalView/JournalTimetables/JournalTimetablesList.vue';
|
|
import { Journal } from '../components/JournalView/typings';
|
|
import { Status } from '../typings/common';
|
|
import { API } from '../typings/api';
|
|
import { useApiStore } from '../store/apiStore';
|
|
|
|
export const journalTimetableFilters: Journal.TimetableFilter[] = [
|
|
{
|
|
id: Journal.TimetableFilterId.ALL_STATUSES,
|
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
|
isActive: true,
|
|
default: true
|
|
},
|
|
|
|
{
|
|
id: Journal.TimetableFilterId.ACTIVE,
|
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
|
isActive: false,
|
|
default: false
|
|
},
|
|
|
|
{
|
|
id: Journal.TimetableFilterId.FULFILLED,
|
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
|
isActive: false,
|
|
default: false
|
|
},
|
|
|
|
{
|
|
id: Journal.TimetableFilterId.ABANDONED,
|
|
filterSection: Journal.FilterSection.TIMETABLE_STATUS,
|
|
isActive: false,
|
|
default: false
|
|
},
|
|
|
|
{
|
|
id: Journal.TimetableFilterId.ALL_SPECIALS,
|
|
filterSection: Journal.FilterSection.SPECIAL,
|
|
isActive: true,
|
|
default: true
|
|
},
|
|
|
|
{
|
|
id: Journal.TimetableFilterId.TWR,
|
|
filterSection: Journal.FilterSection.SPECIAL,
|
|
isActive: false,
|
|
default: false
|
|
},
|
|
|
|
{
|
|
id: Journal.TimetableFilterId.SKR,
|
|
filterSection: Journal.FilterSection.SPECIAL,
|
|
isActive: false,
|
|
default: false
|
|
},
|
|
{
|
|
id: Journal.TimetableFilterId.TWR_SKR,
|
|
filterSection: Journal.FilterSection.SPECIAL,
|
|
isActive: false,
|
|
default: false
|
|
}
|
|
];
|
|
|
|
interface TimetablesQueryParams {
|
|
driverName?: string;
|
|
trainNo?: string;
|
|
timetableId?: string;
|
|
|
|
authorName?: string;
|
|
// timestampFrom?: number;
|
|
// timestampTo?: number;
|
|
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
|
|
issuedFrom?: string;
|
|
|
|
countFrom?: number;
|
|
countLimit?: number;
|
|
|
|
fulfilled?: number;
|
|
terminated?: number;
|
|
|
|
twr?: number;
|
|
skr?: number;
|
|
|
|
sortBy?: Journal.TimetableSorter['id'];
|
|
}
|
|
|
|
export default defineComponent({
|
|
components: {
|
|
JournalOptions,
|
|
JournalStats,
|
|
JournalHeader,
|
|
JournalTimetablesList
|
|
},
|
|
mixins: [dateMixin, routerMixin, modalTrainMixin],
|
|
|
|
name: 'JournalTimetables',
|
|
|
|
props: {
|
|
timetableId: {
|
|
type: String
|
|
}
|
|
},
|
|
|
|
data: () => ({
|
|
journalTimetableFilters,
|
|
mainStore: useMainStore(),
|
|
apiStore: useApiStore(),
|
|
|
|
statsButtons: [
|
|
{
|
|
tab: Journal.StatsTab.DAILY_STATS,
|
|
localeKey: 'journal.daily-stats.button',
|
|
iconName: 'stats',
|
|
disabled: false
|
|
},
|
|
{
|
|
tab: Journal.StatsTab.DRIVER_STATS,
|
|
localeKey: 'journal.driver-stats.button',
|
|
iconName: 'train',
|
|
disabled: true
|
|
}
|
|
],
|
|
|
|
currentQueryParams: {} as TimetablesQueryParams,
|
|
dataRefreshedAt: null as Date | null,
|
|
|
|
scrollDataLoaded: true,
|
|
scrollNoMoreData: false,
|
|
|
|
showReturnButton: false,
|
|
statsCardOpen: false,
|
|
currentOptionsActive: false,
|
|
|
|
timetableHistory: [] as API.TimetableHistory.Response,
|
|
|
|
dataStatus: Status.Data.Loading,
|
|
dataErrorMessage: ''
|
|
}),
|
|
|
|
setup() {
|
|
const sorterActive: Journal.TimetableSorter = reactive({ id: 'timetableId', dir: 'desc' });
|
|
|
|
const initFilters: readonly Journal.TimetableFilter[] = JSON.parse(
|
|
JSON.stringify(journalTimetableFilters)
|
|
);
|
|
|
|
const filterList: Journal.TimetableFilter[] = reactive(JSON.parse(JSON.stringify(initFilters)));
|
|
|
|
const searchersValues = reactive({
|
|
'search-train': '',
|
|
'search-driver': '',
|
|
'search-dispatcher': '',
|
|
'search-issuedFrom': '',
|
|
'search-date': ''
|
|
} as Journal.TimetableSearchType);
|
|
|
|
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
|
|
};
|
|
},
|
|
|
|
watch: {
|
|
currentQueryParams(q: TimetablesQueryParams) {
|
|
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
|
},
|
|
|
|
'mainStore.driverStatsData'(driverStats) {
|
|
this.statsButtons.find((sb) => sb.tab == Journal.StatsTab.DRIVER_STATS)!.disabled =
|
|
driverStats === undefined;
|
|
},
|
|
|
|
async 'mainStore.driverStatsName'() {
|
|
this.fetchDriverStats();
|
|
}
|
|
},
|
|
|
|
// 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 != Status.Data.Loaded)
|
|
return;
|
|
|
|
if (scrollTop > elementHeight * 0.85) this.addHistoryData();
|
|
},
|
|
|
|
handleQueries(query: LocationQuery) {
|
|
this.setOptions(query as any);
|
|
},
|
|
|
|
async fetchDriverStats() {
|
|
if (!this.mainStore.driverStatsName) {
|
|
this.mainStore.driverStatsData = undefined;
|
|
this.mainStore.driverStatsStatus = Status.Data.Initialized;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
this.mainStore.driverStatsStatus = Status.Data.Loading;
|
|
|
|
const statsData: API.DriverStats.Response = await (
|
|
await this.apiStore.client!.get(
|
|
`api/getDriverInfo?name=${this.mainStore.driverStatsName}`
|
|
)
|
|
).data;
|
|
|
|
this.mainStore.driverStatsData = statsData;
|
|
this.mainStore.driverStatsStatus = Status.Data.Loaded;
|
|
} catch (error) {
|
|
this.mainStore.driverStatsData = undefined;
|
|
this.mainStore.driverStatsStatus = Status.Data.Error;
|
|
console.error('Ups! Wystąpił błąd przy próbie pobrania statystyk maszynisty! :/');
|
|
}
|
|
},
|
|
|
|
setOptions(options: { [key: string]: string }) {
|
|
this.searchersValues['search-date'] = options['search-date'] ?? '';
|
|
this.searchersValues['search-driver'] = options['search-driver'] ?? '';
|
|
this.searchersValues['search-train'] = options['search-train'] ?? '';
|
|
this.searchersValues['search-dispatcher'] = options['search-dispatcher'] ?? '';
|
|
this.searchersValues['search-issuedFrom'] = options['search-issuedFrom'] ?? '';
|
|
|
|
this.sorterActive.id =
|
|
(options['sorter-active'] as Journal.TimetableSorterKey) ?? 'timetableId';
|
|
|
|
this.filterList.forEach((f) => {
|
|
f.isActive =
|
|
options[f.filterSection] === f.id ||
|
|
(options[f.filterSection] === undefined && f.default);
|
|
});
|
|
},
|
|
|
|
resetOptions() {
|
|
this.setOptions({});
|
|
},
|
|
|
|
async addHistoryData() {
|
|
this.scrollDataLoaded = false;
|
|
|
|
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
|
|
|
const responseData: API.TimetableHistory.Response = await (
|
|
await this.apiStore.client!.get('api/getTimetables', {
|
|
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 dateFrom = this.searchersValues['search-date'].trim() || undefined;
|
|
const issuedFrom = this.searchersValues['search-issuedFrom'].trim() || undefined;
|
|
|
|
let dateTo: string | undefined = undefined;
|
|
|
|
if (dateFrom) {
|
|
const d = new Date(dateFrom);
|
|
d.setDate(d.getDate() + 1);
|
|
|
|
dateTo = d.toISOString().split('T')[0];
|
|
}
|
|
// const timestampFrom = dateString ? Date.parse(new Date(dateString).toISOString()) : undefined;
|
|
// const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
|
|
|
const queryParams: TimetablesQueryParams = {};
|
|
|
|
this.filterList
|
|
.filter((f) => f.isActive)
|
|
.forEach((f) => {
|
|
switch (f.id) {
|
|
case Journal.TimetableFilterId.ABANDONED:
|
|
queryParams['fulfilled'] = 0;
|
|
queryParams['terminated'] = 1;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.ACTIVE:
|
|
queryParams['fulfilled'] = undefined;
|
|
queryParams['terminated'] = 0;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.FULFILLED:
|
|
queryParams['terminated'] = undefined;
|
|
queryParams['fulfilled'] = 1;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.ALL_STATUSES:
|
|
queryParams['terminated'] = undefined;
|
|
queryParams['fulfilled'] = undefined;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.ALL_SPECIALS:
|
|
queryParams['twr'] = undefined;
|
|
queryParams['skr'] = undefined;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.TWR:
|
|
queryParams['twr'] = 1;
|
|
queryParams['skr'] = 0;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.SKR:
|
|
queryParams['twr'] = 0;
|
|
queryParams['skr'] = 1;
|
|
break;
|
|
|
|
case Journal.TimetableFilterId.TWR_SKR:
|
|
queryParams['twr'] = 1;
|
|
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['dateFrom'] = dateFrom;
|
|
queryParams['dateTo'] = dateTo;
|
|
queryParams['issuedFrom'] = issuedFrom;
|
|
queryParams['sortBy'] =
|
|
this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
|
|
|
|
if (JSON.stringify(this.currentQueryParams) != JSON.stringify(queryParams))
|
|
this.dataStatus = Status.Data.Loading;
|
|
|
|
this.currentQueryParams = queryParams;
|
|
|
|
try {
|
|
const responseData: API.TimetableHistory.Response = await (
|
|
await this.apiStore.client!.get('api/getTimetables', {
|
|
params: this.currentQueryParams
|
|
})
|
|
).data;
|
|
|
|
if (!responseData) {
|
|
this.dataStatus = Status.Data.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 = Status.Data.Loaded;
|
|
this.dataRefreshedAt = new Date();
|
|
} catch (error) {
|
|
this.dataStatus = Status.Data.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>
|