chore: groupped station filters inputs to the top of the card; added project filter

This commit is contained in:
2025-11-27 21:04:55 +01:00
parent 28d78cd2bc
commit aea26fa538
5 changed files with 113 additions and 77 deletions
@@ -21,9 +21,7 @@
<template v-else>{{ $t('filters.no-changed-filters') }}</template> <template v-else>{{ $t('filters.no-changed-filters') }}</template>
</div> </div>
<section class="card_sceneries-search"> <section class="card_input-search">
<h3 class="section-header">{{ $t('filters.sceneries-search') }}</h3>
<datalist id="sceneries"> <datalist id="sceneries">
<option <option
v-for="scenery in sortedStationList" v-for="scenery in sortedStationList"
@@ -32,18 +30,60 @@
></option> ></option>
</datalist> </datalist>
<form action="javascript:void(0);" @submit="handleSceneriesInput"> <input
<input v-model="chosenSearchScenery"
v-model="chosenSearchScenery" id="scenery-search"
id="scenery-search" list="sceneries"
list="sceneries" :placeholder="$t('filters.sceneries-placeholder')"
:placeholder="$t('filters.sceneries-placeholder')" @focus="preventKeyDown = true"
@focus="preventKeyDown = true" @blur="preventKeyDown = false"
@blur="preventKeyDown = false" />
/>
<button class="btn--action">{{ $t('filters.search-button-title') }}</button> <button class="btn--action" @click="handleSceneriesInput">
</form> {{ $t('filters.search-button-title') }}
</button>
</section>
<section class="card_input-search authors">
<datalist id="authors" name="authors">
<option v-for="(author, i) in authorsOptions" :key="i" :value="author"></option>
</datalist>
<input
type="text"
id="author"
list="authors"
name="authors"
v-model="filters['authors']"
:placeholder="$t('filters.authors-placeholder')"
@focus="preventKeyDown = true"
@blur="preventKeyDown = false"
/>
<button class="btn--action btn--image" @click="resetAuthorsInput">
<img src="/images/icon-exit.svg" alt="reset authors search" />
</button>
</section>
<section class="card_input-search">
<datalist id="projects" name="projects">
<option v-for="(project, i) in projectsOptions" :key="i" :value="project"></option>
</datalist>
<input
type="text"
id="projects"
list="projects"
name="projects"
v-model="filters['projects']"
:placeholder="$t('filters.projects-placeholder')"
@focus="preventKeyDown = true"
@blur="preventKeyDown = false"
/>
<button class="btn--action btn--image" @click="resetProjectsInput">
<img src="/images/icon-exit.svg" alt="reset projects search" />
</button>
</section> </section>
<section class="card_options"> <section class="card_options">
@@ -97,29 +137,6 @@
</span> </span>
</section> </section>
<section class="card_authors-search">
<h3 class="section-header">{{ $t('filters.authors-search') }}</h3>
<datalist id="authors" name="authors">
<option v-for="(author, i) in authorsHint" :key="i" :value="author"></option>
</datalist>
<form action="javascript:void(0);" @submit="handleAuthorsInput">
<input
type="text"
id="author"
list="authors"
name="authors"
v-model="authors"
:placeholder="$t('filters.authors-placeholder')"
@focus="preventKeyDown = true"
@blur="preventKeyDown = false"
/>
<button class="btn--action">{{ $t('filters.search-button-title') }}</button>
</form>
</section>
<section class="card_sliders"> <section class="card_sliders">
<div class="slider" v-for="(slider, i) in sliderStates" :key="i"> <div class="slider" v-for="(slider, i) in sliderStates" :key="i">
<input <input
@@ -200,7 +217,8 @@ export default defineComponent({
sliderStates, sliderStates,
minimumHours: 0, minimumHours: 0,
authors: '', authorSearchFilter: '',
projectSearchFilter: '',
currentRegion: { id: '', value: '' }, currentRegion: { id: '', value: '' },
@@ -255,11 +273,7 @@ export default defineComponent({
.sort((s1, s2) => (s1.name > s2.name ? 1 : -1)); .sort((s1, s2) => (s1.name > s2.name ? 1 : -1));
}, },
currentOptionsActive() { authorsOptions() {
return true;
},
authorsHint() {
return this.store.stationList return this.store.stationList
.reduce((acc, station) => { .reduce((acc, station) => {
station.generalInfo?.authors?.forEach((author) => { station.generalInfo?.authors?.forEach((author) => {
@@ -270,6 +284,17 @@ export default defineComponent({
return acc; return acc;
}, [] as string[]) }, [] as string[])
.sort((a, b) => a.localeCompare(b)); .sort((a, b) => a.localeCompare(b));
},
projectsOptions() {
return this.store.stationList
.reduce((acc, station) => {
if (!station.generalInfo || !station.generalInfo.project) return acc;
if (!acc.includes(station.generalInfo.project)) acc.push(station.generalInfo.project);
return acc;
}, [] as string[])
.sort((a, b) => a.localeCompare(b));
} }
}, },
@@ -294,8 +319,12 @@ export default defineComponent({
this.scrollTop = (e.target as HTMLElement).scrollTop; this.scrollTop = (e.target as HTMLElement).scrollTop;
}, },
handleAuthorsInput() { resetAuthorsInput() {
this.filters['authors'] = this.authors; this.filters['authors'] = this.authorSearchFilter;
},
resetProjectsInput() {
this.filters['projects'] = this.projectSearchFilter;
}, },
handleSceneriesInput() { handleSceneriesInput() {
@@ -340,7 +369,7 @@ export default defineComponent({
// Reset local model values // Reset local model values
this.minimumHours = 0; this.minimumHours = 0;
this.authors = ''; this.authorSearchFilter = '';
// Reset global filters // Reset global filters
Object.keys(this.filters).forEach((filterKey) => { Object.keys(this.filters).forEach((filterKey) => {
@@ -456,27 +485,23 @@ h3.section-header {
} }
} }
.card_authors-search, .card_input-search {
.card_sceneries-search {
margin: 1em 0;
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
gap: 0.5em;
form { button {
display: flex; height: 100%;
justify-content: center;
flex-wrap: wrap;
gap: 0.5em;
width: 100%;
margin-top: 1em;
} }
input { input {
width: 70%; width: 100%;
max-width: 400px;
padding: 0.5em; padding: 0.5em;
outline: 1px solid white; border: 1px solid #aaa;
}
&.authors {
margin-top: 1em;
} }
} }
+5 -4
View File
@@ -146,10 +146,11 @@ function filterSliderValues(filters: Record<string, any>, generalInfo: StationGe
function filterInputValues(filters: Record<string, any>, generalInfo: StationGeneralInfo) { function filterInputValues(filters: Record<string, any>, generalInfo: StationGeneralInfo) {
return ( return (
filters['authors'].length > 3 && (filters['authors'].length > 3 &&
!generalInfo.authors !generalInfo.authors
?.map((a) => a.toLocaleLowerCase()) ?.map((a) => a.toLocaleLowerCase())
.includes(filters['authors'].toLocaleLowerCase()) .includes(filters['authors'].toLocaleLowerCase())) ||
(filters['projects'].length > 0 && generalInfo.project != filters['projects'])
); );
} }
+3 -4
View File
@@ -305,10 +305,9 @@
"minTwoWayCatenaryInt": "MIN. INTERNAL CATENARY DOUBLE TRACK ROUTES", "minTwoWayCatenaryInt": "MIN. INTERNAL CATENARY DOUBLE TRACK ROUTES",
"minTwoWayInt": "MIN. INTERNAL OTHER DOUBLE TRACK ROUTES" "minTwoWayInt": "MIN. INTERNAL OTHER DOUBLE TRACK ROUTES"
}, },
"sceneries-search": "SCENERY SEARCH:", "sceneries-placeholder": "Search for scenery",
"sceneries-placeholder": "Enter scenery name...", "authors-placeholder": "Scenery author (other filters apply)",
"authors-search": "SEARCH BY AUTHOR NAME (other filters apply):", "projects-placeholder": "Scenery project (other filters apply)",
"authors-placeholder": "Enter the author nickname...",
"search-button-title": "SEARCH", "search-button-title": "SEARCH",
"minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:", "minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:",
"now": "NOW", "now": "NOW",
+4 -5
View File
@@ -303,10 +303,9 @@
"minTwoWayCatenaryInt": "SZLAKI DWUTOROWE ZELEKTR. WEWNĘTRZNE (MINIMUM)", "minTwoWayCatenaryInt": "SZLAKI DWUTOROWE ZELEKTR. WEWNĘTRZNE (MINIMUM)",
"minTwoWayInt": "SZLAKI DWUTOROWE NIEZELEKTR. WEWNĘTRZNE (MINIMUM)" "minTwoWayInt": "SZLAKI DWUTOROWE NIEZELEKTR. WEWNĘTRZNE (MINIMUM)"
}, },
"sceneries-search": "WYSZUKAJ SCENERIĘ:", "sceneries-placeholder": "Wyszukaj scenerię",
"sceneries-placeholder": "Wpisz nazwę scenerii...", "authors-placeholder": "Autor scenerii (uwzględnia inne filtry)",
"authors-search": "WYSZUKAJ AUTORA (uwzględnia inne filtry):", "projects-placeholder": "Projekt scenerii (uwzględnia inne filtry)",
"authors-placeholder": "Wpisz nick autora...",
"search-button-title": "SZUKAJ", "search-button-title": "SZUKAJ",
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:", "minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
"now": "TERAZ", "now": "TERAZ",
@@ -595,4 +594,4 @@
"history": { "history": {
"title": "DZIENNIK ROZKŁADÓW JAZDY" "title": "DZIENNIK ROZKŁADÓW JAZDY"
} }
} }
+16 -4
View File
@@ -68,7 +68,8 @@ export const initFilters = {
minTwoWayCatenary: 0, minTwoWayCatenary: 0,
minTwoWayInt: 0, minTwoWayInt: 0,
minTwoWayCatenaryInt: 0, minTwoWayCatenaryInt: 0,
authors: '' authors: '',
projects: ''
}; };
export const sliderStates = [ export const sliderStates = [
@@ -83,7 +84,7 @@ export const sliderStates = [
{ id: 'minTwoWay', minRange: 0, maxRange: 5, step: 1 }, { id: 'minTwoWay', minRange: 0, maxRange: 5, step: 1 },
{ id: 'minTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 }, { id: 'minTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 },
{ id: 'minTwoWayInt', minRange: 0, maxRange: 5, step: 1 }, { id: 'minTwoWayInt', minRange: 0, maxRange: 5, step: 1 },
{ id: 'minTwoWayCatenaryInt', minRange: 0, maxRange: 5, step: 1 }, { id: 'minTwoWayCatenaryInt', minRange: 0, maxRange: 5, step: 1 }
]; ];
export type StationFilter = keyof typeof initFilters; export type StationFilter = keyof typeof initFilters;
@@ -97,7 +98,18 @@ export const filtersSections: Record<StationFilterSection, StationFilter[]> = {
stationType: ['junction', 'nonJunction'], stationType: ['junction', 'nonJunction'],
access: ['nonPublic', 'unavailable', 'abandoned'], access: ['nonPublic', 'unavailable', 'abandoned'],
addons: ['SUP', 'ASDEK', 'noSUP', 'noASDEK'], addons: ['SUP', 'ASDEK', 'noSUP', 'noASDEK'],
control: ['SPK', 'SCS', 'SPE', 'SCS-SPK', 'SPK-M', 'SCS-M', 'mechanical', 'SPK-R', 'SCS-R', 'manual'], control: [
'SPK',
'SCS',
'SPE',
'SCS-SPK',
'SPK-M',
'SCS-M',
'mechanical',
'SPK-R',
'SCS-R',
'manual'
],
blockades: ['SBL', 'PBL'], blockades: ['SBL', 'PBL'],
signals: ['modern', 'semaphores', 'mixed', 'historical'] signals: ['modern', 'semaphores', 'mixed', 'historical']
}; };
@@ -118,7 +130,7 @@ export function setupFilters(currentFilters: Record<string, any>) {
}); });
} }
export function getChangedFilters(currentFilters: Record<string, any>): string[] { export function getChangedFilters(currentFilters: Record<string, any>): string[] {
return ( return (
Object.keys(currentFilters).filter( Object.keys(currentFilters).filter(
(filterKey) => (filterKey) =>