mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 13:28:11 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 20cd393e05 | |||
| 31e65c09d6 | |||
| fb2348e774 | |||
| 1ec75bda70 | |||
| 6b6b837dde | |||
| 66a02d76bd | |||
| c7162dbd14 | |||
| 1cfe073bab | |||
| e3b72c81ea | |||
| 5552995564 | |||
| 623d5dd2ce |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stacjownik",
|
"name": "stacjownik",
|
||||||
"version": "1.22.2",
|
"version": "1.23.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
+31
-4
@@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app_container" v-cloak>
|
<div class="app_container" v-cloak>
|
||||||
|
<PopUp />
|
||||||
|
|
||||||
<transition name="modal-anim">
|
<transition name="modal-anim">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<TrainModal v-if="store.chosenModalTrainId" />
|
<TrainModal v-if="store.chosenModalTrainId" />
|
||||||
@@ -36,17 +38,17 @@ import { defineComponent, watch } from 'vue';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { version } from '.././package.json';
|
import { version } from '.././package.json';
|
||||||
|
|
||||||
import Clock from './components/App/Clock.vue';
|
|
||||||
|
|
||||||
import { useMainStore } from './store/mainStore';
|
import { useMainStore } from './store/mainStore';
|
||||||
|
|
||||||
|
import Clock from './components/App/Clock.vue';
|
||||||
import StatusIndicator from './components/App/StatusIndicator.vue';
|
import StatusIndicator from './components/App/StatusIndicator.vue';
|
||||||
import AppHeader from './components/App/AppHeader.vue';
|
import AppHeader from './components/App/AppHeader.vue';
|
||||||
import TrainModal from './components/TrainsView/TrainModal.vue';
|
import TrainModal from './components/TrainsView/TrainModal.vue';
|
||||||
|
|
||||||
import StorageManager from './managers/storageManager';
|
import StorageManager from './managers/storageManager';
|
||||||
|
import PopUp from './components/PopUp/PopUp.vue';
|
||||||
import { useApiStore } from './store/apiStore';
|
import { useApiStore } from './store/apiStore';
|
||||||
import { Status } from './typings/common';
|
import { Status } from './typings/common';
|
||||||
|
import { usePopupStore } from './store/popupStore';
|
||||||
|
|
||||||
const STORAGE_VERSION_KEY = 'app_version';
|
const STORAGE_VERSION_KEY = 'app_version';
|
||||||
|
|
||||||
@@ -55,13 +57,15 @@ export default defineComponent({
|
|||||||
Clock,
|
Clock,
|
||||||
StatusIndicator,
|
StatusIndicator,
|
||||||
AppHeader,
|
AppHeader,
|
||||||
TrainModal
|
TrainModal,
|
||||||
|
PopUp
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
VERSION: version,
|
VERSION: version,
|
||||||
store: useMainStore(),
|
store: useMainStore(),
|
||||||
apiStore: useApiStore(),
|
apiStore: useApiStore(),
|
||||||
|
popupStore: usePopupStore(),
|
||||||
|
|
||||||
currentLang: 'pl',
|
currentLang: 'pl',
|
||||||
releaseURL: '',
|
releaseURL: '',
|
||||||
@@ -79,6 +83,28 @@ export default defineComponent({
|
|||||||
this.apiStore.fetchActiveData();
|
this.apiStore.fetchActiveData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// popup handling
|
||||||
|
window.addEventListener('mousemove', (e: MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const targetEl = e
|
||||||
|
.composedPath()
|
||||||
|
.find((p) => p instanceof HTMLElement && p.getAttribute('data-popup-key'));
|
||||||
|
|
||||||
|
if (!targetEl || !(targetEl instanceof HTMLElement)) {
|
||||||
|
if (this.popupStore.currentPopupComponent != null) this.popupStore.onPopUpHide();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const popupComponentKey = targetEl.getAttribute('data-popup-key');
|
||||||
|
const popupContent = targetEl.getAttribute('data-popup-content');
|
||||||
|
|
||||||
|
if (popupComponentKey && popupContent)
|
||||||
|
this.popupStore.onPopUpShow(e, popupComponentKey, popupContent);
|
||||||
|
else if (this.popupStore.currentPopupComponent != null) this.popupStore.onPopUpHide();
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => this.store.blockScroll,
|
() => this.store.blockScroll,
|
||||||
(value) => {
|
(value) => {
|
||||||
@@ -217,6 +243,7 @@ export default defineComponent({
|
|||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app_main {
|
.app_main {
|
||||||
|
|||||||
@@ -7,11 +7,12 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
class="traction-only"
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${computedStockList[0].split(':')[0]}${
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${computedStockList[0].split(':')[0]}${
|
||||||
/^EN/.test(computedStockList[0]) ? 'rb' : ''
|
/^EN/.test(computedStockList[0]) ? 'rb' : ''
|
||||||
}.png`"
|
}.png`"
|
||||||
@error="onImageError($event, computedStockList[0])"
|
@error="onImageError($event, computedStockList[0])"
|
||||||
width="400"
|
width="300"
|
||||||
height="60"
|
height="60"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,39 +26,53 @@
|
|||||||
|
|
||||||
<span>
|
<span>
|
||||||
<img
|
<img
|
||||||
|
:data-mouseover="stockName"
|
||||||
|
data-popup-key="VehiclePreviewPopUp"
|
||||||
|
:data-popup-content="stockName.split(':')[0]"
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}${
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}${
|
||||||
/^EN/.test(stockName) ? 'rb' : ''
|
/^EN/.test(stockName) ? 'rb' : ''
|
||||||
}.png`"
|
}.png`"
|
||||||
@error="onImageError($event, stockName)"
|
@error="onImageError($event, stockName)"
|
||||||
|
@click.stop="() => {}"
|
||||||
width="400"
|
width="400"
|
||||||
height="60"
|
height="60"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- /// Manualne dodawanie miniaturek członów dla kibelków /// -->
|
<!-- /// Manualne dodawanie miniaturek członów dla kibelków /// -->
|
||||||
<img
|
<img
|
||||||
|
:data-mouseover="stockName"
|
||||||
|
data-popup-key="VehiclePreviewPopUp"
|
||||||
|
:data-popup-content="stockName.split(':')[0]"
|
||||||
v-if="/^(EN|2EN)/.test(stockName)"
|
v-if="/^(EN|2EN)/.test(stockName)"
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}s.png`"
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}s.png`"
|
||||||
@error="
|
@error="
|
||||||
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-s.png')
|
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-s.png')
|
||||||
"
|
"
|
||||||
|
@click.stop="() => {}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
class="train-thumbnail"
|
:data-mouseover="stockName"
|
||||||
|
data-popup-key="VehiclePreviewPopUp"
|
||||||
|
:data-popup-content="stockName.split(':')[0]"
|
||||||
v-if="/^EN71/.test(stockName)"
|
v-if="/^EN71/.test(stockName)"
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}s.png`"
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}s.png`"
|
||||||
@error="
|
@error="
|
||||||
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-s.png')
|
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-s.png')
|
||||||
"
|
"
|
||||||
|
@click.stop="() => {}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
class="train-thumbnail"
|
:data-mouseover="stockName"
|
||||||
|
data-popup-key="VehiclePreviewPopUp"
|
||||||
|
:data-popup-content="stockName.split(':')[0]"
|
||||||
v-if="/^(EN|2EN)/.test(stockName)"
|
v-if="/^(EN|2EN)/.test(stockName)"
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}ra.png`"
|
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stockName.split(':')[0]}ra.png`"
|
||||||
@error="
|
@error="
|
||||||
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-ra.png')
|
(event) => ((event.target as HTMLImageElement).src = '/images/icon-loco-ezt-ra.png')
|
||||||
"
|
"
|
||||||
|
@click.stop="() => {}"
|
||||||
/>
|
/>
|
||||||
<!-- /// -->
|
<!-- /// -->
|
||||||
</span>
|
</span>
|
||||||
@@ -139,6 +154,7 @@ export default defineComponent({
|
|||||||
ul > li > span {
|
ul > li > span {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
cursor: crosshair;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@@ -147,6 +163,10 @@ img {
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.traction-only {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="item-general">
|
<div class="item-general">
|
||||||
<span
|
<span class="general-train">
|
||||||
class="general-train"
|
|
||||||
tabindex="0"
|
|
||||||
@click.stop="showTimetable(timetable, $event.currentTarget)"
|
|
||||||
@keydown.enter="showTimetable(timetable, $event.currentTarget)"
|
|
||||||
>
|
|
||||||
<span class="text--grayed">#{{ timetable.id }}</span>
|
<span class="text--grayed">#{{ timetable.id }}</span>
|
||||||
|
|
||||||
<span class="badges" v-if="timetable.skr || timetable.twr">
|
<span class="badges" v-if="timetable.skr || timetable.twr">
|
||||||
@@ -39,6 +34,15 @@
|
|||||||
<strong v-else>
|
<strong v-else>
|
||||||
{{ timetable.driverName }}
|
{{ timetable.driverName }}
|
||||||
</strong>
|
</strong>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="timetable.terminated == false"
|
||||||
|
class="btn--image btn--action btn-timetable"
|
||||||
|
@click.stop="showTimetable(timetable, $event.currentTarget)"
|
||||||
|
>
|
||||||
|
<img src="/images/icon-train.svg" alt="" />
|
||||||
|
{{ $t('journal.timetable-online-button') }}
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="general-time">
|
<span class="general-time">
|
||||||
@@ -148,6 +152,16 @@ export default defineComponent({
|
|||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-timetable {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.1em 0.4em;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include smallScreen {
|
@include smallScreen {
|
||||||
.item-general {
|
.item-general {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-for="{ timetable, showExtraInfo } in computedTimetableHistory"
|
v-for="{ timetable, showExtraInfo } in computedTimetableHistory"
|
||||||
class="journal_item"
|
class="journal_item"
|
||||||
:key="timetable.id"
|
:key="timetable.id"
|
||||||
@click="showExtraInfo.value = !showExtraInfo.value"
|
@click.stop.prevent="showExtraInfo.value = !showExtraInfo.value"
|
||||||
>
|
>
|
||||||
<div class="journal_item-info">
|
<div class="journal_item-info">
|
||||||
<!-- General -->
|
<!-- General -->
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="popup-content">
|
||||||
|
<img src="/images/icon-diamond.svg" alt="" />
|
||||||
|
<span>{{ popupStore.currentPopupContent }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { usePopupStore } from '../../store/popupStore';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
popupStore: usePopupStore()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.popup-content {
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-color: #333;
|
||||||
|
box-shadow: 0 0 10px 2px #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 1em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="popup" v-show="popupStore.currentPopupComponent" ref="preview">
|
||||||
|
<component v-if="popupStore.currentPopupComponent" :is="popupStore.currentPopupComponent" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import DonatorPopUp from './DonatorPopUp.vue';
|
||||||
|
import TrainCommentsPopUp from './TrainCommentsPopUp.vue';
|
||||||
|
import VehiclePreviewPopUp from './VehiclePreviewPopUp.vue';
|
||||||
|
import { usePopupStore } from '../../store/popupStore';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { DonatorPopUp, TrainCommentsPopUp, VehiclePreviewPopUp },
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
popupStore: usePopupStore()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
'popupStore.popupPosition': {
|
||||||
|
deep: true,
|
||||||
|
handler(val: typeof this.popupStore.popupPosition) {
|
||||||
|
const previewEl = this.$refs['preview'] as HTMLElement;
|
||||||
|
|
||||||
|
previewEl.style.top = `${val.y}px`;
|
||||||
|
previewEl.style.left = `${val.x}px`;
|
||||||
|
previewEl.style.transform = 'translateY(1.5rem)';
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const isOutside =
|
||||||
|
val.y + previewEl.getBoundingClientRect().height > window.innerHeight + window.scrollY;
|
||||||
|
|
||||||
|
// previewEl.style.transform = `translate(-${~~((val.x / window.innerWidth) * 100)}%, calc(${isOutside ? '-100% - 1.5rem' : '1.5rem'}))`;
|
||||||
|
previewEl.style.transform = `translate(-${~~((val.x / window.innerWidth) * 100)}%, calc(${
|
||||||
|
isOutside ? '-100% - 1.5rem' : '1.5rem'
|
||||||
|
}))`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.popup {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 250;
|
||||||
|
max-width: 400px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="popup-content">
|
||||||
|
<span>{{ popupStore.currentPopupContent }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { usePopupStore } from '../../store/popupStore';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
popupStore: usePopupStore()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.popup-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
background-color: #333;
|
||||||
|
box-shadow: 0 0 5px 2px #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div class="popup-content">
|
||||||
|
<div v-if="imageState == 'loading'" class="loading-info">
|
||||||
|
{{ $t('vehicle-preview.loading') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="imageState == 'error'">{{ $t('vehicle-preview.error') }}</div>
|
||||||
|
|
||||||
|
<img
|
||||||
|
v-if="popupStore.currentPopupContent"
|
||||||
|
@load="onImageLoad"
|
||||||
|
@error="onImageError"
|
||||||
|
@click="popupStore.onPopUpHide"
|
||||||
|
width="300"
|
||||||
|
height="176"
|
||||||
|
class="rounded-md w-full h-auto"
|
||||||
|
:src="`https://spythere.github.io/api/td2/images/${popupStore.currentPopupContent}--300px.jpg`"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="vehicle-name" v-if="imageState != 'error'">
|
||||||
|
{{ popupStore.currentPopupContent.replace(/_/g, ' ') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { usePopupStore } from '../../store/popupStore';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
popupStore: usePopupStore(),
|
||||||
|
imageState: 'loading'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.imageState = 'loading';
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onImageLoad() {
|
||||||
|
this.imageState = 'loaded';
|
||||||
|
},
|
||||||
|
|
||||||
|
onImageError(e: Event) {
|
||||||
|
this.imageState = 'error';
|
||||||
|
|
||||||
|
(e.target as HTMLElement).style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.popup-content {
|
||||||
|
// min-w-[300px] min-h-[200px] p-2 bg-slate-800 rounded-md
|
||||||
|
|
||||||
|
width: 300px;
|
||||||
|
min-height: 200px;
|
||||||
|
background-color: #333;
|
||||||
|
box-shadow: 0 0 10px 2px #aaa;
|
||||||
|
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-info {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-name {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
color: #ccc;
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
class="timetable-item"
|
class="timetable-item"
|
||||||
v-else
|
v-else
|
||||||
v-for="scheduledTrain in computedScheduledTrains"
|
v-for="scheduledTrain in computedScheduledTrains"
|
||||||
:key="scheduledTrain.trainId"
|
:key="scheduledTrain.trainId + scheduledTrain.stopInfo.arrivalTimestamp"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@click.prevent.stop="selectModalTrain(scheduledTrain.trainId, $event.currentTarget)"
|
@click.prevent.stop="selectModalTrain(scheduledTrain.trainId, $event.currentTarget)"
|
||||||
@keydown.enter.prevent="selectModalTrain(scheduledTrain.trainId, $event.currentTarget)"
|
@keydown.enter.prevent="selectModalTrain(scheduledTrain.trainId, $event.currentTarget)"
|
||||||
|
|||||||
@@ -119,8 +119,9 @@
|
|||||||
<span v-if="station.onlineInfo?.dispatcherName">
|
<span v-if="station.onlineInfo?.dispatcherName">
|
||||||
<b
|
<b
|
||||||
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
||||||
:title="$t('donations.dispatcher-message')"
|
|
||||||
@click.stop="openDonationModal"
|
@click.stop="openDonationModal"
|
||||||
|
data-popup-key="DonatorPopUp"
|
||||||
|
:data-popup-content="$t('donations.dispatcher-message')"
|
||||||
>
|
>
|
||||||
<img src="/images/icon-diamond.svg" alt="" />
|
<img src="/images/icon-diamond.svg" alt="" />
|
||||||
{{ station.onlineInfo.dispatcherName }}
|
{{ station.onlineInfo.dispatcherName }}
|
||||||
@@ -311,6 +312,7 @@ import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationH
|
|||||||
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
||||||
import { Status } from '../../typings/common';
|
import { Status } from '../../typings/common';
|
||||||
import { useApiStore } from '../../store/apiStore';
|
import { useApiStore } from '../../store/apiStore';
|
||||||
|
import { usePopupStore } from '../../store/popupStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -339,13 +341,16 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const mainStore = useMainStore();
|
const mainStore = useMainStore();
|
||||||
const apiStore = useApiStore();
|
const apiStore = useApiStore();
|
||||||
|
const popupStore = usePopupStore();
|
||||||
|
|
||||||
const stationFiltersStore = useStationFiltersStore();
|
const stationFiltersStore = useStationFiltersStore();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Status: Status.Data,
|
Status: Status.Data,
|
||||||
stationFiltersStore,
|
stationFiltersStore,
|
||||||
mainStore,
|
mainStore,
|
||||||
apiStore
|
apiStore,
|
||||||
|
popupStore
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -368,6 +373,7 @@ export default defineComponent({
|
|||||||
openDonationModal(e: Event) {
|
openDonationModal(e: Event) {
|
||||||
this.$emit('toggleDonationModal', true);
|
this.$emit('toggleDonationModal', true);
|
||||||
this.mainStore.modalLastClickedTarget = e.target;
|
this.mainStore.modalLastClickedTarget = e.target;
|
||||||
|
this.popupStore.currentPopupComponent = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
openForumSite(e: Event, url: string | undefined) {
|
openForumSite(e: Event, url: string | undefined) {
|
||||||
|
|||||||
@@ -36,25 +36,37 @@
|
|||||||
<div class="train-driver">
|
<div class="train-driver">
|
||||||
<b
|
<b
|
||||||
v-if="apiStore.donatorsData.includes(train.driverName)"
|
v-if="apiStore.donatorsData.includes(train.driverName)"
|
||||||
:title="$t('donations.driver-message')"
|
data-popup-key="DonatorPopUp"
|
||||||
|
:data-popup-content="$t('donations.driver-message')"
|
||||||
>
|
>
|
||||||
{{ train.driverName }}
|
{{ train.driverName }}
|
||||||
<img src="/images/icon-diamond.svg" alt="donator diamond icon" />
|
<img src="/images/icon-diamond.svg" alt="donator diamond icon" />
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<span v-else>{{ train.driverName }}</span>
|
<span v-else>{{ train.driverName }}</span>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn--image btn--action btn-timetable"
|
||||||
|
@click="navigateToJournal"
|
||||||
|
v-if="extended"
|
||||||
|
>
|
||||||
|
<img src="/images/icon-train.svg" alt="" />
|
||||||
|
{{ $t('trains.journal-button') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="general-timetable" v-if="train.timetableData">
|
<div class="general-timetable" v-if="train.timetableData">
|
||||||
<strong>{{ train.timetableData.route.replace('|', ' - ') }}</strong>
|
<strong>{{ train.timetableData.route.replace('|', ' - ') }}</strong>
|
||||||
<img
|
<span
|
||||||
v-if="getSceneriesWithComments(train.timetableData).length > 0"
|
v-if="getSceneriesWithComments(train.timetableData).length > 0"
|
||||||
class="image-warning"
|
data-popup-key="TrainCommentsPopUp"
|
||||||
src="/images/icon-warning.svg"
|
:data-popup-content="`${$t('trains.timetable-comments')} (${getSceneriesWithComments(
|
||||||
:title="`${$t('trains.timetable-comments')} (${getSceneriesWithComments(
|
|
||||||
train.timetableData
|
train.timetableData
|
||||||
)})`"
|
)})`"
|
||||||
/>
|
>
|
||||||
|
<img class="image-warning" src="/images/icon-warning.svg" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr style="margin: 0.25em 0" />
|
<hr style="margin: 0.25em 0" />
|
||||||
@@ -125,9 +137,11 @@ import ProgressBar from '../Global/ProgressBar.vue';
|
|||||||
import { useMainStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import { useApiStore } from '../../store/apiStore';
|
import { useApiStore } from '../../store/apiStore';
|
||||||
import StockList from '../Global/StockList.vue';
|
import StockList from '../Global/StockList.vue';
|
||||||
|
import { usePopupStore } from '../../store/popupStore';
|
||||||
|
import modalTrainMixin from '../../mixins/modalTrainMixin';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [trainInfoMixin, styleMixin],
|
mixins: [trainInfoMixin, styleMixin, modalTrainMixin],
|
||||||
components: { ProgressBar, StockList },
|
components: { ProgressBar, StockList },
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@@ -136,16 +150,29 @@ export default defineComponent({
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
extended: {
|
extended: {
|
||||||
type: Boolean,
|
type: Boolean
|
||||||
default: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useMainStore(),
|
store: useMainStore(),
|
||||||
apiStore: useApiStore()
|
apiStore: useApiStore(),
|
||||||
|
popupStore: usePopupStore()
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
navigateToJournal() {
|
||||||
|
this.$router.push({
|
||||||
|
path: '/journal/timetables',
|
||||||
|
query: {
|
||||||
|
'search-driver': this.train.driverName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -156,8 +183,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
.image-warning {
|
.image-warning {
|
||||||
height: 1em;
|
height: 1em;
|
||||||
|
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-stats {
|
.train-stats {
|
||||||
@@ -187,6 +214,12 @@ export default defineComponent({
|
|||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-timetable {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25em;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.timetable-id {
|
.timetable-id {
|
||||||
color: #d2d2d2;
|
color: #d2d2d2;
|
||||||
}
|
}
|
||||||
@@ -262,8 +295,6 @@ export default defineComponent({
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1em 0;
|
gap: 1em 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
font-size: 1.15em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.general-info,
|
.general-info,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<img src="/images/icon-exit.svg" alt="close card" />
|
<img src="/images/icon-exit.svg" alt="close card" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<TrainInfo :train="chosenTrain" :extended="false" ref="trainInfo" />
|
<TrainInfo :train="chosenTrain" :extended="true" ref="trainInfo" />
|
||||||
<TrainSchedule :train="chosenTrain" tabindex="0" />
|
<TrainSchedule :train="chosenTrain" tabindex="0" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition name="status-anim" mode="out-in" tag="div" class="train-table">
|
<transition name="status-anim" mode="out-in" tag="div" class="train-table">
|
||||||
<div :key="apiStore.dataStatuses.connection">
|
<div :key="apiStore.dataStatuses.connection">
|
||||||
<div class="table-info" key="offline" v-if="store.isOffline">
|
<div class="table-warning" key="offline" v-if="store.isOffline">
|
||||||
{{ $t('app.offline') }}
|
{{ $t('app.offline') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Loading v-else-if="apiStore.dataStatuses.connection == Status.Loading" key="loading" />
|
<Loading v-else-if="apiStore.dataStatuses.connection == Status.Loading" key="loading" />
|
||||||
|
|
||||||
<div class="table-info" key="no-trains" v-else-if="trains.length == 0">
|
<div class="table-warning" key="no-trains" v-else-if="trains.length == 0">
|
||||||
{{ $t('trains.no-trains') }}
|
{{ $t('trains.no-trains') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
@click.stop="selectModalTrain(train.trainId, $event.currentTarget)"
|
@click.stop="selectModalTrain(train.trainId, $event.currentTarget)"
|
||||||
@keydown.enter="selectModalTrain(train.trainId, $event.currentTarget)"
|
@keydown.enter="selectModalTrain(train.trainId, $event.currentTarget)"
|
||||||
>
|
>
|
||||||
<TrainInfo :train="train" />
|
<TrainInfo :train="train" :extended="false" />
|
||||||
</li>
|
</li>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,7 +105,7 @@ export default defineComponent({
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-info {
|
.table-warning {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
|
|||||||
+8
-1
@@ -48,6 +48,10 @@
|
|||||||
"confirm-button": "UPDATE NOW",
|
"confirm-button": "UPDATE NOW",
|
||||||
"later-button": "LATER"
|
"later-button": "LATER"
|
||||||
},
|
},
|
||||||
|
"vehicle-preview": {
|
||||||
|
"loading": "Loading preview...",
|
||||||
|
"error": "Oops! The vehicle preview seems to be missing! :/"
|
||||||
|
},
|
||||||
"data-status": {
|
"data-status": {
|
||||||
"S1-offline": "<b>S1 signal</b> <br> The app is working in offline mode!",
|
"S1-offline": "<b>S1 signal</b> <br> The app is working in offline mode!",
|
||||||
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with Stacjownik API service!",
|
"S1a-connection": "<b>S1a signal</b> <br> Cannot connect with Stacjownik API service!",
|
||||||
@@ -315,7 +319,9 @@
|
|||||||
"last-seen-ago": "since {minutes} minutes",
|
"last-seen-ago": "since {minutes} minutes",
|
||||||
|
|
||||||
"scenery-offline": "Offline ride",
|
"scenery-offline": "Offline ride",
|
||||||
"timeout": "An error occured while trying to refresh SWDR timetable data!"
|
"timeout": "An error occured while trying to refresh SWDR timetable data!",
|
||||||
|
|
||||||
|
"journal-button": "DRIVER'S JOURNAL"
|
||||||
},
|
},
|
||||||
"train-stats": {
|
"train-stats": {
|
||||||
"stats-button": "STATISTICS",
|
"stats-button": "STATISTICS",
|
||||||
@@ -351,6 +357,7 @@
|
|||||||
"timetable-active": "ACTIVE",
|
"timetable-active": "ACTIVE",
|
||||||
"timetable-fulfilled": "FULFILLED",
|
"timetable-fulfilled": "FULFILLED",
|
||||||
"timetable-abandoned": "ABANDONED",
|
"timetable-abandoned": "ABANDONED",
|
||||||
|
"timetable-online-button": "ONLINE TIMETABLE",
|
||||||
|
|
||||||
"online-since": "ONLINE SINCE",
|
"online-since": "ONLINE SINCE",
|
||||||
"duty-lasted": "The duty lasted",
|
"duty-lasted": "The duty lasted",
|
||||||
|
|||||||
+8
-1
@@ -38,6 +38,10 @@
|
|||||||
"footer": {
|
"footer": {
|
||||||
"discord": "Serwer Discord Stacjownika"
|
"discord": "Serwer Discord Stacjownika"
|
||||||
},
|
},
|
||||||
|
"vehicle-preview": {
|
||||||
|
"loading": "Ładowanie podglądu...",
|
||||||
|
"error": "Ups! Nie znaleziono podglądu pojazdu! :/"
|
||||||
|
},
|
||||||
"data-status": {
|
"data-status": {
|
||||||
"S1-offline": "<b>Sygnał S1</b> <br> Aplikacja działa w trybie offline!",
|
"S1-offline": "<b>Sygnał S1</b> <br> Aplikacja działa w trybie offline!",
|
||||||
"S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z API Stacjownika!",
|
"S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z API Stacjownika!",
|
||||||
@@ -295,7 +299,9 @@
|
|||||||
|
|
||||||
"scenery-offline": "Przejazd offline",
|
"scenery-offline": "Przejazd offline",
|
||||||
|
|
||||||
"timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR"
|
"timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR",
|
||||||
|
|
||||||
|
"journal-button": "DZIENNIK MASZYNISTY"
|
||||||
},
|
},
|
||||||
"train-stats": {
|
"train-stats": {
|
||||||
"stats-button": "STATYSTYKI",
|
"stats-button": "STATYSTYKI",
|
||||||
@@ -337,6 +343,7 @@
|
|||||||
"timetable-active": "AKTYWNY",
|
"timetable-active": "AKTYWNY",
|
||||||
"timetable-fulfilled": "WYPEŁNIONY",
|
"timetable-fulfilled": "WYPEŁNIONY",
|
||||||
"timetable-abandoned": "PORZUCONY",
|
"timetable-abandoned": "PORZUCONY",
|
||||||
|
"timetable-online-button": "RJ ONLINE",
|
||||||
|
|
||||||
"stock-info": "DODATKOWE INFORMACJE",
|
"stock-info": "DODATKOWE INFORMACJE",
|
||||||
"stock-length": "Długość",
|
"stock-length": "Długość",
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { useMainStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
|
import { usePopupStore } from '../store/popupStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
store: useMainStore()
|
store: useMainStore(),
|
||||||
|
popupStore: usePopupStore()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.store.chosenModalTrainId = undefined;
|
this.store.chosenModalTrainId = undefined;
|
||||||
|
this.popupStore.currentPopupComponent = null;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
(this.store.modalLastClickedTarget as any)?.focus();
|
(this.store.modalLastClickedTarget as any)?.focus();
|
||||||
|
|||||||
@@ -107,7 +107,10 @@ export const useMainStore = defineStore('store', {
|
|||||||
if (
|
if (
|
||||||
acc.findIndex((v) => v.name == name && v.region == train.region) != -1 ||
|
acc.findIndex((v) => v.name == name && v.region == train.region) != -1 ||
|
||||||
apiStore.activeData?.activeSceneries?.findIndex(
|
apiStore.activeData?.activeSceneries?.findIndex(
|
||||||
(sc) => sc.stationName === name && sc.region == train.region
|
(sc) =>
|
||||||
|
sc.stationName === name &&
|
||||||
|
sc.region == train.region &&
|
||||||
|
Date.now() - sc.lastSeen < 1000 * 60 * 2
|
||||||
) != -1
|
) != -1
|
||||||
)
|
)
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const popupKeys = ['DonatorPopUp', 'TrainCommentsPopUp', 'VehiclePreviewPopUp'] as const;
|
||||||
|
export type PopUp = (typeof popupKeys)[number];
|
||||||
|
|
||||||
|
const isPopUp = (v: any): v is PopUp => popupKeys.includes(v);
|
||||||
|
|
||||||
|
export const usePopupStore = defineStore('popupStore', {
|
||||||
|
state: () => ({
|
||||||
|
popupPosition: { x: 0, y: 0 },
|
||||||
|
currentPopupComponent: null as PopUp | null,
|
||||||
|
currentPopupContent: '',
|
||||||
|
donatorPopupVisible: false
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
onPopUpShow(e: MouseEvent, componentKey: string, value?: string) {
|
||||||
|
if (!isPopUp(componentKey)) return;
|
||||||
|
|
||||||
|
this.popupPosition.x = e.pageX;
|
||||||
|
this.popupPosition.y = e.pageY;
|
||||||
|
|
||||||
|
this.currentPopupComponent = componentKey;
|
||||||
|
this.currentPopupContent = value ?? '';
|
||||||
|
},
|
||||||
|
|
||||||
|
onPopUpMove(e: MouseEvent) {
|
||||||
|
this.popupPosition.x = e.pageX;
|
||||||
|
this.popupPosition.y = e.pageY;
|
||||||
|
},
|
||||||
|
|
||||||
|
onPopUpHide() {
|
||||||
|
this.currentPopupComponent = null;
|
||||||
|
this.currentPopupContent = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -11,20 +11,14 @@ export interface RegionCounters {
|
|||||||
|
|
||||||
export interface StoreState {
|
export interface StoreState {
|
||||||
region: { id: string; value: string };
|
region: { id: string; value: string };
|
||||||
|
|
||||||
isOffline: boolean;
|
isOffline: boolean;
|
||||||
|
|
||||||
isNewUpdate: boolean;
|
isNewUpdate: boolean;
|
||||||
|
|
||||||
dispatcherStatsName: string;
|
dispatcherStatsName: string;
|
||||||
dispatcherStatsData?: API.DispatcherStats.Response;
|
dispatcherStatsData?: API.DispatcherStats.Response;
|
||||||
|
|
||||||
driverStatsName: string;
|
driverStatsName: string;
|
||||||
driverStatsData?: API.DriverStats.Response;
|
driverStatsData?: API.DriverStats.Response;
|
||||||
driverStatsStatus: Status.Data;
|
driverStatsStatus: Status.Data;
|
||||||
|
|
||||||
chosenModalTrainId?: string;
|
chosenModalTrainId?: string;
|
||||||
|
|
||||||
blockScroll: boolean;
|
blockScroll: boolean;
|
||||||
modalLastClickedTarget: EventTarget | null;
|
modalLastClickedTarget: EventTarget | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
$animDuration: 150ms;
|
$animDuration: 95ms;
|
||||||
$animType: ease-in-out;
|
$animType: ease-in-out;
|
||||||
|
|
||||||
// List animation
|
// List animation
|
||||||
@@ -72,7 +72,6 @@ $animType: ease-in-out;
|
|||||||
|
|
||||||
&-enter-from,
|
&-enter-from,
|
||||||
&-leave-to {
|
&-leave-to {
|
||||||
transform: translateY(-25%);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ a.a-button {
|
|||||||
padding: 0.35em 0.75em;
|
padding: 0.35em 0.75em;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 1.5em;
|
width: 1.35em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user