mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-04 22:08:12 +00:00
Dodano historię dyżurów do widoku scenerii
This commit is contained in:
+3
-4
@@ -31,12 +31,11 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
@import '../../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
|
|
||||||
|
|
||||||
.info-header {
|
.info-header {
|
||||||
padding: 2em 1em;
|
padding-top: 2em;
|
||||||
|
|
||||||
& > .scenery-name {
|
& > .scenery-name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -3,10 +3,29 @@
|
|||||||
<h2>HISTORIA DYŻURÓW</h2>
|
<h2>HISTORIA DYŻURÓW</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(dispatcher, i) in dispatcherHistory" :key="i">
|
<li v-for="(timeline, i) in dispatcherTimeline" :key="i">
|
||||||
{{ dispatcher.dispatcherName }}
|
<h3
|
||||||
|
@click="toggleTimeline(i)"
|
||||||
{{ timestampToString(dispatcher.dispatcherFrom) }}
|
@keydown.enter="toggleTimeline(i)"
|
||||||
|
@keydown.space="toggleTimeline(i)"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
{{ timeline.date }} <img :src="icons.descArrow" alt="" />
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<span v-if="timeline.showTimeline">
|
||||||
|
<div v-for="dispatcher in timeline.dispatchers" :key="dispatcher.dispatcherFrom">
|
||||||
|
<span>
|
||||||
|
<span class="dispatcher-from text--primary">
|
||||||
|
{{ timestampToString(dispatcher.dispatcherFrom, true) }}
|
||||||
|
</span>
|
||||||
|
>
|
||||||
|
<span class="dispatcher-to text--primary"> {{ timestampToString(dispatcher.dispatcherTo, true) }}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<b>{{ dispatcher.dispatcherName }}</b>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,6 +35,12 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
interface DispatcherTimeline {
|
||||||
|
date: string;
|
||||||
|
dispatchers: DispatcherHistory[];
|
||||||
|
showTimeline: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface DispatcherHistory {
|
interface DispatcherHistory {
|
||||||
dispatcherName: string;
|
dispatcherName: string;
|
||||||
dispatcherId: number;
|
dispatcherId: number;
|
||||||
@@ -38,15 +63,17 @@ interface HistoryResultAPI {
|
|||||||
errorMessage?: any;
|
errorMessage?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROD_MODE = true;
|
const API_URL = 'https://stacjownik-api-di22o.ondigitalocean.app/api/getSceneryHistory';
|
||||||
|
|
||||||
const API_URL = PROD_MODE
|
|
||||||
? 'https://stacjownik-api-di22o.ondigitalocean.app/api/getSceneryHistory'
|
|
||||||
: 'http://localhost:3001/api/getTimetables';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data: () => ({
|
data: () => ({
|
||||||
dispatcherHistory: [] as DispatcherHistory[],
|
dispatcherHistory: [] as DispatcherHistory[],
|
||||||
|
dispatcherTimeline: [] as DispatcherTimeline[],
|
||||||
|
|
||||||
|
icons: {
|
||||||
|
ascArrow: require('@/assets/icon-arrow-asc.svg'),
|
||||||
|
descArrow: require('@/assets/icon-arrow-desc.svg'),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
props: {
|
props: {
|
||||||
name: {
|
name: {
|
||||||
@@ -62,18 +89,42 @@ export default defineComponent({
|
|||||||
try {
|
try {
|
||||||
const apiResult: HistoryResultAPI = (await axios.get(`${API_URL}?name=${this.name}`)).data;
|
const apiResult: HistoryResultAPI = (await axios.get(`${API_URL}?name=${this.name}`)).data;
|
||||||
|
|
||||||
if (!apiResult.errorMessage) this.dispatcherHistory = apiResult.result.dispatcherHistory;
|
if (!apiResult.errorMessage) {
|
||||||
|
this.dispatcherHistory = apiResult.result.dispatcherHistory;
|
||||||
|
|
||||||
|
this.dispatcherTimeline = this.dispatcherHistory
|
||||||
|
.reduce((acc, dispatcher) => {
|
||||||
|
const dateStr = new Date(dispatcher.dispatcherFrom).toLocaleDateString('pl-PL').replace(/\./g, '/');
|
||||||
|
|
||||||
|
const timelineDay = acc.find((timeline) => timeline.date == dateStr) || {
|
||||||
|
date: dateStr,
|
||||||
|
dispatchers: [],
|
||||||
|
showTimeline: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
timelineDay.dispatchers.push(dispatcher);
|
||||||
|
|
||||||
|
if (!acc.find((timeline) => timeline.date == dateStr)) acc.push(timelineDay);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] as DispatcherTimeline[])
|
||||||
|
.reverse();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
timestampToString: (timestamp: number): string =>
|
toggleTimeline(index: number) {
|
||||||
|
this.dispatcherTimeline[index].showTimeline = !this.dispatcherTimeline[index].showTimeline;
|
||||||
|
},
|
||||||
|
|
||||||
|
timestampToString: (timestamp: number, timeOnly = false): string =>
|
||||||
new Date(timestamp).toLocaleTimeString('pl-PL', {
|
new Date(timestamp).toLocaleTimeString('pl-PL', {
|
||||||
day: '2-digit',
|
day: timeOnly ? undefined : '2-digit',
|
||||||
month: '2-digit',
|
month: timeOnly ? undefined : '2-digit',
|
||||||
year: '2-digit',
|
year: timeOnly ? undefined : '2-digit',
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
}),
|
}),
|
||||||
@@ -81,4 +132,55 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.scenery-history {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 1em 0;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
background: #444;
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0 auto 0.5em auto;
|
||||||
|
|
||||||
|
max-width: 700px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 1.3em;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
padding: 0.5em 0;
|
||||||
|
margin: 0.5em auto;
|
||||||
|
|
||||||
|
background-color: #444;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="scenery-info">
|
<div class="scenery-info">
|
||||||
<!-- info header -->
|
|
||||||
<scenery-info-header :station="station" />
|
|
||||||
|
|
||||||
<section v-if="!timetableOnly">
|
<section v-if="!timetableOnly">
|
||||||
<!-- info stats -->
|
<!-- info stats -->
|
||||||
<scenery-info-stats :station="station" />
|
<scenery-info-stats :station="station" />
|
||||||
@@ -24,12 +21,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from '@vue/runtime-core';
|
import { defineComponent } from '@vue/runtime-core';
|
||||||
|
|
||||||
import SceneryInfoDispatcher from './SceneryInfo/SceneryInfoDispatcher.vue';
|
import SceneryInfoDispatcher from './SceneryInfo/SceneryInfoDispatcher.vue';
|
||||||
import SceneryInfoIcons from './SceneryInfo/SceneryInfoIcons.vue';
|
import SceneryInfoIcons from './SceneryInfo/SceneryInfoIcons.vue';
|
||||||
import SceneryInfoStats from './SceneryInfo/SceneryInfoStats.vue';
|
import SceneryInfoStats from './SceneryInfo/SceneryInfoStats.vue';
|
||||||
import SceneryInfoHeader from './SceneryInfo/SceneryInfoHeader.vue';
|
|
||||||
import SceneryInfoUserList from './SceneryInfo/SceneryInfoUserList.vue';
|
import SceneryInfoUserList from './SceneryInfo/SceneryInfoUserList.vue';
|
||||||
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
|
import SceneryInfoSpawnList from './SceneryInfo/SceneryInfoSpawnList.vue';
|
||||||
|
|
||||||
@@ -40,7 +36,6 @@ export default defineComponent({
|
|||||||
SceneryInfoDispatcher,
|
SceneryInfoDispatcher,
|
||||||
SceneryInfoIcons,
|
SceneryInfoIcons,
|
||||||
SceneryInfoStats,
|
SceneryInfoStats,
|
||||||
SceneryInfoHeader,
|
|
||||||
SceneryInfoUserList,
|
SceneryInfoUserList,
|
||||||
SceneryInfoSpawnList,
|
SceneryInfoSpawnList,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<span class="stop-name">
|
<span class="stop-name">
|
||||||
<span v-html="stop.stopName"></span>
|
<span v-html="stop.stopName"></span>
|
||||||
<img v-if="stop.comments" :src="icons.warning" :title="`${$t('trains.comment')}: ${stop.comments}`">
|
<img v-if="stop.comments" :src="icons.warning" :title="`${$t('trains.comment')}: ${stop.comments}`">
|
||||||
|
{{ decodeURIComponent(stop.comments) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="stop-date">
|
<span class="stop-date">
|
||||||
<span
|
<span
|
||||||
|
|||||||
+59
-48
@@ -1,80 +1,83 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="scenery-view">
|
<div class="scenery-view">
|
||||||
<div
|
<div class="scenery-offline" v-if="!stationInfo && isDataLoaded && isComponentVisible">
|
||||||
class="scenery-offline"
|
<div>{{ $t('scenery.no-scenery') }}</div>
|
||||||
v-if="!stationInfo && isDataLoaded && isComponentVisible"
|
|
||||||
>
|
|
||||||
<div>{{ $t("scenery.no-scenery") }}</div>
|
|
||||||
|
|
||||||
<action-button>
|
<action-button>
|
||||||
<router-link to="/">{{ $t("scenery.return-btn") }}</router-link>
|
<router-link to="/">{{ $t('scenery.return-btn') }}</router-link>
|
||||||
</action-button>
|
</action-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="scenery-wrapper" v-if="stationInfo">
|
<div class="scenery-wrapper" v-if="stationInfo" ref="card-wrapper">
|
||||||
<!-- <button class="history-btn btn btn--image">
|
<!-- <scenery-info-header :station="stationInfo" /> -->
|
||||||
<img :src="icons.history" alt="History icon">
|
<SceneryHeader :station="stationInfo" />
|
||||||
</button> -->
|
|
||||||
|
|
||||||
<SceneryInfo :station="stationInfo" :timetableOnly="timetableOnly" />
|
|
||||||
|
|
||||||
<SceneryTimetable
|
<div v-if="viewMode == 'info'">
|
||||||
:station="stationInfo"
|
<button v-if="!timetableOnly" class="history-btn btn btn--image" @click="setCardViewMode('history')" title="Widok historii dyżurnych ruchu">
|
||||||
:timetableOnly="timetableOnly"
|
<img :src="icons.history" alt="History icon" />
|
||||||
/>
|
</button>
|
||||||
<!-- <SceneryHistory :name="stationInfo.name" /> -->
|
|
||||||
|
<SceneryInfo :station="stationInfo" :timetableOnly="timetableOnly" />
|
||||||
|
<SceneryTimetable :station="stationInfo" :timetableOnly="timetableOnly" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="viewMode == 'history'">
|
||||||
|
<button class="history-btn btn btn--image" @click="setCardViewMode('info')">
|
||||||
|
<img :src="icons.user" alt="History icon" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<SceneryHistory :name="stationInfo.name" />
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { StoreData } from "@/scripts/interfaces/StoreData";
|
import { StoreData } from '@/scripts/interfaces/StoreData';
|
||||||
import { DataStatus } from "@/scripts/enums/DataStatus";
|
import { DataStatus } from '@/scripts/enums/DataStatus';
|
||||||
|
|
||||||
import SceneryInfo from "@/components/SceneryView/SceneryInfo.vue";
|
import SceneryInfo from '@/components/SceneryView/SceneryInfo.vue';
|
||||||
import SceneryTimetable from "@/components/SceneryView/SceneryTimetable.vue";
|
import SceneryTimetable from '@/components/SceneryView/SceneryTimetable.vue';
|
||||||
import SceneryHistory from "@/components/SceneryView/SceneryHistory.vue"
|
import SceneryHistory from '@/components/SceneryView/SceneryHistory.vue';
|
||||||
|
import SceneryHeader from "@/components/SceneryView/SceneryHeader.vue";
|
||||||
|
|
||||||
import ActionButton from "@/components/Global/ActionButton.vue";
|
import ActionButton from '@/components/Global/ActionButton.vue';
|
||||||
|
|
||||||
import { computed, ComputedRef, defineComponent } from "@vue/runtime-core";
|
import { computed, ComputedRef, defineComponent } from '@vue/runtime-core';
|
||||||
import { useStore } from "@/store";
|
import { useStore } from '@/store';
|
||||||
import { GETTERS } from "@/constants/storeConstants";
|
import { GETTERS } from '@/constants/storeConstants';
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { SceneryInfo, SceneryTimetable, SceneryHistory, ActionButton },
|
components: { SceneryInfo, SceneryTimetable, SceneryHistory, ActionButton, SceneryHeader },
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
icons: {
|
icons: {
|
||||||
history: require("@/assets/icon-history.svg")
|
history: require('@/assets/icon-history.svg'),
|
||||||
}
|
user: require('@/assets/icon-user.svg'),
|
||||||
|
},
|
||||||
|
|
||||||
|
cardHeight: 0,
|
||||||
|
|
||||||
|
viewMode: 'info',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const data: ComputedRef<StoreData> = computed(
|
const data: ComputedRef<StoreData> = computed(() => store.getters[GETTERS.allData]);
|
||||||
() => store.getters[GETTERS.allData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const timetableOnly = computed(() =>
|
const timetableOnly = computed(() => (route.query['timetable_only'] == '1' ? true : false));
|
||||||
route.query["timetable_only"] == "1" ? true : false
|
|
||||||
);
|
|
||||||
|
|
||||||
const isComponentVisible = computed(() => route.path === "/scenery");
|
const isComponentVisible = computed(() => route.path === '/scenery');
|
||||||
|
|
||||||
const isDataLoaded = computed(
|
const isDataLoaded = computed(() => data.value.dataConnectionStatus === DataStatus.Loaded);
|
||||||
() => data.value.dataConnectionStatus === DataStatus.Loaded
|
|
||||||
);
|
|
||||||
|
|
||||||
const stationInfo = computed(() =>
|
const stationInfo = computed(() =>
|
||||||
data.value.stationList.find(
|
data.value.stationList.find((station) => station.name === route.query.station?.toString().replace(/_/g, ' '))
|
||||||
(station) =>
|
|
||||||
station.name ===
|
|
||||||
route.query.station?.toString().replace(/_/g, " ")
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -85,12 +88,22 @@ export default defineComponent({
|
|||||||
stationInfo,
|
stationInfo,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setCardViewMode(mode: string) {
|
||||||
|
this.viewMode = mode;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.cardHeight = (this.$refs['card-wrapper'] as HTMLElement).getBoundingClientRect().height;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../styles/responsive.scss";
|
@import '../styles/responsive.scss';
|
||||||
@import "../styles/variables.scss";
|
@import '../styles/variables.scss';
|
||||||
|
|
||||||
$sceneryBgCol: #333;
|
$sceneryBgCol: #333;
|
||||||
|
|
||||||
@@ -148,7 +161,5 @@ button.history-btn {
|
|||||||
img {
|
img {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user