chore(profile): added combined journal with timetables and dispatchers; added journal filters

This commit is contained in:
2026-02-06 01:49:18 +01:00
parent ccca1c8752
commit 40a0b47984
2 changed files with 92 additions and 8 deletions
+2
View File
@@ -41,6 +41,8 @@ export namespace API {
export interface Data { export interface Data {
id: number; id: number;
createdAt: string;
updatedAt: string;
currentDuration: number; currentDuration: number;
dispatcherId: number; dispatcherId: number;
dispatcherName: string; dispatcherName: string;
+90 -8
View File
@@ -93,14 +93,18 @@
<h3 class="main-header">OSTATNIA AKTYWNOŚĆ GRACZA</h3> <h3 class="main-header">OSTATNIA AKTYWNOŚĆ GRACZA</h3>
<div class="history-menu"> <div class="history-menu">
<button class="history-menu-button">ROZKŁADY JAZDY</button> <button class="history-menu-button" @click="toggleFilter('Timetable')">
<button class="history-menu-button">SŁUŻBY DR</button> ROZKŁADY JAZDY
<button class="history-menu-button">WYSTAWIONE RJ</button> </button>
<button class="history-menu-button" @click="toggleFilter('Dispatcher')">SŁUŻBY DR</button>
<button class="history-menu-button" @click="toggleFilter('IssuedTimetable')">
WYSTAWIONE RJ
</button>
</div> </div>
<div class="history-list-box"> <div class="history-list-box">
<ul> <ul>
<li></li> <li v-for="entry in combinedJournal">{{ entry.type }} - {{ entry.date }}</li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -109,22 +113,77 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; 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';
type JournalEntryType = 'Timetable' | 'Dispatcher' | 'IssuedTimetable';
interface JournalEntry {
type: JournalEntryType;
date: Date;
value: API.TimetableHistory.DataShort | API.DispatcherHistory.Data;
}
const apiStore = useApiStore(); const apiStore = useApiStore();
const route = useRoute(); const route = useRoute();
const playerName = ref('');
const playerInfo = ref<API.PlayerInfo.Data | null>(null); const playerInfo = ref<API.PlayerInfo.Data | null>(null);
const playerJournal = ref<API.PlayerJournal.Data | null>(null); const playerJournal = ref<API.PlayerJournal.Data | null>(null);
const activeFilterTypes = reactive<Record<JournalEntryType, boolean>>({
Timetable: true,
Dispatcher: true,
IssuedTimetable: true
});
onMounted(() => { onMounted(() => {
fetchPlayerInfoData(); fetchPlayerInfoData();
fetchPlayerJournal(); fetchPlayerJournal();
}); });
const combinedJournal = computed<JournalEntry[]>(() => {
if (!playerJournal.value) return [];
const list = [
...playerJournal.value.timetables,
...playerJournal.value.duties,
...playerJournal.value.issuedTimetables
]
.reduce<JournalEntry[]>((acc, v) => {
// Timetable or dispatcher type
if ('trainNo' in v) {
const isIssued = v.authorName == playerName.value;
if (!isIssued && !activeFilterTypes['Timetable']) return acc;
if (isIssued && !activeFilterTypes['IssuedTimetable']) return acc;
acc.push({
date: new Date(v.createdAt),
type: isIssued ? 'IssuedTimetable' : 'Timetable',
value: v
});
} else {
if (!activeFilterTypes['Dispatcher']) return acc;
acc.push({
date: new Date(v.timestampFrom),
type: 'Dispatcher',
value: v
});
}
return acc;
}, [])
.sort((a, b) => {
return a.date.getTime() - b.date.getTime() > 0 ? -1 : 1;
});
return list;
});
async function fetchPlayerInfoData() { async function fetchPlayerInfoData() {
const playerId = route.params.id.toString(); const playerId = route.params.id.toString();
@@ -156,10 +215,28 @@ async function fetchPlayerJournal() {
}); });
playerJournal.value = response.data; playerJournal.value = response.data;
playerName.value =
response.data.timetables.at(0)?.driverName ||
response.data.duties.at(0)?.dispatcherName ||
'';
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} }
function toggleFilter(filterType: JournalEntryType) {
const toggledState = !activeFilterTypes[filterType];
// Prevent switching off all filters at the same time (at least one must be active)
if (
toggledState === false &&
Object.values(activeFilterTypes).filter((v) => v === false).length ==
Object.values(activeFilterTypes).length - 1
)
return;
activeFilterTypes[filterType] = toggledState;
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -182,7 +259,7 @@ $tileColor: #181818;
max-width: var(--max-container-width); max-width: var(--max-container-width);
width: 100%; width: 100%;
height: calc(100vh - 0.5em); // height: calc(100vh - 0.5em);
min-height: 900px; min-height: 900px;
padding: 1rem 0; padding: 1rem 0;
@@ -191,7 +268,6 @@ $tileColor: #181818;
.view-container > div { .view-container > div {
position: relative; position: relative;
overflow: auto;
// border-radius: 0.5em; // border-radius: 0.5em;
} }
@@ -220,7 +296,7 @@ $tileColor: #181818;
} }
.profile-main { .profile-main {
max-height: 2000px; overflow: hidden;
} }
.main-header { .main-header {
@@ -255,6 +331,12 @@ $tileColor: #181818;
} }
} }
.history-list-box {
overflow: auto;
height: 650px;
margin-top: 1em;
}
@include responsive.midScreen { @include responsive.midScreen {
.view-container { .view-container {
grid-template-columns: 1fr; grid-template-columns: 1fr;