Filtry pociągów

This commit is contained in:
2022-04-14 00:21:22 +02:00
parent e3735c171f
commit 414fd5d5b9
8 changed files with 184 additions and 124 deletions
@@ -1,67 +0,0 @@
<template>
<div class="train-filters">
<span v-for="category in availableCategories" :key="category">
{{ category }}
</span>
</div>
</template>
<script lang="ts">
import Train from '@/scripts/interfaces/Train';
import { computed, defineComponent, inject, TrainFilter } from 'vue';
const defaultFilters: TrainFilter[] = [
{
id: "comments",
isActive: true
},
{
id: "twr",
isActive: true
},
{
id: "skr",
isActive: true
}
]
export default defineComponent({
props: {
trainList: {
type: Object as () => Train[],
required: true
}
},
setup(props) {
const filters = inject('filtersActive') as TrainFilter[];
const trainList = props.trainList;
// Setup default train filters
filters.push(...defaultFilters);
const availableCategories = computed(() => trainList.reduce((acc, train) => {
if(!train.timetableData) return acc;
if(acc.includes(train.timetableData.category)) return acc;
acc.push(train.timetableData.category);
return acc;
}, [] as string[]));
// Remove unavailable train categories
for(let filter of filters) {
if(availableCategories.value.includes(filter.id)) continue;
filters.slice(filters.indexOf(filter), -1);
}
return {
availableCategories
};
},
});
</script>
<style lang="scss" scoped></style>
+70 -5
View File
@@ -26,11 +26,37 @@
</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 } from 'vue';
import { computed, defineComponent, inject, TrainFilter } from 'vue';
import { useI18n } from 'vue-i18n';
import SelectBox from '../Global/SelectBox.vue';
@@ -50,10 +76,6 @@ export default defineComponent({
id: 'distance',
value: 'kilometraż',
},
{
id: 'comments',
value: 'komentarze',
},
{
id: 'progress',
value: 'przebyta trasa',
@@ -76,6 +98,8 @@ export default defineComponent({
},
];
let filterList = inject('filterList') as TrainFilter[];
const translatedSorterOptions = computed(() =>
sorterOptions.map(({ id }) => ({
id,
@@ -88,6 +112,7 @@ export default defineComponent({
searchedTrain: inject('searchedTrain') as string,
searchedDriver: inject('searchedDriver') as string,
sorterActive: inject('sorterActive') as { id: string | number; dir: number },
filterList,
};
},
@@ -96,6 +121,18 @@ export default defineComponent({
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>
@@ -108,6 +145,7 @@ export default defineComponent({
width: 100%;
}
}
.options {
&_wrapper {
display: flex;
@@ -178,4 +216,31 @@ export default defineComponent({
width: 1em;
}
}
.filters {
display: flex;
flex-wrap: wrap;
@include smallScreen() {
justify-content: center;
}
}
.filter {
background: #333;
padding: 0.2em 0.25em;
margin: 0.25em 0.25em 0 0;
font-weight: bold;
cursor: pointer;
color: gray;
&.active {
color: gold;
}
&.reset-btn {
color: salmon;
}
}
</style>
+16 -7
View File
@@ -8,7 +8,7 @@
"error": "An error occured while loading data!",
"no-result": "No results for current search!"
},
"data-status": {
"data-status": {
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with SWDR API service!",
"S1a-sceneries": "<b>S1a signal</b> <br> Cannot load online stations data!",
"S2": "<b>S2 signal</b> <br> All data loaded successfully!",
@@ -66,7 +66,7 @@
"filters": {
"endingStatus": "ENDS SOON",
"afkStatus": "AFK",
"noSpaceStatus":"NO SPACE",
"noSpaceStatus": "NO SPACE",
"unavailableStatus": "UNAVAILABLE",
"title": "STATION FILTER",
@@ -145,6 +145,15 @@
"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",
@@ -166,7 +175,7 @@
"loco-diesel": "Diesel locomotive",
"timetable-comments": "Exploitation comments available for this train",
"comment": "Exploitation comments for: ",
"table-limit": "For performance reasons there's a limit of 10 trains shown at the same time."
"table-limit": "For performance reasons there's a limit of 10 trains shown at the same time."
},
"journal": {
"title": "DISPATCHER HISTORY",
@@ -175,8 +184,8 @@
"search-train": "Train no.",
"search-driver": "Driver name",
"sort-prefix": "Sort: ",
"sort-prefix": "Sort: ",
"option-distance": "distance",
"option-total-stops": "total stops",
@@ -206,7 +215,7 @@
"begins": "BEGINS HERE",
"terminates": "TERMINATES\nHERE"
},
"history": {
"history": {
"title": "TIMETABLE JOURNAL",
"search": "Search",
"search-train": "Train no.",
@@ -219,4 +228,4 @@
"timetable-fulfilled": "FULFILLED",
"timetable-abandoned": "ABANDONED"
}
}
}
+15 -5
View File
@@ -9,7 +9,7 @@
"no-result": "Brak wyników o podanych kryteriach!"
},
"data-status": {
"data-status": {
"S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z serwisem SWDR!",
"S1a-sceneries": "<b>Sygnał S1a</b> <br> Błąd podczas pobierania danych o sceneriach online!",
"S2": "<b>Sygnał S2</b> <br> Pomyślnie załadowano dane!",
@@ -67,7 +67,7 @@
"filters": {
"endingStatus": "KOŃCZY",
"afkStatus": "Z/W",
"noSpaceStatus":"BRAK MIEJSCA",
"noSpaceStatus": "BRAK MIEJSCA",
"unavailableStatus": "NIEDOSTĘPNY",
"title": "FILTRUJ STACJE",
@@ -146,6 +146,16 @@
"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",
@@ -167,7 +177,7 @@
"loco-diesel": "Spalinowóz",
"timetable-comments": "Pociąg z uwagami eksploatacyjnymi",
"comment": "Uwagi eksploatacyjne dla: ",
"table-limit": "Dla płynności działania strony pokazanych jest tylko 10 pociągów zgodnie z wybranymi filtrami."
"table-limit": "Dla płynności działania strony pokazanych jest tylko 10 pociągów zgodnie z wybranymi filtrami."
},
"journal": {
"title": "HISTORIA DYŻURÓW",
@@ -177,7 +187,7 @@
"search-train": "Numer pociągu",
"search-driver": "Nick maszynisty",
"sort-prefix": "Sortuj: ",
"sort-prefix": "Sortuj: ",
"option-distance": "kilometraż",
"option-total-stops": "stacje",
@@ -220,4 +230,4 @@
"timetable-fulfilled": "WYPEŁNIONY",
"timetable-abandoned": "PORZUCONY"
}
}
}
+9
View File
@@ -0,0 +1,9 @@
export const enum TrainFilterType {
comments = "comments",
twr = "twr",
skr = "skr",
passenger = "passenger",
freight = "freight",
other = "other",
noTimetable = "noTimetable"
}
+37 -33
View File
@@ -1,4 +1,5 @@
import { TrainFilter } from "vue";
import { TrainFilterType } from "../enums/TrainFilterType";
import Train from "../interfaces/Train";
import TrainStop from "../interfaces/TrainStop";
@@ -19,11 +20,41 @@ function currentDelay(stops: TrainStop[] | undefined) {
};
function filterTrainList(trainList: Train[], searchedTrain: string, searchedDriver: string, filters: TrainFilter[]) {
return trainList.filter(
(train) =>
(searchedTrain.length > 0 ? train.trainNo.toString().startsWith(searchedTrain) : true) &&
(searchedDriver.length > 0 ? train.driverName.toLowerCase().startsWith(searchedDriver.toLowerCase()) : true)
(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 !/^[PXZ]\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
}
);
}
@@ -78,34 +109,7 @@ export function filteredTrainList(
sorterActive: { id: string; dir: number },
filters: TrainFilter[]
) {
let finalTrainList: Train[] = [];
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
switch (sorterActive.id) {
case 'comments':
const trainsSortedByComments = filtered
.sort((a, b) => {
const commentsA = a.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
const commentsB = b.timetableData?.followingStops.some((s) => s.comments) ? 1 : 0;
return commentsB - commentsA;
});
const trainsWithComments = trainsSortedByComments.filter((train) =>
train.timetableData?.followingStops.some((s) => s.comments)
);
const trainsWithoutComments = trainsSortedByComments.slice(trainsWithComments.length);
finalTrainList.push(...trainsWithComments);
finalTrainList.push(...sortTrainList(trainsWithoutComments, sorterActive));
break;
default:
finalTrainList.push(...sortTrainList(filtered, sorterActive));
break;
}
return finalTrainList;
const filtered = filterTrainList(trainList, searchedTrain, searchedDriver, filters);
return [...sortTrainList(filtered, sorterActive)];
};
+35 -6
View File
@@ -14,7 +14,7 @@
<script lang="ts">
import { computed, ComputedRef, defineComponent, provide, reactive, ref, TrainFilter } from 'vue';
import { filteredTrainList } from "@/scripts/managers/trainFilterManager";
import { filteredTrainList } from '@/scripts/managers/trainFilterManager';
import Train from '@/scripts/interfaces/Train';
@@ -24,7 +24,7 @@ import TrainOptions from '@/components/TrainsView/TrainOptions.vue';
import { useStore } from '@/store';
import { GETTERS } from '@/constants/storeConstants';
import { TrainFilterType } from '@/scripts/enums/TrainFilterType';
export default defineComponent({
components: {
@@ -45,10 +45,39 @@ export default defineComponent({
const trainList: ComputedRef<Train[]> = computed(() => store.getters[GETTERS.trainList]);
// const timetableDataStatus: ComputedRef<DataStatus> = computed(() => store.getters[GETTERS.timetableDataStatus]);
const initFilters: 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,
},
];
const sorterActive = ref({ id: 'distance', dir: -1 });
const filtersActive = reactive([]) as TrainFilter[];
const filterList = reactive([...initFilters]) as TrainFilter[];
const searchedDriver = ref('');
const searchedTrain = ref('');
@@ -56,7 +85,7 @@ export default defineComponent({
provide('searchedTrain', searchedTrain);
provide('searchedDriver', searchedDriver);
provide('sorterActive', sorterActive);
provide('filtersActive', filtersActive);
provide('filterList', filterList);
const computedTrains: ComputedRef<Train[]> = computed(() => {
return filteredTrainList(
@@ -64,7 +93,7 @@ export default defineComponent({
searchedTrain.value,
searchedDriver.value,
sorterActive.value,
filtersActive
filterList
);
});
+2 -1
View File
@@ -1,5 +1,6 @@
import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'
import { TrainFilterType } from './scripts/enums/TrainFilterType';
declare module '@vue/runtime-core' {
// declare your own store states
@@ -14,7 +15,7 @@ declare module '@vue/runtime-core' {
// Train filter for TrainView
interface TrainFilter {
id: string;
id: TrainFilterType;
isActive: boolean;
}
}