mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
refactor: journal timetable entries
This commit is contained in:
+6
-2
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
<div class="details-body" v-if="timetable.stockString && timetable.stockMass && showExtraInfo">
|
<div class="details-body" v-if="timetable.stockString && timetable.stockMass && showExtraInfo">
|
||||||
<hr />
|
<hr />
|
||||||
|
<EntryStops :timetable="timetable" />
|
||||||
|
|
||||||
<div class="stock-specs">
|
<div class="stock-specs">
|
||||||
<span class="badge">
|
<span class="badge">
|
||||||
@@ -79,6 +80,8 @@
|
|||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="timetable.twr">TWR: {{ timetable.warningNotes }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -87,9 +90,10 @@ import { PropType, defineComponent } from 'vue';
|
|||||||
import StockList from '../../Global/StockList.vue';
|
import StockList from '../../Global/StockList.vue';
|
||||||
import { API } from '../../../typings/api';
|
import { API } from '../../../typings/api';
|
||||||
import { RouteLocationRaw } from 'vue-router';
|
import { RouteLocationRaw } from 'vue-router';
|
||||||
|
import EntryStops from './EntryStops.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { StockList },
|
components: { StockList, EntryStops },
|
||||||
|
|
||||||
emits: ['toggleExtraInfo'],
|
emits: ['toggleExtraInfo'],
|
||||||
|
|
||||||
@@ -133,7 +137,7 @@ export default defineComponent({
|
|||||||
query: {
|
query: {
|
||||||
trainId: `${this.timetable.driverId}|${this.timetable.trainNo}|eu`
|
trainId: `${this.timetable.driverId}|${this.timetable.trainNo}|eu`
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="item-status" style="margin: 0.5em 0">
|
<div class="entry-status" style="margin: 0.5em 0">
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
:progressPercent="~~((timetable.currentDistance / timetable.routeDistance) * 100)"
|
:progressPercent="~~((timetable.currentDistance / timetable.routeDistance) * 100)"
|
||||||
:progressType="!timetable.fulfilled && timetable.terminated ? 'abandoned' : ''"
|
:progressType="!timetable.fulfilled && timetable.terminated ? 'abandoned' : ''"
|
||||||
@@ -61,7 +61,7 @@ export default defineComponent({
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../../styles/responsive.scss';
|
@import '../../../styles/responsive.scss';
|
||||||
|
|
||||||
.item-status {
|
.entry-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
+11
-21
@@ -1,12 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="timetable-stops">
|
<div class="entry-stops">
|
||||||
<ul class="stop-list">
|
<ul class="stop-list">
|
||||||
<li
|
<li v-for="(stop, i) in timetableStops" :key="stop.stopName">
|
||||||
v-for="(stop, i) in timetableStops.filter((_, i) =>
|
|
||||||
!showExtraInfo ? i == 0 || i == timetableStops.length - 1 : true
|
|
||||||
)"
|
|
||||||
:key="stop.stopName"
|
|
||||||
>
|
|
||||||
<span class="stop-label" :data-confirmed="stop.isConfirmed">
|
<span class="stop-label" :data-confirmed="stop.isConfirmed">
|
||||||
<span v-if="i > 0">></span>
|
<span v-if="i > 0">></span>
|
||||||
|
|
||||||
@@ -75,7 +70,9 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="path-details" v-if="showExtraInfo && timetablePathDetails">
|
<div class="g-separator" />
|
||||||
|
|
||||||
|
<div class="entry-path-details" v-if="timetablePathDetails">
|
||||||
<span
|
<span
|
||||||
v-for="(pathData, i) in timetablePathDetails"
|
v-for="(pathData, i) in timetablePathDetails"
|
||||||
:data-visited="pathData.isVisited"
|
:data-visited="pathData.isVisited"
|
||||||
@@ -83,12 +80,10 @@
|
|||||||
i < timetablePathDetails.length - 1 && timetablePathDetails[i + 1].isVisited
|
i < timetablePathDetails.length - 1 && timetablePathDetails[i + 1].isVisited
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span class="path-arrival" v-if="pathData.arrival">
|
<span class="path-arrival" v-if="pathData.arrival"> / {{ pathData.arrival }} > </span>
|
||||||
/ {{ pathData.arrival }} →
|
|
||||||
</span>
|
|
||||||
<b class="path-scenery">{{ pathData.sceneryName }}</b>
|
<b class="path-scenery">{{ pathData.sceneryName }}</b>
|
||||||
<span class="path-departure" v-if="pathData.departure">
|
<span class="path-departure" v-if="pathData.departure">
|
||||||
→ {{ pathData.departure }}
|
> {{ pathData.departure }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,11 +110,6 @@ export default defineComponent({
|
|||||||
mixins: [dateMixin],
|
mixins: [dateMixin],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
showExtraInfo: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
|
|
||||||
timetable: {
|
timetable: {
|
||||||
type: Object as PropType<API.TimetableHistory.Data>,
|
type: Object as PropType<API.TimetableHistory.Data>,
|
||||||
required: true
|
required: true
|
||||||
@@ -195,7 +185,7 @@ export default defineComponent({
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../../styles/badge.scss';
|
@import '../../../styles/badge.scss';
|
||||||
|
|
||||||
.timetable-stops {
|
.entry-stops {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
@@ -251,11 +241,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.path-details {
|
.entry-path-details {
|
||||||
margin-top: 0.5em;
|
padding: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.path-details > span[data-visited='true'] {
|
.entry-path-details > span[data-visited='true'] {
|
||||||
.path-arrival,
|
.path-arrival,
|
||||||
.path-scenery {
|
.path-scenery {
|
||||||
color: lightgreen;
|
color: lightgreen;
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<li class="timetable-history-entry">
|
||||||
|
<!-- General -->
|
||||||
|
<EntryGeneral :timetable="timetableEntry" />
|
||||||
|
|
||||||
|
<div @click="toggleExtraInfo" style="cursor: pointer">
|
||||||
|
<!-- Route -->
|
||||||
|
<span class="entry-route">
|
||||||
|
<b>{{ timetableEntry.route.replace('|', ' - ') }}</b>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<!-- Status -->
|
||||||
|
<EntryStatus :timetable="timetableEntry" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Extra -->
|
||||||
|
<EntryDetails
|
||||||
|
:timetable="timetableEntry"
|
||||||
|
:show-extra-info="showExtraInfo"
|
||||||
|
@toggle-extra-info="toggleExtraInfo"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from 'vue';
|
||||||
|
import { API } from '../../../typings/api';
|
||||||
|
import trainCategoryMixin from '../../../mixins/trainCategoryMixin';
|
||||||
|
import dateMixin from '../../../mixins/dateMixin';
|
||||||
|
import { useApiStore } from '../../../store/apiStore';
|
||||||
|
import { Journal } from '../typings';
|
||||||
|
|
||||||
|
import styleMixin from '../../../mixins/styleMixin';
|
||||||
|
import EntryGeneral from './EntryGeneral.vue';
|
||||||
|
import EntryStatus from './EntryStatus.vue';
|
||||||
|
import EntryDetails from './EntryDetails.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
timetableEntry: {
|
||||||
|
type: Object as PropType<API.TimetableHistory.Data>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
showExtraInfo: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
components: { EntryDetails, EntryGeneral, EntryStatus },
|
||||||
|
mixins: [trainCategoryMixin, dateMixin, styleMixin],
|
||||||
|
emits: ['toggleShowExtraInfo'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
apiStore: useApiStore()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
timetablePathDetails() {
|
||||||
|
if (!this.timetableEntry.path || this.timetableEntry.path == '') return null;
|
||||||
|
|
||||||
|
return this.timetableEntry.path.split(';').map((pathEl, i) => {
|
||||||
|
const [arrival, name, departure] = pathEl.split(',');
|
||||||
|
const sceneryName = name.split(' ').slice(0, -1).join(' ');
|
||||||
|
const sceneryHash = name.split(' ').pop()?.replace('.sc', '') ?? '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrival,
|
||||||
|
sceneryName,
|
||||||
|
sceneryHash,
|
||||||
|
departure,
|
||||||
|
isVisited: this.timetableEntry.visitedSceneries?.includes(sceneryHash) ?? false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
timetableStops(): Journal.TimetableStopDetails[] {
|
||||||
|
const timetableEntry = this.timetableEntry;
|
||||||
|
|
||||||
|
const stopNames = timetableEntry.sceneriesString.split('%');
|
||||||
|
|
||||||
|
return stopNames.reduce<Journal.TimetableStopDetails[]>((acc, stopName, i, arr) => {
|
||||||
|
const arrivalDate =
|
||||||
|
i == arr.length - 1
|
||||||
|
? (timetableEntry.checkpointArrivals.at(i) ?? timetableEntry.endDate)
|
||||||
|
: timetableEntry.checkpointArrivals.at(i);
|
||||||
|
|
||||||
|
const scheduledArrivalDate =
|
||||||
|
i == arr.length - 1
|
||||||
|
? (timetableEntry.checkpointArrivalsScheduled.at(i) ?? timetableEntry.scheduledEndDate)
|
||||||
|
: timetableEntry.checkpointArrivalsScheduled.at(i);
|
||||||
|
|
||||||
|
const departureDate =
|
||||||
|
i == 0
|
||||||
|
? (timetableEntry.checkpointDepartures.at(i) ?? timetableEntry.beginDate)
|
||||||
|
: timetableEntry.checkpointDepartures.at(i);
|
||||||
|
|
||||||
|
const scheduledDepartureDate =
|
||||||
|
i == 0
|
||||||
|
? (timetableEntry.checkpointDeparturesScheduled.at(i) ??
|
||||||
|
timetableEntry.scheduledBeginDate)
|
||||||
|
: timetableEntry.checkpointDeparturesScheduled.at(i);
|
||||||
|
|
||||||
|
const stopTime = Number(timetableEntry.checkpointStopTypes.at(i)?.split(',')[0]) || 0;
|
||||||
|
const stopType = timetableEntry.checkpointStopTypes.at(i)?.split(',')[1] || '';
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
stopName,
|
||||||
|
arrivalTimestamp: this.dateStringToTimestamp(arrivalDate),
|
||||||
|
scheduledArrivalTimestamp: this.dateStringToTimestamp(scheduledArrivalDate),
|
||||||
|
departureTimestamp: this.dateStringToTimestamp(departureDate),
|
||||||
|
scheduledDepartureTimestamp: this.dateStringToTimestamp(scheduledDepartureDate),
|
||||||
|
stopTime,
|
||||||
|
stopType,
|
||||||
|
isConfirmed: i < timetableEntry.confirmedStopsCount
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleExtraInfo() {
|
||||||
|
this.$emit('toggleShowExtraInfo', this.timetableEntry.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.timetable-history-entry {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -16,37 +16,15 @@
|
|||||||
{{ $t('app.no-result') }}
|
{{ $t('app.no-result') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<ul v-else class="journal-list">
|
||||||
<transition-group name="list-anim" tag="ul" class="journal-list">
|
<transition-group name="list-anim">
|
||||||
<li v-for="timetable in timetableHistory" class="journal_item" :key="timetable.id">
|
<JournalTimetableEntry
|
||||||
<div class="journal_item-info">
|
v-for="timetableEntry in timetableHistory"
|
||||||
<!-- General -->
|
:key="timetableEntry.id"
|
||||||
<TimetableGeneral :timetable="timetable" />
|
:timetableEntry="timetableEntry"
|
||||||
|
:onToggleShowExtraInfo="toggleExtraInfo"
|
||||||
<div @click="toggleExtraInfo(timetable.id)" style="cursor: pointer">
|
:showExtraInfo="extraInfoIndexes.includes(timetableEntry.id)"
|
||||||
<!-- Route -->
|
/>
|
||||||
<span class="item-route">
|
|
||||||
<b>{{ timetable.route.replace('|', ' - ') }}</b>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
<!-- Stops -->
|
|
||||||
<TimetableStops
|
|
||||||
:timetable="timetable"
|
|
||||||
:showExtraInfo="extraInfoIndexes.includes(timetable.id)"
|
|
||||||
/>
|
|
||||||
<!-- Status -->
|
|
||||||
<TimetableStatus :timetable="timetable" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Extra -->
|
|
||||||
<TimetableDetails
|
|
||||||
:timetable="timetable"
|
|
||||||
:showExtraInfo="extraInfoIndexes.includes(timetable.id)"
|
|
||||||
@toggle-extra-info="toggleExtraInfo"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</transition-group>
|
</transition-group>
|
||||||
|
|
||||||
<AddDataButton
|
<AddDataButton
|
||||||
@@ -55,7 +33,7 @@
|
|||||||
:scrollNoMoreData="scrollNoMoreData"
|
:scrollNoMoreData="scrollNoMoreData"
|
||||||
@addHistoryData="addHistoryData"
|
@addHistoryData="addHistoryData"
|
||||||
/>
|
/>
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
@@ -68,28 +46,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, Prop, PropType, ref } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
|
|
||||||
import Loading from '../../Global/Loading.vue';
|
import Loading from '../../Global/Loading.vue';
|
||||||
import AddDataButton from '../../Global/AddDataButton.vue';
|
import AddDataButton from '../../Global/AddDataButton.vue';
|
||||||
|
import JournalTimetableEntry from './JournalTimetableEntry.vue';
|
||||||
|
|
||||||
import { useMainStore } from '../../../store/mainStore';
|
import { useMainStore } from '../../../store/mainStore';
|
||||||
import { Status } from '../../../typings/common';
|
import { Status } from '../../../typings/common';
|
||||||
import { API } from '../../../typings/api';
|
import { API } from '../../../typings/api';
|
||||||
|
|
||||||
import TimetableGeneral from './TimetableGeneral.vue';
|
|
||||||
import TimetableStops from './TimetableStops.vue';
|
|
||||||
import TimetableStatus from './TimetableStatus.vue';
|
|
||||||
import TimetableDetails from './TimetableDetails.vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
Loading,
|
Loading,
|
||||||
AddDataButton,
|
AddDataButton,
|
||||||
TimetableDetails,
|
JournalTimetableEntry
|
||||||
TimetableGeneral,
|
|
||||||
TimetableStatus,
|
|
||||||
TimetableStops
|
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -66,4 +66,16 @@ export namespace Journal {
|
|||||||
iconName: string;
|
iconName: string;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TimetableStopDetails {
|
||||||
|
stopName: string;
|
||||||
|
arrivalTimestamp: number;
|
||||||
|
scheduledArrivalTimestamp: number;
|
||||||
|
departureTimestamp: number;
|
||||||
|
scheduledDepartureTimestamp: number;
|
||||||
|
stopTime: number;
|
||||||
|
stopType: string;
|
||||||
|
isConfirmed: boolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -345,3 +345,10 @@ a.a-button {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.g-separator {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #aaa;
|
||||||
|
}
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ export namespace API {
|
|||||||
visitedSceneries: string[];
|
visitedSceneries: string[];
|
||||||
sceneryNames: string[];
|
sceneryNames: string[];
|
||||||
path: string;
|
path: string;
|
||||||
|
warningNotes: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Response = Data[];
|
export type Response = Data[];
|
||||||
|
|||||||
Reference in New Issue
Block a user