chore(profile): organized fetching data; added link to profile in scenery view

This commit is contained in:
2026-02-07 01:18:28 +01:00
parent b8574f9ea1
commit 1d49de1c6b
5 changed files with 142 additions and 33 deletions
@@ -8,10 +8,7 @@
{{ onlineScenery.dispatcherExp > 1 ? onlineScenery.dispatcherExp : 'L' }}
</span>
<router-link
class="dispatcher-name"
:to="`/journal/dispatchers?search-dispatcher=${onlineScenery.dispatcherName}`"
>
<router-link class="dispatcher-name" :to="`/profile?playerId=${onlineScenery.dispatcherId}`">
<span
class="text--donator"
v-if="apiStore.donatorsData.includes(onlineScenery.dispatcherName)"
+2
View File
@@ -23,6 +23,8 @@
--clr-donator: #f7a4ff;
--clr-success: springgreen;
--no-scroll-padding: 17px;
--max-container-width: 1700px;
+59
View File
@@ -118,6 +118,9 @@ export namespace API {
export namespace PlayerInfo {
export interface Data {
playerName: string | null;
playerId: number | null;
currentActivity: PlayerActivity.Data;
dispatcherStats: DispatcherStats.Data;
dispatcherStatsLastMonth: DispatcherStats.Data;
@@ -456,6 +459,62 @@ export namespace GithubAPI {
}
}
export namespace Td2API {
export namespace UsersInfoByName {
export interface UserStat {
variable: string;
value: number;
position: number;
server_total: number;
server_max: number;
server_min: number;
server_avg: number;
}
export interface Levels {
driver: number;
dispatcher: number;
}
export interface UserGroup {
id_group: number;
group_name: string;
description: string;
online_color: string;
min_posts: number;
max_messages: number;
stars: string;
group_type: number;
hidden: number;
id_parent: number;
}
export interface UserInfo {
id_member: number;
id_group: number;
additional_groups: string;
member_name: string;
karma_bad: number;
karma_good: number;
date_registered: number;
last_login: number;
avatar: number;
lngfile: string;
user_stats: UserStat[];
levels: Levels;
user_groups: UserGroup[];
}
export type Message = UserInfo[];
export interface Response {
success: boolean;
respCode: number;
message: Message;
}
}
}
export namespace Websocket {
export interface Payload {
activeSceneries: API.ActiveSceneries.Response;
+80 -29
View File
@@ -1,19 +1,25 @@
<template>
<div class="profile-view">
<div class="view-container">
<div class="view-container" v-if="playerInfo">
<div class="profile-sidebar">
<div class="player-summary">
<img
src="https://td2.info.pl/index.php?action=dlattach;attach=83477;type=avatar"
v-if="playerTD2Info"
:src="`https://td2.info.pl/index.php?action=dlattach;attach=${playerTD2Info.avatar};type=avatar`"
alt="player image"
width="110"
width="100"
height="100"
@error="(e) => ((e.target as any).src = '/images/default-avatar.jpg')"
/>
<h3>Spythere</h3>
<h3>{{ playerInfo.playerName }}</h3>
<p>12 poziom maszynisty</p>
<p>12 poziom dyżurnego</p>
<p>Ostatnia aktywność: 02.02.2026 (DR)</p>
<p>Stacjosponsor od 01.01.2024</p>
<!-- <p>Stacjosponsor od 01.01.2024</p> -->
</div>
<div class="player-stats">
@@ -159,12 +165,13 @@
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref } from 'vue';
import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useApiStore } from '../store/apiStore';
import { API } from '../typings/api';
import { API, Td2API } from '../typings/api';
import { humanizeDuration } from '../composables/time';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
type JournalEntryType = 'Timetable' | 'Dispatcher' | 'IssuedTimetable';
@@ -178,10 +185,9 @@ const { t } = useI18n();
const apiStore = useApiStore();
const route = useRoute();
const playerId = ref(-1);
const playerName = ref('');
const playerInfo = ref<API.PlayerInfo.Data | null>(null);
const playerJournal = ref<API.PlayerJournal.Data | null>(null);
const playerTD2Info = ref<Td2API.UsersInfoByName.UserInfo | null>(null);
const activeFilterTypes = reactive<Record<JournalEntryType, boolean>>({
Timetable: true,
@@ -189,16 +195,19 @@ const activeFilterTypes = reactive<Record<JournalEntryType, boolean>>({
IssuedTimetable: true
});
onMounted(() => {
playerId.value = Number(route.query.playerId) || -1;
playerName.value = route.query.playerName?.toString() || '';
watch(
computed(() => route.query.playerId),
(v) => {
fetchAllData();
}
);
fetchPlayerInfoData();
fetchPlayerJournal();
onMounted(() => {
fetchAllData();
});
const combinedJournal = computed<JournalEntry[]>(() => {
if (!playerJournal.value) return [];
if (!playerJournal.value || !playerInfo.value) return [];
const list = [
...playerJournal.value.timetables,
@@ -208,7 +217,7 @@ const combinedJournal = computed<JournalEntry[]>(() => {
.reduce<JournalEntry[]>((acc, v) => {
// Timetable or dispatcher type
if ('trainNo' in v) {
const isIssued = v.authorName == playerName.value;
const isIssued = v.authorName == playerInfo.value!.playerName;
if (!isIssued && !activeFilterTypes['Timetable']) return acc;
if (isIssued && !activeFilterTypes['IssuedTimetable']) return acc;
@@ -237,41 +246,83 @@ const combinedJournal = computed<JournalEntry[]>(() => {
return list;
});
async function fetchPlayerInfoData() {
if (!apiStore.client || !playerId.value) return;
async function fetchAllData() {
const playerId = route.query.playerId?.toString();
if (!playerId) return;
const playerInfoResponse = await fetchPlayerInfoData(playerId);
if (!playerInfoResponse || !playerInfoResponse.playerName) return;
const playerTd2InfoResponse = await fetchPlayerTD2Info(playerInfoResponse.playerName);
const playerJournalResponse = await fetchPlayerJournal(playerId);
playerInfo.value = playerInfoResponse;
playerTD2Info.value = playerTd2InfoResponse;
playerJournal.value = playerJournalResponse;
}
async function fetchPlayerInfoData(playerId: string) {
if (!apiStore.client || !playerId) return null;
try {
const response = await apiStore.client.get<API.PlayerInfo.Data>('api/getPlayerInfo', {
params: {
playerId: playerId.value
playerId: playerId
}
});
playerInfo.value = response.data;
if (response.data.playerName == null || response.data.playerId == null) {
return null;
}
return response.data;
} catch (error) {
console.error(error);
}
return null;
}
async function fetchPlayerJournal() {
if (!apiStore.client || !playerId.value) return;
async function fetchPlayerJournal(playerId: string) {
if (!apiStore.client || !playerId) return null;
try {
const response = await apiStore.client.get<API.PlayerJournal.Data>('api/getPlayerJournal', {
params: {
playerId: playerId.value,
playerId: playerId,
countLimit: 15
}
});
playerJournal.value = response.data;
playerName.value =
response.data.timetables.at(0)?.driverName ||
response.data.duties.at(0)?.dispatcherName ||
'';
return response.data;
} catch (error) {
console.error(error);
}
return null;
}
async function fetchPlayerTD2Info(playerName: string) {
if (!apiStore.client || !playerName) return null;
try {
const response = await axios.get<Td2API.UsersInfoByName.Response>('https://api.td2.info.pl', {
params: {
method: 'getUsersInfoByName',
name: playerName
}
});
if (response.data.success && response.data.message.length == 1) {
return response.data.message[0];
}
} catch (error) {
console.error(error);
}
return null;
}
function toggleFilter(filterType: JournalEntryType) {
@@ -387,7 +438,7 @@ $tileColor: #181818;
}
&[data-active='true'] {
color: var(--clr-primary);
color: var(--clr-success);
}
}