mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 430a05ab38 | |||
| f335ca8fc2 | |||
| 15e599fe3c | |||
| bd25914ed4 | |||
| 01ea259381 | |||
| aea26fa538 | |||
| 28d78cd2bc | |||
| a021deae96 | |||
| 8840576796 | |||
| 5018e21736 | |||
| a7fa1dfb6d | |||
| a3558c0b30 | |||
| ee159fd582 | |||
| 35c9fb7ef1 | |||
| e24097c240 | |||
| 01cbebd019 | |||
| 3a5ef7e025 | |||
| c78a5b4d67 | |||
| 023de9f7b8 | |||
| 1024e44cc0 |
+60
-4
@@ -22,10 +22,64 @@
|
||||
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
|
||||
<link rel="stylesheet" href="fa/css/fontawesome.css" />
|
||||
<link rel="stylesheet" href="fa/css/brands.css" />
|
||||
<link rel="stylesheet" href="fa/css/regular.css" />
|
||||
<link rel="stylesheet" href="fa/css/solid.css" />
|
||||
<link rel="stylesheet" href="/fa/css/fontawesome.css" />
|
||||
<link rel="stylesheet" href="/fa/css/brands.css" />
|
||||
<link rel="stylesheet" href="/fa/css/regular.css" />
|
||||
<link rel="stylesheet" href="/fa/css/solid.css" />
|
||||
|
||||
<!-- Preloads -->
|
||||
<link rel="preload" href="fonts/Quicksand-Bold.woff2" as="font" type="font/woff2" crossorigin />
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Quicksand-Light.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Quicksand-Medium.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Quicksand-Regular.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Quicksand-SemiBold.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
|
||||
<link rel="preload" as="image" href="/images/icon-pl.svg" />
|
||||
<link rel="preload" as="image" href="/images/stacjownik-header-logo.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-dispatcher.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-train.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-arrow-asc.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-arrow-desc.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-filter2.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-stats.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-gnr.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-pojazdownik.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-diamond.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-user.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-like.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-spawn.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-timetableAll.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-timetableUnconfirmed.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-timetableConfirmed.svg" />
|
||||
<link rel="preload" as="image" href="/images/icon-discord.png" />
|
||||
|
||||
<!-- Static OpenGraph meta -->
|
||||
<meta name="description" content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2" />
|
||||
@@ -36,10 +90,12 @@
|
||||
property="og:description"
|
||||
content="Pomocnik maszynisty i dyżurnego symulatora Train Driver 2"
|
||||
/>
|
||||
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://raw.githubusercontent.com/Spythere/api/main/thumbnails/stacjownik.jpg"
|
||||
/>
|
||||
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:site_name" content="Stacjownik" />
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stacjownik",
|
||||
"version": "1.30.6",
|
||||
"version": "1.30.7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -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 |
@@ -1,4 +1,6 @@
|
||||
<svg width="39" height="23" viewBox="0 0 39 23" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="39" height="23" fill="#FF0F0F"/>
|
||||
<rect width="39" height="11.5" fill="white"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-pl" viewBox="0 0 640 480">
|
||||
<g fill-rule="evenodd">
|
||||
<path fill="#fff" d="M640 480H0V0h640z"/>
|
||||
<path fill="#dc143c" d="M640 480H0V240h640z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 199 B After Width: | Height: | Size: 219 B |
+4
-11
@@ -9,11 +9,11 @@
|
||||
|
||||
<Tooltip />
|
||||
|
||||
<AppHeader :current-lang="store.currentLocale" @change-lang="changeLang" />
|
||||
<AppHeader />
|
||||
|
||||
<main class="app_main">
|
||||
<router-view v-slot="{ Component }">
|
||||
<keep-alive exclude="SceneryView">
|
||||
<keep-alive>
|
||||
<component :is="Component" :key="$route.name" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
@@ -159,18 +159,11 @@ export default defineComponent({
|
||||
this.apiStore.connectToAPI();
|
||||
},
|
||||
|
||||
changeLang(lang: string) {
|
||||
this.$i18n.locale = lang;
|
||||
this.store.currentLocale = lang;
|
||||
|
||||
StorageManager.setStringValue('lang', lang);
|
||||
},
|
||||
|
||||
loadLang() {
|
||||
const storageLang = StorageManager.getStringValue('lang');
|
||||
|
||||
if (storageLang) {
|
||||
this.changeLang(storageLang);
|
||||
this.store.changeLocale(storageLang);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -179,7 +172,7 @@ export default defineComponent({
|
||||
const naviLanguage = window.navigator.language.toString();
|
||||
|
||||
if (!naviLanguage.startsWith('pl')) {
|
||||
this.changeLang('en');
|
||||
this.store.changeLocale('en');
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
<template>
|
||||
<header class="app_header">
|
||||
<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">
|
||||
<StatusIndicator />
|
||||
|
||||
@@ -76,27 +64,12 @@ import RegionDropdown from '../Global/RegionDropdown.vue';
|
||||
export default defineComponent({
|
||||
components: { StatusIndicator, Clock, RegionDropdown },
|
||||
|
||||
emits: ['changeLang'],
|
||||
|
||||
props: {
|
||||
currentLang: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useMainStore()
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeLang(lang: string) {
|
||||
this.$emit('changeLang', lang);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
onlineTrainsCount() {
|
||||
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;
|
||||
|
||||
@include responsive.smallScreen{
|
||||
@include responsive.smallScreen {
|
||||
position: relative;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
@@ -180,20 +153,12 @@ export default defineComponent({
|
||||
|
||||
padding: 0.5em;
|
||||
|
||||
@include responsive.smallScreen{
|
||||
@include responsive.smallScreen {
|
||||
transform: translateX(85%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ICONS
|
||||
.icons-top {
|
||||
img {
|
||||
width: 2.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// COUNTER
|
||||
.info_counter {
|
||||
display: flex;
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<h1>{{ $t('welcome.title') }}</h1>
|
||||
|
||||
<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" />
|
||||
</button>
|
||||
|
||||
<button :data-active="$i18n.locale == 'en'" @click="changeLang('en')">
|
||||
<img src="/images/icon-en.jpg" alt="" width="45" />
|
||||
<button :data-active="$i18n.locale == 'en'" @click="store.changeLocale('en')">
|
||||
<img src="/images/icon-en.svg" alt="" width="45" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -114,12 +114,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Card from '../Global/Card.vue';
|
||||
import { useMainStore } from '../../store/mainStore';
|
||||
import StorageManager from '../../managers/storageManager';
|
||||
|
||||
const i18n = useI18n();
|
||||
const store = useMainStore();
|
||||
|
||||
const emit = defineEmits(['toggleCard']);
|
||||
@@ -130,13 +127,6 @@ const props = defineProps({
|
||||
function toggleCard(state: boolean) {
|
||||
emit('toggleCard', state);
|
||||
}
|
||||
|
||||
function changeLang(localeName: string) {
|
||||
i18n.locale.value = localeName;
|
||||
store.currentLocale = localeName;
|
||||
|
||||
StorageManager.setStringValue('lang', localeName);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<img
|
||||
v-for="(thumbnailImage, imageIndex) in images"
|
||||
:src="`https://stacjownik.spythere.eu/static/thumbnails/${thumbnailImage}.png`"
|
||||
height="60"
|
||||
height="70"
|
||||
loading="lazy"
|
||||
data-tooltip-type="VehiclePreviewTooltip"
|
||||
:data-tooltip-content="vehicleString"
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup lang="ts">
|
||||
import { computed, PropType, Ref, ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -56,16 +56,17 @@ function onImageLoad() {
|
||||
transition: opacity 100ms ease-in-out;
|
||||
|
||||
&[data-load-status='loading'] {
|
||||
min-height: 60px;
|
||||
min-height: 70px;
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.stock-text {
|
||||
max-width: 90%;
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 0.25em;
|
||||
font-size: 0.85em;
|
||||
margin: 0 auto;
|
||||
padding: 0.25em 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
historyList: [] as API.DispatcherHistory.Response,
|
||||
lastStationName: '',
|
||||
dataStatus: Status.Data.Loading,
|
||||
DataStatus: Status.Data,
|
||||
apiStore: useApiStore()
|
||||
@@ -103,10 +104,10 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
async activated() {
|
||||
// if (this.historyList.length == 0) {
|
||||
this.historyList.length = 0;
|
||||
|
||||
const fetchedHistory = await this.fetchAPIData();
|
||||
if (fetchedHistory) this.historyList = fetchedHistory;
|
||||
// }
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -194,7 +195,7 @@ export default defineComponent({
|
||||
color: springgreen;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen{
|
||||
@include responsive.smallScreen {
|
||||
.journal-list > div {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
@@ -118,6 +118,7 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
margin: 0.25em;
|
||||
|
||||
border: 2px solid #4e4e4e;
|
||||
|
||||
@@ -40,36 +40,28 @@
|
||||
<span>
|
||||
{{ $t('scenery.timetable-issued-date') }}
|
||||
<b>
|
||||
{{
|
||||
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>
|
||||
{{ parseCreatedDate(timetableHistory, $i18n.locale) }}
|
||||
</b>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{{ $t('scenery.timetable-issued-for') }}
|
||||
<b>
|
||||
<router-link
|
||||
:to="`/journal/timetables?search-driver=${timetableHistory.driverName}`"
|
||||
>
|
||||
{{ timetableHistory.driverName }}
|
||||
</router-link>
|
||||
</b>
|
||||
<router-link
|
||||
class="journal-link"
|
||||
:to="`/journal/timetables?search-driver=${timetableHistory.driverName}`"
|
||||
>
|
||||
{{ timetableHistory.driverName }}
|
||||
</router-link>
|
||||
</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>
|
||||
</div>
|
||||
</span>
|
||||
@@ -106,7 +98,7 @@ import { useApiStore } from '../../store/apiStore';
|
||||
import routerMixin from '../../mixins/routerMixin';
|
||||
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];
|
||||
|
||||
export default defineComponent({
|
||||
@@ -131,17 +123,19 @@ export default defineComponent({
|
||||
dataStatus: Status.Data.Loading,
|
||||
DataStatus: Status.Data,
|
||||
|
||||
checkedHistoryMode: 'via' as HistoryMode
|
||||
checkedHistoryMode: 'includesScenery' as HistoryMode
|
||||
};
|
||||
},
|
||||
|
||||
async activated() {
|
||||
this.checkedHistoryMode = 'includesScenery';
|
||||
this.fetchAPIData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchAPIData() {
|
||||
const stationName = this.$route.query['station'];
|
||||
this.dataStatus = Status.Data.Loading;
|
||||
|
||||
if (!stationName) {
|
||||
this.historyList = [];
|
||||
@@ -152,6 +146,7 @@ export default defineComponent({
|
||||
const requestFilters: Record<string, any> = {};
|
||||
requestFilters[this.checkedHistoryMode] = stationName.toString();
|
||||
requestFilters.countLimit = 30;
|
||||
requestFilters['returnType'] = 'short';
|
||||
|
||||
try {
|
||||
const response: API.TimetableHistory.Response = await (
|
||||
@@ -165,12 +160,12 @@ export default defineComponent({
|
||||
this.dataStatus = Status.Data.Loaded;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.dataStatus = Status.Data.Error;
|
||||
}
|
||||
},
|
||||
|
||||
checkHistoryMode(mode: HistoryMode) {
|
||||
this.checkedHistoryMode = mode;
|
||||
this.dataStatus = Status.Data.Loading;
|
||||
this.fetchAPIData();
|
||||
},
|
||||
|
||||
@@ -181,6 +176,18 @@ export default defineComponent({
|
||||
[`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 }
|
||||
@@ -215,7 +222,15 @@ export default defineComponent({
|
||||
|
||||
button {
|
||||
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>
|
||||
</div>
|
||||
|
||||
<section class="card_sceneries-search">
|
||||
<h3 class="section-header">{{ $t('filters.sceneries-search') }}</h3>
|
||||
|
||||
<section class="card_input-search">
|
||||
<datalist id="sceneries">
|
||||
<option
|
||||
v-for="scenery in sortedStationList"
|
||||
@@ -32,18 +30,60 @@
|
||||
></option>
|
||||
</datalist>
|
||||
|
||||
<form action="javascript:void(0);" @submit="handleSceneriesInput">
|
||||
<input
|
||||
v-model="chosenSearchScenery"
|
||||
id="scenery-search"
|
||||
list="sceneries"
|
||||
:placeholder="$t('filters.sceneries-placeholder')"
|
||||
@focus="preventKeyDown = true"
|
||||
@blur="preventKeyDown = false"
|
||||
/>
|
||||
<input
|
||||
v-model="chosenSearchScenery"
|
||||
id="scenery-search"
|
||||
list="sceneries"
|
||||
:placeholder="$t('filters.sceneries-placeholder')"
|
||||
@focus="preventKeyDown = true"
|
||||
@blur="preventKeyDown = false"
|
||||
/>
|
||||
|
||||
<button class="btn--action">{{ $t('filters.search-button-title') }}</button>
|
||||
</form>
|
||||
<button class="btn--action" @click="handleSceneriesInput">
|
||||
{{ $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 class="card_options">
|
||||
@@ -97,29 +137,6 @@
|
||||
</span>
|
||||
</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">
|
||||
<div class="slider" v-for="(slider, i) in sliderStates" :key="i">
|
||||
<input
|
||||
@@ -200,7 +217,8 @@ export default defineComponent({
|
||||
sliderStates,
|
||||
|
||||
minimumHours: 0,
|
||||
authors: '',
|
||||
authorSearchFilter: '',
|
||||
projectSearchFilter: '',
|
||||
|
||||
currentRegion: { id: '', value: '' },
|
||||
|
||||
@@ -255,11 +273,7 @@ export default defineComponent({
|
||||
.sort((s1, s2) => (s1.name > s2.name ? 1 : -1));
|
||||
},
|
||||
|
||||
currentOptionsActive() {
|
||||
return true;
|
||||
},
|
||||
|
||||
authorsHint() {
|
||||
authorsOptions() {
|
||||
return this.store.stationList
|
||||
.reduce((acc, station) => {
|
||||
station.generalInfo?.authors?.forEach((author) => {
|
||||
@@ -270,6 +284,17 @@ export default defineComponent({
|
||||
return acc;
|
||||
}, [] as string[])
|
||||
.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;
|
||||
},
|
||||
|
||||
handleAuthorsInput() {
|
||||
this.filters['authors'] = this.authors;
|
||||
resetAuthorsInput() {
|
||||
this.filters['authors'] = this.authorSearchFilter;
|
||||
},
|
||||
|
||||
resetProjectsInput() {
|
||||
this.filters['projects'] = this.projectSearchFilter;
|
||||
},
|
||||
|
||||
handleSceneriesInput() {
|
||||
@@ -340,7 +369,7 @@ export default defineComponent({
|
||||
|
||||
// Reset local model values
|
||||
this.minimumHours = 0;
|
||||
this.authors = '';
|
||||
this.authorSearchFilter = '';
|
||||
|
||||
// Reset global filters
|
||||
Object.keys(this.filters).forEach((filterKey) => {
|
||||
@@ -456,27 +485,23 @@ h3.section-header {
|
||||
}
|
||||
}
|
||||
|
||||
.card_authors-search,
|
||||
.card_sceneries-search {
|
||||
margin: 1em 0;
|
||||
.card_input-search {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
width: 100%;
|
||||
margin-top: 1em;
|
||||
button {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 70%;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
outline: 1px solid white;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
&.authors {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,10 +146,11 @@ function filterSliderValues(filters: Record<string, any>, generalInfo: StationGe
|
||||
|
||||
function filterInputValues(filters: Record<string, any>, generalInfo: StationGeneralInfo) {
|
||||
return (
|
||||
filters['authors'].length > 3 &&
|
||||
!generalInfo.authors
|
||||
?.map((a) => a.toLocaleLowerCase())
|
||||
.includes(filters['authors'].toLocaleLowerCase())
|
||||
(filters['authors'].length > 3 &&
|
||||
!generalInfo.authors
|
||||
?.map((a) => a.toLocaleLowerCase())
|
||||
.includes(filters['authors'].toLocaleLowerCase())) ||
|
||||
(filters['projects'].length > 0 && generalInfo.project != filters['projects'])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -76,6 +76,7 @@
|
||||
"tooltip-driver-offline": "Driver is offline",
|
||||
"tooltip-scenery-offline": "Scenery is offline",
|
||||
"pojazdownik-link-content": "POJAZDOWNIK",
|
||||
"language-tooltip-content": "JĘZYK / LANGUAGE",
|
||||
"gnr-link-content": "TRAIN ORDERS <br> GENERATOR"
|
||||
},
|
||||
"footer": {
|
||||
@@ -305,10 +306,9 @@
|
||||
"minTwoWayCatenaryInt": "MIN. INTERNAL CATENARY DOUBLE TRACK ROUTES",
|
||||
"minTwoWayInt": "MIN. INTERNAL OTHER DOUBLE TRACK ROUTES"
|
||||
},
|
||||
"sceneries-search": "SCENERY SEARCH:",
|
||||
"sceneries-placeholder": "Enter scenery name...",
|
||||
"authors-search": "SEARCH BY AUTHOR NAME (other filters apply):",
|
||||
"authors-placeholder": "Enter the author nickname...",
|
||||
"sceneries-placeholder": "Search for scenery",
|
||||
"authors-placeholder": "Scenery author (other filters apply)",
|
||||
"projects-placeholder": "Scenery project (other filters apply)",
|
||||
"search-button-title": "SEARCH",
|
||||
"minimum-hours-title": "SHOW ONLY SCENERIES UNTIL:",
|
||||
"now": "NOW",
|
||||
@@ -560,12 +560,13 @@
|
||||
"option-active-timetables": "Active timetables",
|
||||
"option-timetables-history": "Timetables 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-terminatingAt": "TERMINATES HERE",
|
||||
"timetable-terminatingAt": "ENDS HERE",
|
||||
"timetable-issued-date": "Issued",
|
||||
"timetable-issued-by": " by:",
|
||||
"timetable-issued-for": " for driver:",
|
||||
"timetable-issued-for": " for:",
|
||||
"dispatcher-rate": "Rate:",
|
||||
"dispatcher-status-changes": "Status changes:",
|
||||
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
|
||||
@@ -610,4 +611,4 @@
|
||||
"search-train": "Train no.",
|
||||
"search-driver": "Driver name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -73,6 +73,7 @@
|
||||
"tooltip-driver-offline": "Maszynista offline",
|
||||
"tooltip-scenery-offline": "Sceneria offline",
|
||||
"pojazdownik-link-content": "POJAZDOWNIK",
|
||||
"language-tooltip-content": "JĘZYK / LANGUAGE",
|
||||
"gnr-link-content": "GENERATOR <br> ROZKAZÓW PISEMNYCH"
|
||||
},
|
||||
"footer": {
|
||||
@@ -303,10 +304,9 @@
|
||||
"minTwoWayCatenaryInt": "SZLAKI DWUTOROWE ZELEKTR. WEWNĘTRZNE (MINIMUM)",
|
||||
"minTwoWayInt": "SZLAKI DWUTOROWE NIEZELEKTR. WEWNĘTRZNE (MINIMUM)"
|
||||
},
|
||||
"sceneries-search": "WYSZUKAJ SCENERIĘ:",
|
||||
"sceneries-placeholder": "Wpisz nazwę scenerii...",
|
||||
"authors-search": "WYSZUKAJ AUTORA (uwzględnia inne filtry):",
|
||||
"authors-placeholder": "Wpisz nick autora...",
|
||||
"sceneries-placeholder": "Wyszukaj scenerię",
|
||||
"authors-placeholder": "Autor scenerii (uwzględnia inne filtry)",
|
||||
"projects-placeholder": "Projekt scenerii (uwzględnia inne filtry)",
|
||||
"search-button-title": "SZUKAJ",
|
||||
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
|
||||
"now": "TERAZ",
|
||||
@@ -546,12 +546,13 @@
|
||||
"option-active-timetables": "Aktywne rozkłady jazdy",
|
||||
"option-timetables-history": "Historia rozkładó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-terminatingAt": "KOŃCZY BIEG",
|
||||
"timetable-issued-date": "Wystawiony",
|
||||
"timetable-issued-date": "Wystawiony: ",
|
||||
"timetable-issued-by": " przez:",
|
||||
"timetable-issued-for": " dla maszynisty:",
|
||||
"timetable-issued-for": " dla:",
|
||||
"dispatcher-rate": "Ocena:",
|
||||
"dispatcher-status-changes": "Zmiany statusów:",
|
||||
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
|
||||
@@ -594,4 +595,4 @@
|
||||
"history": {
|
||||
"title": "DZIENNIK ROZKŁADÓW JAZDY"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,8 @@ export const initFilters = {
|
||||
minTwoWayCatenary: 0,
|
||||
minTwoWayInt: 0,
|
||||
minTwoWayCatenaryInt: 0,
|
||||
authors: ''
|
||||
authors: '',
|
||||
projects: ''
|
||||
};
|
||||
|
||||
export const sliderStates = [
|
||||
@@ -83,7 +84,7 @@ export const sliderStates = [
|
||||
{ id: 'minTwoWay', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'minTwoWayCatenary', 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;
|
||||
@@ -97,7 +98,18 @@ export const filtersSections: Record<StationFilterSection, StationFilter[]> = {
|
||||
stationType: ['junction', 'nonJunction'],
|
||||
access: ['nonPublic', 'unavailable', 'abandoned'],
|
||||
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'],
|
||||
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 (
|
||||
Object.keys(currentFilters).filter(
|
||||
(filterKey) =>
|
||||
|
||||
+17
-2
@@ -11,6 +11,8 @@ import {
|
||||
} from '../typings/common';
|
||||
import { useApiStore } from './apiStore';
|
||||
import { MainStoreState } from './typings';
|
||||
import i18n from '../i18n';
|
||||
import StorageManager from '../managers/storageManager';
|
||||
|
||||
const checkpointsTrains: Map<string, CheckpointTrain[]> = new Map();
|
||||
const unknownSceneryCheckpoints: Map<string, Set<string>> = new Map();
|
||||
@@ -37,6 +39,15 @@ export const useMainStore = defineStore('mainStore', {
|
||||
currentLocale: 'pl'
|
||||
}) as MainStoreState,
|
||||
|
||||
actions: {
|
||||
changeLocale(localeName: string) {
|
||||
(i18n.global.locale.value as any) = localeName;
|
||||
this.currentLocale = localeName;
|
||||
|
||||
StorageManager.setStringValue('lang', localeName);
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
trainList(): Train[] {
|
||||
const apiStore = useApiStore();
|
||||
@@ -333,8 +344,12 @@ export const useMainStore = defineStore('mainStore', {
|
||||
const missingCheckpointsToAdd = unknownSceneryCheckpoints.get(scenery.name);
|
||||
|
||||
if (missingCheckpointsToAdd) {
|
||||
checkpoints.push(...missingCheckpointsToAdd);
|
||||
scenery.missingCheckpoints.push(...missingCheckpointsToAdd);
|
||||
[...missingCheckpointsToAdd].forEach((cp) => {
|
||||
if (cp.toLowerCase() == scenery.name.toLowerCase()) return;
|
||||
|
||||
checkpoints.push(cp);
|
||||
scenery.missingCheckpoints.push(cp);
|
||||
});
|
||||
}
|
||||
|
||||
const uniqueTrainIds: string[] = [];
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface StationJSONData {
|
||||
project: string;
|
||||
projectUrl: string;
|
||||
hash: string;
|
||||
hidden: boolean;
|
||||
|
||||
reqLevel: number;
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ export interface StationGeneralInfo {
|
||||
availability: Availability;
|
||||
routes: StationRoutes;
|
||||
checkpoints: string[];
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export interface StationRoutes {
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
</div>
|
||||
|
||||
<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
|
||||
class="a-button btn--image gnr-link"
|
||||
href="https://generator-td2.web.app/"
|
||||
@@ -96,6 +108,10 @@ export default defineComponent({
|
||||
methods: {
|
||||
toggleDonationCard(value: boolean) {
|
||||
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 {
|
||||
background-color: #1f263b;
|
||||
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
server: { port: 5123, open: true },
|
||||
preview: { port: 4001, open: true },
|
||||
preview: { port: 4001, open: false },
|
||||
publicDir: 'public',
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
@@ -23,7 +23,7 @@ export default defineConfig({
|
||||
registerType: 'autoUpdate',
|
||||
workbox: {
|
||||
disableDevLogs: true,
|
||||
globPatterns: ['**/*.{js,css,html,png,svg,jpg,ico,woff,woff2,ttf}'],
|
||||
globPatterns: ['**/*.{js,css,html,ico,woff,woff2,ttf}', '**/*.{png,jpg,jpeg,svg,webp,gif}'],
|
||||
cleanupOutdatedCaches: true,
|
||||
runtimeCaching: [
|
||||
{
|
||||
@@ -34,7 +34,7 @@ export default defineConfig({
|
||||
cacheName: 'stacjownik-api-cache',
|
||||
cacheableResponse: { statuses: [0, 200] }
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
devOptions: { enabled: true, suppressWarnings: true }
|
||||
|
||||
Reference in New Issue
Block a user