Compare commits

..

13 Commits

14 changed files with 1542 additions and 8264 deletions
-6962
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.30.4", "version": "1.30.5",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -17,7 +17,7 @@
}, },
"dependencies": { "dependencies": {
"core-js": "^3.42.0", "core-js": "^3.42.0",
"dotenv": "^16.5.0", "dotenv": "^17.2.2",
"pinia": "^3.0.2", "pinia": "^3.0.2",
"sass": "^1.87.0", "sass": "^1.87.0",
"showdown": "^2.1.0", "showdown": "^2.1.0",
@@ -26,17 +26,17 @@
"vue-router": "^4.4.0" "vue-router": "^4.4.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.15.15", "@types/node": "^24.3.1",
"@types/showdown": "^2.0.6", "@types/showdown": "^2.0.6",
"@vite-pwa/assets-generator": "^1.0.0", "@vite-pwa/assets-generator": "^1.0.0",
"@vitejs/plugin-vue": "^5.1.0", "@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.8.1",
"axios": "^1.9.0", "axios": "^1.9.0",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"vite": "^6.3.5", "vite": "^7.1.4",
"vite-plugin-pwa": "^1.0.0", "vite-plugin-pwa": "^1.0.0",
"vue-tsc": "^2.0.28" "vue-tsc": "^3.0.6"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
@@ -43,8 +43,12 @@
<span :class="{ 'no-catenary': !route.isElectric, internal: route.isInternal }"> <span :class="{ 'no-catenary': !route.isElectric, internal: route.isInternal }">
{{ route.routeName }} {{ route.routeName }}
</span> </span>
<span v-if="route.routeSpeed" class="speed">{{ route.routeSpeed }}</span> <span v-if="route.routeSpeed" class="speed">
<span v-if="route.routeSpeedExit" class="speed">| {{ route.routeSpeedExit }}</span> <span>{{ route.routeSpeed }}</span>
<span v-if="route.routeSpeedExit && route.routeSpeedExit != route.routeSpeed">
| {{ route.routeSpeedExit }}
</span>
</span>
<span v-if="route.routeLength" class="length"> <span v-if="route.routeLength" class="length">
{{ (route.routeLength / 1000).toFixed(1) + 'km' }} {{ (route.routeLength / 1000).toFixed(1) + 'km' }}
</span> </span>
@@ -156,7 +160,7 @@ ul.routes-list {
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
span { & > span {
padding: 0.2em; padding: 0.2em;
background-color: #007599; background-color: #007599;
font-weight: bold; font-weight: bold;
@@ -87,7 +87,8 @@ export default defineComponent({
const stop = train.timetableData?.followingStops.find( const stop = train.timetableData?.followingStops.find(
(stop) => (stop) =>
stop.stopNameRAW.toLowerCase() == name.toLowerCase() || stop.stopNameRAW.toLowerCase() == name.toLowerCase() ||
this.station?.generalInfo?.checkpoints.includes(stop.stopNameRAW) this.station?.generalInfo?.checkpoints.includes(stop.stopNameRAW) ||
this.onlineScenery?.missingCheckpoints.includes(stop.stopNameRAW)
); );
const sceneryName = const sceneryName =
+32 -10
View File
@@ -54,6 +54,18 @@
> >
</template> </template>
</div> </div>
<div class="timetable-checkpoints" v-else-if="onlineScenery">
<template v-for="(ch, i) in onlineScenery.missingCheckpoints" :key="i">
<template v-if="i > 0">&bull;</template>
<router-link
class="checkpoint-item"
:class="{ current: chosenCheckpoint === ch }"
:to="`/scenery?station=${onlineScenery.name}&checkpoint=${ch}`"
>{{ ch }}</router-link
>
</template>
</div>
</div> </div>
<div class="timetable-list"> <div class="timetable-list">
@@ -287,6 +299,7 @@ export default defineComponent({
const chosenCheckpoint = ref( const chosenCheckpoint = ref(
props.station?.generalInfo?.checkpoints[0] ?? props.station?.generalInfo?.checkpoints[0] ??
props.onlineScenery?.missingCheckpoints[0] ??
props.station?.name ?? props.station?.name ??
route.query['station']?.toString() ?? route.query['station']?.toString() ??
'' ''
@@ -365,21 +378,30 @@ export default defineComponent({
methods: { methods: {
loadSelectedOption() { loadSelectedOption() {
if (!this.station) return;
if (!this.station.generalInfo) {
this.chosenCheckpoint = this.station.name;
return;
}
const queryCheckpoint = this.$route.query['checkpoint']?.toString(); const queryCheckpoint = this.$route.query['checkpoint']?.toString();
let checkpointsListRef: string[] | null = null;
let sceneryName = '';
if (this.station && this.station.generalInfo) {
checkpointsListRef = this.station.generalInfo.checkpoints;
sceneryName = this.station.name;
} else if (this.onlineScenery) {
checkpointsListRef = this.onlineScenery.missingCheckpoints;
sceneryName = this.onlineScenery.name;
} else if (this.station) {
this.chosenCheckpoint = this.station.name;
sceneryName = this.station.name;
}
if (checkpointsListRef) {
this.chosenCheckpoint = this.chosenCheckpoint =
this.station.generalInfo.checkpoints.find( checkpointsListRef.find(
(ch) => ch.toLocaleLowerCase() === queryCheckpoint?.toLocaleLowerCase() (ch) => ch.toLocaleLowerCase() === queryCheckpoint?.toLocaleLowerCase()
) ?? ) ??
this.station.generalInfo.checkpoints[0] ?? checkpointsListRef[0] ??
this.station.name; sceneryName;
}
}, },
setCheckpoint(cp: string) { setCheckpoint(cp: string) {
+2 -1
View File
@@ -111,7 +111,7 @@
</div> </div>
<div <div
v-if="!train.online && train.lastSeen >= Date.now() - 60000" v-if="!train.online && train.lastSeen <= Date.now() - 60000"
class="train-badge offline" class="train-badge offline"
> >
<i class="fa-solid fa-user-slash"></i> <i class="fa-solid fa-user-slash"></i>
@@ -397,6 +397,7 @@ export default defineComponent({
.status-badges { .status-badges {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin-left: 0.25em;
gap: 0.25em; gap: 0.25em;
@@ -43,6 +43,8 @@
id="search-active-driver" id="search-active-driver"
:placeholder="$t(`options.search-driver`)" :placeholder="$t(`options.search-driver`)"
v-model="searchedDriver" v-model="searchedDriver"
@focus="preventKeyDown = true"
@blur="preventKeyDown = false"
/> />
<button class="btn btn--action search-exit" @click="onInputClear('driver')"> <button class="btn btn--action search-exit" @click="onInputClear('driver')">
+30 -19
View File
@@ -12,8 +12,16 @@
:data-delayed="stop.departureDelay > 0" :data-delayed="stop.departureDelay > 0"
:data-stop-type="stop.type" :data-stop-type="stop.type"
:data-is-active="stop.isActive" :data-is-active="stop.isActive"
:data-track-count-departure="stop.departureLineInfo?.routeTracks ?? 2" :data-track-count-departure="
:data-track-count-arrival="stop.arrivalLineInfo?.routeTracks ?? 2" stop.departureLineInfo?.routeTracks ??
stop.nextPointRef?.arrivalLineInfo?.routeTracks ??
2
"
:data-track-count-arrival="
stop.arrivalLineInfo?.routeTracks ??
scheduleStops[i - 1]?.departureLineInfo?.routeTracks ??
2
"
> >
<span class="stop_info"> <span class="stop_info">
<span class="distance"> <span class="distance">
@@ -60,7 +68,8 @@
<span> <span>
| |
{{ {{
stop.departureLineInfo.routeSpeedExit stop.departureLineInfo.routeSpeedExit &&
stop.departureLineInfo.routeSpeedExit != stop.departureLineInfo.routeSpeed
? `${stop.departureLineInfo.routeSpeedExit} (${stop.departureLineInfo.routeSpeed})` ? `${stop.departureLineInfo.routeSpeedExit} (${stop.departureLineInfo.routeSpeed})`
: stop.departureLineInfo.routeSpeed : stop.departureLineInfo.routeSpeed
}}</span }}</span
@@ -113,10 +122,16 @@
<span> {{ stop.nextPointRef.arrivalLine }}</span> <span> {{ stop.nextPointRef.arrivalLine }}</span>
<span v-if="stop.nextPointRef.arrivalLineInfo"> <span v-if="stop.nextPointRef.arrivalLineInfo">
<span> | {{ stop.nextPointRef.arrivalLineInfo!.routeSpeed }}</span> <span> | {{ stop.nextPointRef.arrivalLineInfo.routeSpeed }}</span>
<span v-if="stop.nextPointRef.arrivalLineInfo!.routeSpeedExit" <span
>({{ stop.nextPointRef.arrivalLineInfo!.routeSpeedExit }})</span v-if="
stop.nextPointRef.arrivalLineInfo.routeSpeedExit &&
stop.nextPointRef.arrivalLineInfo.routeSpeedExit !=
stop.nextPointRef.arrivalLineInfo.routeSpeed
"
> >
({{ stop.nextPointRef.arrivalLineInfo.routeSpeedExit }})
</span>
<img <img
:src=" :src="
@@ -186,26 +201,28 @@ export default defineComponent({
const sceneryData = const sceneryData =
this.store.stationList?.find((sc) => sc.name == pathEl.stationName) ?? null; this.store.stationList?.find((sc) => sc.name == pathEl.stationName) ?? null;
if (!sceneryData || !sceneryData.generalInfo) return null;
const activeScenery = this.apiStore.activeData?.activeSceneries?.find( const activeScenery = this.apiStore.activeData?.activeSceneries?.find(
(sc) => sc.stationName == pathEl.stationName (sc) => sc.stationName == pathEl.stationName
); );
const arrivalLineData = pathEl.arrivalRouteExt const arrivalLineData = sceneryData?.generalInfo
? pathEl.arrivalRouteExt
? (sceneryData.generalInfo.routes.all.find( ? (sceneryData.generalInfo.routes.all.find(
(rt) => rt.routeName == pathEl.arrivalRouteExt (rt) => rt.routeName == pathEl.arrivalRouteExt
) ?? null) ) ?? null)
: null
: null; : null;
const departureLineData = pathEl.departureRouteExt const departureLineData = sceneryData?.generalInfo
? pathEl.departureRouteExt
? (sceneryData.generalInfo.routes.all.find( ? (sceneryData.generalInfo.routes.all.find(
(rt) => rt.routeName == pathEl.departureRouteExt (rt) => rt.routeName == pathEl.departureRouteExt
) ?? null) ) ?? null)
: null
: null; : null;
return { return {
generalInfo: sceneryData.generalInfo, generalInfo: sceneryData?.generalInfo ?? null,
isOnline: isOnline:
activeScenery && activeScenery &&
(activeScenery.isOnline == 1 || activeScenery.lastSeen >= Date.now() - 60000), (activeScenery.isOnline == 1 || activeScenery.lastSeen >= Date.now() - 60000),
@@ -234,7 +251,7 @@ export default defineComponent({
let isActive = false; let isActive = false;
if (pathData?.departureLineData) { if (pathData?.departureLineData) {
// arrivalLineInfo = pathData.departureLineData; arrivalLineInfo = pathData.departureLineData;
departureLineInfo = pathData.departureLineData; departureLineInfo = pathData.departureLineData;
} }
@@ -245,22 +262,16 @@ export default defineComponent({
isExternal = true; isExternal = true;
departureLineInfo = pathData?.arrivalLineData ?? null; departureLineInfo = pathData?.arrivalLineData ?? null;
if (pathData?.arrivalLineData) {
arrivalLineInfo = pathData.arrivalLineData; arrivalLineInfo = pathData.arrivalLineData;
} }
}
let correctedDepartureLineData: StationRoutesInfo | null = null;
const internalRouteInfo = stop.departureLine const internalRouteInfo = stop.departureLine
? pathData?.generalInfo.routes.all.find( ? pathData?.generalInfo?.routes.all.find(
(route) => route.isInternal && route.routeName == stop.departureLine (route) => route.isInternal && route.routeName == stop.departureLine
) )
: undefined; : undefined;
if (internalRouteInfo) { if (internalRouteInfo) {
correctedDepartureLineData = internalRouteInfo;
departureLineInfo = internalRouteInfo; departureLineInfo = internalRouteInfo;
} }
+4 -4
View File
@@ -142,7 +142,7 @@
"title": "Control type", "title": "Control type",
"SPK": "SPK", "SPK": "SPK",
"SCS": "SCS", "SCS": "SCS",
"SCS-SPK": "SCS/SPK", "SCS-SPK": "SCS + SPK",
"SPE": "SPE", "SPE": "SPE",
"ręczne": "manual", "ręczne": "manual",
"ręczne+SPK": "manual + SPK", "ręczne+SPK": "manual + SPK",
@@ -153,7 +153,7 @@
"abbrevs": { "abbrevs": {
"SPK": "SPK", "SPK": "SPK",
"SCS": "SCS", "SCS": "SCS",
"SCS-SPK": "S/S", "SCS-SPK": "S+S",
"SPE": "SPE", "SPE": "SPE",
"ręczne": "R", "ręczne": "R",
"ręczne+SPK": "R", "ręczne+SPK": "R",
@@ -343,8 +343,8 @@
"ASDEK": "ASDEK program available (defect detection of moving rolling stock)", "ASDEK": "ASDEK program available (defect detection of moving rolling stock)",
"TWB-all": "This scenery has two-way route blockade on all routes", "TWB-all": "This scenery has two-way route blockade on all routes",
"TWB-routes": "This scenery has two-way route blockade on following routes: ", "TWB-routes": "This scenery has two-way route blockade on following routes: ",
"default": "Scenery available in game package", "default": "Scenery available in the game package",
"nonDefault": "Sceneria available to download from forum site", "nonDefault": "Scenery available to download from the forum site",
"req-level": "all dispatcher levels | requries {lvl} dispatcher lvl | requires {lvl} dispatcher lvl", "req-level": "all dispatcher levels | requries {lvl} dispatcher lvl | requires {lvl} dispatcher lvl",
"non-public": "Non-public scenery", "non-public": "Non-public scenery",
"unavailable": "Unavailable scenery", "unavailable": "Unavailable scenery",
+3 -3
View File
@@ -139,7 +139,7 @@
"title": "Sterowanie", "title": "Sterowanie",
"SPK": "SPK", "SPK": "SPK",
"SCS": "SCS", "SCS": "SCS",
"SCS-SPK": "SCS/SPK", "SCS-SPK": "SCS + SPK",
"SPE": "SPE", "SPE": "SPE",
"ręczne": "ręczne", "ręczne": "ręczne",
"ręczne+SPK": "ręczne z SPK", "ręczne+SPK": "ręczne z SPK",
@@ -150,7 +150,7 @@
"abbrevs": { "abbrevs": {
"SPK": "SPK", "SPK": "SPK",
"SCS": "SCS", "SCS": "SCS",
"SCS-SPK": "S/S", "SCS-SPK": "S+S",
"SPE": "SPE", "SPE": "SPE",
"ręczne": "R", "ręczne": "R",
"ręczne+SPK": "R", "ręczne+SPK": "R",
@@ -340,7 +340,7 @@
"SUP": "Wymaga programu SUP do kontroli systemu RASP-UZK", "SUP": "Wymaga programu SUP do kontroli systemu RASP-UZK",
"ASDEK": "Dostępny program ASDEK do detekcji stanów awaryjnych taboru w ruchu", "ASDEK": "Dostępny program ASDEK do detekcji stanów awaryjnych taboru w ruchu",
"default": "Sceneria dostępna domyślnie w paczce z grą", "default": "Sceneria dostępna domyślnie w paczce z grą",
"nonDefault": "Sceneria dostępna do pobrania ze strony forum", "nonDefault": "Sceneria dostępna do pobrania z forum symulatora",
"req-level": "ogólnodostępna | od {lvl} poz. DR | od {lvl} poz. DR", "req-level": "ogólnodostępna | od {lvl} poz. DR | od {lvl} poz. DR",
"non-public": "Sceneria niepubliczna", "non-public": "Sceneria niepubliczna",
"unavailable": "Sceneria niedostępna", "unavailable": "Sceneria niedostępna",
+50 -6
View File
@@ -13,6 +13,7 @@ import { useApiStore } from './apiStore';
import { MainStoreState } from './typings'; import { MainStoreState } from './typings';
const checkpointsTrains: Map<string, CheckpointTrain[]> = new Map(); const checkpointsTrains: Map<string, CheckpointTrain[]> = new Map();
const unknownSceneryCheckpoints: Map<string, Set<string>> = new Map();
const sceneriesTrains: Map<string, Train[]> = new Map(); const sceneriesTrains: Map<string, Train[]> = new Map();
export const useMainStore = defineStore('mainStore', { export const useMainStore = defineStore('mainStore', {
@@ -42,6 +43,7 @@ export const useMainStore = defineStore('mainStore', {
checkpointsTrains.clear(); checkpointsTrains.clear();
sceneriesTrains.clear(); sceneriesTrains.clear();
unknownSceneryCheckpoints.clear();
const dateNow = new Date(); const dateNow = new Date();
@@ -133,8 +135,13 @@ export const useMainStore = defineStore('mainStore', {
// Checkpoints trains map // Checkpoints trains map
if (trainObj.timetableData) { if (trainObj.timetableData) {
let currentSceneryIndex = 0;
const timetablePath = trainObj.timetableData.timetablePath; const timetablePath = trainObj.timetableData.timetablePath;
let currentSceneryIndex = 0;
let currentSceneryData: Station | null =
this.stationList.find(
(s) => s.name == timetablePath[currentSceneryIndex].stationName
) ?? null;
trainObj.timetableData.followingStops.forEach((stop, i) => { trainObj.timetableData.followingStops.forEach((stop, i) => {
if (/strong|podg|pe/.test(stop.stopName)) { if (/strong|podg|pe/.test(stop.stopName)) {
@@ -153,16 +160,41 @@ export const useMainStore = defineStore('mainStore', {
timetablePathElement: timetablePath[currentSceneryIndex] timetablePathElement: timetablePath[currentSceneryIndex]
}; };
// Adding missing sceneries checkpoints as a fallback when scenery data is missing (and "generalInfo" is unavailable)
if (!currentSceneryData) {
const sceneryCheckpointsSet = unknownSceneryCheckpoints.get(
checkpointTrain.timetablePathElement.stationName
);
if (!sceneryCheckpointsSet) {
unknownSceneryCheckpoints.set(
checkpointTrain.timetablePathElement.stationName,
new Set([stop.stopNameRAW])
);
} else {
sceneryCheckpointsSet.add(stop.stopNameRAW);
}
}
// Adding trains to their corresponding checkpoints
if (checkpointsTrains.has(stop.stopNameRAW.toLowerCase())) { if (checkpointsTrains.has(stop.stopNameRAW.toLowerCase())) {
checkpointsTrains.set(stop.stopNameRAW.toLowerCase(), [ checkpointsTrains.set(stop.stopNameRAW.toLowerCase(), [
...checkpointsTrains.get(stop.stopNameRAW.toLowerCase())!, ...checkpointsTrains.get(stop.stopNameRAW.toLowerCase())!,
checkpointTrain checkpointTrain
]); ]);
} else checkpointsTrains.set(stop.stopNameRAW.toLowerCase(), [checkpointTrain]); } else {
checkpointsTrains.set(stop.stopNameRAW.toLowerCase(), [checkpointTrain]);
}
} }
if (timetablePath[currentSceneryIndex].departureRouteExt == stop.departureLine) if (timetablePath[currentSceneryIndex].departureRouteExt == stop.departureLine) {
currentSceneryIndex++; currentSceneryIndex++;
currentSceneryData =
this.stationList.find(
(s) => s.name == timetablePath[currentSceneryIndex].stationName
) ?? null;
}
}); });
} }
@@ -222,7 +254,9 @@ export const useMainStore = defineStore('mainStore', {
all: 0, all: 0,
confirmed: 0, confirmed: 0,
unconfirmed: 0 unconfirmed: 0
} },
missingCheckpoints: []
}); });
}); });
@@ -266,7 +300,9 @@ export const useMainStore = defineStore('mainStore', {
all: 0, all: 0,
confirmed: 0, confirmed: 0,
unconfirmed: 0 unconfirmed: 0
} },
missingCheckpoints: []
}); });
return list; return list;
@@ -277,7 +313,7 @@ export const useMainStore = defineStore('mainStore', {
for (let i = 0, n = allActiveSceneries.length; i < n; i++) { for (let i = 0, n = allActiveSceneries.length; i < n; i++) {
const scenery = allActiveSceneries[i]; const scenery = allActiveSceneries[i];
const station = this.stationList.find((s) => s.name === scenery.name); let station = this.stationList.find((s) => s.name === scenery.name);
let checkpointsSet: Set<string> = new Set(); let checkpointsSet: Set<string> = new Set();
@@ -293,6 +329,14 @@ export const useMainStore = defineStore('mainStore', {
scenery.stationTrains = scenery.stationTrains =
sceneriesTrains.get(scenery.name)?.filter((sc) => sc.region == this.region.id) ?? []; sceneriesTrains.get(scenery.name)?.filter((sc) => sc.region == this.region.id) ?? [];
// Missing checkpoints as a fallback for sceneries without generalInfo & checkpoints property
const missingCheckpointsToAdd = unknownSceneryCheckpoints.get(scenery.name);
if (missingCheckpointsToAdd) {
checkpoints.push(...missingCheckpointsToAdd);
scenery.missingCheckpoints.push(...missingCheckpointsToAdd);
}
const uniqueTrainIds: string[] = []; const uniqueTrainIds: string[] = [];
checkpoints.forEach((cp) => { checkpoints.forEach((cp) => {
const scheduledTrains = checkpointsTrains.get(cp.toLowerCase()); const scheduledTrains = checkpointsTrains.get(cp.toLowerCase());
+1 -1
View File
@@ -36,6 +36,6 @@
} }
&.SCS-SPK { &.SCS-SPK {
color: white; color: #aefff8;
} }
} }
+1
View File
@@ -170,6 +170,7 @@ export interface ActiveScenery {
confirmed: number; confirmed: number;
unconfirmed: number; unconfirmed: number;
}; };
missingCheckpoints: string[];
} }
export interface ScenerySpawn { export interface ScenerySpawn {
+1391 -1237
View File
File diff suppressed because it is too large Load Diff