refactor: journal timetable entries

This commit is contained in:
2024-09-03 14:29:59 +02:00
parent 0861d92e4b
commit c93514fdf0
9 changed files with 192 additions and 67 deletions
@@ -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: {
@@ -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;
@@ -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">&gt;</span> <span v-if="i > 0">&gt;</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 }} &gt; </span>
/ {{ pathData.arrival }} &RightArrow;
</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">
&RightArrow; {{ pathData.departure }}&nbsp; &gt; {{ pathData.departure }}&nbsp;
</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: {
+12
View File
@@ -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;
}
} }
+7
View File
@@ -345,3 +345,10 @@ a.a-button {
width: 100%; width: 100%;
} }
} }
.g-separator {
display: block;
width: 100%;
height: 2px;
background-color: #aaa;
}
+1
View File
@@ -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[];