Compare commits

..

12 Commits

Author SHA1 Message Date
Spythere 41e60bc69e Merge do wersji 1.16.3 2023-07-06 01:54:21 +02:00
Spythere 933bdecb3c bump: 1.16.3 2023-07-02 14:50:55 +02:00
Spythere 10e183d96b zamiana infinite scrolla na przyciski 2023-07-02 14:50:18 +02:00
Spythere 5429d39f5e tłumaczenia historii dr scenerii 2023-07-01 23:39:08 +02:00
Spythere ff31e7f903 bump: 1.16.2.1 2023-07-01 23:29:48 +02:00
Spythere 91f4c6bc57 Merge do wersji 1.16.2 2023-07-01 01:25:10 +02:00
Spythere c133eb060b bump: 1.16.2 2023-07-01 01:20:04 +02:00
Spythere 7ffc169d8a hotfix 2023-07-01 01:19:50 +02:00
Spythere 1b85cc5f58 poprawki dziennika rj / dr 2023-06-30 20:50:03 +02:00
Spythere 72ff857fff dodatkowe info o postojach w dzienniku RJ 2023-06-27 02:52:41 +02:00
Spythere 96d64e77fc feature: nieskończona lista historii dr/rj scenerii 2023-06-24 13:46:37 +02:00
Spythere 6ceae3f161 revamp tabeli historii dyżurów 2023-06-23 14:19:28 +02:00
18 changed files with 433 additions and 274 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "stacjownik",
"version": "1.16.1",
"version": "1.16.3",
"private": true,
"scripts": {
"dev": "vite",
+1 -1
View File
@@ -49,7 +49,7 @@ let data = reactive({
{
name: 'driver',
titlePath: 'journal.driver-stats-title',
inactive: true,
// inactive: true,
},
] as { name: TStatTab; titlePath: string; inactive?: boolean }[],
});
@@ -1,10 +1,10 @@
<template>
<transition-group class="journal-list" tag="ul" name="list-anim">
<li
v-for="{ timetable, sceneryList, stockHistoryComp, ...item } in computedTimetableHistory"
v-for="{ timetable, stockHistoryComp, stops, showExtraInfo, ...item } in computedTimetableHistory"
class="journal_item"
:key="timetable.id"
@click="item.showExtra.value = !item.showExtra.value"
@click="showExtraInfo.value = !showExtraInfo.value"
>
<div class="journal_item-info">
<div class="info-general">
@@ -43,7 +43,7 @@
<span class="general-time">
<b class="info-date">{{ localeDay(timetable.beginDate, $i18n.locale) }}</b>
<b
class="info-status"
class="info-badge"
:class="{
fulfilled: timetable.fulfilled,
terminated: timetable.terminated && !timetable.fulfilled,
@@ -67,45 +67,28 @@
<hr />
<div class="scenery-list">
<!-- Spis postojów -->
<div class="stop-list">
<span
v-for="(scenery, i) in sceneryList.filter((_, i) =>
!item.showExtra.value ? i == 0 || i == sceneryList.length - 1 : true
)"
:key="scenery.name"
:class="{ confirmed: scenery.confirmed }"
v-for="(stop, i) in stops.filter((_, i) => (!showExtraInfo.value ? i == 0 || i == stops.length - 1 : true))"
class="stop-list-item"
:key="stop.stopName"
:data-confirmed="stop.confirmed"
>
<span v-if="i > 0">
&gt;
<span v-if="!item.showExtra.value && i == 1 && sceneryList.length > 2">
... (+{{ sceneryList.length - 2 }}) &gt;
<span v-if="!showExtraInfo.value && i == 1 && stops.length > 2">
... (+{{ stops.length - 2 }}) &gt;
</span>
</span>
{{ scenery.name }}
<!-- Data odjazdu ze stacji początkowej -->
<span v-if="i == 0" v-html="scenery.beginDateHTML"></span>
<!-- Data przyjazdu do stacji końcowej -->
<span
v-else-if="i == sceneryList.length - 1 || (i == 1 && !item.showExtra.value)"
v-html="scenery.endDateHTML"
></span>
<!-- Data przyjazdu i odjazdu ze stacji pośredniej -->
<span v-if="item.showExtra.value && i > 0 && i < sceneryList.length - 1">
<span v-if="timetable.checkpointArrivals && i < timetable.checkpointArrivals.length">
&lpar;p. {{ localeTime(timetable.checkpointArrivals[i], $i18n.locale)
}}<span v-if="timetable.checkpointDepartures && i < timetable.checkpointDepartures.length">
/ o. {{ localeTime(timetable.checkpointDepartures[i], $i18n.locale) }}</span
>&rpar;
</span>
</span>
<span class="stop-name">{{ stop.stopName }}</span>
<span v-html="stop.html"></span>
</span>
</div>
<!-- Status RJ -->
<div style="margin: 0.5em 0">
<div class="info-status" style="margin: 0.5em 0">
<span>
<b>{{ $t('journal.route-length') }}</b>
{{ !timetable.fulfilled ? timetable.currentDistance + ' /' : '' }}
@@ -122,12 +105,24 @@
<b>
{{ $t(`journal.${timetable.terminated ? 'last-seen-at' : 'currently-at'}`) }}
{{ timetable.currentSceneryName.replace(/.[a-zA-Z0-9]+.sc/, '') }}
<span v-if="timetable.currentLocation[0] || timetable.currentLocation[1]">&lpar;</span>
<span v-if="timetable.currentLocation[1]">
{{ $t('journal.timetable-location-route') }} {{ timetable.currentLocation[1] }}
</span>
<span v-else-if="timetable.currentLocation[0]">
{{ $t('journal.timetable-location-signal') }} {{ timetable.currentLocation[0] }}
</span>
<span v-if="timetable.currentLocation[0] || timetable.currentLocation[1]">&rpar;</span>
</b>
</span>
</div>
<!-- Nick dyżurnego -->
<div v-if="timetable.authorName">
<!-- Info o autorze RJ -->
<div class="info-author" v-if="timetable.authorName">
<b class="text--grayed">{{ $t('journal.dispatcher-name') }}&nbsp;</b>
<router-link class="dispatcher-link" :to="`/journal/dispatchers?dispatcherName=${timetable.authorName}`">
<b>{{ timetable.authorName }}</b>
@@ -144,11 +139,11 @@
<button class="btn--option btn--show">
{{ $t('journal.stock-info') }}
<img :src="getIcon(`arrow-${item.showExtra.value ? 'asc' : 'desc'}`)" alt="Arrow" />
<img :src="getIcon(`arrow-${showExtraInfo.value ? 'asc' : 'desc'}`)" alt="Arrow" />
</button>
<!-- Dodatkowe informacje -->
<div class="info-extended" v-if="timetable.stockString && timetable.stockMass && item.showExtra.value">
<div class="info-extended" v-if="timetable.stockString && timetable.stockMass && showExtraInfo.value">
<hr />
<div class="stock-specs">
@@ -180,6 +175,7 @@
</span>
</div>
<!-- Historia zmian w składzie -->
<div class="stock-history" v-if="stockHistoryComp.length > 1">
<button
class="btn--action"
@@ -235,7 +231,6 @@ export default defineComponent({
computedTimetableHistory() {
return this.timetableHistory.map((timetable) => ({
timetable,
sceneryList: this.getSceneryList(timetable),
stockHistoryComp: timetable.stockHistory
.slice()
.reverse()
@@ -252,38 +247,64 @@ export default defineComponent({
stockLength: Number(historyData[3]) || undefined,
};
}),
showExtra: ref(false),
showExtraInfo: ref(false),
stops: this.getTimetableStops(timetable),
currentHistoryIndex: ref(0),
}));
},
},
methods: {
getSceneryList(timetable: TimetableHistory) {
return timetable.sceneriesString.split('%').map((name, i) => {
const beginDateHTML =
' (o. ' +
(timetable.beginDate != timetable.scheduledBeginDate
? `<s class='text--grayed'>${this.localeTime(timetable.beginDate, this.$i18n.locale)}</s> `
: '') +
`<span>${this.localeTime(timetable.scheduledBeginDate, this.$i18n.locale)}</span>)`;
getTimetableStops(timetable: TimetableHistory) {
const stopNames = timetable.sceneriesString.split('%');
const endDateHTML =
' (p. ' +
(timetable.endDate != timetable.scheduledEndDate && timetable.fulfilled
? `<s class='text--grayed'>${this.localeTime(
timetable.fulfilled ? timetable.endDate : timetable.scheduledEndDate,
this.$i18n.locale
)}</s> `
: '') +
`<span>${this.localeTime(
timetable.fulfilled || (timetable.terminated && !timetable.fulfilled)
? timetable.scheduledEndDate
: timetable.endDate,
this.$i18n.locale
)}</span>)`;
const beginDateHTML = ` (o. ${
timetable.beginDate != timetable.scheduledBeginDate
? `<s class="text--grayed">${this.localeTime(timetable.beginDate, this.$i18n.locale)}</s>`
: ''
} <span>${this.localeTime(timetable.scheduledBeginDate, this.$i18n.locale)}</span>)`;
return { name, confirmed: i < timetable.confirmedStopsCount, beginDateHTML, endDateHTML };
const endDateHTML = ` (p. ${
timetable.endDate != timetable.scheduledEndDate && timetable.fulfilled
? `<s class="text--grayed">${this.localeTime(timetable.endDate, this.$i18n.locale)}</s>`
: ''
} <span>${this.localeTime(timetable.scheduledEndDate, this.$i18n.locale)}</span>)`;
return stopNames.map((stopName, i) => {
const confirmed = i < timetable.confirmedStopsCount;
if (i == 0) return { stopName, html: beginDateHTML, confirmed };
if (i == stopNames.length - 1) return { stopName, html: endDateHTML, confirmed };
const departureDateScheduled = this.stringToDate(timetable.checkpointDeparturesScheduled?.at(i));
const departureDateReal = this.stringToDate(timetable.checkpointDepartures?.at(i));
const arrivalDateScheduled = this.stringToDate(timetable.checkpointArrivalsScheduled?.at(i));
const arrivalDateReal = this.stringToDate(timetable.checkpointArrivals?.at(i));
// const arrivalDelay =
// arrivalDateReal && arrivalDateScheduled ? arrivalDateReal.getTime() - arrivalDateScheduled.getTime() : 0;
// const departureDelay =
// departureDateReal && departureDateScheduled
// ? departureDateReal.getTime() - departureDateScheduled.getTime()
// : 0;
const arrivalHTML =
(arrivalDateReal && arrivalDateScheduled && arrivalDateReal?.getTime() != arrivalDateScheduled?.getTime()
? `<s class="text--grayed">${this.parseDateToTimeString(arrivalDateScheduled)}</s> `
: '') + this.parseDateToTimeString(arrivalDateReal || arrivalDateScheduled);
const departureHTML =
(departureDateReal &&
departureDateScheduled &&
departureDateReal?.getTime() != departureDateScheduled?.getTime()
? `<s class="text--grayed">${this.parseDateToTimeString(departureDateScheduled)}</s> `
: '') + this.parseDateToTimeString(departureDateReal || departureDateScheduled);
let html = `${arrivalHTML}${departureHTML ? ` / ${departureHTML}` : ''}`;
if (html) html = ` (${html})`;
return { stopName, html, confirmed };
});
},
@@ -322,7 +343,7 @@ hr {
margin-right: 0.5em;
}
&-status {
&-badge {
padding: 0.05em 0.35em;
color: black;
@@ -418,10 +439,19 @@ ul.stock-list {
}
}
.scenery-list {
.stop-list {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
color: #adadad;
span.confirmed {
&-item[data-confirmed='true'] {
color: #a3eba3;
.stop-name {
font-weight: bold;
}
}
}
@@ -437,17 +467,10 @@ ul.stock-list {
}
@include smallScreen {
.info-general {
flex-direction: column;
}
.info-extended {
.journal_item-info {
text-align: center;
}
.general-train {
justify-content: center;
}
.info-route {
display: flex;
justify-content: center;
@@ -457,10 +480,9 @@ ul.stock-list {
margin: 1em auto 0 auto;
}
.stock-specs {
justify-content: center;
}
.info-general,
.general-train,
.stock-specs,
.stock-history {
justify-content: center;
}
@@ -1,39 +1,61 @@
<template>
<section class="scenery-dispatchers-history scenery-section">
<Loading v-if="dataStatus != 2" />
<section class="scenery-table-section">
<Loading v-if="dataStatus != DataStatus.Loaded && historyList.length == 0" />
<div class="no-history" v-else-if="historyList.length == 0">{{ $t('scenery.history-list-empty') }}</div>
<div class="list-warning" v-else-if="dispatcherHistoryList.length == 0">{{ $t('scenery.history-list-empty') }}</div>
<table class="scenery-history-table" v-else="historyList.length">
<thead>
<th>{{ $t('scenery.dispatchers-history-hash') }}</th>
<th>{{ $t('scenery.dispatchers-history-dispatcher') }}</th>
<th>{{ $t('scenery.dispatchers-history-level') }}</th>
<th>{{ $t('scenery.dispatchers-history-rate') }}</th>
<th>{{ $t('scenery.dispatchers-history-date') }}</th>
</thead>
<ul class="history-list" v-else>
<li class="list-item" v-for="item in dispatcherHistoryList">
<router-link class="item-general" :to="`/journal/dispatchers?dispatcherName=${item.dispatcherName}`">
<span class="text--grayed">#{{ item.stationHash }}&nbsp;</span>
<b
v-if="item.dispatcherLevel !== null"
class="level-badge dispatcher"
:style="calculateExpStyle(item.dispatcherLevel, item.dispatcherIsSupporter)"
>
{{ item.dispatcherLevel >= 2 ? item.dispatcherLevel : 'L' }}
</b>
<tbody>
<tr v-for="historyItem in historyList">
<td>#{{ historyItem.stationHash }}</td>
<td>
<router-link :to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`">
<b>{{ historyItem.dispatcherName }}</b>
</router-link>
</td>
<td>
<b
v-if="historyItem.dispatcherLevel !== null"
class="level-badge dispatcher"
:style="calculateExpStyle(historyItem.dispatcherLevel, historyItem.dispatcherIsSupporter)"
>
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
</b>
</td>
<td class="text--primary">
<b>{{ historyItem.dispatcherRate }}</b>
</td>
<td style="min-width: 300px">
<div v-if="historyItem.timestampTo">
<b>{{ $d(historyItem.timestampFrom) }}</b>
<b>{{ item.dispatcherName }}</b>
</router-link>
{{ timestampToString(historyItem.timestampFrom) }}
- {{ timestampToString(historyItem.timestampTo) }} ({{ calculateDuration(historyItem.currentDuration) }})
</div>
<div v-if="item.timestampTo">
<b>{{ $d(item.timestampFrom) }}</b>
{{ timestampToString(item.timestampFrom) }}
- {{ timestampToString(item.timestampTo) }} ({{ calculateDuration(item.currentDuration) }})
</div>
<div class="dispatcher-online" v-else>
{{ $t('journal.online-since') }}
<b>{{ timestampToString(item.timestampFrom) }}</b>
({{ calculateDuration(item.currentDuration) }})
</div>
</li>
</ul>
<div class="dispatcher-online" v-else>
{{ $t('journal.online-since') }}
<b>{{ timestampToString(historyItem.timestampFrom) }}</b>
({{ calculateDuration(historyItem.currentDuration) }})
</div>
</td>
</tr>
</tbody>
</table>
</section>
<div class="bottom-info">
<button class="btn btn--option" v-if="historyList.length > 0" @click="navigateToHistory">
{{ $t('scenery.bottom-info') }}
</button>
</div>
</template>
<script lang="ts">
@@ -46,37 +68,52 @@ import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue';
import styleMixin from '../../mixins/styleMixin';
import listObserverMixin from '../../mixins/listObserverMixin';
export default defineComponent({
name: 'SceneryDispatchersHistory',
mixins: [dateMixin, styleMixin],
mixins: [dateMixin, styleMixin, listObserverMixin],
props: {
station: {
type: Object as PropType<Station>,
required: true,
},
},
data() {
return {
dispatcherHistoryList: [] as DispatcherHistory[],
historyList: [] as DispatcherHistory[],
dataStatus: DataStatus.Loading,
DataStatus,
};
},
activated() {
this.fetchAPIData();
async activated() {
// if (this.historyList.length == 0) {
const fetchedHistory = await this.fetchAPIData();
if (fetchedHistory) this.historyList = fetchedHistory;
// }
},
methods: {
async fetchAPIData(countFrom = 0, countLimit = 30) {
async fetchAPIData(countFrom = 0, countLimit = 30): Promise<DispatcherHistory[] | null> {
try {
this.dataStatus = DataStatus.Loading;
const requestString = `${URLs.stacjownikAPI}/api/getDispatchers?stationName=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
const historyAPIData: DispatcherHistory[] = await (await axios.get(requestString)).data;
this.dispatcherHistoryList = historyAPIData;
this.dataStatus = DataStatus.Loaded;
return historyAPIData;
} catch (error) {
this.dataStatus = DataStatus.Error;
console.error(error);
return null;
}
},
navigateToHistory() {
this.$router.push(`/journal/dispatchers?sceneryName=${this.station.name}`);
},
},
components: { Loading },
});
@@ -84,30 +121,10 @@ export default defineComponent({
<style lang="scss" scoped>
@import '../../styles/responsive.scss';
@import '../../styles/SceneryView/styles.scss';
@import '../../styles/sceneryViewTables.scss';
.history-list {
padding: 0 0.5em;
}
.list-item {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
text-align: left;
background-color: #353535;
padding: 0.5em;
margin: 0.5em 0;
line-height: 1.5em;
}
.item-general {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.25em;
.level-badge {
margin: 0 auto;
}
.dispatcher-online {
+7 -1
View File
@@ -4,7 +4,9 @@
{{ station.name }}
</a>
<div class="scenery-abbrev">{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b></div>
<div class="scenery-abbrev">
{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b>
</div>
<div class="scenery-hash" v-if="station.onlineInfo?.hash">#{{ station.onlineInfo.hash }}</div>
</section>
@@ -28,6 +30,10 @@ export default defineComponent({
@import '../../styles/variables.scss';
@import '../../styles/responsive.scss';
.info-header {
margin-top: 1em;
}
.scenery-name {
font-weight: bold;
font-size: 3em;
@@ -1,8 +1,9 @@
<template>
<section class="scenery-timetables-history scenery-section">
<Loading v-if="dataStatus != 2" />
<section class="scenery-table-section">
<Loading v-if="dataStatus != DataStatus.Loaded" />
<div class="no-history" v-else-if="historyList.length == 0">{{ $t('scenery.history-list-empty') }}</div>
<table v-else-if="sceneryHistoryList.length">
<table class="scenery-history-table" v-else>
<thead>
<th>{{ $t('scenery.timetables-history-id') }}</th>
<th>{{ $t('scenery.timetables-history-number') }}</th>
@@ -13,7 +14,7 @@
</thead>
<tbody>
<tr v-for="historyItem in sceneryHistoryList">
<tr v-for="historyItem in historyList">
<td>
<router-link :to="`/journal/timetables?timetableId=${historyItem.id}`">#{{ historyItem.id }}</router-link>
</td>
@@ -26,7 +27,7 @@
<td>
<router-link
v-if="historyItem.authorName"
:to="`/journal/dispatchers?dispatcherName=${historyItem.authorName}`"
:to="`/journal/timetables?authorName=${historyItem.authorName}`"
>{{ historyItem.authorName }}
</router-link>
<i v-else>{{ $t('scenery.timetable-author-unknown') }}</i>
@@ -38,9 +39,13 @@
</tr>
</tbody>
</table>
<div class="list-warning" v-else>{{ $t('scenery.history-list-empty') }}</div>
</section>
<div class="bottom-info">
<button class="btn btn--option" v-if="historyList.length > 0" @click="navigateToHistory()">
{{ $t('scenery.bottom-info') }}
</button>
</div>
</template>
<script lang="ts">
@@ -52,37 +57,48 @@ import { TimetableHistory, SceneryTimetableHistory } from '../../scripts/interfa
import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue';
import listObserverMixin from '../../mixins/listObserverMixin';
export default defineComponent({
name: 'SceneryTimetablesHistory',
mixins: [dateMixin],
mixins: [dateMixin, listObserverMixin],
props: {
station: {
type: Object as PropType<Station>,
required: true,
},
},
data() {
return {
sceneryHistoryList: [] as TimetableHistory[],
historyList: [] as TimetableHistory[],
dataStatus: DataStatus.Loading,
DataStatus,
};
},
activated() {
this.fetchAPIData();
async activated() {
const fetchedHistory = await this.fetchAPIData();
if (fetchedHistory) this.historyList = fetchedHistory.timetables;
},
methods: {
async fetchAPIData(countFrom = 0, countLimit = 15) {
async fetchAPIData(countFrom = 0, countLimit = 15): Promise<SceneryTimetableHistory | null> {
try {
const requestString = `${URLs.stacjownikAPI}/api/getIssuedTimetables?name=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`;
const historyAPIData: SceneryTimetableHistory = await (await axios.get(requestString)).data;
this.sceneryHistoryList = historyAPIData.timetables;
this.dataStatus = DataStatus.Loaded;
return historyAPIData;
} catch (error) {
console.error(error);
return null;
}
},
navigateToHistory() {
this.$router.push(`/journal/timetables?issuedFrom=${this.station.name}`);
},
},
components: { Loading },
});
@@ -90,46 +106,5 @@ export default defineComponent({
<style lang="scss" scoped>
@import '../../styles/responsive.scss';
@import '../../styles/SceneryView/styles.scss';
.list-warning {
padding: 1em 0.5em;
background-color: #444;
font-size: 1.2em;
}
.history-list {
padding: 0 0.5em;
}
table {
width: 100%;
border-collapse: collapse;
thead {
position: sticky;
top: 0;
background-color: #222222;
}
th {
padding: 0.5em;
}
tr {
background-color: #353535;
border: none;
}
td {
padding: 0.75em;
border-bottom: solid 5px #111;
}
}
@include smallScreen {
.list-item {
grid-template-columns: 1fr 1fr;
}
}
@import '../../styles/sceneryViewTables.scss';
</style>
+18 -4
View File
@@ -274,6 +274,7 @@
"title": "DISPATCHER HISTORY",
"loading": "Loading dispatcher history data...",
"no-history": "No dispatcher history found!",
"data-refreshed-at": "Data refreshed at",
"section-timetables": "TIMETABLES",
"section-dispatchers": "DISPATCHERS",
@@ -291,8 +292,10 @@
"online-since": "ONLINE SINCE",
"duty-lasted": "The duty lasted",
"minutes": "{minutes} mins",
"hours": "{hours}h {minutes} mins",
"hours": "{value} hour | {value} hours",
"minutes": "{value} min | {value} mins",
"seconds": "{value} s",
"stock-info": "EXTRA INFO",
"stock-length": "Length",
@@ -329,7 +332,10 @@
"driver-stats-info": "Enter a proper nickname into filters [F] to see user's driving statistics!",
"stats-loading": "Fetching statistics...",
"stats-error": "Oops! An unexpected error occurred while trying to fetch statistics! :/"
"stats-error": "Oops! An unexpected error occurred while trying to fetch statistics! :/",
"timetable-location-signal": "signal:",
"timetable-location-route": "route:"
},
"scenery": {
"users": "PLAYERS ONLINE",
@@ -364,13 +370,21 @@
"timetables-history-author": "TT author",
"timetables-history-date": "Date",
"dispatchers-history-hash": "Hash",
"dispatchers-history-dispatcher": "Dispatcher",
"dispatchers-history-level": "Level",
"dispatchers-history-rate": "Rate",
"dispatchers-history-date": "Service date",
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
"history-list-empty": "No recorded scenery history!",
"forum-topic": "Official {name} forum topic",
"pragotron-link": "Timetable pallet board (beta)",
"tablice-link": "Timetable summary board (by Thundo)"
"tablice-link": "Timetable summary board (by Thundo)",
"bottom-info": "Show full history in the Journal tab"
},
"availability": {
"title": "Availability",
+17 -4
View File
@@ -279,6 +279,7 @@
"title": "HISTORIA DYŻURÓW",
"loading": "Ładowanie historii dyżurów...",
"no-history": "Brak historii dyżurów dla tej scenerii!",
"data-refreshed-at": "Dane odświeżone o",
"section-timetables": "ROZKŁADY JAZDY",
"section-dispatchers": "DYŻURNI",
@@ -288,8 +289,9 @@
"online-since": "ONLINE OD",
"duty-lasted": "Dyżur trwał",
"minutes": "{minutes} min.",
"hours": "{hours} godz. {minutes} min.",
"hours": "{value} godz.",
"minutes": "{value} min.",
"seconds": "{value} sek.",
"route-length": "Kilometraż:",
"station-count": "Stacje:",
@@ -333,7 +335,10 @@
"driver-stats-info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki maszynisty!",
"stats-loading": "Pobieranie statystyk...",
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk! :/"
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk! :/",
"timetable-location-signal": "semafor:",
"timetable-location-route": "szlak:"
},
"scenery": {
"users": "GRACZE ONLINE",
@@ -368,13 +373,21 @@
"timetables-history-author": "Autor RJ",
"timetables-history-date": "Data",
"dispatchers-history-hash": "Hash",
"dispatchers-history-dispatcher": "Dyżurny",
"dispatchers-history-level": "Poziom",
"dispatchers-history-rate": "Ocena",
"dispatchers-history-date": "Data służby",
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
"history-list-empty": "Brak historii dla tej scenerii!",
"forum-topic": "Oficjalny wątek scenerii {name}",
"pragotron-link": "Paletowa tablica informacyjna (beta)",
"tablice-link": "Tablica informacyjna zbiorcza (autorstwa Thundo)"
"tablice-link": "Tablica informacyjna zbiorcza (autorstwa Thundo)",
"bottom-info": "Pokaż pełną historię w zakładce Dziennika"
},
"availability": {
"title": "Dostępność",
+70 -50
View File
@@ -1,50 +1,70 @@
import { defineComponent } from 'vue';
export default defineComponent({
methods: {
localeDate(dateString: string, locale: string) {
return new Date(dateString).toLocaleDateString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
weekday: 'long',
day: 'numeric',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
},
localeDay(dateString: string, locale: string) {
return new Date(dateString).toLocaleDateString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
day: 'numeric',
month: '2-digit',
year: 'numeric',
});
},
localeTime(dateString: string, locale: string) {
return new Date(dateString).toLocaleTimeString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
hour: '2-digit',
minute: '2-digit',
});
},
timestampToString(timestamp: number | null) {
return timestamp
? new Date(timestamp).toLocaleTimeString('pl-PL', {
hour: '2-digit',
minute: '2-digit',
})
: '';
},
calculateDuration(timestampMs: number) {
const minsTotal = Math.round(timestampMs / 60000);
const hoursTotal = Math.floor(minsTotal / 60);
const minsInHour = minsTotal % 60;
return minsTotal > 60
? this.$t('journal.hours', { hours: hoursTotal, minutes: minsInHour })
: this.$t('journal.minutes', { minutes: minsTotal });
},
},
});
import { defineComponent } from 'vue';
export default defineComponent({
methods: {
localeDate(dateString: string, locale: string) {
return new Date(dateString).toLocaleDateString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
weekday: 'long',
day: 'numeric',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
},
localeDay(dateString: string, locale: string) {
return new Date(dateString).toLocaleDateString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
day: 'numeric',
month: '2-digit',
year: 'numeric',
});
},
localeTime(dateString: string, locale: string) {
return new Date(dateString).toLocaleTimeString(locale == 'pl' ? 'pl-PL' : 'en-GB', {
hour: '2-digit',
minute: '2-digit',
});
},
stringToDate(dateString?: string) {
return dateString ? new Date(dateString) : null;
},
parseDateToTimeString(date: Date | null) {
return (
date?.toLocaleTimeString('pl-PL', {
hour: '2-digit',
minute: '2-digit',
}) || ''
);
},
timestampToString(timestamp: number | null) {
return timestamp
? new Date(timestamp).toLocaleTimeString('pl-PL', {
hour: '2-digit',
minute: '2-digit',
})
: '';
},
calculateDuration(timestampMs: number, showSeconds = false) {
const secondsTotal = Math.floor(timestampMs / 1000);
const minsTotal = Math.round(timestampMs / 60000);
const hoursTotal = Math.floor(minsTotal / 60);
const minsInHour = minsTotal % 60;
return minsTotal >= 60
? `${this.$t('journal.hours', { value: hoursTotal }, hoursTotal)} ${this.$t(
'journal.minutes',
{ value: minsInHour },
minsInHour
)}`
: showSeconds && secondsTotal <= 60
? this.$t('journal.seconds', { value: secondsTotal }, secondsTotal)
: this.$t('journal.minutes', { value: minsTotal }, minsTotal);
},
},
});
+26
View File
@@ -0,0 +1,26 @@
import { defineComponent } from 'vue';
export default defineComponent({
data: () => ({
observer: null as IntersectionObserver | null,
observerTarget: null as Element | null,
}),
methods: {
mountObserver(actionFunction: () => void, target: Element) {
this.observer = new IntersectionObserver((entries) => {
console.log(entries);
if (entries[0].intersectionRatio > 0.5) actionFunction();
}, { threshold: 0.2 });
this.observer.observe(target);
},
unmountObserver() {
if (!this.observerTarget) return;
this.observer?.unobserve(this.observerTarget);
},
},
});
@@ -16,6 +16,7 @@ export interface TimetableHistory {
twr: number;
skr: number;
sceneriesString: string;
currentLocation: string[];
routeDistance: number;
currentDistance: number;
@@ -52,6 +53,11 @@ export interface TimetableHistory {
checkpointArrivals?: string[];
checkpointDepartures?: string[];
checkpointArrivalsScheduled?: string[];
checkpointDeparturesScheduled?: string[];
checkpointStopTypes?: string[];
}
export interface SceneryTimetableHistory {
+13
View File
@@ -23,6 +23,15 @@
padding: 1em 0;
}
.journal_refreshed-date {
background-color: #333;
color: #ddd;
text-align: end;
padding: 0.25em;
margin: 0.5em 0;
}
.journal_warning {
text-align: center;
font-size: 1.3em;
@@ -71,6 +80,10 @@
justify-content: center;
flex-wrap: wrap;
}
.journal_refreshed-date {
text-align: center;
}
}
@media (orientation: landscape) {
-11
View File
@@ -1,11 +0,0 @@
.scenery-section {
position: relative;
height: 100%;
overflow-y: scroll;
}
.list-warning {
padding: 1em 0.5em;
background-color: #444;
font-size: 1.2em;
}
-1
View File
@@ -30,7 +30,6 @@
display: flex;
justify-content: center;
align-items: center;
font-size: 0.9em;
&.driver {
border-radius: 50%;
+46
View File
@@ -0,0 +1,46 @@
.scenery-table-section {
position: relative;
height: 100%;
overflow-y: scroll;
}
table.scenery-history-table {
width: 100%;
border-collapse: collapse;
thead {
position: sticky;
top: 0;
background-color: #222222;
}
th {
padding: 0.5em;
}
tr {
background-color: #353535;
border: none;
}
td {
padding: 0.75em;
border-bottom: solid 5px #111;
}
}
.no-history {
padding: 1em 0.5em;
background-color: #444;
font-size: 1.2em;
color: #ccc;
}
.bottom-info {
display: flex;
justify-content: center;
button {
padding: 0.5em;
}
}
+10 -2
View File
@@ -13,6 +13,10 @@
optionsType="dispatchers"
/>
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
</div>
<div class="list_wrapper" @scroll="handleScroll">
<transition name="status-anim" mode="out-in">
<div :key="dataStatus">
@@ -104,6 +108,7 @@ export default defineComponent({
data: () => ({
currentQuery: '',
currentQueryArray: [] as string[],
dataRefreshedAt: null as Date | null,
scrollDataLoaded: true,
scrollNoMoreData: false,
@@ -189,8 +194,10 @@ export default defineComponent({
},
handleQueries(query: LocationQuery) {
if ('sceneryName' in query) this.searchersValues['search-station'] = `${query.sceneryName}`;
if ('dispatcherName' in query) this.searchersValues['search-dispatcher'] = `${query.dispatcherName}`;
const queryKeys = Object.keys(query);
if (queryKeys.includes('sceneryName')) this.setSearchers('', `${query.sceneryName}`, '');
if (queryKeys.includes('dispatcherName')) this.setSearchers('', '', `${query.dispatcherName}`);
},
setSearchers(date: string, station: string, dispatcher: string) {
@@ -273,6 +280,7 @@ export default defineComponent({
? this.historyList[0].dispatcherName
: '';
this.dataRefreshedAt = new Date();
this.dataStatus = DataStatus.Loaded;
} catch (error) {
this.dataStatus = DataStatus.Error;
+11 -1
View File
@@ -16,6 +16,10 @@
<JournalStats />
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
</div>
<div class="list_wrapper" @scroll="handleScroll">
<transition name="status-anim" mode="out-in">
<div :key="dataStatus">
@@ -101,6 +105,7 @@ export default defineComponent({
data: () => ({
currentQueryParams: {} as TimetablesQueryParams,
dataRefreshedAt: null as Date | null,
scrollDataLoaded: true,
scrollNoMoreData: false,
@@ -185,7 +190,11 @@ export default defineComponent({
},
handleQueries(query: LocationQuery) {
if ('timetableId' in query) this.searchersValues['search-train'] = `#${query.timetableId}`;
const queryKeys = Object.keys(query);
if (queryKeys.includes('timetableId')) this.setSearchers('', '', `#${query.timetableId}`, '', '');
if (queryKeys.includes('issuedFrom')) this.setSearchers('', '', '', '', `${query.issuedFrom}`);
if (queryKeys.includes('authorName')) this.setSearchers('', '', '', `${query.authorName}`, '');
},
setSearchers(date: string, driver: string, train: string, dispatcher: string, issuedFrom: string) {
@@ -326,6 +335,7 @@ export default defineComponent({
: '';
this.dataStatus = DataStatus.Loaded;
this.dataRefreshedAt = new Date();
} catch (error) {
this.dataStatus = DataStatus.Error;
this.dataErrorMessage = 'Ups! Coś poszło nie tak!';
+4 -9
View File
@@ -1,4 +1,4 @@
<template>
<template>
<div class="scenery-view">
<div class="scenery-offline" v-if="!stationInfo && isComponentVisible && store.dataStatuses.sceneries == 2">
<div>{{ $t('scenery.no-scenery') }}</div>
@@ -33,12 +33,7 @@
</div>
<keep-alive>
<component
:is="currentViewCompontent"
:station="stationInfo"
:timetableOnly="timetableOnly"
:key="currentViewCompontent"
></component>
<component :is="currentViewCompontent" :station="stationInfo" :key="currentViewCompontent"></component>
</keep-alive>
</div>
</div>
@@ -208,14 +203,14 @@ button.back-btn {
.scenery-right {
background: #181818;
padding: 2em 0.5em;
padding: 1em 0.5em;
height: 95vh;
min-height: 550px;
max-height: 1000px;
display: grid;
grid-template-rows: auto 1fr;
grid-template-rows: auto 1fr auto;
gap: 1em;
}