mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4fa7459e39 | |||
| 0602c12914 | |||
| 9d7e70c7e2 | |||
| 88b02b20a5 | |||
| 2b16213531 | |||
| 69c604f1e7 | |||
| 5386820b24 | |||
| c185a8a22e | |||
| 7af08f3cb8 | |||
| 5a684ddc66 | |||
| 5b5c0ea5c2 | |||
| 119d79b071 | |||
| 91ab3ad8ab | |||
| af12a299b6 | |||
| 221bba32d2 | |||
| 987819d42e | |||
| 125b43be4a | |||
| cdc188c5b0 | |||
| d10283c183 | |||
| 14dfa97cc5 | |||
| 6f99de8ec3 | |||
| b999e84b15 | |||
| e1f4a740ac | |||
| 0a88880e98 | |||
| ef105f680d | |||
| cfe8deff8b | |||
| 9337cb011c | |||
| 85aefd850b |
+1
-2
@@ -15,13 +15,12 @@ pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
node_modules
|
||||
.vscode/settings.json
|
||||
|
||||
*.log
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "esbenp.prettier-vscode"]
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stacjownik",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -26,12 +26,12 @@
|
||||
"vue-router": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.3.1",
|
||||
"@tsconfig/node24": "^24.0.4",
|
||||
"@types/node": "^24.12.0",
|
||||
"@types/showdown": "^2.0.6",
|
||||
"@vite-pwa/assets-generator": "^1.0.0",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"axios": "^1.9.0",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^7.1.4",
|
||||
|
||||
+9
-6
@@ -30,7 +30,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
import { version } from '../package.json';
|
||||
import { Status } from './typings/common';
|
||||
@@ -114,11 +113,15 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
try {
|
||||
const releaseData = await (
|
||||
await axios.get('https://api.github.com/repos/Spythere/stacjownik/releases/latest')
|
||||
).data;
|
||||
const response = await fetch(
|
||||
'https://api.github.com/repos/Spythere/stacjownik/releases/latest'
|
||||
);
|
||||
|
||||
if (!releaseData) return;
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch release data from repository!');
|
||||
}
|
||||
|
||||
const releaseData = await response.json();
|
||||
|
||||
this.store.appUpdate = {
|
||||
version,
|
||||
@@ -130,7 +133,7 @@ export default defineComponent({
|
||||
(storageVersion != '' && storageVersion != version && this.isOnProductionHost) ||
|
||||
import.meta.env.VITE_UPDATE_TEST === 'test';
|
||||
} catch (error) {
|
||||
console.error(`Wystąpił błąd podczas pobierania danych z API GitHuba: ${error}`);
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
StorageManager.setStringValue(STORAGE_VERSION_KEY, version);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
:images="images"
|
||||
:image-fallbacks="imagesFallbacks"
|
||||
:show-previews="showPreviews"
|
||||
:thumbnail-size="thumbnailSize"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -25,7 +26,8 @@ export default defineComponent({
|
||||
props: {
|
||||
trainStockList: { type: Array as PropType<string[]>, required: true },
|
||||
tractionOnly: { type: Boolean, required: false },
|
||||
showPreviews: { type: Boolean }
|
||||
showPreviews: { type: Boolean },
|
||||
thumbnailSize: { type: Number }
|
||||
},
|
||||
|
||||
data() {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<img
|
||||
v-for="(thumbnailImage, imageIndex) in images"
|
||||
:src="`https://stacjownik.spythere.eu/static/thumbnails/${thumbnailImage}.png`"
|
||||
height="70"
|
||||
:height="thumbnailSize || 70"
|
||||
loading="lazy"
|
||||
:data-crosshair-cursor="showPreviews"
|
||||
:data-tooltip-type="showPreviews ? 'VehiclePreviewTooltip' : ''"
|
||||
@@ -28,7 +28,8 @@ const props = defineProps({
|
||||
vehicleString: { type: String, required: true },
|
||||
images: { type: Object as PropType<string[]>, required: true },
|
||||
imageFallbacks: { type: Object as PropType<string[]>, required: true },
|
||||
showPreviews: { type: Boolean }
|
||||
showPreviews: { type: Boolean },
|
||||
thumbnailSize: { type: Number }
|
||||
});
|
||||
|
||||
const thumbRef = ref(null) as Ref<HTMLElement | null>;
|
||||
@@ -67,7 +68,7 @@ function onImageLoad() {
|
||||
max-width: 90%;
|
||||
text-align: center;
|
||||
color: #aaa;
|
||||
font-size: 0.85em;
|
||||
font-size: 0.8em;
|
||||
margin: 0 auto;
|
||||
padding: 0.25em 0;
|
||||
}
|
||||
|
||||
@@ -269,9 +269,9 @@ export default defineComponent({
|
||||
|
||||
this.searchTimeout = window.setTimeout(async () => {
|
||||
try {
|
||||
const suggestions: string[] = await (
|
||||
await this.apiStore.client!.get(`api/get${type}Suggestions?name=${value}`)
|
||||
).data;
|
||||
const suggestions: string[] = await this.apiStore.client.get(
|
||||
`api/get${type}Suggestions?name=${value}`
|
||||
);
|
||||
|
||||
this[`${type}Suggestions`] = suggestions;
|
||||
} catch (error) {
|
||||
@@ -336,10 +336,17 @@ export default defineComponent({
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
overflow: hidden;
|
||||
max-height: 530px;
|
||||
max-height: calc(100% - 4.5em);
|
||||
top: 3.5em;
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
.options_content {
|
||||
overflow: auto;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.options_actions {
|
||||
padding: 0 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -69,5 +69,6 @@ function navigateToProfile() {
|
||||
left: auto;
|
||||
right: 0;
|
||||
max-width: 700px;
|
||||
top: 3.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -201,22 +201,20 @@ const driverRouteLocation = computed<RouteLocationRaw | null>(() => {
|
||||
|
||||
async function fetchTimetableDetails() {
|
||||
try {
|
||||
const responseData = await apiStore.client!.get<API.TimetableHistory.Response>(
|
||||
const responseData = await apiStore.client.get<API.TimetableHistory.Response>(
|
||||
'api/getTimetables',
|
||||
{
|
||||
params: {
|
||||
timetableId: props.timetableEntry.id,
|
||||
returnType: 'detailed'
|
||||
}
|
||||
timetableId: props.timetableEntry.id,
|
||||
returnType: 'detailed'
|
||||
}
|
||||
);
|
||||
|
||||
if (!responseData || responseData.data.length != 1) {
|
||||
if (!responseData || responseData.length != 1) {
|
||||
timetableDetails.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
timetableDetails.value = responseData.data[0];
|
||||
timetableDetails.value = responseData[0];
|
||||
} catch (error) {
|
||||
// this.dataStatus = Status.Data.Error;
|
||||
console.error(error);
|
||||
|
||||
@@ -15,7 +15,8 @@ export namespace Journal {
|
||||
| 'search-issuedFrom'
|
||||
| 'search-terminatingAt'
|
||||
| 'search-via'
|
||||
| 'select-categoryCode';
|
||||
| 'select-categoryCode'
|
||||
| 'search-headUnit';
|
||||
|
||||
export type TimetableSearchType = {
|
||||
[key in TimetableSearchKey]: string;
|
||||
|
||||
@@ -127,9 +127,8 @@ export default defineComponent({
|
||||
this.station?.name || this.onlineScenery?.name
|
||||
}&countFrom=${countFrom}&countLimit=${countLimit}`;
|
||||
|
||||
const historyAPIData: API.DispatcherHistory.Response = await (
|
||||
await this.apiStore.client!.get(requestString)
|
||||
).data;
|
||||
const historyAPIData: API.DispatcherHistory.Response =
|
||||
await this.apiStore.client.get(requestString);
|
||||
|
||||
this.dataStatus = Status.Data.Loaded;
|
||||
return historyAPIData;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<section class="info-routes" v-if="station.generalInfo">
|
||||
<div class="routes one-way" v-if="oneWayRoutes.length > 0">
|
||||
<div class="routes one-way" v-if="singleRoutesAvailable.length > 0">
|
||||
<button
|
||||
class="routes-btn"
|
||||
@click="toggleRoutesVisibility('single')"
|
||||
@@ -12,7 +12,7 @@
|
||||
</button>
|
||||
|
||||
<ul class="routes-list">
|
||||
<li v-for="route in oneWayRoutes" :key="route.routeName">
|
||||
<li v-for="route in singleRoutesFiltered" :key="route.routeName">
|
||||
<span :class="{ 'no-catenary': !route.isElectric, internal: route.isInternal }">
|
||||
{{ route.routeName }}</span
|
||||
>
|
||||
@@ -24,10 +24,17 @@
|
||||
</span>
|
||||
<span v-if="route.isRouteSBL" class="sbl">SBL</span>
|
||||
</li>
|
||||
|
||||
<li v-if="singleRoutesFiltered.length == 0">
|
||||
<span class="routes-hidden">
|
||||
<i class="fa-solid fa-eye-slash"></i>
|
||||
{{ $t('scenery.routes-hidden') }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="routes two-way" v-if="twoWayRoutes.length > 0">
|
||||
<div class="routes two-way" v-if="doubleRoutesAvailable.length > 0">
|
||||
<button
|
||||
class="routes-btn"
|
||||
@click="toggleRoutesVisibility('double')"
|
||||
@@ -39,7 +46,7 @@
|
||||
</button>
|
||||
|
||||
<ul class="routes-list">
|
||||
<li v-for="route in twoWayRoutes" :key="route.routeName">
|
||||
<li v-for="route in doubleRoutesFiltered" :key="route.routeName">
|
||||
<span :class="{ 'no-catenary': !route.isElectric, internal: route.isInternal }">
|
||||
{{ route.routeName }}
|
||||
</span>
|
||||
@@ -54,6 +61,13 @@
|
||||
</span>
|
||||
<span v-if="route.isRouteSBL" class="sbl">SBL</span>
|
||||
</li>
|
||||
|
||||
<li v-if="doubleRoutesFiltered.length == 0">
|
||||
<span class="routes-hidden">
|
||||
<i class="fa-solid fa-eye-slash"></i>
|
||||
{{ $t('scenery.routes-hidden') }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
@@ -102,20 +116,32 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
computed: {
|
||||
oneWayRoutes() {
|
||||
singleRoutesAvailable() {
|
||||
return (
|
||||
this.station.generalInfo?.routes.single
|
||||
.filter((r) => !r.isInternal || r.isInternal == this.showInternalSingleRoutes)
|
||||
.filter((r) => !r.hidden)
|
||||
.sort((r1, r2) => r1.routeName.localeCompare(r2.routeName)) ?? []
|
||||
);
|
||||
},
|
||||
|
||||
twoWayRoutes() {
|
||||
doubleRoutesAvailable() {
|
||||
return (
|
||||
this.station.generalInfo?.routes.double
|
||||
.filter((r) => !r.isInternal || r.isInternal == this.showInternalDoubleRoutes)
|
||||
.filter((r) => !r.hidden)
|
||||
.sort((r1, r2) => r1.routeName.localeCompare(r2.routeName)) ?? []
|
||||
);
|
||||
},
|
||||
|
||||
singleRoutesFiltered() {
|
||||
return this.singleRoutesAvailable.filter(
|
||||
(r) => this.showInternalSingleRoutes || !r.isInternal
|
||||
);
|
||||
},
|
||||
|
||||
doubleRoutesFiltered() {
|
||||
return this.doubleRoutesAvailable.filter(
|
||||
(r) => this.showInternalDoubleRoutes || !r.isInternal
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -154,11 +180,6 @@ ul.routes-list {
|
||||
|
||||
li {
|
||||
margin: 0.5em 0.25em;
|
||||
cursor: pointer;
|
||||
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
& > span {
|
||||
padding: 0.2em;
|
||||
@@ -182,11 +203,16 @@ ul.routes-list {
|
||||
background-color: #303030;
|
||||
color: #cfcfcf;
|
||||
}
|
||||
|
||||
&.sbl {
|
||||
color: var(--clr-primary);
|
||||
background-color: #404040;
|
||||
}
|
||||
|
||||
&.routes-hidden {
|
||||
background-color: #4b4b4b;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0.5em 0.5em 0;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<i
|
||||
v-if="
|
||||
train.timetableData != undefined &&
|
||||
(train.lastSeen <= Date.now() - 60000 || !train.online)
|
||||
train.lastSeen <= Date.now() - 60000 &&
|
||||
!train.online
|
||||
"
|
||||
class="fa-solid fa-user-slash"
|
||||
style="color: lightcoral"
|
||||
|
||||
@@ -210,7 +210,7 @@
|
||||
</div>
|
||||
|
||||
<div class="item-stock-list" v-if="showStockThumbnails">
|
||||
<StockList :trainStockList="row.train.stockList" />
|
||||
<StockList :trainStockList="row.train.stockList" :thumbnailSize="45" />
|
||||
</div>
|
||||
</router-link>
|
||||
</transition-group>
|
||||
@@ -348,7 +348,7 @@ const tabliceZbiorczeHref = computed(() => {
|
||||
});
|
||||
|
||||
const pragotronHref = computed(() => {
|
||||
let url = `https://pragotron-td2.web.app/board?name=${props.station!.name}®ion=${mainStore.region.id}`;
|
||||
let url = `https://pragotron-td2.spythere.eu/board?name=${props.station!.name}®ion=${mainStore.region.id}`;
|
||||
if (props.chosenCheckpoint) url += `&checkpoint=${props.chosenCheckpoint}`;
|
||||
|
||||
return url;
|
||||
|
||||
@@ -149,11 +149,12 @@ export default defineComponent({
|
||||
requestFilters['returnType'] = 'short';
|
||||
|
||||
try {
|
||||
const response: API.TimetableHistory.ResponseShort = await (
|
||||
await this.apiStore.client!.get('api/getTimetables', {
|
||||
params: requestFilters
|
||||
})
|
||||
).data;
|
||||
const response: API.TimetableHistory.ResponseShort = await this.apiStore.client.get(
|
||||
'api/getTimetables',
|
||||
requestFilters
|
||||
);
|
||||
|
||||
console.log(response);
|
||||
|
||||
this.historyList = response;
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="scenery-top-list">
|
||||
<h2 class="header">{{ t('scenery.top-list.header') }}</h2>
|
||||
|
||||
<div class="top-actions">
|
||||
<div class="actions-modes">
|
||||
<button
|
||||
v-for="mode in availableModes"
|
||||
:class="`btn btn--option ${mode == currentListMode ? 'checked' : ''}`"
|
||||
@click="selectListMode(mode)"
|
||||
>
|
||||
{{ t(`scenery.top-list.mode-${mode}`) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="actions-scopes">
|
||||
<button
|
||||
v-for="scope in availableScopes"
|
||||
:class="`btn btn--option ${scope == currentListScope ? 'checked' : ''}`"
|
||||
@click="selectListScope(scope)"
|
||||
>
|
||||
{{ t(`scenery.top-list.scope-${scope}`) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rating-list-wrapper">
|
||||
<Loading v-if="listState == Status.Data.Loading" />
|
||||
<div v-else-if="listState == Status.Data.Error">Ups, coś poszło nie tak...</div>
|
||||
|
||||
<ul v-else>
|
||||
<li v-for="(value, i) in bestScoreList">
|
||||
<div>
|
||||
{{ t('scenery.top-list.place', i + 1) }} -
|
||||
<router-link :to="`/profile?playerId=${value.dispatcherId}`">{{
|
||||
value.dispatcherName
|
||||
}}</router-link>
|
||||
</div>
|
||||
<div>
|
||||
<b class="text--primary" v-if="currentListMode == 'dutyCount'">{{
|
||||
t('scenery.top-list.duty-count', value.value)
|
||||
}}</b>
|
||||
|
||||
<b class="text--primary" v-else-if="currentListMode == 'dispatcherRating'">{{
|
||||
t('scenery.top-list.dispatcher-rating', value.value)
|
||||
}}</b>
|
||||
|
||||
<b class="text--primary" v-else>
|
||||
{{ t('scenery.top-list.duration') }}
|
||||
{{ humanizeDuration(value.value) }}
|
||||
</b>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, PropType, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useApiStore } from '../../store/apiStore';
|
||||
import { Station, ActiveScenery, Status } from '../../typings/common';
|
||||
import Loading from '../Global/Loading.vue';
|
||||
import { humanizeDuration } from '../../composables/time';
|
||||
|
||||
interface SceneryBestScoreItem {
|
||||
dispatcherName: string;
|
||||
dispatcherId: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const apiStore = useApiStore();
|
||||
|
||||
defineOptions({
|
||||
name: 'SceneryTopList'
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
station: {
|
||||
type: Object as PropType<Station>
|
||||
},
|
||||
|
||||
onlineScenery: {
|
||||
type: Object as PropType<ActiveScenery>
|
||||
}
|
||||
});
|
||||
|
||||
const availableModes = ['dutyCount', 'dispatcherRating', 'dutyDuration'] as const;
|
||||
const availableScopes = ['name', 'hash'] as const;
|
||||
|
||||
type ListMode = (typeof availableModes)[number];
|
||||
type ListScope = (typeof availableScopes)[number];
|
||||
|
||||
const currentListMode = ref<ListMode>('dutyCount');
|
||||
const currentListScope = ref<ListScope>('name');
|
||||
|
||||
const listState = ref<Status.Data>(Status.Data.Loading);
|
||||
|
||||
const bestScoreList = ref<SceneryBestScoreItem[]>([]);
|
||||
|
||||
onActivated(() => {
|
||||
fetchTopDispatchersList();
|
||||
});
|
||||
|
||||
function selectListMode(mode: ListMode) {
|
||||
currentListMode.value = mode;
|
||||
fetchTopDispatchersList();
|
||||
}
|
||||
|
||||
function selectListScope(scope: ListScope) {
|
||||
currentListScope.value = scope;
|
||||
fetchTopDispatchersList();
|
||||
}
|
||||
|
||||
async function fetchTopDispatchersList() {
|
||||
const searchedStationValue =
|
||||
currentListScope.value == 'name'
|
||||
? props.station?.name
|
||||
: apiStore.sceneryData.find((sc) => sc.name == props.station!.name)?.hash;
|
||||
|
||||
bestScoreList.value = [];
|
||||
|
||||
if (!searchedStationValue) {
|
||||
listState.value = Status.Data.Loaded;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
listState.value = Status.Data.Loading;
|
||||
|
||||
const response: SceneryBestScoreItem[] = await apiStore.client.get(`api/getSceneryBestScores`, {
|
||||
[currentListScope.value]: searchedStationValue,
|
||||
type: currentListMode.value,
|
||||
countLimit: 40
|
||||
});
|
||||
|
||||
bestScoreList.value = response;
|
||||
listState.value = Status.Data.Loaded;
|
||||
} catch (error) {
|
||||
listState.value = Status.Data.Error;
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.scenery-top-list {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
overflow: hidden;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em 1.5em;
|
||||
}
|
||||
|
||||
.actions-modes,
|
||||
.actions-scopes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
font-weight: bold;
|
||||
|
||||
button {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.rating-list-wrapper {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.rating-list-wrapper > ul {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
align-items: center;
|
||||
gap: 0.65em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.rating-list-wrapper > ul > li {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
padding: 0.25em;
|
||||
background-color: #2b2b2b;
|
||||
height: 100%;
|
||||
|
||||
line-height: 1.5em;
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -18,23 +18,31 @@ export function getTrainStopStatus(
|
||||
return StopStatus.TERMINATED;
|
||||
}
|
||||
|
||||
if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName == sceneryName) {
|
||||
if (
|
||||
!stopInfo.terminatesHere &&
|
||||
stopInfo.confirmed &&
|
||||
currentStationName.startsWith(sceneryName)
|
||||
) {
|
||||
return StopStatus.DEPARTED;
|
||||
}
|
||||
|
||||
if (!stopInfo.terminatesHere && stopInfo.confirmed && currentStationName != sceneryName) {
|
||||
if (
|
||||
!stopInfo.terminatesHere &&
|
||||
stopInfo.confirmed &&
|
||||
!currentStationName.startsWith(sceneryName)
|
||||
) {
|
||||
return StopStatus.DEPARTED_AWAY;
|
||||
}
|
||||
|
||||
if (currentStationName == sceneryName && !stopInfo.stopped) {
|
||||
if (currentStationName.startsWith(sceneryName) && !stopInfo.stopped) {
|
||||
return StopStatus.ONLINE;
|
||||
}
|
||||
|
||||
if (currentStationName == sceneryName && stopInfo.stopped) {
|
||||
if (currentStationName.startsWith(sceneryName) && stopInfo.stopped) {
|
||||
return StopStatus.STOPPED;
|
||||
}
|
||||
|
||||
if (currentStationName != sceneryName) {
|
||||
if (!currentStationName.startsWith(sceneryName)) {
|
||||
return StopStatus.ARRIVING;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,6 +278,10 @@ export default defineComponent({
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.dropdown_wrapper {
|
||||
top: 2.5em;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen {
|
||||
.stats-title {
|
||||
text-align: center;
|
||||
@@ -286,5 +290,9 @@ export default defineComponent({
|
||||
.filter-button > span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -210,6 +210,10 @@ export default defineComponent({
|
||||
@use '../../styles/dropdown';
|
||||
@use '../../styles/dropdown-filters';
|
||||
|
||||
.dropdown_wrapper {
|
||||
top: 2.5em;
|
||||
}
|
||||
|
||||
.search_content > div {
|
||||
margin: 0.5em auto;
|
||||
}
|
||||
|
||||
@@ -250,9 +250,10 @@ h3 {
|
||||
|
||||
.dropdown_wrapper {
|
||||
max-width: 600px;
|
||||
top: 2.5em;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen{
|
||||
@include responsive.smallScreen {
|
||||
.no-data {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
export class HttpClient {
|
||||
constructor(private readonly baseURL: string) {}
|
||||
|
||||
async get<T>(url: string, params?: Record<string, any>): Promise<T> {
|
||||
const absoluteURL = new URL(this.baseURL + '/' + url);
|
||||
|
||||
if (params) {
|
||||
Object.keys(params).forEach((key) => {
|
||||
if (params[key] === undefined) return;
|
||||
|
||||
absoluteURL.searchParams.append(key, params[key]);
|
||||
});
|
||||
}
|
||||
|
||||
const data = await fetch(absoluteURL);
|
||||
|
||||
if (!data.ok) {
|
||||
throw new Error(`Cannot fetch ${absoluteURL}: ${data.statusText}`);
|
||||
}
|
||||
|
||||
return data.json();
|
||||
}
|
||||
}
|
||||
+23
@@ -3,12 +3,35 @@ import plLang from './locales/pl.json';
|
||||
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
function customRule(choice: number, choicesLength: number) {
|
||||
if (choice === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const teen = choice > 10 && choice < 20;
|
||||
const endsWithOne = choice % 10 === 1;
|
||||
|
||||
if (!teen && endsWithOne) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return choicesLength < 4 ? 2 : 3;
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'pl',
|
||||
legacy: false,
|
||||
warnHtmlMessage: false,
|
||||
fallbackLocale: 'pl',
|
||||
|
||||
pluralizationRules: {
|
||||
pl: customRule
|
||||
},
|
||||
|
||||
messages: {
|
||||
en: enLang,
|
||||
pl: plLang
|
||||
|
||||
+17
-1
@@ -199,6 +199,7 @@
|
||||
"search-date-from": "Date (UTC+2 / CEST)",
|
||||
"search-date-to": "Date (UTC+2 / CEST)",
|
||||
"select-categoryCode": "Train category",
|
||||
"search-headUnit": "Traction unit (e.g. EP09, ET22-401)",
|
||||
"sort-mass": "mass",
|
||||
"sort-speed": "speed",
|
||||
"sort-length": "length",
|
||||
@@ -568,10 +569,12 @@
|
||||
"additional-tools-title": "Additional tools",
|
||||
"one-way-routes": "Single track routes",
|
||||
"two-way-routes": "Double track routes",
|
||||
"routes-hidden": "Hidden internal routes",
|
||||
"no-data": "No available data about this scenery",
|
||||
"option-active-timetables": "Active timetables",
|
||||
"option-timetables-history": "Timetables history PL1",
|
||||
"option-dispatchers-history": "Dispatchers history PL1",
|
||||
"option-top-list": "Scenery records",
|
||||
"btn-show-timetable-thumbnails": "Show rolling stock thumbnails",
|
||||
"btn-hide-timetable-thumbnails": "Hide rolling stock thumbnails",
|
||||
"timetable-includesScenery": "ALL TIMETABLES",
|
||||
@@ -591,7 +594,20 @@
|
||||
"tablice-link": "Timetable summary board <br> (by Thundo)",
|
||||
"bottom-info": "Show full history in the Journal tab",
|
||||
"btn-show-internal-routes": "Show internal routes",
|
||||
"btn-hide-internal-routes": "Hide internal routes"
|
||||
"btn-hide-internal-routes": "Hide internal routes",
|
||||
"top-list": {
|
||||
"header": "RECORDS ON THE SCENERY (PL1)",
|
||||
"mode-dutyCount": "DUTIES",
|
||||
"mode-dispatcherRating": "RATING",
|
||||
"mode-dutyDuration": "DUTY DURATION",
|
||||
"scope-name": "GENERAL",
|
||||
"scope-hash": "CURRENT HASH",
|
||||
|
||||
"place": "{n}. place",
|
||||
"dispatcher-rating": "Rating: {n}",
|
||||
"duty-count": "No duties | 1 duty | Duties: {n}",
|
||||
"duration": "Duration:"
|
||||
}
|
||||
},
|
||||
"availability": {
|
||||
"title": "Availability",
|
||||
|
||||
+17
-1
@@ -196,6 +196,7 @@
|
||||
"search-date-from": "Data (UTC+2 / CEST)",
|
||||
"search-date-to": "Data (UTC+2 / CEST)",
|
||||
"select-categoryCode": "Kategoria pociągu",
|
||||
"search-headUnit": "Pojazd trakcyjny (np. EP09, ET22-137)",
|
||||
"sort-routeDistance": "kilometraż",
|
||||
"sort-allStopsCount": "stacje",
|
||||
"sort-beginDate": "data",
|
||||
@@ -554,10 +555,12 @@
|
||||
"additional-tools-title": "Dodatkowe narzędzia",
|
||||
"one-way-routes": "Szlaki jednotorowe",
|
||||
"two-way-routes": "Szlaki dwutorowe",
|
||||
"routes-hidden": "Ukryto szlaki wewnętrzne",
|
||||
"no-data": "Brak informacji o tej scenerii",
|
||||
"option-active-timetables": "Aktywne rozkłady jazdy",
|
||||
"option-timetables-history": "Historia rozkładów PL1",
|
||||
"option-dispatchers-history": "Historia dyżurów PL1",
|
||||
"option-top-list": "Rekordy scenerii",
|
||||
"btn-show-timetable-thumbnails": "Pokazuj podglądy składów",
|
||||
"btn-hide-timetable-thumbnails": "Ukrywaj podglądy składów",
|
||||
"timetable-includesScenery": "WSZYSTKIE RJ",
|
||||
@@ -577,7 +580,20 @@
|
||||
"tablice-link": "Tablica informacyjna zbiorcza <br> (autorstwa Thundo)",
|
||||
"bottom-info": "Pokaż pełną historię w zakładce Dziennika",
|
||||
"btn-show-internal-routes": "Pokazuj szlaki wewnętrzne",
|
||||
"btn-hide-internal-routes": "Ukrywaj szlaki wewnętrzne"
|
||||
"btn-hide-internal-routes": "Ukrywaj szlaki wewnętrzne",
|
||||
"top-list": {
|
||||
"header": "REKORDY NA SCENERII (PL1)",
|
||||
"mode-dutyCount": "DYŻURY",
|
||||
"mode-dispatcherRating": "OCENA",
|
||||
"mode-dutyDuration": "CZAS DYŻURU",
|
||||
"scope-name": "OGÓLNIE",
|
||||
"scope-hash": "OBECNY HASH",
|
||||
|
||||
"place": "{n}. miejsce",
|
||||
"dispatcher-rating": "Ocena: {n}",
|
||||
"duty-count": "Brak dyżurów | 1 dyżur | Dyżury: {n}",
|
||||
"duration": "Czas:"
|
||||
}
|
||||
},
|
||||
"availability": {
|
||||
"title": "Dostępność",
|
||||
|
||||
@@ -94,14 +94,14 @@ export const initFilters = {
|
||||
minTwoWayCatenary: 0,
|
||||
minTwoWayInt: 0,
|
||||
minTwoWayCatenaryInt: 0,
|
||||
maxOneWay: 5,
|
||||
maxOneWayCatenary: 5,
|
||||
maxOneWayInt: 5,
|
||||
maxOneWayCatenaryInt: 5,
|
||||
maxTwoWay: 5,
|
||||
maxTwoWayCatenary: 5,
|
||||
maxTwoWayInt: 5,
|
||||
maxTwoWayCatenaryInt: 5,
|
||||
maxOneWay: 10,
|
||||
maxOneWayCatenary: 10,
|
||||
maxOneWayInt: 20,
|
||||
maxOneWayCatenaryInt: 20,
|
||||
maxTwoWay: 10,
|
||||
maxTwoWayCatenary: 10,
|
||||
maxTwoWayInt: 20,
|
||||
maxTwoWayCatenaryInt: 20,
|
||||
authors: '',
|
||||
projects: '',
|
||||
lines: ''
|
||||
@@ -122,62 +122,62 @@ export const sliderGroups: SliderGroup[] = [
|
||||
|
||||
export const sliderGroupsOptions: Record<SliderGroup, SliderOptions[]> = {
|
||||
vMax: [
|
||||
{ id: 'minVmax', minRange: 0, maxRange: 200, step: 10 },
|
||||
{ id: 'maxVmax', minRange: 0, maxRange: 200, step: 10 }
|
||||
{ id: 'minVmax', minRange: 0, maxRange: 200, step: 20 },
|
||||
{ id: 'maxVmax', minRange: 0, maxRange: 200, step: 20 }
|
||||
],
|
||||
level: [
|
||||
{ id: 'minLevel', minRange: 0, maxRange: 20, step: 1 },
|
||||
{ id: 'maxLevel', minRange: 0, maxRange: 20, step: 1 }
|
||||
],
|
||||
routeOneWay: [
|
||||
{ id: 'minOneWay', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'maxOneWay', minRange: 0, maxRange: 5, step: 1 }
|
||||
{ id: 'minOneWay', minRange: 0, maxRange: 10, step: 1 },
|
||||
{ id: 'maxOneWay', minRange: 0, maxRange: 10, step: 1 }
|
||||
],
|
||||
routeOneWayCatenary: [
|
||||
{ id: 'minOneWayCatenary', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'maxOneWayCatenary', minRange: 0, maxRange: 5, step: 1 }
|
||||
{ id: 'minOneWayCatenary', minRange: 0, maxRange: 10, step: 1 },
|
||||
{ id: 'maxOneWayCatenary', minRange: 0, maxRange: 10, step: 1 }
|
||||
],
|
||||
routeOneWayInternal: [
|
||||
{ id: 'minOneWayInt', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'maxOneWayInt', minRange: 0, maxRange: 5, step: 1 }
|
||||
{ id: 'minOneWayInt', minRange: 0, maxRange: 20, step: 1 },
|
||||
{ id: 'maxOneWayInt', minRange: 0, maxRange: 20, step: 1 }
|
||||
],
|
||||
routeOneWayInternalCatenary: [
|
||||
{
|
||||
id: 'minOneWayCatenaryInt',
|
||||
minRange: 0,
|
||||
maxRange: 5,
|
||||
maxRange: 20,
|
||||
step: 1
|
||||
},
|
||||
{
|
||||
id: 'maxOneWayCatenaryInt',
|
||||
minRange: 0,
|
||||
maxRange: 5,
|
||||
maxRange: 20,
|
||||
step: 1
|
||||
}
|
||||
],
|
||||
routeTwoWay: [
|
||||
{ id: 'minTwoWay', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'maxTwoWay', minRange: 0, maxRange: 5, step: 1 }
|
||||
{ id: 'minTwoWay', minRange: 0, maxRange: 10, step: 1 },
|
||||
{ id: 'maxTwoWay', minRange: 0, maxRange: 10, step: 1 }
|
||||
],
|
||||
routeTwoWayCatenary: [
|
||||
{ id: 'minTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'maxTwoWayCatenary', minRange: 0, maxRange: 5, step: 1 }
|
||||
{ id: 'minTwoWayCatenary', minRange: 0, maxRange: 10, step: 1 },
|
||||
{ id: 'maxTwoWayCatenary', minRange: 0, maxRange: 10, step: 1 }
|
||||
],
|
||||
routeTwoWayInternal: [
|
||||
{ id: 'minTwoWayInt', minRange: 0, maxRange: 5, step: 1 },
|
||||
{ id: 'maxTwoWayInt', minRange: 0, maxRange: 5, step: 1 }
|
||||
{ id: 'minTwoWayInt', minRange: 0, maxRange: 20, step: 1 },
|
||||
{ id: 'maxTwoWayInt', minRange: 0, maxRange: 20, step: 1 }
|
||||
],
|
||||
routeTwoWayInternalCatenary: [
|
||||
{
|
||||
id: 'minTwoWayCatenaryInt',
|
||||
minRange: 0,
|
||||
maxRange: 5,
|
||||
maxRange: 20,
|
||||
step: 1
|
||||
},
|
||||
{
|
||||
id: 'maxTwoWayCatenaryInt',
|
||||
minRange: 0,
|
||||
maxRange: 5,
|
||||
maxRange: 20,
|
||||
step: 1
|
||||
}
|
||||
]
|
||||
|
||||
+24
-32
@@ -2,7 +2,20 @@ import { defineStore } from 'pinia';
|
||||
import { API } from '../typings/api';
|
||||
import { Status } from '../typings/common';
|
||||
import { StationJSONData } from './typings';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import { HttpClient } from '../http';
|
||||
|
||||
let baseURL = 'https://stacjownik.spythere.eu';
|
||||
|
||||
switch (import.meta.env.VITE_API_MODE) {
|
||||
case 'development':
|
||||
baseURL = 'http://localhost:3001';
|
||||
break;
|
||||
case 'mocking':
|
||||
baseURL = 'http://localhost:3123';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
export const useApiStore = defineStore('apiStore', {
|
||||
state: () => ({
|
||||
@@ -25,30 +38,13 @@ export const useApiStore = defineStore('apiStore', {
|
||||
nextUpdateTime: 0,
|
||||
nextDataCheckTime: 0,
|
||||
|
||||
client: undefined as AxiosInstance | undefined,
|
||||
client: new HttpClient(baseURL),
|
||||
|
||||
activeDataScheduler: undefined as number | undefined
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async setupAPIData() {
|
||||
let baseURL = 'https://stacjownik.spythere.eu';
|
||||
|
||||
switch (import.meta.env.VITE_API_MODE) {
|
||||
case 'development':
|
||||
baseURL = 'http://localhost:3001';
|
||||
break;
|
||||
case 'mocking':
|
||||
baseURL = 'http://localhost:3123';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL
|
||||
});
|
||||
|
||||
this.connectToAPI();
|
||||
},
|
||||
|
||||
@@ -82,9 +78,9 @@ export const useApiStore = defineStore('apiStore', {
|
||||
if (!this.activeData) this.dataStatuses.connection = Status.Data.Loading;
|
||||
|
||||
try {
|
||||
const response = await this.client!.get<API.ActiveData.Response>('api/getActiveData');
|
||||
const response = await this.client.get<API.ActiveData.Response>('api/getActiveData');
|
||||
|
||||
this.activeData = response.data;
|
||||
this.activeData = response;
|
||||
this.dataStatuses.connection = Status.Data.Loaded;
|
||||
} catch (error) {
|
||||
this.dataStatuses.connection = Status.Data.Error;
|
||||
@@ -94,9 +90,9 @@ export const useApiStore = defineStore('apiStore', {
|
||||
|
||||
async fetchDonatorsData() {
|
||||
try {
|
||||
const response = await this.client!.get<API.Donators.Response>('api/getDonators');
|
||||
const response = await this.client.get<API.Donators.Response>('api/getDonators');
|
||||
|
||||
this.donatorsData = response.data;
|
||||
this.donatorsData = response;
|
||||
} catch (error) {
|
||||
console.error('Ups! Wystąpił błąd podczas pobierania informacji o donatorach:', error);
|
||||
}
|
||||
@@ -104,9 +100,7 @@ export const useApiStore = defineStore('apiStore', {
|
||||
|
||||
async fetchStationsGeneralInfo() {
|
||||
try {
|
||||
const sceneryData: StationJSONData[] = (
|
||||
await this.client!.get<StationJSONData[]>(`api/getSceneries`)
|
||||
).data;
|
||||
const sceneryData = await this.client.get<StationJSONData[]>(`api/getSceneries`);
|
||||
|
||||
this.dataStatuses.sceneries = Status.Data.Loaded;
|
||||
this.sceneryData = sceneryData;
|
||||
@@ -118,10 +112,10 @@ export const useApiStore = defineStore('apiStore', {
|
||||
|
||||
async fetchVehiclesInfo() {
|
||||
try {
|
||||
const response = await this.client!.get<API.VehiclesData.Response>('api/getVehiclesData');
|
||||
const response = await this.client.get<API.VehiclesData.Response>('api/getVehiclesData');
|
||||
|
||||
this.vehiclesData = response.data;
|
||||
this.dataStatuses.vehicles = response.data ? Status.Data.Loaded : Status.Data.Warning;
|
||||
this.vehiclesData = response;
|
||||
this.dataStatuses.vehicles = response ? Status.Data.Loaded : Status.Data.Warning;
|
||||
} catch (error) {
|
||||
this.dataStatuses.vehicles = Status.Data.Error;
|
||||
console.error('Ups! Wystąpił błąd podczas pobierania informacji o pojazdach:', error);
|
||||
@@ -130,9 +124,7 @@ export const useApiStore = defineStore('apiStore', {
|
||||
|
||||
async fetchDailyStats() {
|
||||
try {
|
||||
const res: API.DailyStats.Response = await (
|
||||
await this.client!.get('api/getDailyStats')
|
||||
).data;
|
||||
const res = await this.client.get<API.DailyStats.Response>('api/getDailyStats');
|
||||
|
||||
this.dailyStatsData = res;
|
||||
|
||||
|
||||
@@ -78,14 +78,14 @@ h1.option-title {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
width: 100%;
|
||||
margin-top: 0.5em;
|
||||
margin-top: 1em;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include responsive.smallScreen{
|
||||
@include responsive.smallScreen {
|
||||
h1 {
|
||||
text-align: center;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
.dropdown_wrapper {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(100% + 0.5em);
|
||||
top: 0;
|
||||
|
||||
background-color: var(--clr-bg3);
|
||||
box-shadow: 0 0 5px 1px var(--clr-primary);
|
||||
@@ -34,7 +34,6 @@
|
||||
width: 100%;
|
||||
max-width: 550px;
|
||||
|
||||
max-height: 750px;
|
||||
overflow: auto;
|
||||
|
||||
padding: 1em;
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
width: 100%;
|
||||
|
||||
margin: 0 auto;
|
||||
|
||||
padding: 1em 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.journal_refreshed-date {
|
||||
@@ -57,7 +57,6 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn--load-data {
|
||||
@@ -68,7 +67,7 @@
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen{
|
||||
@include responsive.smallScreen {
|
||||
.journal_top-bar {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
gap: 0.25em;
|
||||
|
||||
min-width: 200px;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
&-input {
|
||||
@@ -61,4 +60,4 @@
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -253,8 +253,10 @@ export namespace API {
|
||||
pn?: number;
|
||||
tn?: number;
|
||||
|
||||
returnType?: 'all' | 'short' | 'detailed';
|
||||
headUnitName?: string;
|
||||
headUnitType?: string;
|
||||
|
||||
returnType?: 'all' | 'short' | 'detailed';
|
||||
sortBy?: Journal.TimetableSorter['id'];
|
||||
}
|
||||
|
||||
|
||||
@@ -217,9 +217,10 @@ export default defineComponent({
|
||||
this.scrollDataLoaded = false;
|
||||
this.currentQueryParams['countFrom'] = this.historyList.length;
|
||||
|
||||
const responseData: API.DispatcherHistory.Response = await (
|
||||
await this.apiStore.client!.get(`api/getDispatchers`, { params: this.currentQueryParams })
|
||||
).data;
|
||||
const responseData: API.DispatcherHistory.Response = await await this.apiStore.client.get(
|
||||
`api/getDispatchers`,
|
||||
this.currentQueryParams
|
||||
);
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
@@ -276,9 +277,10 @@ export default defineComponent({
|
||||
this.currentQueryParams = queryParams;
|
||||
|
||||
try {
|
||||
const responseData: API.DispatcherHistory.Response = await (
|
||||
await this.apiStore.client!.get(`api/getDispatchers`, { params: this.currentQueryParams })
|
||||
).data;
|
||||
const responseData: API.DispatcherHistory.Response = await this.apiStore.client.get(
|
||||
`api/getDispatchers`,
|
||||
this.currentQueryParams
|
||||
);
|
||||
|
||||
if (!responseData) {
|
||||
this.dataStatus = Status.Data.Error;
|
||||
|
||||
@@ -173,8 +173,9 @@ export default defineComponent({
|
||||
'search-issuedFrom': '',
|
||||
'search-via': '',
|
||||
'search-terminatingAt': '',
|
||||
'select-categoryCode': '',
|
||||
'search-date-from': ''
|
||||
'search-headUnit': '',
|
||||
'search-date-from': '',
|
||||
'select-categoryCode': ''
|
||||
} as Journal.TimetableSearchType);
|
||||
|
||||
const countFromIndex = ref(0);
|
||||
@@ -277,11 +278,10 @@ export default defineComponent({
|
||||
|
||||
this.currentQueryParams['countFrom'] = this.timetableHistory.length;
|
||||
|
||||
const responseData: API.TimetableHistory.Response = await (
|
||||
await this.apiStore.client!.get('api/getTimetables', {
|
||||
params: this.currentQueryParams
|
||||
})
|
||||
).data;
|
||||
const responseData: API.TimetableHistory.Response = await this.apiStore.client.get(
|
||||
'api/getTimetables',
|
||||
this.currentQueryParams
|
||||
);
|
||||
|
||||
if (!responseData) return;
|
||||
|
||||
@@ -297,6 +297,8 @@ export default defineComponent({
|
||||
async fetchHistoryData() {
|
||||
this.extraInfoIndexes.length = 0;
|
||||
|
||||
const queryParams: API.TimetableHistory.QueryParams = {};
|
||||
|
||||
const driverName = this.searchersValues['search-driver'].trim() || undefined;
|
||||
const trainNo = this.searchersValues['search-train'].trim() || undefined;
|
||||
const authorName = this.searchersValues['search-dispatcher'].trim() || undefined;
|
||||
@@ -306,6 +308,7 @@ export default defineComponent({
|
||||
const via = this.searchersValues['search-via'].trim() || undefined;
|
||||
const terminatingAt = this.searchersValues['search-terminatingAt'].trim() || undefined;
|
||||
const categoryCode = this.searchersValues['select-categoryCode'].trim() || undefined;
|
||||
const headUnit = this.searchersValues['search-headUnit'].trim() || undefined;
|
||||
|
||||
let dateFromISO: string | undefined = undefined;
|
||||
let dateToISO: string | undefined = undefined;
|
||||
@@ -321,8 +324,6 @@ export default defineComponent({
|
||||
dateToISO = dateTo.toISOString();
|
||||
}
|
||||
|
||||
const queryParams: API.TimetableHistory.QueryParams = {};
|
||||
|
||||
this.filterList
|
||||
.filter((f) => f.isActive)
|
||||
.forEach((f) => {
|
||||
@@ -394,17 +395,27 @@ export default defineComponent({
|
||||
queryParams['sortBy'] =
|
||||
this.sorterActive.id != 'timetableId' ? this.sorterActive.id : undefined;
|
||||
|
||||
// Head unit params
|
||||
if (headUnit) {
|
||||
const [headUnitName, headUnitNumber] = headUnit.split('-');
|
||||
|
||||
if (headUnitNumber && !isNaN(Number(headUnitNumber))) {
|
||||
queryParams['headUnitName'] = `${headUnitName}-${headUnitNumber}`;
|
||||
} else {
|
||||
queryParams['headUnitType'] = headUnitName;
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(this.currentQueryParams) != JSON.stringify(queryParams))
|
||||
this.dataStatus = Status.Data.Loading;
|
||||
|
||||
this.currentQueryParams = queryParams;
|
||||
|
||||
try {
|
||||
const responseData: API.TimetableHistory.ResponseShort = await (
|
||||
await this.apiStore.client!.get('api/getTimetables', {
|
||||
params: this.currentQueryParams
|
||||
})
|
||||
).data;
|
||||
const responseData: API.TimetableHistory.ResponseShort = await this.apiStore.client.get(
|
||||
'api/getTimetables',
|
||||
this.currentQueryParams
|
||||
);
|
||||
|
||||
if (!responseData) {
|
||||
this.dataStatus = Status.Data.Error;
|
||||
|
||||
@@ -40,7 +40,6 @@ import Loading from '../components/Global/Loading.vue';
|
||||
import ProfileSummary from '../components/PlayerProfileView/ProfileSummary.vue';
|
||||
import ProfileRecentStats from '../components/PlayerProfileView/ProfileRecentStats.vue';
|
||||
import ProfileHistoryList from '../components/PlayerProfileView/ProfileHistoryList.vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
@@ -71,29 +70,28 @@ onDeactivated(() => {
|
||||
});
|
||||
|
||||
async function fetchPlayerInfo(playerId: number) {
|
||||
return apiStore.client!.get<API.PlayerInfo.Data>('api/getPlayerInfo', {
|
||||
params: {
|
||||
playerId
|
||||
}
|
||||
return apiStore.client.get<API.PlayerInfo.Data>('api/getPlayerInfo', {
|
||||
playerId
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchPlayerJournal(playerId: number) {
|
||||
return apiStore.client!.get<API.PlayerJournal.Data>('api/getPlayerJournal', {
|
||||
params: {
|
||||
playerId,
|
||||
dateScope: '30d'
|
||||
}
|
||||
return apiStore.client.get<API.PlayerJournal.Data>('api/getPlayerJournal', {
|
||||
playerId,
|
||||
dateScope: '30d'
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchPlayerTd2Info(playerName: string) {
|
||||
return axios.get<Td2API.UsersInfoByName.Response>('https://api.td2.info.pl', {
|
||||
params: {
|
||||
method: 'getUsersInfoByName',
|
||||
name: playerName
|
||||
}
|
||||
});
|
||||
async function fetchPlayerTd2Info(playerName: string): Promise<Td2API.UsersInfoByName.Response> {
|
||||
const response = await fetch(
|
||||
`https://api.td2.info.pl?method=getUsersInfoByName&name=${playerName}`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('fetchPlayerTd2Info: could not fetch data');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function fetchPlayerData() {
|
||||
@@ -116,23 +114,21 @@ async function fetchPlayerData() {
|
||||
const playerInfoResp = await fetchPlayerInfo(playerId.value);
|
||||
|
||||
playerName.value =
|
||||
playerInfoResp.data.driverStats.driverName ||
|
||||
playerInfoResp.data.dispatcherStats.dispatcherName ||
|
||||
'';
|
||||
playerInfoResp.driverStats.driverName || playerInfoResp.dispatcherStats.dispatcherName || '';
|
||||
|
||||
if (!playerName.value) {
|
||||
router.push('/');
|
||||
return;
|
||||
}
|
||||
|
||||
playerInfo.value = playerName.value ? playerInfoResp.data : undefined;
|
||||
playerInfo.value = playerName.value ? playerInfoResp : undefined;
|
||||
playerInfoStatus.value = Status.Data.Loaded;
|
||||
|
||||
if (playerName.value) {
|
||||
const playerTD2InfoResp = await fetchPlayerTd2Info(playerName.value);
|
||||
|
||||
if (playerTD2InfoResp.data.success && playerTD2InfoResp.data.message.length == 1) {
|
||||
playerTD2Info.value = playerTD2InfoResp.data.message[0];
|
||||
if (playerTD2InfoResp.success && playerTD2InfoResp.message.length == 1) {
|
||||
playerTD2Info.value = playerTD2InfoResp.message[0];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -144,7 +140,7 @@ async function fetchPlayerData() {
|
||||
try {
|
||||
const playerJournalResp = await fetchPlayerJournal(playerId.value);
|
||||
|
||||
playerJournal.value = playerJournalResp.data;
|
||||
playerJournal.value = playerJournalResp;
|
||||
playerJournalStatus.value = Status.Data.Loaded;
|
||||
} catch (error) {
|
||||
playerJournal.value = undefined;
|
||||
|
||||
@@ -58,6 +58,7 @@ import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatch
|
||||
|
||||
import { useApiStore } from '../store/apiStore';
|
||||
import { Status } from '../typings/common';
|
||||
import SceneryTopList from '../components/SceneryView/SceneryTopList.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -89,6 +90,10 @@ const viewModes = [
|
||||
{
|
||||
id: 'scenery.option-dispatchers-history',
|
||||
component: SceneryDispatchersHistory
|
||||
},
|
||||
{
|
||||
id: 'scenery.option-top-list',
|
||||
component: SceneryTopList
|
||||
}
|
||||
];
|
||||
|
||||
@@ -184,7 +189,7 @@ function setViewMode(componentName: string) {
|
||||
|
||||
background-color: #181818;
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.scenery-left {
|
||||
|
||||
@@ -116,13 +116,10 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
@use '../styles/responsive';
|
||||
|
||||
.trains-view {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trains_wrapper {
|
||||
margin: 1rem auto;
|
||||
max-width: var(--max-container-width);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.trains_topbar {
|
||||
@@ -130,7 +127,6 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
position: relative;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"verbatimModuleSyntax": null,
|
||||
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
+5
-18
@@ -1,24 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"types": ["vite/client", "vite-plugin-pwa/client"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -1,9 +1,12 @@
|
||||
// TSConfig for modules that run in Node.js environment via either transpilation or type-stripping.
|
||||
{
|
||||
"extends": "@tsconfig/node24/tsconfig.json",
|
||||
"include": ["vite.config.*", "eslint.config.*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
"module": "preserve",
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["node", "vite/client", "vite-plugin-pwa/client"],
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,7 +1,7 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export default defineConfig({
|
||||
server: { port: 5123, open: false },
|
||||
@@ -14,7 +14,7 @@ export default defineConfig({
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
@@ -29,10 +29,13 @@ export default defineConfig({
|
||||
{
|
||||
urlPattern:
|
||||
/^https:\/\/stacjownik.spythere.eu\/api\/(getVehiclesData|getDonators|getSceneries)/i,
|
||||
handler: 'CacheFirst',
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'stacjownik-api-cache',
|
||||
cacheableResponse: { statuses: [0, 200] }
|
||||
cacheableResponse: { statuses: [0, 200] },
|
||||
expiration: {
|
||||
maxAgeSeconds: 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user