mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 21:38:13 +00:00
Aktualizacja 1.10.4
Aktualizacja Stacjownika do wersji 1.10.4
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.10.3",
|
"version": "1.10.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -2,48 +2,55 @@
|
|||||||
<section class="journal-timetables">
|
<section class="journal-timetables">
|
||||||
<div class="journal_wrapper">
|
<div class="journal_wrapper">
|
||||||
<JournalOptions
|
<JournalOptions
|
||||||
@on-filter-change="search"
|
@on-search-confirm="searchHistory"
|
||||||
@on-input-change="search"
|
@on-options-reset="resetOptions"
|
||||||
@on-sorter-change="search"
|
|
||||||
:sorter-option-ids="['timestampFrom', 'duration']"
|
:sorter-option-ids="['timestampFrom', 'duration']"
|
||||||
|
:data-status="dataStatus"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="timetables_wrapper" ref="scrollElement">
|
<div class="list_wrapper" @scroll="handleScroll">
|
||||||
<transition name="warning" mode="out-in">
|
<!-- <transition name="warning" mode="out-in"> -->
|
||||||
<div :key="dataStatus">
|
<!-- <div :key="dataStatus"> -->
|
||||||
<Loading v-if="dataStatus == (DataStatus.Loading || DataStatus.Initialized)" />
|
<Loading
|
||||||
|
v-if="dataStatus == DataStatus.Initialized || (dataStatus == DataStatus.Loading && historyList.length == 0)"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
||||||
{{ $t('app.error') }}
|
{{ $t('app.error') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="journal_warning" v-else-if="historyList.length == 0">
|
<div class="journal_warning" v-else-if="historyList.length == 0 && dataStatus != DataStatus.Loading">
|
||||||
{{ $t('app.no-result') }}
|
{{ $t('app.no-result') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<JournalDispatchersList :dispatcherHistory="computedHistoryList" />
|
<JournalDispatchersList :dispatcherHistory="computedHistoryList" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn--option btn--load-data"
|
class="btn btn--option btn--load-data"
|
||||||
v-if="!scrollNoMoreData && scrollDataLoaded"
|
v-if="!scrollNoMoreData && scrollDataLoaded && computedHistoryList.length > 15"
|
||||||
@click="addHistoryData"
|
@click="addHistoryData"
|
||||||
>
|
>
|
||||||
{{ $t('journal.load-data') }}
|
{{ $t('journal.load-data') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- </div>
|
||||||
</transition>
|
</transition> -->
|
||||||
|
|
||||||
|
<div class="journal_warning" v-if="scrollNoMoreData">
|
||||||
|
{{ $t('journal.no-further-data') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="journal_warning" v-else-if="!scrollDataLoaded">
|
||||||
|
{{ $t('journal.loading-further-data') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
|
|
||||||
<div class="journal_warning" v-else-if="!scrollDataLoaded">{{ $t('journal.loading-further-data') }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, JournalFilter, provide, reactive, Ref, ref } from 'vue';
|
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import ActionButton from '../../components/Global/ActionButton.vue';
|
import ActionButton from '../../components/Global/ActionButton.vue';
|
||||||
@@ -56,8 +63,9 @@ import { URLs } from '../../scripts/utils/apiURLs';
|
|||||||
import { DataStatus } from '../../scripts/enums/DataStatus';
|
import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||||
import { useStore } from '../../store/store';
|
import { useStore } from '../../store/store';
|
||||||
import JournalDispatchersList from './JournalDispatchersList.vue';
|
import JournalDispatchersList from './JournalDispatchersList.vue';
|
||||||
import { JournalDispatcherSearcher } from '../../types/JournalDispatcherTypes';
|
import { JournalDispatcherSearcher, JournalDispatcherSorter } from '../../types/Journal/JournalDispatcherTypes';
|
||||||
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
|
import { DispatcherHistory } from '../../scripts/interfaces/api/DispatchersAPIData';
|
||||||
|
import { JournalTimetableFilter } from '../../types/Journal/JournalTimetablesTypes';
|
||||||
|
|
||||||
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
|
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
|
||||||
|
|
||||||
@@ -92,12 +100,13 @@ export default defineComponent({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const sorterActive = ref({ id: 'timestampFrom', dir: -1 });
|
const sorterActive: JournalDispatcherSorter = reactive({ id: 'timestampFrom', dir: -1 });
|
||||||
const journalFilterActive = ref({});
|
const journalFilterActive = ref({});
|
||||||
|
|
||||||
const searchersValues = reactive({
|
const searchersValues = reactive({
|
||||||
'search-dispatcher': '',
|
'search-dispatcher': '',
|
||||||
'search-station': '',
|
'search-station': '',
|
||||||
|
'search-date': '',
|
||||||
} as JournalDispatcherSearcher);
|
} as JournalDispatcherSearcher);
|
||||||
|
|
||||||
const countFromIndex = ref(0);
|
const countFromIndex = ref(0);
|
||||||
@@ -135,42 +144,36 @@ export default defineComponent({
|
|||||||
if (this.sceneryName || this.dispatcherName) {
|
if (this.sceneryName || this.dispatcherName) {
|
||||||
this.searchersValues['search-station'] = this.sceneryName?.toString() || '';
|
this.searchersValues['search-station'] = this.sceneryName?.toString() || '';
|
||||||
this.searchersValues['search-dispatcher'] = this.dispatcherName?.toString() || '';
|
this.searchersValues['search-dispatcher'] = this.dispatcherName?.toString() || '';
|
||||||
this.search();
|
this.searchHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('scroll', this.handleScroll);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
if (!this.sceneryName && !this.dispatcherName) {
|
if (!this.sceneryName && !this.dispatcherName) {
|
||||||
this.search();
|
this.searchHistory();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivated() {
|
|
||||||
window.removeEventListener('scroll', this.handleScroll);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
closeDispatcherStatsCard() {
|
handleScroll(e: Event) {
|
||||||
this.statsCardOpen = false;
|
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();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleScroll() {
|
resetOptions() {
|
||||||
this.showReturnButton = window.scrollY > window.innerHeight;
|
this.searchersValues['search-station'] = '';
|
||||||
|
this.searchersValues['search-dispatcher'] = '';
|
||||||
|
this.sorterActive.id = 'timestampFrom';
|
||||||
|
|
||||||
const element = this.$refs.scrollElement as HTMLElement;
|
this.searchHistory();
|
||||||
|
|
||||||
if (
|
|
||||||
element.getBoundingClientRect().bottom * 0.85 < window.innerHeight &&
|
|
||||||
this.scrollDataLoaded &&
|
|
||||||
!this.scrollNoMoreData &&
|
|
||||||
this.dataStatus == DataStatus.Loaded
|
|
||||||
)
|
|
||||||
this.addHistoryData();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
search() {
|
searchHistory() {
|
||||||
this.fetchHistoryData({
|
this.fetchHistoryData({
|
||||||
searchers: this.searchersValues,
|
searchers: this.searchersValues,
|
||||||
});
|
});
|
||||||
@@ -202,21 +205,22 @@ export default defineComponent({
|
|||||||
async fetchHistoryData(
|
async fetchHistoryData(
|
||||||
props: {
|
props: {
|
||||||
searchers?: JournalDispatcherSearcher;
|
searchers?: JournalDispatcherSearcher;
|
||||||
filter?: JournalFilter;
|
filter?: JournalTimetableFilter;
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
this.dataStatus = DataStatus.Loading;
|
this.dataStatus = DataStatus.Loading;
|
||||||
|
|
||||||
const queries: string[] = [];
|
const queries: string[] = [];
|
||||||
|
|
||||||
// const dispatcher = props.searchers?.find((s) => s.id == 'search-dispatcher')?.value.trim();
|
|
||||||
// const station = props.searchers?.find((s) => s.id == 'search-station')?.value.trim();
|
|
||||||
|
|
||||||
const dispatcher = props.searchers?.['search-dispatcher'].trim();
|
const dispatcher = props.searchers?.['search-dispatcher'].trim();
|
||||||
const station = props.searchers?.['search-station'].trim();
|
const station = props.searchers?.['search-station'].trim();
|
||||||
|
const dateString = props.searchers?.['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 (dispatcher) queries.push(`dispatcherName=${dispatcher}`);
|
||||||
if (station) queries.push(`stationName=${station}`);
|
if (station) queries.push(`stationName=${station}`);
|
||||||
|
if (timestampFrom && timestampTo) queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
||||||
|
|
||||||
// Z API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
// Z API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
||||||
if (this.sorterActive.id == 'timestampFrom') queries.push('sortBy=timestampFrom');
|
if (this.sorterActive.id == 'timestampFrom') queries.push('sortBy=timestampFrom');
|
||||||
|
|||||||
@@ -1,41 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="journal-list">
|
<ul class="journal-list">
|
||||||
<transition-group name="journal-list-anim">
|
<!-- <transition-group name="journal-list-anim"> -->
|
||||||
<li v-for="(doc, i) in dispatcherHistory" :key="doc.id">
|
<li v-for="item in computedDispatcherHistory" :class="{ sticky: typeof item == 'string' }">
|
||||||
<div class="journal_day" v-if="isAnotherDay(i - 1, i)">
|
<div v-if="typeof item == 'string'" class="journal_day">
|
||||||
<span>{{ new Date(doc.timestampFrom).toLocaleDateString('pl-PL') }}</span>
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="journal_item"
|
v-else
|
||||||
:class="{ online: doc.isOnline }"
|
class="journal_item"
|
||||||
@click="navigateToScenery(doc.stationName, doc.isOnline)"
|
:class="{ online: item.isOnline }"
|
||||||
@keydown.enter="navigateToScenery(doc.stationName, doc.isOnline)"
|
@click="navigateToScenery(item.stationName, item.isOnline)"
|
||||||
tabindex="0"
|
@keydown.enter="navigateToScenery(item.stationName, item.isOnline)"
|
||||||
>
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<b class="text--primary">{{ item.dispatcherName }}</b> • <b>{{ item.stationName }}</b>
|
||||||
|
<span class="text--grayed"> #{{ item.stationHash }} </span>
|
||||||
|
<span class="region-badge" :class="item.region">PL1</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span :data-status="item.isOnline"> {{ item.isOnline ? $t('journal.online-since') : 'OFFLINE' }} </span>
|
||||||
<span>
|
<span>
|
||||||
<b class="text--primary">{{ doc.dispatcherName }}</b> • <b>{{ doc.stationName }}</b>
|
{{ new Date(item.timestampFrom).toLocaleTimeString('pl-PL', { timeStyle: 'short' }) }}
|
||||||
<span class="text--grayed"> #{{ doc.stationHash }} </span>
|
|
||||||
<span class="region-badge" :class="doc.region">PL1</span>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span v-if="item.currentDuration && item.isOnline"> ({{ calculateDuration(item.currentDuration) }}) </span>
|
||||||
<span :data-status="doc.isOnline"> {{ doc.isOnline ? $t('journal.online-since') : 'OFFLINE' }} </span>
|
|
||||||
<span>
|
|
||||||
{{ new Date(doc.timestampFrom).toLocaleTimeString('pl-PL', { timeStyle: 'short' }) }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-if="doc.currentDuration && doc.isOnline"> ({{ calculateDuration(doc.currentDuration) }}) </span>
|
<span v-if="item.timestampTo">
|
||||||
|
>
|
||||||
<span v-if="doc.timestampTo">
|
{{ new Date(item.timestampTo).toLocaleTimeString('pl-PL', { timeStyle: 'short' }) }}
|
||||||
>
|
({{ $t('journal.duty-lasted') }} {{ calculateDuration(item.currentDuration!) }})
|
||||||
{{ new Date(doc.timestampTo).toLocaleTimeString('pl-PL', { timeStyle: 'short' }) }}
|
|
||||||
({{ $t('journal.duty-lasted') }} {{ calculateDuration(doc.currentDuration!) }})
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</li>
|
</div>
|
||||||
</transition-group>
|
</li>
|
||||||
|
<!-- </transition-group> -->
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -54,6 +55,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
mixins: [dateMixin],
|
mixins: [dateMixin],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
computedDispatcherHistory() {
|
||||||
|
return this.dispatcherHistory.reduce((acc, historyItem, i) => {
|
||||||
|
if (this.isAnotherDay(i - 1, i)) acc.push(new Date(historyItem.timestampFrom).toLocaleDateString('pl-PL'));
|
||||||
|
acc.push(historyItem);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] as (DispatcherHistory | string)[]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
navigateToScenery(name: string, isOnline: boolean) {
|
navigateToScenery(name: string, isOnline: boolean) {
|
||||||
if (!isOnline) return;
|
if (!isOnline) return;
|
||||||
@@ -87,6 +99,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li.sticky {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.journal_item {
|
.journal_item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -108,36 +125,19 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.journal_day {
|
.journal_day {
|
||||||
position: relative;
|
margin: 1em 0;
|
||||||
text-align: center;
|
padding: 0.5em;
|
||||||
background-color: #4d4d4d;
|
font-weight: bold;
|
||||||
|
|
||||||
margin-top: 1em;
|
background-color: #333;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #4d4d4d;
|
background-color: inherit;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
padding-right: 1em;
|
||||||
|
|
||||||
padding: 0 0.5em;
|
font-weight: bold;
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
|
|
||||||
z-index: 0;
|
|
||||||
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
|
|
||||||
height: 3px;
|
|
||||||
width: 60%;
|
|
||||||
min-width: 200px;
|
|
||||||
|
|
||||||
background-color: white;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +1,100 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="journal-options">
|
<div class="journal-options">
|
||||||
<div class="options_wrapper">
|
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
|
||||||
<div class="options_content">
|
|
||||||
<div class="content_select">
|
|
||||||
<select-box
|
|
||||||
:itemList="translatedSorterOptions"
|
|
||||||
:defaultItemIndex="0"
|
|
||||||
@selected="onSorterChange"
|
|
||||||
:prefix="$t('journal.sort-prefix')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content_search">
|
<button class="btn--image" @click="showOptions = !showOptions">
|
||||||
<div class="search-box" v-for="(value, propName) in searchersValues" :key="propName">
|
<img :src="getIcon('filter2')" alt="Open filters" />
|
||||||
<input
|
{{ $t('options.filters') }}
|
||||||
class="search-input"
|
</button>
|
||||||
:placeholder="$t(`journal.${propName}`)"
|
|
||||||
v-model="searchersValues[propName]"
|
|
||||||
@keydown.enter="onInputSearch"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button class="search-exit">
|
<transition name="options-anim">
|
||||||
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear(propName)" />
|
<div class="options_wrapper" v-if="showOptions">
|
||||||
|
<div class="options_content">
|
||||||
|
<h1>{{ $t('options.sort-title') }}</h1>
|
||||||
|
|
||||||
|
<div class="options_sorters">
|
||||||
|
<div v-for="opt in translatedSorterOptions">
|
||||||
|
<button class="sort-option" :data-selected="opt.id == sorterActive.id" @click="onSorterChange(opt)">
|
||||||
|
{{ opt.value.toUpperCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 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}`) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="search-box">
|
|
||||||
<input
|
|
||||||
class="search-input"
|
|
||||||
v-model="searchedTrain"
|
|
||||||
:placeholder="$t('journal.search-train')"
|
|
||||||
@keydown.enter="search"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<img class="search-exit" :src="exitIcon" alt="exit-icon" @click="clearTrain" />
|
<h1>{{ $t('options.search-title') }}</h1>
|
||||||
|
<div class="search_content">
|
||||||
|
<div class="search" v-for="(_, propName) in searchersValues" :key="propName">
|
||||||
|
<label v-if="propName == 'search-date'" for="date">{{ $t('options.search-date') }}</label>
|
||||||
|
|
||||||
|
<div class="search-box">
|
||||||
|
<input
|
||||||
|
v-if="propName == 'search-date'"
|
||||||
|
class="search-input"
|
||||||
|
id="date"
|
||||||
|
type="date"
|
||||||
|
min="2022-02-01"
|
||||||
|
@keydown.enter="onSearchConfirm"
|
||||||
|
v-model="searchersValues[propName]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
v-else
|
||||||
|
class="search-input"
|
||||||
|
@keydown.enter="onSearchConfirm"
|
||||||
|
:placeholder="$t(`options.${propName}`)"
|
||||||
|
v-model="searchersValues[propName]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button class="search-exit">
|
||||||
|
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear(propName)" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search_actions">
|
||||||
|
<action-button class="search-button" @click="onResetButtonClick">
|
||||||
|
{{ $t('options.reset-button') }}
|
||||||
|
</action-button>
|
||||||
|
|
||||||
|
<action-button class="search-button" @click="onSearchConfirm">
|
||||||
|
{{ $t('options.search-button') }}
|
||||||
|
</action-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="search-box">
|
|
||||||
<input
|
|
||||||
class="search-input"
|
|
||||||
v-model="searchedDriver"
|
|
||||||
:placeholder="$t('journal.search-driver')"
|
|
||||||
@keydown.enter="search"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<img class="search-exit" :src="exitIcon" alt="exit-icon" @click="clearDriver" />
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<action-button class="search-button" @click="onInputSearch">
|
|
||||||
{{ $t('journal.search') }}
|
|
||||||
</action-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="options_filters">
|
<!-- <div class="data-status">
|
||||||
<button
|
<span v-if="dataStatus == DataStatus.Loading"> Pobieranie danych...</span>
|
||||||
v-for="filter in filters"
|
<span v-if="dataStatus == DataStatus.Loaded"> Pobrano dane </span>
|
||||||
class="journal-filter-option btn--option"
|
</div> -->
|
||||||
:class="{ checked: journalFilterActive.id === filter.id }"
|
|
||||||
:id="filter.id"
|
|
||||||
@click="onFilterChange(filter)"
|
|
||||||
>
|
|
||||||
{{ $t(`journal.filter-${filter.id}`) }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, inject, JournalFilter, PropType } from 'vue';
|
import { defineComponent, inject, Prop, PropType } from 'vue';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||||
|
import { JournalTimetableFilter } from '../../types/Journal/JournalTimetablesTypes';
|
||||||
import ActionButton from '../Global/ActionButton.vue';
|
import ActionButton from '../Global/ActionButton.vue';
|
||||||
import SelectBox from '../Global/SelectBox.vue';
|
import SelectBox from '../Global/SelectBox.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { SelectBox, ActionButton },
|
components: { SelectBox, ActionButton },
|
||||||
emits: ['onSorterChange', 'onInputChange', 'onFilterChange'],
|
emits: ['onSearchConfirm', 'onOptionsReset'],
|
||||||
mixins: [imageMixin],
|
mixins: [imageMixin],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@@ -85,16 +104,28 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
filters: {
|
filters: {
|
||||||
type: Array as PropType<JournalFilter[]>,
|
type: Array as PropType<JournalTimetableFilter[]>,
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dataStatus: {
|
||||||
|
type: Number as PropType<DataStatus>,
|
||||||
|
default: DataStatus.Initialized,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showOptions: false,
|
||||||
|
DataStatus,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
return {
|
return {
|
||||||
searchersValues: inject('searchersValues') as { [key: string]: string },
|
searchersValues: inject('searchersValues') as { [key: string]: string },
|
||||||
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
||||||
journalFilterActive: inject('journalFilterActive') as JournalFilter,
|
journalFilterActive: inject('journalFilterActive') as JournalTimetableFilter,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -102,7 +133,7 @@ export default defineComponent({
|
|||||||
translatedSorterOptions() {
|
translatedSorterOptions() {
|
||||||
return this.$props.sorterOptionIds.map((id) => ({
|
return this.$props.sorterOptionIds.map((id) => ({
|
||||||
id,
|
id,
|
||||||
value: this.$t(`journal.option-${id}`),
|
value: this.$t(`options.sort-${id}`),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -111,108 +142,185 @@ export default defineComponent({
|
|||||||
onSorterChange(item: { id: string | number; value: string }) {
|
onSorterChange(item: { id: string | number; value: string }) {
|
||||||
this.sorterActive.id = item.id;
|
this.sorterActive.id = item.id;
|
||||||
this.sorterActive.dir = -1;
|
this.sorterActive.dir = -1;
|
||||||
|
this.$emit('onSearchConfirm');
|
||||||
this.$emit('onSorterChange');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onFilterChange(filter: JournalFilter) {
|
onFilterChange(filter: JournalTimetableFilter) {
|
||||||
this.journalFilterActive = filter;
|
this.journalFilterActive = filter;
|
||||||
this.$emit('onFilterChange');
|
this.$emit('onSearchConfirm');
|
||||||
},
|
|
||||||
|
|
||||||
onInputSearch() {
|
|
||||||
this.$emit('onInputChange');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onInputClear(id: any) {
|
onInputClear(id: any) {
|
||||||
this.searchersValues[id] = '';
|
this.searchersValues[id] = '';
|
||||||
this.onInputSearch();
|
this.$emit('onSearchConfirm');
|
||||||
|
},
|
||||||
|
|
||||||
|
onSearchConfirm() {
|
||||||
|
this.$emit('onSearchConfirm');
|
||||||
|
},
|
||||||
|
|
||||||
|
onResetButtonClick() {
|
||||||
|
this.$emit('onOptionsReset');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/responsive';
|
@import '../../styles/responsive.scss';
|
||||||
@import '../../styles/option.scss';
|
|
||||||
@import '../../styles/search_box.scss';
|
@import '../../styles/search_box.scss';
|
||||||
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
.options {
|
.options-anim {
|
||||||
&_wrapper {
|
&-enter-from,
|
||||||
display: flex;
|
&-leave-to {
|
||||||
flex-direction: column;
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
&_content {
|
&-enter-active,
|
||||||
display: flex;
|
&-leave-active {
|
||||||
flex-wrap: wrap;
|
transition: all 150ms ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.content_search,
|
.bg {
|
||||||
.content_select {
|
position: fixed;
|
||||||
display: flex;
|
top: 0;
|
||||||
align-items: center;
|
left: 0;
|
||||||
flex-wrap: wrap;
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
padding: 0.25em 0.25em 0 0;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.journal-options {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options_wrapper {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
background-color: #111111ee;
|
||||||
|
box-shadow: 0 0 10px 2px #111;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
position: relative;
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin: 0.7em 0 0.25em 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
|
||||||
|
width: 50%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options_sorters {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
padding: 0.25em 0.25em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options_filters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-option,
|
||||||
|
.filter-option {
|
||||||
|
margin: 0 0.25em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-option[data-selected='true'] {
|
||||||
|
color: $accentCol;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-option {
|
||||||
|
&#abandoned {
|
||||||
|
color: salmon;
|
||||||
}
|
}
|
||||||
|
|
||||||
&_filters {
|
&#fulfilled {
|
||||||
display: flex;
|
color: lightgreen;
|
||||||
flex-wrap: wrap;
|
|
||||||
margin: 0.5em 0 0 0;
|
|
||||||
|
|
||||||
.journal-filter-option {
|
|
||||||
margin: 0 0.25em 0 0;
|
|
||||||
|
|
||||||
&#abandoned {
|
|
||||||
color: salmon;
|
|
||||||
}
|
|
||||||
|
|
||||||
&#fulfilled {
|
|
||||||
color: lightgreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
&#active {
|
|
||||||
color: lightblue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&#active {
|
||||||
|
color: lightblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search_content > .search {
|
||||||
|
margin: 0.5em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search_content > button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search_content > .search_actions {
|
||||||
|
display: flex;
|
||||||
|
margin: 1em 0 0.5em 0;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0.25em 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-status {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.1em;
|
||||||
|
|
||||||
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
.journal-options {
|
h1 {
|
||||||
width: 100%;
|
text-align: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 75%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.options {
|
.options_wrapper {
|
||||||
&_wrapper {
|
max-width: 100%;
|
||||||
justify-content: center;
|
}
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_content {
|
.btn--image {
|
||||||
padding: 0 1em;
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
flex-direction: column;
|
.filter-option,
|
||||||
|
.sort-option {
|
||||||
|
margin: 0.25em 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
.content_select {
|
.options_filters,
|
||||||
margin: 0 auto;
|
.options_sorters {
|
||||||
padding: 0;
|
justify-content: center;
|
||||||
}
|
|
||||||
|
|
||||||
.content_search {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_filters {
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.journal-filter-option {
|
|
||||||
margin: 0.25em 0.25em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -6,54 +6,58 @@
|
|||||||
|
|
||||||
<div class="journal_wrapper">
|
<div class="journal_wrapper">
|
||||||
<JournalOptions
|
<JournalOptions
|
||||||
@on-input-change="searchHistory"
|
@on-search-confirm="searchHistory"
|
||||||
@on-filter-change="searchHistory"
|
@on-options-reset="resetOptions"
|
||||||
@on-sorter-change="searchHistory"
|
|
||||||
:sorter-option-ids="['timetableId', 'beginDate', 'distance', 'total-stops']"
|
:sorter-option-ids="['timetableId', 'beginDate', 'distance', 'total-stops']"
|
||||||
:filters="journalTimetableFilters"
|
:filters="journalTimetableFilters"
|
||||||
|
:data-status="dataStatus"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="timetables_wrapper" ref="scrollElement">
|
<div class="list_wrapper" @scroll="handleScroll">
|
||||||
<transition name="warning" mode="out-in">
|
<!-- <transition name="warning" mode="out-in"> -->
|
||||||
<div :key="dataStatus">
|
<!-- <div :key="dataStatus"> -->
|
||||||
<Loading v-if="dataStatus == (DataStatus.Loading || DataStatus.Initialized)" />
|
<Loading
|
||||||
|
v-if="
|
||||||
|
dataStatus == DataStatus.Initialized || (dataStatus == DataStatus.Loading && timetableHistory.length == 0)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
<div v-else-if="dataStatus == DataStatus.Error" class="journal_warning error">
|
||||||
{{ $t('app.error') }}
|
{{ $t('app.error') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="timetableHistory.length == 0" class="journal_warning">
|
<div v-else-if="timetableHistory.length == 0 && dataStatus != DataStatus.Loading" class="journal_warning">
|
||||||
{{ $t('app.no-result') }}
|
{{ $t('app.no-result') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<JournalTimetablesList :timetableHistory="timetableHistory" />
|
<JournalTimetablesList :timetableHistory="timetableHistory" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn--option btn--load-data"
|
class="btn btn--option btn--load-data"
|
||||||
v-if="!scrollNoMoreData && scrollDataLoaded"
|
v-if="!scrollNoMoreData && scrollDataLoaded && timetableHistory.length >= 15"
|
||||||
@click="addHistoryData"
|
@click="addHistoryData"
|
||||||
>
|
>
|
||||||
{{ $t('journal.load-data') }}
|
{{ $t('journal.load-data') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<!-- </div> -->
|
||||||
</transition>
|
<!-- </transition> -->
|
||||||
|
|
||||||
|
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
|
||||||
|
<div class="journal_warning" v-else-if="!scrollDataLoaded">{{ $t('journal.loading-further-data') }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="journal_warning" v-if="scrollNoMoreData">{{ $t('journal.no-further-data') }}</div>
|
|
||||||
<div class="journal_warning" v-else-if="!scrollDataLoaded">{{ $t('journal.loading-further-data') }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, JournalFilter, provide, reactive, Ref, ref } from 'vue';
|
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import DriverStats from './DriverStats.vue';
|
import DriverStats from './DriverStats.vue';
|
||||||
import Loading from '../Global/Loading.vue';
|
import Loading from '../Global/Loading.vue';
|
||||||
import { journalTimetableFilters } from '../../data/journalFilters';
|
import { JournalTimetableFilter, JournalTimetableSorter } from '../../types/Journal/JournalTimetablesTypes';
|
||||||
import dateMixin from '../../mixins/dateMixin';
|
import dateMixin from '../../mixins/dateMixin';
|
||||||
import routerMixin from '../../mixins/routerMixin';
|
import routerMixin from '../../mixins/routerMixin';
|
||||||
import { DataStatus } from '../../scripts/enums/DataStatus';
|
import { DataStatus } from '../../scripts/enums/DataStatus';
|
||||||
@@ -62,10 +66,11 @@ import { TimetableHistory } from '../../scripts/interfaces/api/TimetablesAPIData
|
|||||||
import { URLs } from '../../scripts/utils/apiURLs';
|
import { URLs } from '../../scripts/utils/apiURLs';
|
||||||
import { useStore } from '../../store/store';
|
import { useStore } from '../../store/store';
|
||||||
import JournalOptions from './JournalOptions.vue';
|
import JournalOptions from './JournalOptions.vue';
|
||||||
import { JournalTimetableSearcher } from '../../types/JournalTimetablesTypes';
|
import { JorunalTimetableSearchType } from '../../types/Journal/JournalTimetablesTypes';
|
||||||
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import JournalTimetablesList from './JournalTimetablesList.vue';
|
import JournalTimetablesList from './JournalTimetablesList.vue';
|
||||||
|
import { journalTimetableFilters } from '../../constants/Journal/JournalTimetablesConsts';
|
||||||
|
|
||||||
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
|
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
|
||||||
|
|
||||||
@@ -99,13 +104,14 @@ export default defineComponent({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const sorterActive = ref({ id: 'timetableId', dir: -1 });
|
const sorterActive: JournalTimetableSorter = reactive({ id: 'timetableId', dir: 1 });
|
||||||
const journalFilterActive = ref(journalTimetableFilters[0]);
|
const journalFilterActive = ref(journalTimetableFilters[0]);
|
||||||
|
|
||||||
const searchersValues = reactive({
|
const searchersValues = reactive({
|
||||||
'search-train': '',
|
'search-train': '',
|
||||||
'search-driver': '',
|
'search-driver': '',
|
||||||
} as JournalTimetableSearcher);
|
'search-date': '',
|
||||||
|
} as JorunalTimetableSearchType);
|
||||||
|
|
||||||
const countFromIndex = ref(0);
|
const countFromIndex = ref(0);
|
||||||
const countLimit = 15;
|
const countLimit = 15;
|
||||||
@@ -130,35 +136,36 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
activated() {
|
activated() {
|
||||||
window.addEventListener('scroll', this.handleScroll);
|
|
||||||
|
|
||||||
if (this.timetableId) {
|
if (this.timetableId) {
|
||||||
this.searchersValues['search-train'] = `#${this.timetableId}`;
|
this.searchersValues['search-train'] = `#${this.timetableId}`;
|
||||||
this.searchHistory();
|
this.searchHistory();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivated() {
|
|
||||||
window.removeEventListener('scroll', this.handleScroll);
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
if (!this.timetableId) this.searchHistory();
|
if (!this.timetableId) this.searchHistory();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleScroll() {
|
handleScroll(e: Event) {
|
||||||
this.showReturnButton = window.scrollY > window.innerHeight;
|
const listElement = e.target as HTMLElement;
|
||||||
|
const scrollTop = listElement.scrollTop;
|
||||||
|
const elementHeight = listElement.scrollHeight - listElement.offsetHeight;
|
||||||
|
|
||||||
const element = this.$refs.scrollElement as HTMLElement;
|
if (!this.scrollDataLoaded || this.scrollNoMoreData || this.dataStatus != DataStatus.Loaded) return;
|
||||||
|
|
||||||
if (
|
if (scrollTop > elementHeight * 0.85) this.addHistoryData();
|
||||||
element.getBoundingClientRect().bottom * 0.85 < window.innerHeight &&
|
},
|
||||||
this.scrollDataLoaded &&
|
|
||||||
!this.scrollNoMoreData &&
|
resetOptions() {
|
||||||
this.dataStatus == DataStatus.Loaded
|
this.searchersValues['search-date'] = '';
|
||||||
)
|
this.searchersValues['search-driver'] = '';
|
||||||
this.addHistoryData();
|
this.searchersValues['search-train'] = '';
|
||||||
|
|
||||||
|
this.journalFilterActive = this.journalTimetableFilters[0];
|
||||||
|
this.sorterActive.id = 'timetableId';
|
||||||
|
|
||||||
|
this.searchHistory();
|
||||||
},
|
},
|
||||||
|
|
||||||
searchHistory() {
|
searchHistory() {
|
||||||
@@ -193,8 +200,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
async fetchHistoryData(
|
async fetchHistoryData(
|
||||||
props: {
|
props: {
|
||||||
searchers?: JournalTimetableSearcher;
|
searchers?: JorunalTimetableSearchType;
|
||||||
filter?: JournalFilter;
|
filter?: JournalTimetableFilter;
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
this.dataStatus = DataStatus.Loading;
|
this.dataStatus = DataStatus.Loading;
|
||||||
@@ -204,8 +211,13 @@ export default defineComponent({
|
|||||||
const driver = props.searchers?.['search-driver'].trim();
|
const driver = props.searchers?.['search-driver'].trim();
|
||||||
const train = props.searchers?.['search-train'].trim();
|
const train = props.searchers?.['search-train'].trim();
|
||||||
|
|
||||||
|
const dateString = props.searchers?.['search-date'].trim();
|
||||||
|
const timestampFrom = dateString ? Date.parse(new Date(dateString).toISOString()) - 120 * 60 * 1000 : undefined;
|
||||||
|
const timestampTo = timestampFrom ? timestampFrom + 86400000 : undefined;
|
||||||
|
|
||||||
if (driver) queries.push(`driverName=${driver}`);
|
if (driver) queries.push(`driverName=${driver}`);
|
||||||
if (train) queries.push(train.startsWith('#') ? `timetableId=${train.replace('#', '')}` : `trainNo=${train}`);
|
if (train) queries.push(train.startsWith('#') ? `timetableId=${train.replace('#', '')}` : `trainNo=${train}`);
|
||||||
|
if (timestampFrom && timestampTo) queries.push(`timestampFrom=${timestampFrom}`, `timestampTo=${timestampTo}`);
|
||||||
|
|
||||||
// Z API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
// Z API: const SORT_TYPES = ['allStopsCount', 'endDate', 'beginDate', 'routeDistance'];
|
||||||
if (this.sorterActive.id == 'distance') queries.push('sortBy=routeDistance');
|
if (this.sorterActive.id == 'distance') queries.push('sortBy=routeDistance');
|
||||||
@@ -257,8 +269,6 @@ export default defineComponent({
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
this.dataStatus = DataStatus.Loaded;
|
this.dataStatus = DataStatus.Loaded;
|
||||||
|
|
||||||
console.log(this.dataStatus);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.dataStatus = DataStatus.Error;
|
this.dataStatus = DataStatus.Error;
|
||||||
this.dataErrorMessage = 'Ups! Coś poszło nie tak!';
|
this.dataErrorMessage = 'Ups! Coś poszło nie tak!';
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
<img :src="getIcon(`arrow-${item.showStock.value ? 'asc' : 'desc'}`)" alt="Arrow" />
|
<img :src="getIcon(`arrow-${item.showStock.value ? 'asc' : 'desc'}`)" alt="Arrow" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="info-extended" v-if="timetable.stockString" v-show="item.showStock.value">
|
<div class="info-extended" v-if="timetable.stockString && item.showStock.value">
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="filter-card" v-click-outside="closeCard">
|
<section class="filter-card" v-click-outside="closeCard">
|
||||||
<div class="card_btn">
|
<div class="card_btn">
|
||||||
<button class="btn btn--option" @click="toggleCard">
|
<button class="btn--image" @click="toggleCard">
|
||||||
<img class="button_icon" :src="getIcon('filter2')" alt="icon-filter" />
|
<img class="button_icon" :src="getIcon('filter2')" alt="filter icon" />
|
||||||
{{ $t('options.filters') }}
|
{{ $t('options.filters') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,7 +91,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import { defineComponent, inject } from 'vue';
|
import { defineComponent, inject } from 'vue';
|
||||||
import inputData from '../../data/options.json';
|
import inputData from '../../data/options.json';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
@@ -107,7 +106,6 @@ export default defineComponent({
|
|||||||
mixins: [imageMixin],
|
mixins: [imageMixin],
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
|
||||||
inputs: { ...inputData },
|
inputs: { ...inputData },
|
||||||
saveOptions: false,
|
saveOptions: false,
|
||||||
STORAGE_KEY: 'options_saved',
|
STORAGE_KEY: 'options_saved',
|
||||||
@@ -263,6 +261,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
&_btn {
|
&_btn {
|
||||||
button {
|
button {
|
||||||
@@ -389,6 +388,7 @@ export default defineComponent({
|
|||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
border: 1px solid white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,6 +437,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
&::-webkit-slider-thumb {
|
&::-webkit-slider-thumb {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
|||||||
@@ -1,230 +1,285 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="train-options">
|
<div class="train-options">
|
||||||
<div class="options_wrapper">
|
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
|
||||||
<div class="options_content">
|
|
||||||
<div class="content_select">
|
|
||||||
<select-box
|
|
||||||
:itemList="translatedSorterOptions"
|
|
||||||
:defaultItemIndex="0"
|
|
||||||
@selected="changeSorter"
|
|
||||||
:prefix="$t('trains.sorter-prefix')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content_search">
|
<button class="btn--open" @click="showOptions = !showOptions">
|
||||||
<div class="search-box">
|
<img :src="getIcon('filter2')" alt="Open filters" />
|
||||||
<input class="search-input" v-model="searchedTrain" :placeholder="$t('trains.search-train')" />
|
{{ $t('options.filters') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<button class="search-exit">
|
<transition name="options-anim">
|
||||||
<img :src="getIcon('exit')" alt="exit-icon" @click="() => (searchedTrain = '')" />
|
<div class="options_wrapper" v-if="showOptions">
|
||||||
</button>
|
<div class="options_content">
|
||||||
|
<h1>{{ $t('options.sort-title') }}</h1>
|
||||||
|
<div class="options_sorters">
|
||||||
|
<div v-for="opt in translatedSorterOptions">
|
||||||
|
<button class="sort-option" :data-selected="opt.id == sorterActive.id" @click="onSorterChange(opt)">
|
||||||
|
{{ opt.value.toUpperCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="search-box">
|
<h1 v-if="trainFilterList.length != 0">{{ $t('options.filter-title') }}</h1>
|
||||||
<input class="search-input" v-model="searchedDriver" :placeholder="$t('trains.search-driver')" />
|
<div class="options_filters">
|
||||||
|
<div class="filter-option" v-for="filter in trainFilterList">
|
||||||
<button class="search-exit">
|
<button class="btn--option" :data-disabled="!filter.isActive" @click="onFilterChange(filter)">
|
||||||
<img :src="getIcon('exit')" alt="exit-icon" @click="() => (searchedDriver = '')" />
|
{{ $t(`options.filter-${filter.id}`) }}
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="options_filters">
|
||||||
|
<div class="filter-option">
|
||||||
|
<button @click="clearAllFilters">{{ $t('options.filter-clear') }}</button>
|
||||||
|
</div>
|
||||||
|
<div class="filter-option">
|
||||||
|
<button @click="resetAllFilters">{{ $t('options.filter-reset') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1>{{ $t('options.search-title') }}</h1>
|
||||||
|
<div class="content_search">
|
||||||
|
<div class="search-box">
|
||||||
|
<input class="search-input" :placeholder="$t(`options.search-train`)" v-model="searchedTrain" />
|
||||||
|
<button class="search-exit">
|
||||||
|
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear('train')" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="search-box">
|
||||||
|
<input class="search-input" :placeholder="$t(`options.search-driver`)" v-model="searchedDriver" />
|
||||||
|
<button class="search-exit">
|
||||||
|
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear('driver')" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</transition>
|
||||||
|
|
||||||
<div class="filters">
|
|
||||||
<span
|
|
||||||
:class="{ active: filter.isActive }"
|
|
||||||
class="filter"
|
|
||||||
v-for="filter in filterList"
|
|
||||||
:key="filter.id"
|
|
||||||
tabindex="0"
|
|
||||||
@contextmenu="
|
|
||||||
(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
@click.left="toggleFilter(filter)"
|
|
||||||
@keydown.enter="toggleFilter(filter)"
|
|
||||||
@click.right="setFilterOnly(filter)"
|
|
||||||
@keydown.space="setFilterOnly(filter)"
|
|
||||||
>
|
|
||||||
{{ $t(`trains.filter-${filter.id}`) }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="filter reset-btn" @click="resetFilters" tabindex="0">
|
|
||||||
{{ $t('trains.filter-reset') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, inject, TrainFilter } from 'vue';
|
import { defineComponent, inject, PropType } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
|
||||||
|
import ActionButton from '../Global/ActionButton.vue';
|
||||||
import SelectBox from '../Global/SelectBox.vue';
|
import SelectBox from '../Global/SelectBox.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { SelectBox },
|
components: { SelectBox, ActionButton },
|
||||||
emits: ['changeSearchedTrain', 'changeSearchedDriver', 'changeSorter'],
|
|
||||||
mixins: [imageMixin],
|
mixins: [imageMixin],
|
||||||
|
|
||||||
setup() {
|
props: {
|
||||||
const { t } = useI18n();
|
sorterOptionIds: {
|
||||||
|
type: Array as PropType<Array<string>>,
|
||||||
const sorterOptions = [
|
required: true,
|
||||||
{
|
},
|
||||||
id: 'distance',
|
},
|
||||||
value: 'kilometraż',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'progress',
|
|
||||||
value: 'przebyta trasa',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'delay',
|
|
||||||
value: 'opóźnienie',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'mass',
|
|
||||||
value: 'masa',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'speed',
|
|
||||||
value: 'prędkość',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'length',
|
|
||||||
value: 'długość',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let filterList = inject('filterList') as TrainFilter[];
|
|
||||||
|
|
||||||
const translatedSorterOptions = computed(() =>
|
|
||||||
sorterOptions.map(({ id }) => ({
|
|
||||||
id,
|
|
||||||
value: t(`trains.option-${id}`),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
|
data() {
|
||||||
return {
|
return {
|
||||||
translatedSorterOptions,
|
showOptions: false,
|
||||||
searchedTrain: inject('searchedTrain') as string,
|
|
||||||
searchedDriver: inject('searchedDriver') as string,
|
|
||||||
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
|
||||||
filterList,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
searchedTrain: inject('searchedTrain') as string,
|
||||||
|
searchedDriver: inject('searchedDriver') as string,
|
||||||
|
|
||||||
|
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
||||||
|
trainFilterList: inject('filterList') as TrainFilter[],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
translatedSorterOptions() {
|
||||||
|
return this.$props.sorterOptionIds.map((id) => ({
|
||||||
|
id,
|
||||||
|
value: this.$t(`options.sort-${id}`),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
changeSorter(item: { id: string | number; value: string }) {
|
onSorterChange(item: { id: string | number; value: string }) {
|
||||||
this.sorterActive.id = item.id;
|
this.sorterActive.id = item.id;
|
||||||
this.sorterActive.dir = -1;
|
this.sorterActive.dir = -1;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleFilter(filter: TrainFilter) {
|
onFilterChange(filter: TrainFilter) {
|
||||||
filter.isActive = !filter.isActive;
|
filter.isActive = !filter.isActive;
|
||||||
},
|
},
|
||||||
|
|
||||||
setFilterOnly(filter: TrainFilter) {
|
clearAllFilters() {
|
||||||
this.filterList.forEach((f) => (f.isActive = f.id == filter.id));
|
this.trainFilterList.forEach((filter) => {
|
||||||
|
filter.isActive = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
resetFilters() {
|
resetAllFilters() {
|
||||||
this.filterList.forEach((f) => (f.isActive = true));
|
this.trainFilterList.forEach((filter) => {
|
||||||
this.searchedDriver = '';
|
filter.isActive = true;
|
||||||
this.searchedTrain = '';
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onInputClear(id: 'driver' | 'train') {
|
||||||
|
if (id == 'driver') this.searchedDriver = '';
|
||||||
|
if (id == 'train') this.searchedTrain = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
test(e: Event) {
|
||||||
|
console.log(e.target);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/responsive';
|
@import '../../styles/responsive.scss';
|
||||||
@import '../../styles/search_box.scss';
|
@import '../../styles/search_box.scss';
|
||||||
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
.train-options {
|
.options-anim {
|
||||||
@include smallScreen() {
|
&-enter-from,
|
||||||
width: 100%;
|
&-leave-to {
|
||||||
font-size: 1.25em;
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 150ms ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.options {
|
.bg {
|
||||||
&_wrapper {
|
position: fixed;
|
||||||
display: flex;
|
top: 0;
|
||||||
}
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
&_content {
|
z-index: 10;
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.content_search,
|
|
||||||
.content_select {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
padding: 0.25em 0.25em 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters {
|
.journal-options {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options_wrapper {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
background-color: #111111ee;
|
||||||
|
box-shadow: 0 0 10px 2px #111;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--open {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
padding: 0.4em 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1em;
|
||||||
|
|
||||||
|
border-radius: 0.75em 0.75em 0 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 1.3em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
position: relative;
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin: 0.7em 0 0.25em 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
|
||||||
|
width: 50%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options_sorters {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
margin-top: 0.5em;
|
padding: 0.25em 0.25em 0 0;
|
||||||
|
|
||||||
@include smallScreen() {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter {
|
.content_search > div {
|
||||||
background: #333;
|
margin: 0.5em auto;
|
||||||
padding: 0.2em 0.25em;
|
}
|
||||||
margin: 0.25em 0.25em 0 0;
|
|
||||||
|
.content_search > button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options_filters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-option,
|
||||||
|
.filter-option {
|
||||||
|
margin: 0.25em 0.25em 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-option[data-selected='true'] {
|
||||||
|
color: $accentCol;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
cursor: pointer;
|
.filter-option {
|
||||||
color: gray;
|
button {
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
&.active {
|
&[data-disabled='true'] {
|
||||||
color: var(--clr-primary);
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.reset-btn {
|
|
||||||
color: salmon;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
.journal-options {
|
h1 {
|
||||||
width: 100%;
|
text-align: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
width: 75%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.options {
|
.options_wrapper {
|
||||||
&_wrapper {
|
max-width: 100%;
|
||||||
justify-content: center;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&_content {
|
.btn--open {
|
||||||
padding: 0 1em;
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
flex-direction: column;
|
.options_filters,
|
||||||
|
.options_sorters {
|
||||||
.content_select {
|
justify-content: center;
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content_search {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section class="filter-card" v-click-outside="closeCard">
|
|
||||||
<div class="card_btn">
|
|
||||||
<action-button @click="toggleCard">
|
|
||||||
<img class="button_icon" :src="getIcon('filter2')" alt="icon-filter" />
|
|
||||||
<p>{{ $t('options.filters') }}</p>
|
|
||||||
</action-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<transition name="card-anim">
|
|
||||||
<div class="card_content card" v-if="isVisible">
|
|
||||||
<div class="card_exit" @click="closeCard"></div>
|
|
||||||
|
|
||||||
<div class="options_wrapper">
|
|
||||||
<div class="options_content">
|
|
||||||
<div class="content_select">
|
|
||||||
<select-box
|
|
||||||
:itemList="translatedSorterOptions"
|
|
||||||
:defaultItemIndex="0"
|
|
||||||
@selected="changeSorter"
|
|
||||||
:prefix="$t('trains.sorter-prefix')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content_search">
|
|
||||||
<div class="search-box">
|
|
||||||
<input class="search-input" v-model="searchedTrain" :placeholder="$t('trains.search-train')" />
|
|
||||||
|
|
||||||
<img class="search-exit" :src="getIcon('exit')" alt="exit-icon" @click="() => (searchedTrain = '')" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="search-box">
|
|
||||||
<input class="search-input" v-model="searchedDriver" :placeholder="$t('trains.search-driver')" />
|
|
||||||
|
|
||||||
<img class="search-exit" :src="getIcon('exit')" alt="exit-icon" @click="() => (searchedDriver = '')" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="card_actions flex">
|
|
||||||
<action-button class="outlined">
|
|
||||||
{{ $t('filters.reset') }}
|
|
||||||
</action-button>
|
|
||||||
<action-button class="outlined" @click="closeCard">{{ $t('filters.close') }}</action-button>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import inputData from "../../data/options.json";
|
|
||||||
|
|
||||||
import { TrainFilter, computed, defineComponent, inject } from 'vue';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import SelectBox from '../Global/SelectBox.vue';
|
|
||||||
import ActionButton from '../Global/ActionButton.vue';
|
|
||||||
import { sorterOptions } from '../../data/trainOptions';
|
|
||||||
import imageMixin from "../../mixins/imageMixin";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { ActionButton, SelectBox },
|
|
||||||
emits: ['changeFilterValue', 'invertFilters', 'resetFilters'],
|
|
||||||
mixins: [imageMixin],
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
inputs: { ...inputData },
|
|
||||||
}),
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const isVisible = inject('isTrainOptionsCardVisible');
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
let filterList = inject('filterList') as TrainFilter[];
|
|
||||||
|
|
||||||
const translatedSorterOptions = computed(() =>
|
|
||||||
sorterOptions.map(({ id }) => ({
|
|
||||||
id,
|
|
||||||
value: t(`trains.option-${id}`),
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
translatedSorterOptions,
|
|
||||||
searchedTrain: inject('searchedTrain') as string,
|
|
||||||
searchedDriver: inject('searchedDriver') as string,
|
|
||||||
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
|
||||||
filterList,
|
|
||||||
isVisible,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
closeCard() {
|
|
||||||
this.isVisible = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleCard() {
|
|
||||||
this.isVisible = !this.isVisible;
|
|
||||||
},
|
|
||||||
|
|
||||||
changeSorter(item: { id: string | number; value: string }) {
|
|
||||||
this.sorterActive.id = item.id;
|
|
||||||
this.sorterActive.dir = -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleFilter(filter: TrainFilter) {
|
|
||||||
filter.isActive = !filter.isActive;
|
|
||||||
},
|
|
||||||
|
|
||||||
setFilterOnly(filter: TrainFilter) {
|
|
||||||
this.filterList.forEach((f) => (f.isActive = f.id == filter.id));
|
|
||||||
},
|
|
||||||
|
|
||||||
resetFilters() {
|
|
||||||
this.filterList.forEach((f) => (f.isActive = true));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '../../styles/responsive';
|
|
||||||
@import '../../styles/card';
|
|
||||||
|
|
||||||
|
|
||||||
.card {
|
|
||||||
section {
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
font-size: 2em;
|
|
||||||
font-weight: 700;
|
|
||||||
color: $accentCol;
|
|
||||||
|
|
||||||
margin: 0.5em 0;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -118,11 +118,10 @@ export default defineComponent({
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
margin: 1em 0;
|
|
||||||
|
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
|
|
||||||
background: #333;
|
background: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.train-image {
|
img.train-image {
|
||||||
@@ -139,8 +138,6 @@ img.train-image {
|
|||||||
&-list {
|
&-list {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
margin-top: 1em;
|
|
||||||
|
|
||||||
@include smallScreen() {
|
@include smallScreen() {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { JournalFilterType } from "../../scripts/enums/JournalFilterType";
|
||||||
|
import { JournalTimetableFilter } from "../../types/Journal/JournalTimetablesTypes";
|
||||||
|
|
||||||
|
export const journalTimetableFilters: JournalTimetableFilter[] = [
|
||||||
|
{
|
||||||
|
id: JournalFilterType.all,
|
||||||
|
filterSection: 'timetable-status',
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: JournalFilterType.active,
|
||||||
|
filterSection: 'timetable-status',
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: JournalFilterType.fulfilled,
|
||||||
|
filterSection: 'timetable-status',
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: JournalFilterType.abandoned,
|
||||||
|
filterSection: 'timetable-status',
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TrainFilter } from "vue";
|
import { TrainFilterType } from '../../scripts/enums/TrainFilterType';
|
||||||
import { TrainFilterType } from "../scripts/enums/TrainFilterType";
|
import { TrainFilter } from '../../types/Trains/TrainOptionsTypes';
|
||||||
|
|
||||||
export const trainFilters: TrainFilter[] = [
|
export const trainFilters: TrainFilter[] = [
|
||||||
{
|
{
|
||||||
@@ -56,5 +56,5 @@ export const sorterOptions = [
|
|||||||
{
|
{
|
||||||
id: 'length',
|
id: 'length',
|
||||||
value: 'długość',
|
value: 'długość',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { JournalFilter } from "vue";
|
|
||||||
import { JournalFilterType } from "../scripts/enums/JournalFilterType";
|
|
||||||
|
|
||||||
export const journalTimetableFilters: JournalFilter[] = [
|
|
||||||
{
|
|
||||||
id: JournalFilterType.all,
|
|
||||||
filterSection: "timetable-status",
|
|
||||||
isActive: true
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: JournalFilterType.active,
|
|
||||||
filterSection: "timetable-status",
|
|
||||||
isActive: false
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: JournalFilterType.fulfilled,
|
|
||||||
filterSection: "timetable-status",
|
|
||||||
isActive: false
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: JournalFilterType.abandoned,
|
|
||||||
filterSection: "timetable-status",
|
|
||||||
isActive: false
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const journalDispatcherFilters: JournalFilter[] = []
|
|
||||||
+50
-48
@@ -11,10 +11,10 @@
|
|||||||
"migration-confirm": "Roger that!"
|
"migration-confirm": "Roger that!"
|
||||||
},
|
},
|
||||||
"update": {
|
"update": {
|
||||||
"title": "New Stacjownik version is available!",
|
"title": "New Stacjownik version is available!",
|
||||||
"paragraph1": "Enjoy the application and may the green signal be with you!",
|
"paragraph1": "Enjoy the application and may the green signal be with you!",
|
||||||
"release-link": "Click here to browse version changelog (GitHub)",
|
"release-link": "Click here to browse version changelog (GitHub)",
|
||||||
"confirm-button": "Understood!"
|
"confirm-button": "Understood!"
|
||||||
},
|
},
|
||||||
"data-status": {
|
"data-status": {
|
||||||
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with Stacjownik API service!",
|
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with Stacjownik API service!",
|
||||||
@@ -72,7 +72,51 @@
|
|||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"filters": "FILTERS",
|
"filters": "FILTERS",
|
||||||
"donate": "DONATE"
|
"donate": "DONATE",
|
||||||
|
|
||||||
|
"search-button": "Search",
|
||||||
|
"reset-button": "Reset",
|
||||||
|
|
||||||
|
"sort-title": "SORT BY:",
|
||||||
|
"filter-title": "FILTER BY:",
|
||||||
|
"search-title": "SEARCH:",
|
||||||
|
|
||||||
|
"search-train-no": "Train no. / #",
|
||||||
|
"search-train": "Train no.",
|
||||||
|
"search-driver": "Driver name",
|
||||||
|
"search-dispatcher": "Dispatcher name",
|
||||||
|
"search-station": "Scenery name",
|
||||||
|
"search-date": "Timetable date (CEST / GMT+2)",
|
||||||
|
|
||||||
|
"sort-mass": "mass",
|
||||||
|
"sort-speed": "speed",
|
||||||
|
"sort-length": "length",
|
||||||
|
"sort-distance": "distance",
|
||||||
|
"sort-timetable": "train no.",
|
||||||
|
"sort-progress": "route progress",
|
||||||
|
"sort-delay": "current delay",
|
||||||
|
|
||||||
|
"sort-total-stops": "total stops",
|
||||||
|
"sort-beginDate": "date",
|
||||||
|
"sort-timetableId": "timetable ID",
|
||||||
|
"sort-timestampFrom": "date",
|
||||||
|
"sort-duration": "duration",
|
||||||
|
|
||||||
|
"filter-comments": "COMMENTS",
|
||||||
|
"filter-twr": "TWR",
|
||||||
|
"filter-skr": "SKR",
|
||||||
|
"filter-passenger": "PASSENGER",
|
||||||
|
"filter-freight": "FREIGHT",
|
||||||
|
"filter-other": "OTHER",
|
||||||
|
"filter-noTimetable": "NO TIMETABLE",
|
||||||
|
|
||||||
|
"filter-reset": "RESET FILTERS",
|
||||||
|
"filter-clear": "CLEAR FILTERS",
|
||||||
|
|
||||||
|
"filter-all": "ALL ENTRIES",
|
||||||
|
"filter-abandoned": "ABANDONED",
|
||||||
|
"filter-fulfilled": "FULFILLED",
|
||||||
|
"filter-active": "ACTIVE"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"endingStatus": "ENDS SOON",
|
"endingStatus": "ENDS SOON",
|
||||||
@@ -116,7 +160,7 @@
|
|||||||
"hour": "h",
|
"hour": "h",
|
||||||
"no-limit": "NO LIMIT",
|
"no-limit": "NO LIMIT",
|
||||||
"include-selected": "INCLUDE SELECTED",
|
"include-selected": "INCLUDE SELECTED",
|
||||||
"save": "↵ SAVE FILTERS",
|
"save": "SAVE FILTERS",
|
||||||
"reset": "RESET FILTERS",
|
"reset": "RESET FILTERS",
|
||||||
"close": "CLOSE FILTERS"
|
"close": "CLOSE FILTERS"
|
||||||
},
|
},
|
||||||
@@ -150,28 +194,6 @@
|
|||||||
"current-signal": "at signal",
|
"current-signal": "at signal",
|
||||||
"current-track": "on track",
|
"current-track": "on track",
|
||||||
|
|
||||||
"option-mass": "mass",
|
|
||||||
"option-speed": "speed",
|
|
||||||
"option-length": "length",
|
|
||||||
"option-distance": "distance",
|
|
||||||
"option-timetable": "train no.",
|
|
||||||
"option-progress": "route progress",
|
|
||||||
"option-delay": "current delay",
|
|
||||||
"option-comments": "comments",
|
|
||||||
|
|
||||||
"filter-comments": "comments",
|
|
||||||
"filter-twr": "TWR",
|
|
||||||
"filter-skr": "SKR",
|
|
||||||
"filter-passenger": "passenger",
|
|
||||||
"filter-freight": "freight",
|
|
||||||
"filter-other": "other",
|
|
||||||
"filter-noTimetable": "no timetable",
|
|
||||||
"filter-reset": "X RESET",
|
|
||||||
|
|
||||||
"sorter-prefix": "Sort: ",
|
|
||||||
"search-train": "Train no.",
|
|
||||||
"search-driver": "Driver name",
|
|
||||||
|
|
||||||
"delayed": "Delayed: ",
|
"delayed": "Delayed: ",
|
||||||
"preponed": "Ahead of schedule: ",
|
"preponed": "Ahead of schedule: ",
|
||||||
"on-time": "On time",
|
"on-time": "On time",
|
||||||
@@ -205,26 +227,6 @@
|
|||||||
"section-timetables": "TIMETABLES",
|
"section-timetables": "TIMETABLES",
|
||||||
"section-dispatchers": "DISPATCHERS",
|
"section-dispatchers": "DISPATCHERS",
|
||||||
|
|
||||||
"search": "Search",
|
|
||||||
"search-train": "Train no. / #",
|
|
||||||
"search-driver": "Driver name",
|
|
||||||
"search-dispatcher": "Dispatcher name",
|
|
||||||
"search-station": "Scenery name",
|
|
||||||
|
|
||||||
"sort-prefix": "Sort: ",
|
|
||||||
|
|
||||||
"option-distance": "distance",
|
|
||||||
"option-total-stops": "total stops",
|
|
||||||
"option-beginDate": "date",
|
|
||||||
"option-timetableId": "timetable ID",
|
|
||||||
"option-timestampFrom": "date",
|
|
||||||
"option-duration": "duration",
|
|
||||||
|
|
||||||
"filter-all": "ALL ENTRIES",
|
|
||||||
"filter-abandoned": "ABANDONED",
|
|
||||||
"filter-fulfilled": "FULFILLED",
|
|
||||||
"filter-active": "ACTIVE",
|
|
||||||
|
|
||||||
"no-further-data": "No further data for current parameters",
|
"no-further-data": "No further data for current parameters",
|
||||||
"loading-further-data": "Loading...",
|
"loading-further-data": "Loading...",
|
||||||
|
|
||||||
|
|||||||
+46
-43
@@ -74,7 +74,52 @@
|
|||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"filters": "FILTRY",
|
"filters": "FILTRY",
|
||||||
"donate": "WESPRZYJ"
|
"donate": "WESPRZYJ",
|
||||||
|
|
||||||
|
"search-button": "Szukaj",
|
||||||
|
"reset-button": "Zresetuj",
|
||||||
|
|
||||||
|
"sort-title": "SORTUJ WG:",
|
||||||
|
"filter-title": "FILTRUJ WG:",
|
||||||
|
"search-title": "SZUKAJ:",
|
||||||
|
|
||||||
|
"search-train-no": "Nr pociągu",
|
||||||
|
"search-train": "Nr pociągu / #",
|
||||||
|
"search-driver": "Nick maszynisty",
|
||||||
|
"search-dispatcher": "Nick dyżurnego",
|
||||||
|
"search-station": "Nazwa scenerii",
|
||||||
|
"search-date": "Data rozkładu jazdy (czas polski)",
|
||||||
|
|
||||||
|
"sort-distance": "kilometraż",
|
||||||
|
"sort-total-stops": "stacje",
|
||||||
|
"sort-beginDate": "data",
|
||||||
|
"sort-timetableId": "ID rozkładu",
|
||||||
|
"sort-timestampFrom": "data",
|
||||||
|
"sort-duration": "czas dyżuru",
|
||||||
|
|
||||||
|
"sort-mass": "masa",
|
||||||
|
"sort-speed": "prędkość",
|
||||||
|
"sort-length": "długość",
|
||||||
|
"sort-timetable": "nr pociągu",
|
||||||
|
"sort-progress": "przebyta trasa",
|
||||||
|
"sort-delay": "opóźnienie",
|
||||||
|
"sort-comments": "uwagi ekspl.",
|
||||||
|
|
||||||
|
"filter-comments": "UWAGI EKSPLOATACYJNE",
|
||||||
|
"filter-twr": "TWR",
|
||||||
|
"filter-skr": "PRZEKR. SKRAJNIA",
|
||||||
|
"filter-passenger": "PASAŻERSKIE",
|
||||||
|
"filter-freight": "TOWAROWE",
|
||||||
|
"filter-other": "INNE",
|
||||||
|
"filter-noTimetable": "BEZ RJ",
|
||||||
|
|
||||||
|
"filter-reset": "ZRESETUJ FILTRY",
|
||||||
|
"filter-clear": "WYŁĄCZ FILTRY",
|
||||||
|
|
||||||
|
"filter-all": "WSZYSTKIE",
|
||||||
|
"filter-abandoned": "PORZUCONE",
|
||||||
|
"filter-fulfilled": "WYPEŁNIONE",
|
||||||
|
"filter-active": "AKTYWNE"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"endingStatus": "KOŃCZY",
|
"endingStatus": "KOŃCZY",
|
||||||
@@ -152,28 +197,6 @@
|
|||||||
"current-signal": "przy semaforze",
|
"current-signal": "przy semaforze",
|
||||||
"current-track": "na szlaku",
|
"current-track": "na szlaku",
|
||||||
|
|
||||||
"option-mass": "masa",
|
|
||||||
"option-speed": "prędkość",
|
|
||||||
"option-length": "długość",
|
|
||||||
"option-distance": "kilometraż",
|
|
||||||
"option-timetable": "nr pociągu",
|
|
||||||
"option-progress": "przebyta trasa",
|
|
||||||
"option-delay": "opóźnienie",
|
|
||||||
"option-comments": "uwagi ekspl.",
|
|
||||||
|
|
||||||
"filter-comments": "uwagi ekspl.",
|
|
||||||
"filter-twr": "TWR",
|
|
||||||
"filter-skr": "SKR",
|
|
||||||
"filter-passenger": "pasażerskie",
|
|
||||||
"filter-freight": "towarowe",
|
|
||||||
"filter-other": "inne",
|
|
||||||
"filter-noTimetable": "bez RJ",
|
|
||||||
"filter-reset": "X RESETUJ",
|
|
||||||
|
|
||||||
"sorter-prefix": "Sortuj: ",
|
|
||||||
"search-train": "Numer pociągu",
|
|
||||||
"search-driver": "Nick maszynisty",
|
|
||||||
|
|
||||||
"delayed": "Opóźniony: ",
|
"delayed": "Opóźniony: ",
|
||||||
"preponed": "Przed czasem: ",
|
"preponed": "Przed czasem: ",
|
||||||
"on-time": "Planowo",
|
"on-time": "Planowo",
|
||||||
@@ -207,26 +230,6 @@
|
|||||||
"section-timetables": "ROZKŁADY JAZDY",
|
"section-timetables": "ROZKŁADY JAZDY",
|
||||||
"section-dispatchers": "DYŻURNI",
|
"section-dispatchers": "DYŻURNI",
|
||||||
|
|
||||||
"search": "Szukaj",
|
|
||||||
"search-train": "Nr pociągu / #",
|
|
||||||
"search-driver": "Nick maszynisty",
|
|
||||||
"search-dispatcher": "Nick dyżurnego",
|
|
||||||
"search-station": "Nazwa scenerii",
|
|
||||||
|
|
||||||
"sort-prefix": "Sortuj: ",
|
|
||||||
|
|
||||||
"option-distance": "kilometraż",
|
|
||||||
"option-total-stops": "stacje",
|
|
||||||
"option-beginDate": "data",
|
|
||||||
"option-timetableId": "ID rozkładu",
|
|
||||||
"option-timestampFrom": "data",
|
|
||||||
"option-duration": "czas dyżuru",
|
|
||||||
|
|
||||||
"filter-all": "WSZYSTKIE",
|
|
||||||
"filter-abandoned": "PORZUCONE",
|
|
||||||
"filter-fulfilled": "WYPEŁNIONE",
|
|
||||||
"filter-active": "AKTYWNE",
|
|
||||||
|
|
||||||
"no-further-data": "Brak dalszych wyników dla podanych parametrów",
|
"no-further-data": "Brak dalszych wyników dla podanych parametrów",
|
||||||
"loading-further-data": "Ładowanie...",
|
"loading-further-data": "Ładowanie...",
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TrainFilter } from "vue";
|
import { TrainFilter } from "../../types/Trains/TrainOptionsTypes";
|
||||||
import { TrainFilterType } from "../enums/TrainFilterType";
|
import { TrainFilterType } from "../enums/TrainFilterType";
|
||||||
import Train from "../interfaces/Train";
|
import Train from "../interfaces/Train";
|
||||||
import TrainStop from "../interfaces/TrainStop";
|
import TrainStop from "../interfaces/TrainStop";
|
||||||
|
|||||||
@@ -8,16 +8,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-enter-active {
|
&-enter-active {
|
||||||
transition: all 150ms ease-out;
|
transition: all 150ms 100ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-leave-active {
|
&-leave-active {
|
||||||
transition: all 150ms ease-out;
|
transition: all 150ms 100ms ease-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Styles
|
//Styles
|
||||||
|
|
||||||
|
.list_wrapper {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 90vh;
|
||||||
|
min-height: 550px;
|
||||||
|
|
||||||
|
padding-right: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
.journal_wrapper {
|
.journal_wrapper {
|
||||||
max-width: 1350px;
|
max-width: 1350px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -40,9 +48,9 @@
|
|||||||
|
|
||||||
.journal_item,
|
.journal_item,
|
||||||
.journal_warning {
|
.journal_warning {
|
||||||
background: #202020;
|
background-color: #1a1a1a;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: 1em 0;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.journal_top-bar {
|
.journal_top-bar {
|
||||||
@@ -69,3 +77,9 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (orientation: landscape) {
|
||||||
|
.journal_wrapper {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+40
-29
@@ -12,6 +12,24 @@
|
|||||||
|
|
||||||
--clr-error: #df3e3e;
|
--clr-error: #df3e3e;
|
||||||
--clr-warning: #c59429;
|
--clr-warning: #c59429;
|
||||||
|
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&-track {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-thumb {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -36,28 +54,6 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*:focus-visible {
|
|
||||||
outline: 1px solid white;
|
|
||||||
outline-offset: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
|
|
||||||
&-track {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-thumb {
|
|
||||||
background-color: #777;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.g-tooltip {
|
.g-tooltip {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@@ -113,7 +109,6 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: 1px solid white;
|
|
||||||
background: none;
|
background: none;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
@@ -190,6 +185,16 @@ ul {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
background-color: #333;
|
||||||
|
|
||||||
|
border-radius: 0.25em;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
background: none;
|
background: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -211,8 +216,18 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&--image {
|
&--image {
|
||||||
color: white;
|
display: flex;
|
||||||
transition: color 0.3s;
|
|
||||||
|
padding: 0.4em 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1em;
|
||||||
|
|
||||||
|
border-radius: 0.75em 0.75em 0 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 1.3em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--option {
|
&--option {
|
||||||
@@ -224,10 +239,6 @@ ul {
|
|||||||
border-radius: 0.25em;
|
border-radius: 0.25em;
|
||||||
padding: 0.25em 0.5em;
|
padding: 0.25em 0.5em;
|
||||||
|
|
||||||
&:hover:not(:disabled) {
|
|
||||||
background-color: #3c3c3c;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.checked {
|
&.checked {
|
||||||
color: var(--clr-primary);
|
color: var(--clr-primary);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
@import 'responsive.scss';
|
@import 'responsive.scss';
|
||||||
|
|
||||||
.search {
|
.search {
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
color: #ccc;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
&-box {
|
&-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@@ -9,7 +15,6 @@
|
|||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
background-color: #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-input {
|
&-input {
|
||||||
@@ -18,7 +23,6 @@
|
|||||||
background-color: #333;
|
background-color: #333;
|
||||||
|
|
||||||
padding: 0.35em 0.5em;
|
padding: 0.35em 0.5em;
|
||||||
margin-right: 0.2em;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +37,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
width: 80%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
&-box,
|
&-box,
|
||||||
&-button {
|
&-button {
|
||||||
@@ -42,10 +51,5 @@
|
|||||||
&-box {
|
&-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-button {
|
|
||||||
width: 80%;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export type JournalDispatcherSearcher = {
|
||||||
|
[key in 'search-dispatcher' | 'search-station' | 'search-date']: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface JournalDispatcherSorter {
|
||||||
|
id: 'timestampFrom' | 'duration';
|
||||||
|
dir: -1 | 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { JournalFilterType } from '../../scripts/enums/JournalFilterType';
|
||||||
|
|
||||||
|
export type JorunalTimetableSearchType = {
|
||||||
|
[key in 'search-driver' | 'search-train' | 'search-date']: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface JournalTimetableFilter {
|
||||||
|
id: JournalFilterType;
|
||||||
|
filterSection: string;
|
||||||
|
isActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JournalTimetableSorter {
|
||||||
|
id: 'timetableId' | 'beginDate' | 'distance' | 'total-stops';
|
||||||
|
dir: -1 | 1;
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export type JournalDispatcherSearcher = {
|
|
||||||
[key in 'search-dispatcher' | 'search-station']: string;
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export type JournalTimetableSearcher = {
|
|
||||||
[key in 'search-driver' | 'search-train']: string;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { TrainFilterType } from "../../scripts/enums/TrainFilterType";
|
||||||
|
|
||||||
|
export interface TrainFilter {
|
||||||
|
id: TrainFilterType;
|
||||||
|
isActive: boolean;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<section class="trains-view">
|
<section class="trains-view">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="options-bar">
|
<div class="options-bar">
|
||||||
<train-options />
|
<TrainOptions :sorter-option-ids="['distance', 'progress', 'delay', 'mass', 'speed', 'length']" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TrainTable :trains="computedTrains" />
|
<TrainTable :trains="computedTrains" />
|
||||||
@@ -11,14 +11,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, ComputedRef, defineComponent, provide, reactive, ref, TrainFilter } from 'vue';
|
import { computed, ComputedRef, defineComponent, provide, reactive, ref } from 'vue';
|
||||||
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
import TrainOptions from '../components/TrainsView/TrainOptions.vue';
|
||||||
import TrainStats from '../components/TrainsView/TrainStats.vue';
|
import TrainStats from '../components/TrainsView/TrainStats.vue';
|
||||||
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
||||||
import { trainFilters } from '../data/trainOptions';
|
import { trainFilters } from '../constants/Trains/TrainOptionsConsts';
|
||||||
import Train from '../scripts/interfaces/Train';
|
import Train from '../scripts/interfaces/Train';
|
||||||
import { filteredTrainList } from '../scripts/managers/trainFilterManager';
|
import { filteredTrainList } from '../scripts/managers/trainFilterManager';
|
||||||
import { useStore } from '../store/store';
|
import { useStore } from '../store/store';
|
||||||
|
import { TrainFilter } from '../types/Trains/TrainOptionsTypes';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -48,7 +49,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
const sorterActive = ref({ id: 'distance', dir: -1 });
|
const sorterActive = ref({ id: 'distance', dir: -1 });
|
||||||
const filterList = reactive([...trainFilters]) as TrainFilter[];
|
const filterList = reactive([...trainFilters]) as TrainFilter[];
|
||||||
const isTrainOptionsCardVisible = ref(false);
|
|
||||||
|
|
||||||
const searchedDriver = ref('');
|
const searchedDriver = ref('');
|
||||||
const searchedTrain = ref('');
|
const searchedTrain = ref('');
|
||||||
@@ -57,7 +57,6 @@ export default defineComponent({
|
|||||||
provide('searchedDriver', searchedDriver);
|
provide('searchedDriver', searchedDriver);
|
||||||
provide('sorterActive', sorterActive);
|
provide('sorterActive', sorterActive);
|
||||||
provide('filterList', filterList);
|
provide('filterList', filterList);
|
||||||
provide('isTrainOptionsCardVisible', isTrainOptionsCardVisible);
|
|
||||||
|
|
||||||
const computedTrains: ComputedRef<Train[]> = computed(() => {
|
const computedTrains: ComputedRef<Train[]> = computed(() => {
|
||||||
return filteredTrainList(
|
return filteredTrainList(
|
||||||
@@ -82,10 +81,6 @@ export default defineComponent({
|
|||||||
this.searchedTrain = this.train;
|
this.searchedTrain = this.train;
|
||||||
this.searchedDriver = this.driver || '';
|
this.searchedDriver = this.driver || '';
|
||||||
}
|
}
|
||||||
// if (this.train) {
|
|
||||||
// this.searchedTrain = this.train;
|
|
||||||
// if(this.x) this.searchedDriver = this.x;
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -102,5 +97,4 @@ export default defineComponent({
|
|||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
max-width: 1350px;
|
max-width: 1350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Vendored
-30
@@ -1,30 +0,0 @@
|
|||||||
import { ComponentCustomProperties } from 'vue'
|
|
||||||
import { Store } from 'vuex'
|
|
||||||
import { JournalFilterType } from './scripts/enums/JournalFilterType';
|
|
||||||
import { TrainFilterType } from './scripts/enums/TrainFilterType';
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
// declare your own store states
|
|
||||||
interface State {
|
|
||||||
count: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// provide typings for `this.$store`
|
|
||||||
interface ComponentCustomProperties {
|
|
||||||
$store: Store<State>
|
|
||||||
}
|
|
||||||
|
|
||||||
// Train filter for TrainView
|
|
||||||
interface TrainFilter {
|
|
||||||
id: TrainFilterType;
|
|
||||||
isActive: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface JournalFilter {
|
|
||||||
id: JournalFilterType;
|
|
||||||
filterSection: string;
|
|
||||||
isActive: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user