Dodano historię dyżurów do widoku scenerii

This commit is contained in:
2022-01-10 03:12:08 +01:00
parent 9c66afc9de
commit 53842ec3be
5 changed files with 184 additions and 76 deletions
@@ -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;
+117 -15
View File
@@ -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
&nbsp; @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 -6
View File
@@ -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
+58 -47
View File
@@ -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> -->
<div v-if="viewMode == 'info'">
<button v-if="!timetableOnly" class="history-btn btn btn--image" @click="setCardViewMode('history')" title="Widok historii dyżurnych ruchu">
<img :src="icons.history" alt="History icon" />
</button>
<SceneryInfo :station="stationInfo" :timetableOnly="timetableOnly" /> <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>
<SceneryTimetable
:station="stationInfo"
:timetableOnly="timetableOnly"
/>
<!-- <SceneryHistory :name="stationInfo.name" /> -->
</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>