mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
Filtry aktywnych pociągów
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="journal-options">
|
||||
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
|
||||
|
||||
<button class="btn--open" @click="showOptions = !showOptions">
|
||||
<img :src="getIcon('filter2')" alt="Open filters" />
|
||||
FILTRY
|
||||
@@ -27,19 +29,19 @@
|
||||
:id="filter.id"
|
||||
@click="onFilterChange(filter)"
|
||||
>
|
||||
{{ $t(`journal.filter-${filter.id}`) }}
|
||||
{{ $t(`options.filter-${filter.id}`) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1>SZUKAJ:</h1>
|
||||
|
||||
<div class="content_search">
|
||||
<div class="search-box" v-for="(value, propName) in searchersValues" :key="propName">
|
||||
<div class="search-box" v-for="(_, propName) in searchersValues" :key="propName">
|
||||
<input
|
||||
class="search-input"
|
||||
:type="propName == 'search-date' ? 'date' : 'input'"
|
||||
@keydown.enter="onSearchConfirm"
|
||||
:placeholder="$t(`journal.${propName}`)"
|
||||
:placeholder="$t(`options.${propName}`)"
|
||||
v-model="searchersValues[propName]"
|
||||
/>
|
||||
|
||||
@@ -58,7 +60,7 @@
|
||||
</div> -->
|
||||
|
||||
<action-button class="search-button" @click="onSearchConfirm">
|
||||
{{ $t('journal.search') }}
|
||||
{{ $t('options.search-button') }}
|
||||
</action-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,7 +109,7 @@ export default defineComponent({
|
||||
translatedSorterOptions() {
|
||||
return this.$props.sorterOptionIds.map((id) => ({
|
||||
id,
|
||||
value: this.$t(`journal.option-${id}`),
|
||||
value: this.$t(`options.sort-${id}`),
|
||||
}));
|
||||
},
|
||||
},
|
||||
@@ -141,6 +143,16 @@ export default defineComponent({
|
||||
@import '../../styles/search_box.scss';
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
.bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.journal-options {
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -1,230 +1,266 @@
|
||||
<template>
|
||||
<div class="train-options">
|
||||
<div class="options_wrapper">
|
||||
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
|
||||
|
||||
<button class="btn--open" @click="showOptions = !showOptions">
|
||||
<img :src="getIcon('filter2')" alt="Open filters" />
|
||||
FILTRY
|
||||
</button>
|
||||
|
||||
<div class="options_wrapper" v-if="showOptions">
|
||||
<div class="options_content">
|
||||
<div class="content_select">
|
||||
<select-box
|
||||
:itemList="translatedSorterOptions"
|
||||
:defaultItemIndex="0"
|
||||
@selected="changeSorter"
|
||||
:prefix="$t('trains.sorter-prefix')"
|
||||
/>
|
||||
<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="trainFilterList.length != 0">{{ $t('options.filter-title') }}</h1>
|
||||
|
||||
<div class="options_filters">
|
||||
<button
|
||||
v-for="filter in trainFilterList"
|
||||
class="filter-option btn--option"
|
||||
:class="{ checked: filter.isActive }"
|
||||
:id="filter.id"
|
||||
@click="onFilterChange(filter)"
|
||||
>
|
||||
{{ $t(`options.filter-${filter.id}`) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1>{{ $t('options.search-title') }}</h1>
|
||||
|
||||
<div class="content_search">
|
||||
<div class="search-box">
|
||||
<input class="search-input" v-model="searchedTrain" :placeholder="$t('trains.search-train')" />
|
||||
<input class="search-input" :placeholder="$t(`options.search-train`)" v-model="searchedTrain" />
|
||||
|
||||
<button class="search-exit">
|
||||
<img :src="getIcon('exit')" alt="exit-icon" @click="() => (searchedTrain = '')" />
|
||||
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear('train')" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="search-box">
|
||||
<input class="search-input" v-model="searchedDriver" :placeholder="$t('trains.search-driver')" />
|
||||
<input class="search-input" :placeholder="$t(`options.search-driver`)" v-model="searchedDriver" />
|
||||
|
||||
<button class="search-exit">
|
||||
<img :src="getIcon('exit')" alt="exit-icon" @click="() => (searchedDriver = '')" />
|
||||
<img :src="getIcon('exit')" alt="exit-icon" @click="onInputClear('driver')" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, TrainFilter } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import imageMixin from '../../mixins/imageMixin';
|
||||
import { TrainFilter } from '../../types/TrainOptionsTypes';
|
||||
import ActionButton from '../Global/ActionButton.vue';
|
||||
import SelectBox from '../Global/SelectBox.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SelectBox },
|
||||
emits: ['changeSearchedTrain', 'changeSearchedDriver', 'changeSorter'],
|
||||
components: { SelectBox, ActionButton },
|
||||
emits: ['onSearchConfirm'],
|
||||
mixins: [imageMixin],
|
||||
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
|
||||
const sorterOptions = [
|
||||
{
|
||||
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}`),
|
||||
}))
|
||||
);
|
||||
props: {
|
||||
sorterOptionIds: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
translatedSorterOptions,
|
||||
searchedTrain: inject('searchedTrain') as string,
|
||||
searchedDriver: inject('searchedDriver') as string,
|
||||
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
|
||||
filterList,
|
||||
showOptions: false,
|
||||
};
|
||||
},
|
||||
|
||||
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: {
|
||||
changeSorter(item: { id: string | number; value: string }) {
|
||||
onSorterChange(item: { id: string | number; value: string }) {
|
||||
this.sorterActive.id = item.id;
|
||||
this.sorterActive.dir = -1;
|
||||
this.$emit('onSearchConfirm');
|
||||
},
|
||||
|
||||
toggleFilter(filter: TrainFilter) {
|
||||
onFilterChange(filter: TrainFilter) {
|
||||
filter.isActive = !filter.isActive;
|
||||
},
|
||||
|
||||
setFilterOnly(filter: TrainFilter) {
|
||||
this.filterList.forEach((f) => (f.isActive = f.id == filter.id));
|
||||
onInputClear(id: 'driver' | 'train') {
|
||||
if (id == 'driver') this.searchedDriver = '';
|
||||
if (id == 'train') this.searchedTrain = '';
|
||||
},
|
||||
|
||||
resetFilters() {
|
||||
this.filterList.forEach((f) => (f.isActive = true));
|
||||
this.searchedDriver = '';
|
||||
this.searchedTrain = '';
|
||||
test(e: Event) {
|
||||
console.log(e.target);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/responsive';
|
||||
@import '../../styles/responsive.scss';
|
||||
@import '../../styles/search_box.scss';
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
.train-options {
|
||||
@include smallScreen() {
|
||||
width: 100%;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
.bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.options {
|
||||
&_wrapper {
|
||||
display: flex;
|
||||
}
|
||||
.journal-options {
|
||||
position: relative;
|
||||
|
||||
&_content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.content_search,
|
||||
.content_select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
padding: 0.25em 0.25em 0 0;
|
||||
}
|
||||
}
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.filters {
|
||||
.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;
|
||||
|
||||
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;
|
||||
|
||||
margin-top: 0.5em;
|
||||
|
||||
@include smallScreen() {
|
||||
justify-content: center;
|
||||
}
|
||||
padding: 0.25em 0.25em 0 0;
|
||||
}
|
||||
|
||||
.filter {
|
||||
background: #333;
|
||||
padding: 0.2em 0.25em;
|
||||
margin: 0.25em 0.25em 0 0;
|
||||
.content_search > div {
|
||||
margin: 0.5em auto;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
color: gray;
|
||||
|
||||
&.active {
|
||||
color: var(--clr-primary);
|
||||
.filter-option {
|
||||
&#abandoned {
|
||||
color: salmon;
|
||||
}
|
||||
|
||||
&.reset-btn {
|
||||
color: salmon;
|
||||
&#fulfilled {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
&#active {
|
||||
color: lightblue;
|
||||
}
|
||||
}
|
||||
|
||||
@include smallScreen() {
|
||||
.journal-options {
|
||||
width: 100%;
|
||||
h1 {
|
||||
text-align: center;
|
||||
|
||||
&::before {
|
||||
width: 75%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
&_wrapper {
|
||||
justify-content: center;
|
||||
}
|
||||
.options_wrapper {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&_content {
|
||||
padding: 0 1em;
|
||||
.btn--open {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
.content_select {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content_search {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.options_filters,
|
||||
.options_sorters {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
+59
-59
@@ -1,60 +1,60 @@
|
||||
import { TrainFilter } from "vue";
|
||||
import { TrainFilterType } from "../scripts/enums/TrainFilterType";
|
||||
|
||||
export const trainFilters: TrainFilter[] = [
|
||||
{
|
||||
id: TrainFilterType.twr,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.skr,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.passenger,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.freight,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.other,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.comments,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.noTimetable,
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const sorterOptions = [
|
||||
{
|
||||
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ść',
|
||||
}
|
||||
import { TrainFilterType } from "../scripts/enums/TrainFilterType";
|
||||
import { TrainFilter } from "../types/TrainOptionsTypes";
|
||||
|
||||
export const trainFilters: TrainFilter[] = [
|
||||
{
|
||||
id: TrainFilterType.twr,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.skr,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.passenger,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.freight,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.other,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.comments,
|
||||
isActive: true,
|
||||
},
|
||||
{
|
||||
id: TrainFilterType.noTimetable,
|
||||
isActive: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const sorterOptions = [
|
||||
{
|
||||
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ść',
|
||||
}
|
||||
];
|
||||
+46
-48
@@ -11,10 +11,10 @@
|
||||
"migration-confirm": "Roger that!"
|
||||
},
|
||||
"update": {
|
||||
"title": "New Stacjownik version is available!",
|
||||
"paragraph1": "Enjoy the application and may the green signal be with you!",
|
||||
"release-link": "Click here to browse version changelog (GitHub)",
|
||||
"confirm-button": "Understood!"
|
||||
"title": "New Stacjownik version is available!",
|
||||
"paragraph1": "Enjoy the application and may the green signal be with you!",
|
||||
"release-link": "Click here to browse version changelog (GitHub)",
|
||||
"confirm-button": "Understood!"
|
||||
},
|
||||
"data-status": {
|
||||
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with Stacjownik API service!",
|
||||
@@ -72,7 +72,47 @@
|
||||
},
|
||||
"options": {
|
||||
"filters": "FILTERS",
|
||||
"donate": "DONATE"
|
||||
"donate": "DONATE",
|
||||
|
||||
"search-button": "Search",
|
||||
|
||||
"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-scenery": "Scenery name",
|
||||
|
||||
"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": "X RESET",
|
||||
|
||||
"filter-all": "ALL ENTRIES",
|
||||
"filter-abandoned": "ABANDONED",
|
||||
"filter-fulfilled": "FULFILLED",
|
||||
"filter-active": "ACTIVE"
|
||||
},
|
||||
"filters": {
|
||||
"endingStatus": "ENDS SOON",
|
||||
@@ -116,7 +156,7 @@
|
||||
"hour": "h",
|
||||
"no-limit": "NO LIMIT",
|
||||
"include-selected": "INCLUDE SELECTED",
|
||||
"save": "↵ SAVE FILTERS",
|
||||
"save": "SAVE FILTERS",
|
||||
"reset": "RESET FILTERS",
|
||||
"close": "CLOSE FILTERS"
|
||||
},
|
||||
@@ -150,28 +190,6 @@
|
||||
"current-signal": "at signal",
|
||||
"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: ",
|
||||
"preponed": "Ahead of schedule: ",
|
||||
"on-time": "On time",
|
||||
@@ -205,26 +223,6 @@
|
||||
"section-timetables": "TIMETABLES",
|
||||
"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",
|
||||
"loading-further-data": "Loading...",
|
||||
|
||||
|
||||
+42
-43
@@ -74,7 +74,48 @@
|
||||
},
|
||||
"options": {
|
||||
"filters": "FILTRY",
|
||||
"donate": "WESPRZYJ"
|
||||
"donate": "WESPRZYJ",
|
||||
|
||||
"search-button": "Szukaj",
|
||||
|
||||
"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",
|
||||
|
||||
"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": "X RESETUJ",
|
||||
|
||||
"filter-all": "WSZYSTKIE",
|
||||
"filter-abandoned": "PORZUCONE",
|
||||
"filter-fulfilled": "WYPEŁNIONE",
|
||||
"filter-active": "AKTYWNE"
|
||||
},
|
||||
"filters": {
|
||||
"endingStatus": "KOŃCZY",
|
||||
@@ -152,28 +193,6 @@
|
||||
"current-signal": "przy semaforze",
|
||||
"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: ",
|
||||
"preponed": "Przed czasem: ",
|
||||
"on-time": "Planowo",
|
||||
@@ -207,26 +226,6 @@
|
||||
"section-timetables": "ROZKŁADY JAZDY",
|
||||
"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",
|
||||
"loading-further-data": "Ładowanie...",
|
||||
|
||||
|
||||
@@ -1,115 +1,115 @@
|
||||
import { TrainFilter } from "vue";
|
||||
import { TrainFilterType } from "../enums/TrainFilterType";
|
||||
import Train from "../interfaces/Train";
|
||||
import TrainStop from "../interfaces/TrainStop";
|
||||
|
||||
function confirmedPercentage(stops: TrainStop[] | undefined) {
|
||||
if (!stops) return -1;
|
||||
|
||||
return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
|
||||
};
|
||||
|
||||
function currentDelay(stops: TrainStop[] | undefined) {
|
||||
if (!stops) return -Infinity;
|
||||
|
||||
const delay =
|
||||
stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
|
||||
?.departureDelay || 0;
|
||||
|
||||
return delay;
|
||||
};
|
||||
|
||||
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
|
||||
return trainList.filter(
|
||||
(train) => {
|
||||
const isFiltered = filters.every(f => {
|
||||
if (f.isActive) return true;
|
||||
|
||||
if (!train.timetableData) return filters.find(filter => filter.id == TrainFilterType.noTimetable)!.isActive;
|
||||
|
||||
switch (f.id) {
|
||||
case TrainFilterType.comments:
|
||||
return !train.timetableData.followingStops.some(stop => stop.comments);
|
||||
|
||||
case TrainFilterType.twr:
|
||||
return !train.timetableData.TWR;
|
||||
|
||||
case TrainFilterType.skr:
|
||||
return !train.timetableData.SKR;
|
||||
|
||||
case TrainFilterType.passenger:
|
||||
return !/^[AMRE]\D{2}$/.test(train.timetableData.category);
|
||||
|
||||
case TrainFilterType.freight:
|
||||
return !train.timetableData.category.startsWith('T');
|
||||
|
||||
case TrainFilterType.other:
|
||||
return !/^[PXZL]\D{2}$/.test(train.timetableData.category);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
|
||||
return (searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
|
||||
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) && isFiltered
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: number }) {
|
||||
return trainList.sort((a: Train, b: Train) => {
|
||||
switch (sorterActive.id) {
|
||||
case 'mass':
|
||||
if (a.mass > b.mass) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'distance':
|
||||
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'progress':
|
||||
if (confirmedPercentage(a.timetableData?.followingStops) > confirmedPercentage(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'delay':
|
||||
if (currentDelay(a.timetableData?.followingStops) > currentDelay(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'speed':
|
||||
if (a.speed > b.speed) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'timetable':
|
||||
if (a.trainNo > b.trainNo) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'length':
|
||||
if (a.length > b.length) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
export function filteredTrainList(
|
||||
trainList: Train[],
|
||||
searchedTrain: string,
|
||||
searchedDriver: string,
|
||||
sorterActive: { id: string; dir: number },
|
||||
filters: TrainFilter[]
|
||||
) {
|
||||
|
||||
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
|
||||
return [...sortTrainList(filtered, sorterActive)];
|
||||
import { TrainFilter } from "../../types/TrainOptionsTypes";
|
||||
import { TrainFilterType } from "../enums/TrainFilterType";
|
||||
import Train from "../interfaces/Train";
|
||||
import TrainStop from "../interfaces/TrainStop";
|
||||
|
||||
function confirmedPercentage(stops: TrainStop[] | undefined) {
|
||||
if (!stops) return -1;
|
||||
|
||||
return Number(((stops.filter((stop) => stop.confirmed).length / stops.length) * 100).toFixed(0));
|
||||
};
|
||||
|
||||
function currentDelay(stops: TrainStop[] | undefined) {
|
||||
if (!stops) return -Infinity;
|
||||
|
||||
const delay =
|
||||
stops.find((stop, i) => (i == 0 && !stop.confirmed) || (i > 0 && stops[i - 1].confirmed && !stop.confirmed))
|
||||
?.departureDelay || 0;
|
||||
|
||||
return delay;
|
||||
};
|
||||
|
||||
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
|
||||
return trainList.filter(
|
||||
(train) => {
|
||||
const isFiltered = filters.every(f => {
|
||||
if (f.isActive) return true;
|
||||
|
||||
if (!train.timetableData) return filters.find(filter => filter.id == TrainFilterType.noTimetable)!.isActive;
|
||||
|
||||
switch (f.id) {
|
||||
case TrainFilterType.comments:
|
||||
return !train.timetableData.followingStops.some(stop => stop.comments);
|
||||
|
||||
case TrainFilterType.twr:
|
||||
return !train.timetableData.TWR;
|
||||
|
||||
case TrainFilterType.skr:
|
||||
return !train.timetableData.SKR;
|
||||
|
||||
case TrainFilterType.passenger:
|
||||
return !/^[AMRE]\D{2}$/.test(train.timetableData.category);
|
||||
|
||||
case TrainFilterType.freight:
|
||||
return !train.timetableData.category.startsWith('T');
|
||||
|
||||
case TrainFilterType.other:
|
||||
return !/^[PXZL]\D{2}$/.test(train.timetableData.category);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
|
||||
return (searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
|
||||
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true) && isFiltered
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
function sortTrainList(trainList: Train[], sorterActive: { id: string; dir: number }) {
|
||||
return trainList.sort((a: Train, b: Train) => {
|
||||
switch (sorterActive.id) {
|
||||
case 'mass':
|
||||
if (a.mass > b.mass) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'distance':
|
||||
if ((a.timetableData?.routeDistance || -1) > (b.timetableData?.routeDistance || -1)) return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'progress':
|
||||
if (confirmedPercentage(a.timetableData?.followingStops) > confirmedPercentage(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'delay':
|
||||
if (currentDelay(a.timetableData?.followingStops) > currentDelay(b.timetableData?.followingStops))
|
||||
return sorterActive.dir;
|
||||
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'speed':
|
||||
if (a.speed > b.speed) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'timetable':
|
||||
if (a.trainNo > b.trainNo) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
case 'length':
|
||||
if (a.length > b.length) return sorterActive.dir;
|
||||
return -sorterActive.dir;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
export function filteredTrainList(
|
||||
trainList: Train[],
|
||||
searchedTrain: string,
|
||||
searchedDriver: string,
|
||||
sorterActive: { id: string; dir: number },
|
||||
filters: TrainFilter[]
|
||||
) {
|
||||
|
||||
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
|
||||
return [...sortTrainList(filtered, sorterActive)];
|
||||
};
|
||||
@@ -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">
|
||||
<div class="wrapper">
|
||||
<div class="options-bar">
|
||||
<train-options />
|
||||
<TrainOptions :sorter-option-ids="['distance', 'progress', 'delay', 'mass', 'speed', 'length']" />
|
||||
</div>
|
||||
|
||||
<TrainTable :trains="computedTrains" />
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
|
||||
<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 TrainStats from '../components/TrainsView/TrainStats.vue';
|
||||
import TrainTable from '../components/TrainsView/TrainTable.vue';
|
||||
@@ -19,6 +19,7 @@ import { trainFilters } from '../data/trainOptions';
|
||||
import Train from '../scripts/interfaces/Train';
|
||||
import { filteredTrainList } from '../scripts/managers/trainFilterManager';
|
||||
import { useStore } from '../store/store';
|
||||
import { TrainFilter } from '../types/TrainOptionsTypes';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -48,7 +49,6 @@ export default defineComponent({
|
||||
|
||||
const sorterActive = ref({ id: 'distance', dir: -1 });
|
||||
const filterList = reactive([...trainFilters]) as TrainFilter[];
|
||||
const isTrainOptionsCardVisible = ref(false);
|
||||
|
||||
const searchedDriver = ref('');
|
||||
const searchedTrain = ref('');
|
||||
@@ -57,7 +57,6 @@ export default defineComponent({
|
||||
provide('searchedDriver', searchedDriver);
|
||||
provide('sorterActive', sorterActive);
|
||||
provide('filterList', filterList);
|
||||
provide('isTrainOptionsCardVisible', isTrainOptionsCardVisible);
|
||||
|
||||
const computedTrains: ComputedRef<Train[]> = computed(() => {
|
||||
return filteredTrainList(
|
||||
@@ -82,10 +81,6 @@ export default defineComponent({
|
||||
this.searchedTrain = this.train;
|
||||
this.searchedDriver = this.driver || '';
|
||||
}
|
||||
// if (this.train) {
|
||||
// this.searchedTrain = this.train;
|
||||
// if(this.x) this.searchedDriver = this.x;
|
||||
// }
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -102,5 +97,4 @@ export default defineComponent({
|
||||
margin: 1rem auto;
|
||||
max-width: 1350px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Vendored
+1
-5
@@ -14,11 +14,7 @@ declare module '@vue/runtime-core' {
|
||||
$store: Store<State>
|
||||
}
|
||||
|
||||
// Train filter for TrainView
|
||||
interface TrainFilter {
|
||||
id: TrainFilterType;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
|
||||
interface JournalFilter {
|
||||
id: JournalFilterType;
|
||||
|
||||
Reference in New Issue
Block a user