chore(profile): journal history list design

This commit is contained in:
2026-02-06 03:20:25 +01:00
parent 40a0b47984
commit 565b0dfd8c
2 changed files with 100 additions and 6 deletions
+31
View File
@@ -0,0 +1,31 @@
import { useI18n } from 'vue-i18n';
export function calculateDuration(timestampMs: number) {
const secondsTotal = Math.floor(timestampMs / 1000);
const minsTotal = Math.round(timestampMs / 60000);
const hoursTotal = Math.floor(minsTotal / 60);
const minsInHour = minsTotal % 60;
return {
secondsTotal,
minsTotal,
hoursTotal,
minsInHour
};
}
export function humanizeDuration(timestampMs: number, showSeconds = false) {
const { t } = useI18n();
const duration = calculateDuration(timestampMs);
return duration.minsTotal >= 60
? `${t('journal.hours', { value: duration.hoursTotal }, duration.hoursTotal)} ${t(
'journal.minutes',
{ value: duration.minsInHour },
duration.minsInHour
)}`
: showSeconds && duration.secondsTotal <= 60
? t('journal.seconds', { value: duration.secondsTotal }, duration.secondsTotal)
: t('journal.minutes', { value: duration.minsTotal }, duration.minsTotal);
}
+69 -6
View File
@@ -34,7 +34,7 @@
</div> </div>
<div class="stats-dispatcher"> <div class="stats-dispatcher">
<img src="/images/icon-user.svg" width="35" alt="clock icon" /> <img src="/images/icon-user.svg" width="35" alt="user icon" />
<h3>STATYSTYKI DYŻURNEGO RUCHU</h3> <h3>STATYSTYKI DYŻURNEGO RUCHU</h3>
<hr /> <hr />
@@ -62,7 +62,7 @@
</div> </div>
<div class="month-stat"> <div class="month-stat">
<div><img src="/images/icon-spawn.svg" width="30" alt="train icon" /></div> <div><img src="/images/icon-spawn.svg" width="30" alt="spawn icon" /></div>
<div><h3 class="text--primary">5500</h3></div> <div><h3 class="text--primary">5500</h3></div>
<div> <div>
POKONANYCH <br /> POKONANYCH <br />
@@ -71,7 +71,7 @@
</div> </div>
<div class="month-stat"> <div class="month-stat">
<div><img src="/images/icon-user.svg" width="30" alt="train icon" /></div> <div><img src="/images/icon-user.svg" width="30" alt="user icon" /></div>
<div><h3 class="text--primary">15</h3></div> <div><h3 class="text--primary">15</h3></div>
<div> <div>
SŁUŻB <br /> SŁUŻB <br />
@@ -80,7 +80,7 @@
</div> </div>
<div class="month-stat"> <div class="month-stat">
<div><img src="/images/icon-timetable.svg" width="30" alt="train icon" /></div> <div><img src="/images/icon-timetable.svg" width="30" alt="timetable icon" /></div>
<div><h3 class="text--primary">12</h3></div> <div><h3 class="text--primary">12</h3></div>
<div> <div>
WYSTAWIONYCH <br /> WYSTAWIONYCH <br />
@@ -104,7 +104,54 @@
<div class="history-list-box"> <div class="history-list-box">
<ul> <ul>
<li v-for="entry in combinedJournal">{{ entry.type }} - {{ entry.date }}</li> <li v-for="entry in combinedJournal">
<img
v-if="entry.type == 'Dispatcher'"
src="/images/icon-user.svg"
width="20"
alt="user icon"
/>
<img
v-else-if="entry.type == 'Timetable'"
src="/images/icon-train.svg"
width="20"
alt="train icon"
/>
<img v-else src="/images/icon-timetable.svg" width="20" alt="timetable icon" />
<b class="text--grayed">
{{
entry.date.toLocaleString('pl-PL', { dateStyle: 'long', timeStyle: 'short' })
}}
</b>
<!-- Timetables -->
<span v-if="'trainNo' in entry.value">
<b class="text--primary">
{{ entry.value.trainCategoryCode }} {{ entry.value.trainNo }}
</b>
<b class="text--grayed" v-if="entry.type == 'IssuedTimetable'">
dla: {{ entry.value.driverName }}
</b>
{{ ' ' }}
<b>{{ entry.value.route.replace('|', ' > ') }}</b>
{{ ' ' }}
<b>({{ entry.value.currentDistance }} / {{ entry.value.routeDistance }}km) </b>
</span>
<!-- Dispatchers -->
<span v-else>
<b>{{ entry.value.stationName }}</b>
{{ ' - ' }}
<b>{{
humanizeDuration(
(entry.value.timestampTo || Date.now()) - entry.value.timestampFrom
)
}}</b>
</span>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -117,6 +164,8 @@ import { computed, onMounted, reactive, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useApiStore } from '../store/apiStore'; import { useApiStore } from '../store/apiStore';
import { API } from '../typings/api'; import { API } from '../typings/api';
import { useI18n } from 'vue-i18n';
import { humanizeDuration } from '../composables/time';
type JournalEntryType = 'Timetable' | 'Dispatcher' | 'IssuedTimetable'; type JournalEntryType = 'Timetable' | 'Dispatcher' | 'IssuedTimetable';
@@ -128,6 +177,7 @@ interface JournalEntry {
const apiStore = useApiStore(); const apiStore = useApiStore();
const route = useRoute(); const route = useRoute();
const { t } = useI18n();
const playerName = ref(''); const playerName = ref('');
const playerInfo = ref<API.PlayerInfo.Data | null>(null); const playerInfo = ref<API.PlayerInfo.Data | null>(null);
@@ -210,7 +260,8 @@ async function fetchPlayerJournal() {
try { try {
const response = await apiStore.client.get<API.PlayerJournal.Data>('api/getPlayerJournal', { const response = await apiStore.client.get<API.PlayerJournal.Data>('api/getPlayerJournal', {
params: { params: {
playerId: playerId playerId: playerId,
countLimit: 15
} }
}); });
@@ -335,6 +386,18 @@ $tileColor: #181818;
overflow: auto; overflow: auto;
height: 650px; height: 650px;
margin-top: 1em; margin-top: 1em;
& > ul > li {
display: flex;
align-items: center;
gap: 0.25em;
background-color: $tileColor;
padding: 0.5em;
margin-bottom: 0.5em;
text-align: initial;
}
} }
@include responsive.midScreen { @include responsive.midScreen {