Merge pull request #145 from Spythere/development

v1.30.7
This commit is contained in:
Spythere
2025-11-28 01:14:13 +01:00
committed by GitHub
17 changed files with 228 additions and 183 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.30.6", "version": "1.30.7",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-gb" viewBox="0 0 640 480">
<path fill="#012169" d="M0 0h640v480H0z"/>
<path fill="#FFF" d="m75 0 244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0z"/>
<path fill="#C8102E" d="m424 281 216 159v40L369 281zm-184 20 6 35L54 480H0zM640 0v3L391 191l2-44L590 0zM0 0l239 176h-60L0 42z"/>
<path fill="#FFF" d="M241 0v480h160V0zM0 160v160h640V160z"/>
<path fill="#C8102E" d="M0 193v96h640v-96zM273 0v480h96V0z"/>
</svg>

After

Width:  |  Height:  |  Size: 504 B

+5 -3
View File
@@ -1,4 +1,6 @@
<svg width="39" height="23" viewBox="0 0 39 23" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-pl" viewBox="0 0 640 480">
<rect width="39" height="23" fill="#FF0F0F"/> <g fill-rule="evenodd">
<rect width="39" height="11.5" fill="white"/> <path fill="#fff" d="M640 480H0V0h640z"/>
<path fill="#dc143c" d="M640 480H0V240h640z"/>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 199 B

After

Width:  |  Height:  |  Size: 219 B

+3 -10
View File
@@ -9,7 +9,7 @@
<Tooltip /> <Tooltip />
<AppHeader :current-lang="store.currentLocale" @change-lang="changeLang" /> <AppHeader />
<main class="app_main"> <main class="app_main">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
@@ -159,18 +159,11 @@ export default defineComponent({
this.apiStore.connectToAPI(); this.apiStore.connectToAPI();
}, },
changeLang(lang: string) {
this.$i18n.locale = lang;
this.store.currentLocale = lang;
StorageManager.setStringValue('lang', lang);
},
loadLang() { loadLang() {
const storageLang = StorageManager.getStringValue('lang'); const storageLang = StorageManager.getStringValue('lang');
if (storageLang) { if (storageLang) {
this.changeLang(storageLang); this.store.changeLocale(storageLang);
return; return;
} }
@@ -179,7 +172,7 @@ export default defineComponent({
const naviLanguage = window.navigator.language.toString(); const naviLanguage = window.navigator.language.toString();
if (!naviLanguage.startsWith('pl')) { if (!naviLanguage.startsWith('pl')) {
this.changeLang('en'); this.store.changeLocale('en');
return; return;
} }
}, },
+2 -37
View File
@@ -1,18 +1,6 @@
<template> <template>
<header class="app_header"> <header class="app_header">
<div class="header_container"> <div class="header_container">
<div class="header_icons">
<span class="icons-top">
<img
src="/images/icon-pl.svg"
alt="icon-pl"
@click="changeLang('en')"
v-if="currentLang == 'pl'"
/>
<img src="/images/icon-en.jpg" alt="icon-en" @click="changeLang('pl')" v-else />
</span>
</div>
<div class="header_body"> <div class="header_body">
<StatusIndicator /> <StatusIndicator />
@@ -76,27 +64,12 @@ import RegionDropdown from '../Global/RegionDropdown.vue';
export default defineComponent({ export default defineComponent({
components: { StatusIndicator, Clock, RegionDropdown }, components: { StatusIndicator, Clock, RegionDropdown },
emits: ['changeLang'],
props: {
currentLang: {
type: String,
required: true
}
},
setup() { setup() {
return { return {
store: useMainStore() store: useMainStore()
}; };
}, },
methods: {
changeLang(lang: string) {
this.$emit('changeLang', lang);
}
},
computed: { computed: {
onlineTrainsCount() { onlineTrainsCount() {
return this.store.trainList.filter((train) => train.region == this.store.region.id).length; return this.store.trainList.filter((train) => train.region == this.store.region.id).length;
@@ -141,7 +114,7 @@ export default defineComponent({
border-radius: 0 0 1em 1em; border-radius: 0 0 1em 1em;
@include responsive.smallScreen{ @include responsive.smallScreen {
position: relative; position: relative;
margin-top: 0.5em; margin-top: 0.5em;
} }
@@ -180,20 +153,12 @@ export default defineComponent({
padding: 0.5em; padding: 0.5em;
@include responsive.smallScreen{ @include responsive.smallScreen {
transform: translateX(85%); transform: translateX(85%);
} }
} }
} }
// ICONS
.icons-top {
img {
width: 2.5em;
cursor: pointer;
}
}
// COUNTER // COUNTER
.info_counter { .info_counter {
display: flex; display: flex;
+3 -13
View File
@@ -4,12 +4,12 @@
<h1>{{ $t('welcome.title') }}</h1> <h1>{{ $t('welcome.title') }}</h1>
<div class="language-select"> <div class="language-select">
<button :data-active="$i18n.locale == 'pl'" @click="changeLang('pl')"> <button :data-active="$i18n.locale == 'pl'" @click="store.changeLocale('pl')">
<img src="/images/icon-pl.svg" alt="" width="45" /> <img src="/images/icon-pl.svg" alt="" width="45" />
</button> </button>
<button :data-active="$i18n.locale == 'en'" @click="changeLang('en')"> <button :data-active="$i18n.locale == 'en'" @click="store.changeLocale('en')">
<img src="/images/icon-en.jpg" alt="" width="45" /> <img src="/images/icon-en.svg" alt="" width="45" />
</button> </button>
</div> </div>
@@ -114,12 +114,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from 'vue-i18n';
import Card from '../Global/Card.vue'; import Card from '../Global/Card.vue';
import { useMainStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import StorageManager from '../../managers/storageManager';
const i18n = useI18n();
const store = useMainStore(); const store = useMainStore();
const emit = defineEmits(['toggleCard']); const emit = defineEmits(['toggleCard']);
@@ -130,13 +127,6 @@ const props = defineProps({
function toggleCard(state: boolean) { function toggleCard(state: boolean) {
emit('toggleCard', state); emit('toggleCard', state);
} }
function changeLang(localeName: string) {
i18n.locale.value = localeName;
store.currentLocale = localeName;
StorageManager.setStringValue('lang', localeName);
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
+5 -4
View File
@@ -9,7 +9,7 @@
<img <img
v-for="(thumbnailImage, imageIndex) in images" v-for="(thumbnailImage, imageIndex) in images"
:src="`https://stacjownik.spythere.eu/static/thumbnails/${thumbnailImage}.png`" :src="`https://stacjownik.spythere.eu/static/thumbnails/${thumbnailImage}.png`"
height="60" height="70"
loading="lazy" loading="lazy"
data-tooltip-type="VehiclePreviewTooltip" data-tooltip-type="VehiclePreviewTooltip"
:data-tooltip-content="vehicleString" :data-tooltip-content="vehicleString"
@@ -56,16 +56,17 @@ function onImageLoad() {
transition: opacity 100ms ease-in-out; transition: opacity 100ms ease-in-out;
&[data-load-status='loading'] { &[data-load-status='loading'] {
min-height: 60px; min-height: 70px;
min-width: 200px; min-width: 200px;
} }
} }
.stock-text { .stock-text {
max-width: 90%;
text-align: center; text-align: center;
color: #aaa; color: #aaa;
font-size: 0.9em; font-size: 0.85em;
margin-bottom: 0.25em; margin: 0 auto;
padding: 0.25em 0; padding: 0.25em 0;
} }
@@ -40,36 +40,28 @@
<span> <span>
{{ $t('scenery.timetable-issued-date') }} {{ $t('scenery.timetable-issued-date') }}
<b> <b>
{{ {{ parseCreatedDate(timetableHistory, $i18n.locale) }}
localeDateTime(
timetableHistory.createdAt > timetableHistory.beginDate
? timetableHistory.beginDate
: timetableHistory.createdAt,
$i18n.locale
)
}}
</b></span
>
<span v-if="timetableHistory.authorName">
{{ $t('scenery.timetable-issued-by') }}
<b>
<router-link
:to="`/journal/timetables?search-dispatcher=${timetableHistory.authorName}`"
>
{{ timetableHistory.authorName }}
</router-link>
</b> </b>
</span> </span>
<span> <span>
{{ $t('scenery.timetable-issued-for') }} {{ $t('scenery.timetable-issued-for') }}
<b> <router-link
<router-link class="journal-link"
:to="`/journal/timetables?search-driver=${timetableHistory.driverName}`" :to="`/journal/timetables?search-driver=${timetableHistory.driverName}`"
> >
{{ timetableHistory.driverName }} {{ timetableHistory.driverName }}
</router-link> </router-link>
</b> </span>
<span v-if="timetableHistory.authorName">
{{ $t('scenery.timetable-issued-by') }}
<router-link
class="journal-link"
:to="`/journal/timetables?search-dispatcher=${timetableHistory.authorName}`"
>
{{ timetableHistory.authorName }}
</router-link>
</span> </span>
</div> </div>
</span> </span>
@@ -106,7 +98,7 @@ import { useApiStore } from '../../store/apiStore';
import routerMixin from '../../mixins/routerMixin'; import routerMixin from '../../mixins/routerMixin';
import { useMainStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
const historyModeList = ['via', 'issuedFrom', 'terminatingAt'] as const; const historyModeList = ['includesScenery', 'issuedFrom', 'via', 'terminatingAt'] as const;
type HistoryMode = (typeof historyModeList)[number]; type HistoryMode = (typeof historyModeList)[number];
export default defineComponent({ export default defineComponent({
@@ -131,12 +123,12 @@ export default defineComponent({
dataStatus: Status.Data.Loading, dataStatus: Status.Data.Loading,
DataStatus: Status.Data, DataStatus: Status.Data,
checkedHistoryMode: 'via' as HistoryMode checkedHistoryMode: 'includesScenery' as HistoryMode
}; };
}, },
async activated() { async activated() {
this.checkedHistoryMode = 'via'; this.checkedHistoryMode = 'includesScenery';
this.fetchAPIData(); this.fetchAPIData();
}, },
@@ -154,6 +146,7 @@ export default defineComponent({
const requestFilters: Record<string, any> = {}; const requestFilters: Record<string, any> = {};
requestFilters[this.checkedHistoryMode] = stationName.toString(); requestFilters[this.checkedHistoryMode] = stationName.toString();
requestFilters.countLimit = 30; requestFilters.countLimit = 30;
requestFilters['returnType'] = 'short';
try { try {
const response: API.TimetableHistory.Response = await ( const response: API.TimetableHistory.Response = await (
@@ -183,6 +176,18 @@ export default defineComponent({
[`search-${this.checkedHistoryMode}`]: this.station?.name || this.onlineScenery?.name [`search-${this.checkedHistoryMode}`]: this.station?.name || this.onlineScenery?.name
} }
}); });
},
parseCreatedDate(timetable: API.TimetableHistory.Data, locale: string) {
const createdDate =
timetable.createdAt > timetable.beginDate
? new Date(timetable.beginDate)
: new Date(timetable.createdAt);
return createdDate.toLocaleString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
timeStyle: 'short',
dateStyle: 'medium'
});
} }
}, },
components: { Loading } components: { Loading }
@@ -217,7 +222,15 @@ export default defineComponent({
button { button {
padding: 0.35em; padding: 0.35em;
min-width: 120px; }
}
.journal-link {
font-weight: bold;
color: #eee;
&:hover {
color: var(--clr-primary);
} }
} }
@@ -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 || station.generalInfo.hidden) return acc;
if (!acc.includes(station.generalInfo.project.trim())) acc.push(station.generalInfo.project.trim());
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'])
); );
} }
+8 -7
View File
@@ -76,6 +76,7 @@
"tooltip-driver-offline": "Driver is offline", "tooltip-driver-offline": "Driver is offline",
"tooltip-scenery-offline": "Scenery is offline", "tooltip-scenery-offline": "Scenery is offline",
"pojazdownik-link-content": "POJAZDOWNIK", "pojazdownik-link-content": "POJAZDOWNIK",
"language-tooltip-content": "JĘZYK / LANGUAGE",
"gnr-link-content": "TRAIN ORDERS <br> GENERATOR" "gnr-link-content": "TRAIN ORDERS <br> GENERATOR"
}, },
"footer": { "footer": {
@@ -305,10 +306,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",
@@ -560,12 +560,13 @@
"option-active-timetables": "Active timetables", "option-active-timetables": "Active timetables",
"option-timetables-history": "Timetables history PL1", "option-timetables-history": "Timetables history PL1",
"option-dispatchers-history": "Dispatchers history PL1", "option-dispatchers-history": "Dispatchers history PL1",
"timetable-via": "ALL TIMETABLES", "timetable-includesScenery": "ALL TIMETABLES",
"timetable-via": "PASSES THROUGH",
"timetable-issuedFrom": "BEGINS HERE", "timetable-issuedFrom": "BEGINS HERE",
"timetable-terminatingAt": "TERMINATES HERE", "timetable-terminatingAt": "ENDS HERE",
"timetable-issued-date": "Issued", "timetable-issued-date": "Issued",
"timetable-issued-by": " by:", "timetable-issued-by": " by:",
"timetable-issued-for": " for driver:", "timetable-issued-for": " for:",
"dispatcher-rate": "Rate:", "dispatcher-rate": "Rate:",
"dispatcher-status-changes": "Status changes:", "dispatcher-status-changes": "Status changes:",
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required", "req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
+8 -7
View File
@@ -73,6 +73,7 @@
"tooltip-driver-offline": "Maszynista offline", "tooltip-driver-offline": "Maszynista offline",
"tooltip-scenery-offline": "Sceneria offline", "tooltip-scenery-offline": "Sceneria offline",
"pojazdownik-link-content": "POJAZDOWNIK", "pojazdownik-link-content": "POJAZDOWNIK",
"language-tooltip-content": "JĘZYK / LANGUAGE",
"gnr-link-content": "GENERATOR <br> ROZKAZÓW PISEMNYCH" "gnr-link-content": "GENERATOR <br> ROZKAZÓW PISEMNYCH"
}, },
"footer": { "footer": {
@@ -303,10 +304,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",
@@ -546,12 +546,13 @@
"option-active-timetables": "Aktywne rozkłady jazdy", "option-active-timetables": "Aktywne rozkłady jazdy",
"option-timetables-history": "Historia rozkładów PL1", "option-timetables-history": "Historia rozkładów PL1",
"option-dispatchers-history": "Historia dyżurów PL1", "option-dispatchers-history": "Historia dyżurów PL1",
"timetable-via": "WSZYSTKIE RJ", "timetable-includesScenery": "WSZYSTKIE RJ",
"timetable-via": "PRZEJEŻDŻA",
"timetable-issuedFrom": "ROZPOCZYNA BIEG", "timetable-issuedFrom": "ROZPOCZYNA BIEG",
"timetable-terminatingAt": "KOŃCZY BIEG", "timetable-terminatingAt": "KOŃCZY BIEG",
"timetable-issued-date": "Wystawiony", "timetable-issued-date": "Wystawiony: ",
"timetable-issued-by": " przez:", "timetable-issued-by": " przez:",
"timetable-issued-for": " dla maszynisty:", "timetable-issued-for": " dla:",
"dispatcher-rate": "Ocena:", "dispatcher-rate": "Ocena:",
"dispatcher-status-changes": "Zmiany statusów:", "dispatcher-status-changes": "Zmiany statusów:",
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego", "req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
+15 -3
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']
}; };
+11
View File
@@ -11,6 +11,8 @@ import {
} from '../typings/common'; } from '../typings/common';
import { useApiStore } from './apiStore'; import { useApiStore } from './apiStore';
import { MainStoreState } from './typings'; import { MainStoreState } from './typings';
import i18n from '../i18n';
import StorageManager from '../managers/storageManager';
const checkpointsTrains: Map<string, CheckpointTrain[]> = new Map(); const checkpointsTrains: Map<string, CheckpointTrain[]> = new Map();
const unknownSceneryCheckpoints: Map<string, Set<string>> = new Map(); const unknownSceneryCheckpoints: Map<string, Set<string>> = new Map();
@@ -37,6 +39,15 @@ export const useMainStore = defineStore('mainStore', {
currentLocale: 'pl' currentLocale: 'pl'
}) as MainStoreState, }) as MainStoreState,
actions: {
changeLocale(localeName: string) {
(i18n.global.locale.value as any) = localeName;
this.currentLocale = localeName;
StorageManager.setStringValue('lang', localeName);
}
},
getters: { getters: {
trainList(): Train[] { trainList(): Train[] {
const apiStore = useApiStore(); const apiStore = useApiStore();
+1
View File
@@ -23,6 +23,7 @@ export interface StationJSONData {
project: string; project: string;
projectUrl: string; projectUrl: string;
hash: string; hash: string;
hidden: boolean;
reqLevel: number; reqLevel: number;
+1
View File
@@ -118,6 +118,7 @@ export interface StationGeneralInfo {
availability: Availability; availability: Availability;
routes: StationRoutes; routes: StationRoutes;
checkpoints: string[]; checkpoints: string[];
hidden: boolean;
} }
export interface StationRoutes { export interface StationRoutes {
+21
View File
@@ -13,6 +13,18 @@
</div> </div>
<div class="topbar-links"> <div class="topbar-links">
<button
class="btn--image lang-button"
@click="toggleLocales()"
data-tooltip-type="HtmlTooltip"
:data-tooltip-content="`<b>${$t('app.language-tooltip-content')}</b>`"
>
<img
:src="`/images/icon-${mainStore.currentLocale}.svg`"
alt="change language flag icon"
/>
</button>
<a <a
class="a-button btn--image gnr-link" class="a-button btn--image gnr-link"
href="https://generator-td2.web.app/" href="https://generator-td2.web.app/"
@@ -96,6 +108,10 @@ export default defineComponent({
methods: { methods: {
toggleDonationCard(value: boolean) { toggleDonationCard(value: boolean) {
this.isDonationCardOpen = value; this.isDonationCardOpen = value;
},
toggleLocales() {
this.mainStore.changeLocale(this.mainStore.currentLocale == 'pl' ? 'en' : 'pl');
} }
} }
}); });
@@ -149,6 +165,11 @@ button.donation-button {
} }
} }
button.lang-button {
padding: 0 0.5em;
background-color: #111;
}
a.pojazdownik-link { a.pojazdownik-link {
background-color: #1f263b; background-color: #1f263b;